1/*
2 * Copyright (c) 2003-2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * security.c
24 */
25
26#include "security.h"
27
28#include "leaks.h"
29#include "readline.h"
30
31#include "cmsutil.h"
32#include "db_commands.h"
33#include "keychain_add.h"
34#include "keychain_create.h"
35#include "keychain_delete.h"
36#include "keychain_list.h"
37#include "keychain_lock.h"
38#include "keychain_set_settings.h"
39#include "keychain_show_info.h"
40#include "keychain_unlock.h"
41#include "keychain_recode.h"
42#include "key_create.h"
43#include "keychain_find.h"
44#include "keychain_import.h"
45#include "keychain_export.h"
46#include "identity_find.h"
47#include "identity_prefs.h"
48#include "mds_install.h"
49#include "trusted_cert_add.h"
50#include "trusted_cert_dump.h"
51#include "user_trust_enable.h"
52#include "trust_settings_impexp.h"
53#include "verify_cert.h"
54#include "authz.h"
55#include "display_error_code.h"
56#include "createFVMaster.h"
57
58#include <ctype.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <unistd.h>
63#include <dispatch/dispatch.h>
64
65#include <CoreFoundation/CFRunLoop.h>
66#include <Security/SecBasePriv.h>
67#include <Security/SecKeychainPriv.h>
68#include <security_asn1/secerr.h>
69
70/* Maximum length of an input line in interactive mode. */
71#define MAX_LINE_LEN 4096
72/* Maximum number of arguments on an input line in interactive mode. */
73#define MAX_ARGS 32
74
75/* Entry in commands array for a command. */
76typedef struct command
77{
78	const char *c_name;    /* name of the command. */
79	command_func c_func;   /* function to execute the command. */
80	const char *c_usage;   /* usage sting for command. */
81	const char *c_help;    /* help string for (or description of) command. */
82} command;
83
84/* The default prompt. */
85const char *prompt_string = "security> ";
86
87/* The name of this program. */
88const char *prog_name;
89
90
91/* Forward declarations of static functions. */
92static int help(int argc, char * const *argv);
93
94/*
95 * The command array itself.
96 * Add commands here at will.
97 * Matching is done on a prefix basis.  The first command in the array
98 * gets matched first.
99 */
100const command commands[] =
101{
102	{ "help", help,
103	  "[command ...]",
104	  "Show all commands, or show usage for a command." },
105
106	{ "list-keychains", keychain_list,
107	  "[-d user|system|common|dynamic] [-s [keychain...]]\n"
108	  "    -d  Use the specified preference domain\n"
109	  "    -s  Set the search list to the specified keychains\n"
110	  "With no parameters, display the search list.",
111	  "Display or manipulate the keychain search list." },
112
113	{ "default-keychain", keychain_default,
114	  "[-d user|system|common|dynamic] [-s [keychain]]\n"
115	  "    -d  Use the specified preference domain\n"
116	  "    -s  Set the default keychain to the specified keychain\n"
117	  "With no parameters, display the default keychain.",
118	  "Display or set the default keychain." },
119
120	{ "login-keychain", keychain_login,
121	  "[-d user|system|common|dynamic] [-s [keychain]]\n"
122	  "    -d  Use the specified preference domain\n"
123	  "    -s  Set the login keychain to the specified keychain\n"
124	  "With no parameters, display the login keychain.",
125	  "Display or set the login keychain." },
126
127	{ "create-keychain", keychain_create,
128	  "[-P] [-p password] [keychains...]\n"
129	  "    -p  Use \"password\" as the password for the keychains being created\n"
130	  "    -P  Prompt the user for a password using the SecurityAgent",
131	  "Create keychains and add them to the search list." },
132
133	{ "delete-keychain", keychain_delete,
134	  "[keychains...]",
135	  "Delete keychains and remove them from the search list." },
136
137	{ "lock-keychain", keychain_lock,
138	  "[-a | keychain]\n"
139	  "    -a  Lock all keychains",
140	  "Lock the specified keychain."},
141
142	{ "unlock-keychain", keychain_unlock,
143	  "[-u] [-p password] [keychain]\n"
144	  "    -p  Use \"password\" as the password to unlock the keychain\n"
145	  "    -u  Do not use the password",
146	  "Unlock the specified keychain."},
147
148	{ "set-keychain-settings", keychain_set_settings,
149	  "[-lu] [-t timeout] [keychain]\n"
150	  "    -l  Lock keychain when the system sleeps\n"
151	  "    -u  Lock keychain after timeout interval\n"
152	  "    -t  Timeout in seconds (omitting this option specifies \"no timeout\")\n",
153	  "Set settings for a keychain."},
154
155	{ "set-keychain-password", keychain_set_password,
156	  "[-o oldPassword] [-p newPassword] [keychain]\n"
157	  "    -o  Old keychain password (if not provided, will prompt)\n"
158	  "    -p  New keychain password (if not provided, will prompt)\n",
159	  "Set password for a keychain."},
160
161	{ "show-keychain-info", keychain_show_info,
162	  "[keychain]",
163	  "Show the settings for keychain." },
164
165	{ "dump-keychain", keychain_dump,
166	  "[-adir] [keychain...]\n"
167	  "    -a  Dump access control list of items\n"
168	  "    -d  Dump (decrypted) data of items\n"
169	  "    -i  Interactive access control list editing mode\n"
170	  "    -r  Dump the raw (encrypted) data of items",
171	  "Dump the contents of one or more keychains." },
172
173#ifndef NDEBUG
174	{ "recode-keychain", keychain_recode,
175	  "keychain_to_recode keychain_to_get_secrets_from",
176	  "Recode a keychain to use the secrets from another one."},
177#endif
178
179	{ "create-keypair", key_create_pair,
180	  "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
181	  "    -a  Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
182	  "    -s  Specify the keysize in bits (default 512)\n"
183	  "    -f  Make a key valid from the specified date\n"
184	  "    -t  Make a key valid to the specified date\n"
185	  "    -d  Make a key valid for the number of days specified from today\n"
186	  "    -k  Use the specified keychain rather than the default\n"
187	  "    -A  Allow any application to access this key without warning (insecure, not recommended!)\n"
188	  "    -T  Specify an application which may access this key (multiple -T options are allowed)\n"
189	  "If no options are provided, ask the user interactively.",
190	  "Create an asymmetric key pair." },
191
192	#if 0
193	/* this was added in mb's integration of PR-3420772, but this is an unimplemented command */
194	{ "create-csr", csr_create,
195	  "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
196	  "    -a  Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
197	  "    -s  Specify the keysize in bits (default 512)\n"
198	  "    -f  Make a key valid from the specified date\n"
199	  "    -t  Make a key valid to the specified date\n"
200	  "    -d  Make a key valid for the number of days specified from today\n"
201	  "    -k  Use the specified keychain rather than the default\n"
202	  "    -A  Allow any application to access this key without warning (insecure, not recommended!)\n"
203	  "    -T  Specify an application which may access this key (multiple -T options are allowed)\n"
204	  "If no options are provided, ask the user interactively.",
205	  "Create a certificate signing request." },
206	#endif
207
208	{ "add-generic-password", keychain_add_generic_password,
209	  "[-a account] [-s service] [-w password] [options...] [-A|-T appPath] [keychain]\n"
210	  "    -a  Specify account name (required)\n"
211	  "    -c  Specify item creator (optional four-character code)\n"
212	  "    -C  Specify item type (optional four-character code)\n"
213	  "    -D  Specify kind (default is \"application password\")\n"
214	  "    -G  Specify generic attribute (optional)\n"
215	  "    -j  Specify comment string (optional)\n"
216	  "    -l  Specify label (if omitted, service name is used as default label)\n"
217	  "    -s  Specify service name (required)\n"
218	  "    -p  Specify password to be added (legacy option, equivalent to -w)\n"
219	  "    -w  Specify password to be added\n"
220	  "    -A  Allow any application to access this item without warning (insecure, not recommended!)\n"
221	  "    -T  Specify an application which may access this item (multiple -T options are allowed)\n"
222	  "    -U  Update item if it already exists (if omitted, the item cannot already exist)\n"
223	  "\n"
224	  "By default, the application which creates an item is trusted to access its data without warning.\n"
225	  "You can remove this default access by explicitly specifying an empty app pathname: -T \"\"\n"
226	  "If no keychain is specified, the password is added to the default keychain.",
227	  "Add a generic password item."},
228
229	{ "add-internet-password", keychain_add_internet_password,
230	  "[-a account] [-s server] [-w password] [options...] [-A|-T appPath] [keychain]\n"
231	  "    -a  Specify account name (required)\n"
232	  "    -c  Specify item creator (optional four-character code)\n"
233	  "    -C  Specify item type (optional four-character code)\n"
234	  "    -d  Specify security domain string (optional)\n"
235	  "    -D  Specify kind (default is \"Internet password\")\n"
236	  "    -j  Specify comment string (optional)\n"
237	  "    -l  Specify label (if omitted, server name is used as default label)\n"
238	  "    -p  Specify path string (optional)\n"
239	  "    -P  Specify port number (optional)\n"
240	  "    -r  Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n"
241	  "    -s  Specify server name (required)\n"
242	  "    -t  Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n"
243  	  "    -w  Specify password to be added\n"
244	  "    -A  Allow any application to access this item without warning (insecure, not recommended!)\n"
245	  "    -T  Specify an application which may access this item (multiple -T options are allowed)\n"
246	  "    -U  Update item if it already exists (if omitted, the item cannot already exist)\n"
247	  "\n"
248	  "By default, the application which creates an item is trusted to access its data without warning.\n"
249	  "You can remove this default access by explicitly specifying an empty app pathname: -T \"\"\n"
250	  "If no keychain is specified, the password is added to the default keychain.",
251	  "Add an internet password item."},
252
253	{ "add-certificates", keychain_add_certificates,
254	  "[-k keychain] file...\n"
255	  "If no keychain is specified, the certificates are added to the default keychain.",
256	  "Add certificates to a keychain."},
257
258	{ "find-generic-password", keychain_find_generic_password,
259	  "[-a account] [-s service] [options...] [-g] [keychain...]\n"
260	  "    -a  Match \"account\" string\n"
261	  "    -c  Match \"creator\" (four-character code)\n"
262	  "    -C  Match \"type\" (four-character code)\n"
263	  "    -D  Match \"kind\" string\n"
264	  "    -G  Match \"value\" string (generic attribute)\n"
265	  "    -j  Match \"comment\" string\n"
266	  "    -l  Match \"label\" string\n"
267	  "    -s  Match \"service\" string\n"
268	  "    -g  Display the password for the item found\n"
269	  "    -w  Display only the password on stdout\n"
270	  "If no keychains are specified to search, the default search list is used.",
271	  "Find a generic password item."},
272
273	{ "delete-generic-password", keychain_delete_generic_password,
274		"[-a account] [-s service] [options...] keychain...]\n"
275		"    -a  Match \"account\" string\n"
276		"    -c  Match \"creator\" (four-character code)\n"
277		"    -C  Match \"type\" (four-character code)\n"
278		"    -D  Match \"kind\" string\n"
279		"    -G  Match \"value\" string (generic attribute)\n"
280		"    -j  Match \"comment\" string\n"
281		"    -l  Match \"label\" string\n"
282		"    -s  Match \"service\" string\n"
283		"If no keychains are specified to search, the default search list is used.",
284		"Delete a generic password item."},
285
286	{ "find-internet-password", keychain_find_internet_password,
287	  "[-a account] [-s server] [options...] [-g] [keychain...]\n"
288	  "    -a  Match \"account\" string\n"
289	  "    -c  Match \"creator\" (four-character code)\n"
290	  "    -C  Match \"type\" (four-character code)\n"
291	  "    -d  Match \"securityDomain\" string\n"
292	  "    -D  Match \"kind\" string\n"
293	  "    -j  Match \"comment\" string\n"
294	  "    -l  Match \"label\" string\n"
295	  "    -p  Match \"path\" string\n"
296	  "    -P  Match port number\n"
297	  "    -r  Match \"protocol\" (four-character code)\n"
298	  "    -s  Match \"server\" string\n"
299	  "    -t  Match \"authenticationType\" (four-character code)\n"
300	  "    -g  Display the password for the item found\n"
301	  "    -w  Display only the password on stdout\n"
302	  "If no keychains are specified to search, the default search list is used.",
303	  "Find an internet password item."},
304
305	{ "delete-internet-password", keychain_delete_internet_password,
306		"[-a account] [-s server] [options...] [keychain...]\n"
307		"    -a  Match \"account\" string\n"
308		"    -c  Match \"creator\" (four-character code)\n"
309		"    -C  Match \"type\" (four-character code)\n"
310		"    -d  Match \"securityDomain\" string\n"
311		"    -D  Match \"kind\" string\n"
312		"    -j  Match \"comment\" string\n"
313		"    -l  Match \"label\" string\n"
314		"    -p  Match \"path\" string\n"
315		"    -P  Match port number\n"
316		"    -r  Match \"protocol\" (four-character code)\n"
317		"    -s  Match \"server\" string\n"
318		"    -t  Match \"authenticationType\" (four-character code)\n"
319		"If no keychains are specified to search, the default search list is used.",
320		"Delete an internet password item."},
321
322	{ "find-certificate", keychain_find_certificate,
323	  "[-a] [-c name] [-e emailAddress] [-m] [-p] [-Z] [keychain...]\n"
324	  "    -a  Find all matching certificates, not just the first one\n"
325	  "    -c  Match on \"name\" when searching (optional)\n"
326	  "    -e  Match on \"emailAddress\" when searching (optional)\n"
327	  "    -m  Show the email addresses in the certificate\n"
328	  "    -p  Output certificate in pem format\n"
329	  "    -Z  Print SHA-1 hash of the certificate\n"
330	  "If no keychains are specified to search, the default search list is used.",
331	  "Find a certificate item."},
332
333	{ "find-identity", keychain_find_identity,
334		"[-p policy] [-s string] [-v] [keychain...]\n"
335		"    -p  Specify policy to evaluate (multiple -p options are allowed)\n"
336		"        Supported policies: basic, ssl-client, ssl-server, smime, eap,\n"
337		"        ipsec, ichat, codesigning, sys-default, sys-kerberos-kdc, macappstore, appleID\n"
338		"    -s  Specify optional policy-specific string (e.g. DNS hostname for SSL,\n"
339		"        or RFC822 email address for S/MIME)\n"
340		"    -v  Show valid identities only (default is to show all identities)\n"
341		"If no keychains are specified to search, the default search list is used.",
342	"Find an identity (certificate + private key)."},
343
344	{ "delete-certificate", keychain_delete_certificate,
345	  "[-c name] [-Z hash] [-t] [keychain...]\n"
346	  "    -c  Specify certificate to delete by its common name\n"
347	  "    -Z  Specify certificate to delete by its SHA-1 hash value\n"
348	  "    -t  Also delete user trust settings for this certificate\n"
349	  "The certificate to be deleted must be uniquely specified either by a\n"
350	  "string found in its common name, or by its SHA-1 hash.\n"
351	  "If no keychains are specified to search, the default search list is used.",
352	  "Delete a certificate from a keychain."},
353
354	{ "set-identity-preference", set_identity_preference,
355	  "[-n] [-c identity] [-s service] [-u keyUsage] [-Z hash] [keychain...]\n"
356	  "    -n  Specify no identity (clears existing preference for service)\n"
357	  "    -c  Specify identity by common name of the certificate\n"
358	  "    -s  Specify service (may be a URL, RFC822 email address, DNS host, or\n"
359	  "        other name) for which this identity is to be preferred\n"
360	  "    -u  Specify key usage (optional) - see man page for values\n"
361	  "    -Z  Specify identity by SHA-1 hash of certificate (optional)\n",
362	  "Set the preferred identity to use for a service."},
363
364	{ "get-identity-preference", get_identity_preference,
365		"[-s service] [-u keyUsage] [-p] [-c] [-Z] [keychain...]\n"
366		"    -s  Specify service (may be a URL, RFC822 email address, DNS host, or\n"
367		"        other name)\n"
368		"    -u  Specify key usage (optional) - see man page for values\n"
369		"    -p  Output identity certificate in pem format\n"
370		"    -c  Print common name of the preferred identity certificate\n"
371		"    -Z  Print SHA-1 hash of the preferred identity certificate\n",
372	"Get the preferred identity to use for a service."},
373
374	{ "create-db", db_create,
375	  "[-ao0] [-g dl|cspdl] [-m mode] [name]\n"
376	  "    -a  Turn off autocommit\n"
377	  "    -g  Attach to \"guid\" rather than the AppleFileDL\n"
378	  "    -m  Set the inital mode of the created db to \"mode\"\n"
379	  "    -o  Force using openparams argument\n"
380	  "    -0  Force using version 0 openparams\n"
381	  "If no name is provided, ask the user interactively.",
382	  "Create a db using the DL." },
383
384	{ "export" , keychain_export,
385	  "[-k keychain] [-t type] [-f format] [-w] [-p] [-P passphrase] [-o outfile]\n"
386	  "    -k  keychain to export items from\n"
387	  "    -t  Type = certs|allKeys|pubKeys|privKeys|identities|all  (Default: all)\n"
388	  "    -f  Format = openssl|openssh1|openssh2|bsafe|pkcs7|pkcs8|pkcs12|pemseq|x509\n"
389	  "        ...default format is pemseq for aggregate, openssl for single\n"
390	  "    -w  Private keys are wrapped\n"
391	  "    -p  PEM encode the output\n"
392	  "    -P  Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
393	  "    -o  Specify output file (default is stdout)",
394	  "Export items from a keychain." },
395
396	{ "import", keychain_import,
397	  "inputfile [-k keychain] [-t type] [-f format] [-w] [-P passphrase] [options...]\n"
398	  "    -k  Target keychain to import into\n"
399	  "    -t  Type = pub|priv|session|cert|agg\n"
400	  "    -f  Format = openssl|openssh1|openssh2|bsafe|raw|pkcs7|pkcs8|pkcs12|netscape|pemseq\n"
401	  "    -w  Specify that private keys are wrapped and must be unwrapped on import\n"
402	  "    -x  Specify that private keys are non-extractable after being imported\n"
403	  "    -P  Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
404	  "    -a  Specify name and value of extended attribute (can be used multiple times)\n"
405	  "    -A  Allow any application to access the imported key without warning (insecure, not recommended!)\n"
406	  "    -T  Specify an application which may access the imported key (multiple -T options are allowed)\n",
407	  "Import items into a keychain." },
408
409	{ "cms", cms_util,
410	  "[-C|-D|-E|-S] [<options>]\n"
411	  "  -C           create a CMS encrypted message\n"
412	  "  -D           decode a CMS message\n"
413	  "  -E           create a CMS enveloped message\n"
414	  "  -S           create a CMS signed message\n"
415	  "\n"
416	  "Decoding options:\n"
417	  "  -c content   use this detached content file\n"
418	  "  -h level     generate email headers with info about CMS message\n"
419	  "                 (output level >= 0)\n"
420	  "  -n           suppress output of content\n"
421	  "\n"
422	  "Encoding options:\n"
423	  "  -r id,...    create envelope for these recipients,\n"
424	  "               where id can be a certificate nickname or email address\n"
425	  "  -G           include a signing time attribute\n"
426	  "  -H hash      hash = MD2|MD4|MD5|SHA1|SHA256|SHA384|SHA512 (default: SHA1)\n"
427	  "  -N nick      use certificate named \"nick\" for signing\n"
428	  "  -P           include a SMIMECapabilities attribute\n"
429	  "  -T           do not include content in CMS message\n"
430	  "  -Y nick      include an EncryptionKeyPreference attribute with certificate\n"
431	  "                 (use \"NONE\" to omit)\n"
432	  "  -Z hash      find a certificate by subject key ID\n"
433	  "\n"
434	  "Common options:\n"
435	  "  -e envelope  specify envelope file (valid with -D or -E)\n"
436	  "  -k keychain  specify keychain to use\n"
437	  "  -i infile    use infile as source of data (default: stdin)\n"
438	  "  -o outfile   use outfile as destination of data (default: stdout)\n"
439	  "  -p password  use password as key db password (default: prompt)\n"
440	  "  -s           pass data a single byte at a time to CMS\n"
441	  "  -u certusage set type of certificate usage (default: certUsageEmailSigner)\n"
442	  "  -v           print debugging information\n"
443	  "\n"
444	  "Cert usage codes:\n"
445	  "                  0 - certUsageSSLClient\n"
446	  "                  1 - certUsageSSLServer\n"
447	  "                  2 - certUsageSSLServerWithStepUp\n"
448	  "                  3 - certUsageSSLCA\n"
449	  "                  4 - certUsageEmailSigner\n"
450	  "                  5 - certUsageEmailRecipient\n"
451	  "                  6 - certUsageObjectSigner\n"
452	  "                  7 - certUsageUserCertImport\n"
453	  "                  8 - certUsageVerifyCA\n"
454	  "                  9 - certUsageProtectedObjectSigner\n"
455	  "                 10 - certUsageStatusResponder\n"
456	  "                 11 - certUsageAnyCA",
457	  "Encode or decode CMS messages." },
458
459	{ "install-mds" , mds_install,
460	  "",		/* no options */
461	  "Install (or re-install) the MDS database." },
462
463	{ "add-trusted-cert" , trusted_cert_add,
464	  " [<options>] [certFile]\n"
465	  "    -d                  Add to admin cert store; default is user\n"
466	  "    -r resultType       resultType = trustRoot|trustAsRoot|deny|unspecified;\n"
467	  "                              default is trustRoot\n"
468	  "    -p policy           Specify policy constraint (ssl, smime, codeSign, IPSec, iChat,\n"
469	  "                              basic, swUpdate, pkgSign, pkinitClient, pkinitServer, eap)\n"
470	  "    -a appPath          Specify application constraint\n"
471	  "    -s policyString     Specify policy-specific string\n"
472	  "    -e allowedError     Specify allowed error (certExpired, hostnameMismatch) or integer\n"
473  	  "    -u keyUsage         Specify key usage, an integer\n"
474	  "    -k keychain         Specify keychain to which cert is added\n"
475	  "    -i settingsFileIn   Input trust settings file; default is user domain\n"
476	  "    -o settingsFileOut  Output trust settings file; default is user domain\n"
477	  "    -D                  Add default setting instead of per-cert setting\n"
478	  "    certFile            Certificate(s)",
479	  "Add trusted certificate(s)." },
480
481	{ "remove-trusted-cert" , trusted_cert_remove,
482	  " [-d] [-D] [certFile]\n"
483	  "    -d                  Remove from admin cert store (default is user)\n"
484	  "    -D                  Remove default setting instead of per-cert setting\n"
485	  "    certFile            Certificate(s)",
486	  "Remove trusted certificate(s)." },
487
488	{ "dump-trust-settings" , trusted_cert_dump,
489	  " [-s] [-d]\n"
490	  "    -s                  Display trusted system certs (default is user)\n"
491	  "    -d                  Display trusted admin certs (default is user)\n",
492	  "Display contents of trust settings." },
493
494	{ "user-trust-settings-enable", user_trust_enable,
495	  "[-d] [-e]\n"
496	  "    -d                  Disable user-level trust Settings\n"
497	  "    -e                  Enable user-level trust Settings\n"
498	  "With no parameters, show current enable state of user-level trust settings.",
499	  "Display or manipulate user-level trust settings." },
500
501	{ "trust-settings-export", trust_settings_export,
502	  " [-s] [-d] settings_file\n"
503	  "    -s                  Export system trust settings (default is user)\n"
504	  "    -d                  Export admin trust settings (default is user)\n",
505	  "Export trust settings." },
506
507	{ "trust-settings-import", trust_settings_import,
508	  " [-d] settings_file\n"
509	  "    -d                  Import admin trust settings (default is user)\n",
510	  "Import trust settings." },
511
512	{ "verify-cert" , verify_cert,
513	  " [<options>]\n"
514	  "    -c certFile         Certificate to verify. Can be specified multiple times, leaf first.\n"
515	  "    -r rootCertFile     Root Certificate. Can be specified multiple times.\n"
516	  "    -p policy           Verify Policy (basic, ssl, smime, codeSign, IPSec, iChat, swUpdate,\n"
517	  "                                       pkgSign, pkinitClient, pkinitServer, eap, appleID,\n"
518	  "                                       macappstore, timestamping); default is basic.\n"
519	  "    -k keychain         Keychain. Can be called multiple times. Default is default search list.\n"
520	  "    -n                  No keychain search list.\n"
521	  "    -L                  Local certificates only (do not try to fetch missing CA certs from net).\n"
522	  "    -l                  Leaf cert is a CA (normally an error, unless this option is given).\n"
523	  "    -e emailAddress     Email address for smime policy.\n"
524	  "    -s sslHost          SSL host name for ssl policy.\n"
525	  "    -q                  Quiet.\n",
526	  "Verify certificate(s)." },
527
528	{ "authorize" , authorize,
529	  "[<options>] <right(s)...>\n"
530	  "  -u        Allow user interaction.\n"
531	  "  -c        Use login name and prompt for password.\n"
532	  "  -C login  Use given login name and prompt for password.\n"
533	  "  -x        Do NOT share -c/-C explicit credentials\n"
534#ifndef NDEBUG
535	  "  -E        Don't extend rights.\n"
536#endif
537	  "  -p        Allow returning partial rights.\n"
538	  "  -d        Destroy acquired rights.\n"
539	  "  -P        Pre-authorize rights only.\n"
540	  "  -l        Operate authorizations in least privileged mode.\n"
541	  "  -i        Internalize authref passed on stdin.\n"
542	  "  -e        Externalize authref to stdout.\n"
543	  "  -w        Wait until stdout is closed (to allow reading authref from pipe).\n"
544	  "Extend rights flag is passed per default.",
545	  "Perform authorization operations." },
546
547	{ "authorizationdb" , authorizationdb,
548	  "read <right-name>\n"
549	  "       authorizationdb remove <right-name>\n"
550	  "       authorizationdb write <right-name> [allow|deny|<rulename>]\n"
551	  "If no rulename is specified, write will read a plist from stdin.\n"
552	  "       authorizationdb merge source [destination]\n"
553	  "If no destination path is specified, merge will merge to /etc/authorization.\n"
554      "       authorizationdb smartcard <enable|disable|status>\n"
555      "Enables/disables smartcard login support or report current status.",
556      "Make changes to the authorization policy database.\n" },
557
558	{ "execute-with-privileges" , execute_with_privileges,
559	  "<program> [args...]\n"
560	  "On success, stdin will be read and forwarded to the tool.",
561	  "Execute tool with privileges." },
562
563	{ "leaks", leaks,
564	  "[-cycles] [-nocontext] [-nostacks] [-exclude symbol]\n"
565	  "    -cycles       Use a stricter algorithm (\"man leaks\" for details)\n"
566	  "    -nocontext    Withhold hex dumps of the leaked memory\n"
567	  "    -nostacks     Don't show stack traces of leaked memory\n"
568	  "    -exclude      Ignore leaks called from \"symbol\"\n"
569	  "(Set the environment variable MallocStackLogging to get symbolic traces.)",
570	  "Run /usr/bin/leaks on this process." },
571
572	{ "error", display_error_code,
573	  "<error code(s)...>\n"
574	  "Display an error string for the given security-related error code.\n"
575	  "The error can be in decimal or hex, e.g. 1234 or 0x1234. Multiple "
576	  "errors can be separated by spaces.",
577	  "Display a descriptive message for the given error code(s)." },
578
579	{ "create-filevaultmaster-keychain", keychain_createMFV,
580	  "[-p password] [keychain name]\n"
581	  "    -p       Use \"password\" as the password for the keychain being created\n"
582      "    -s  Specify the keysize in bits (default 2048; 1024 & 4096 are allowed)\n"
583	  "By default the keychain will be created in ~/Library/Keychains/\n",
584      "Create a keychain containing a key pair for FileVault recovery use."
585      },
586
587	{}
588};
589
590/* Global variables. */
591int do_quiet = 0;
592int do_verbose = 0;
593
594/* Return 1 if name matches command. */
595static int
596match_command(const char *command, const char *name)
597{
598	return !strncmp(command, name, strlen(name));
599}
600
601/* The help command. */
602static int
603help(int argc, char * const *argv)
604{
605	const command *c;
606
607	if (argc > 1)
608	{
609		char * const *arg;
610		for (arg = argv + 1; *arg; ++arg)
611		{
612			int found = 0;
613
614			for (c = commands; c->c_name; ++c)
615			{
616				if (match_command(c->c_name, *arg))
617				{
618					found = 1;
619					break;
620				}
621			}
622
623			if (found)
624				printf("Usage: %s %s\n", c->c_name, c->c_usage);
625			else
626			{
627				sec_error("%s: no such command: %s", argv[0], *arg);
628				return 1;
629			}
630		}
631	}
632	else
633	{
634		for (c = commands; c->c_name; ++c)
635			printf("    %-17s %s\n", c->c_name, c->c_help);
636	}
637
638	return 0;
639}
640
641/* States for split_line parser. */
642typedef enum
643{
644	SKIP_WS,
645	READ_ARG,
646	READ_ARG_ESCAPED,
647	QUOTED_ARG,
648	QUOTED_ARG_ESCAPED
649} parse_state;
650
651/* Split a line into multiple arguments and return them in *pargc and *pargv. */
652static void
653split_line(char *line, int *pargc, char * const **pargv)
654{
655	static char *argvec[MAX_ARGS + 1];
656	int argc = 0;
657	char *ptr = line;
658	char *dst = line;
659	parse_state state = SKIP_WS;
660	int quote_ch = 0;
661
662	for (ptr = line; *ptr; ++ptr)
663	{
664		if (state == SKIP_WS)
665		{
666			if (isspace(*ptr))
667				continue;
668
669			if (*ptr == '"' || *ptr == '\'')
670			{
671				quote_ch = *ptr;
672				state = QUOTED_ARG;
673				argvec[argc] = dst;
674				continue; /* Skip the quote. */
675			}
676			else
677			{
678				state = READ_ARG;
679				argvec[argc] = dst;
680			}
681		}
682
683		if (state == READ_ARG)
684		{
685			if (*ptr == '\\')
686			{
687				state = READ_ARG_ESCAPED;
688				continue;
689			}
690			else if (isspace(*ptr))
691			{
692				/* 0 terminate each arg. */
693				*dst++ = '\0';
694				argc++;
695				state = SKIP_WS;
696				if (argc >= MAX_ARGS)
697					break;
698			}
699			else
700				*dst++ = *ptr;
701		}
702
703		if (state == QUOTED_ARG)
704		{
705			if (*ptr == '\\')
706			{
707				state = QUOTED_ARG_ESCAPED;
708				continue;
709			}
710			if (*ptr == quote_ch)
711			{
712				/* 0 terminate each arg. */
713				*dst++ = '\0';
714				argc++;
715				state = SKIP_WS;
716				if (argc >= MAX_ARGS)
717					break;
718			}
719			else
720				*dst++ = *ptr;
721		}
722
723		if (state == READ_ARG_ESCAPED)
724		{
725			*dst++ = *ptr;
726			state = READ_ARG;
727		}
728
729		if (state == QUOTED_ARG_ESCAPED)
730		{
731			*dst++ = *ptr;
732			state = QUOTED_ARG;
733		}
734	}
735
736	if (state != SKIP_WS)
737	{
738		/* Terminate last arg. */
739		*dst++ = '\0';
740		argc++;
741	}
742
743	/* Teminate arg vector. */
744	argvec[argc] = NULL;
745
746	*pargv = argvec;
747	*pargc = argc;
748}
749
750/* Print a (hopefully) useful usage message. */
751static int
752usage(void)
753{
754	printf(
755		"Usage: %s [-h] [-i] [-l] [-p prompt] [-q] [-v] [command] [opt ...]\n"
756		"    -i    Run in interactive mode.\n"
757		"    -l    Run /usr/bin/leaks -nocontext before exiting.\n"
758		"    -p    Set the prompt to \"prompt\" (implies -i).\n"
759		"    -q    Be less verbose.\n"
760		"    -v    Be more verbose about what's going on.\n"
761		"%s commands are:\n", prog_name, prog_name);
762	help(0, NULL);
763	return 2;
764}
765
766/* Execute a single command. */
767static int
768execute_command(int argc, char * const *argv)
769{
770	const command *c;
771	int found = 0;
772
773	/* Nothing to do. */
774	if (argc == 0)
775		return 0;
776
777	for (c = commands; c->c_name; ++c)
778	{
779		if (match_command(c->c_name, argv[0]))
780		{
781			found = 1;
782			break;
783		}
784	}
785
786	if (found)
787	{
788		int result;
789
790		/* Reset getopt for command proc. */
791		optind = 1;
792		optreset = 1;
793
794		if (do_verbose)
795		{
796			int ix;
797
798			fprintf(stderr, "%s", c->c_name);
799			for (ix = 1; ix < argc; ++ix)
800				fprintf(stderr, " \"%s\"", argv[ix]);
801			fprintf(stderr, "\n");
802		}
803
804		result = c->c_func(argc, argv);
805		if (result == 2)
806			fprintf(stderr, "Usage: %s %s\n        %s\n", c->c_name, c->c_usage, c->c_help);
807
808		return result;
809	}
810	else
811	{
812		sec_error("unknown command \"%s\"", argv[0]);
813		return 1;
814	}
815}
816
817static void
818receive_notifications(void)
819{
820	/* Run the CFRunloop to get any pending notifications. */
821	while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource);
822}
823
824
825const char *
826sec_errstr(int err)
827{
828    const char *errString;
829    if (IS_SEC_ERROR(err))
830        errString = SECErrorString(err);
831    else
832        errString = cssmErrorString(err);
833    return errString;
834}
835
836void
837sec_error(const char *msg, ...)
838{
839    va_list args;
840
841    fprintf(stderr, "%s: ", prog_name);
842
843    va_start(args, msg);
844    vfprintf(stderr, msg, args);
845    va_end(args);
846
847    fprintf(stderr, "\n");
848}
849
850void
851sec_perror(const char *msg, int err)
852{
853    sec_error("%s: %s", msg, sec_errstr(err));
854}
855
856int
857main(int argc, char * const *argv)
858{
859	int result = 0;
860	int do_help = 0;
861	int do_interactive = 0;
862	int do_leaks = 0;
863	int ch;
864
865
866	/* Remember my name. */
867	prog_name = strrchr(argv[0], '/');
868	prog_name = prog_name ? prog_name + 1 : argv[0];
869
870	/* Do getopt stuff for global options. */
871	optind = 1;
872	optreset = 1;
873	while ((ch = getopt(argc, argv, "hilp:qvR")) != -1)
874	{
875		switch  (ch)
876		{
877		case 'h':
878			do_help = 1;
879			break;
880		case 'i':
881			do_interactive = 1;
882			break;
883		case 'l':
884			do_leaks = 1;
885			break;
886		case 'p':
887			do_interactive = 1;
888			prompt_string = optarg;
889			break;
890		case 'q':
891			do_quiet = 1;
892			break;
893		case 'v':
894			do_verbose = 1;
895			break;
896		case 'R':
897			// "Recovery mode", do NOT ask security-checksystem to run when using keychain APIs
898			// NOTE: this is a hidden option (not in the usage message)
899                SecKeychainSystemKeychainCheckWouldDeadlock();
900			break;
901		case '?':
902		default:
903			return usage();
904		}
905	}
906
907	argc -= optind;
908	argv += optind;
909
910	if (do_help)
911	{
912		/* Munge argc/argv so that argv[0] is something. */
913		return help(argc + 1, argv - 1);
914	}
915	else if (argc > 0)
916	{
917		receive_notifications();
918		result = execute_command(argc, argv);
919		receive_notifications();
920	}
921	else if (do_interactive)
922	{
923		/* In interactive mode we just read commands and run them until readline returns NULL. */
924
925        /* Only show prompt string if stdin is a tty. */
926        int show_prompt = isatty(0);
927
928		for (;;)
929		{
930			static char buffer[MAX_LINE_LEN];
931			char * const *av, *input;
932			int ac;
933
934            if (show_prompt)
935                fprintf(stderr, "%s", prompt_string);
936
937			input = readline(buffer, MAX_LINE_LEN);
938			if (!input)
939				break;
940
941			split_line(input, &ac, &av);
942			receive_notifications();
943			result = execute_command(ac, av);
944			receive_notifications();
945			if (result == -1)
946			{
947				result = 0;
948				break;
949			}
950
951			if (result && ! do_quiet)
952			{
953				fprintf(stderr, "%s: returned %d\n", av[0], result);
954			}
955		}
956	}
957	else
958		result = usage();
959
960	if (do_leaks)
961	{
962		char *const argvec[3] = { "leaks", "-nocontext", NULL };
963		leaks(2, argvec);
964	}
965
966	return result;
967}
968