]> git.saurik.com Git - apple/security.git/blob - SecurityTool/security.c
Security-57337.40.85.tar.gz
[apple/security.git] / SecurityTool / security.c
1 /*
2 * Copyright (c) 2003-2014 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. */
76 typedef 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. */
85 const char *prompt_string = "security> ";
86
87 /* The name of this program. */
88 const char *prog_name;
89
90
91 /* Forward declarations of static functions. */
92 static 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 */
100 const 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 { "set-generic-password-partition-list", keychain_set_generic_password_partition_list,
287 "[-a account] [-s service] [-S partition-list] [-k keychain password] [options...] [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 \"kind\" string\n"
292 " -G Match \"value\" string (generic attribute)\n"
293 " -j Match \"comment\" string\n"
294 " -l Match \"label\" string\n"
295 " -s Match \"service\" string\n"
296 " -S Comma-separated list of allowed partition IDs\n"
297 " -k The password for the keychain (required)\n"
298 "If no keychains are specified to search, the default search list is used.",
299 "Set the partition ID list of a generic password item."},
300
301 { "find-internet-password", keychain_find_internet_password,
302 "[-a account] [-s server] [options...] [-g] [keychain...]\n"
303 " -a Match \"account\" string\n"
304 " -c Match \"creator\" (four-character code)\n"
305 " -C Match \"type\" (four-character code)\n"
306 " -d Match \"securityDomain\" string\n"
307 " -D Match \"kind\" string\n"
308 " -j Match \"comment\" string\n"
309 " -l Match \"label\" string\n"
310 " -p Match \"path\" string\n"
311 " -P Match port number\n"
312 " -r Match \"protocol\" (four-character code)\n"
313 " -s Match \"server\" string\n"
314 " -t Match \"authenticationType\" (four-character code)\n"
315 " -g Display the password for the item found\n"
316 " -w Display only the password on stdout\n"
317 "If no keychains are specified to search, the default search list is used.",
318 "Find an internet password item."},
319
320 { "delete-internet-password", keychain_delete_internet_password,
321 "[-a account] [-s server] [options...] [keychain...]\n"
322 " -a Match \"account\" string\n"
323 " -c Match \"creator\" (four-character code)\n"
324 " -C Match \"type\" (four-character code)\n"
325 " -d Match \"securityDomain\" string\n"
326 " -D Match \"kind\" string\n"
327 " -j Match \"comment\" string\n"
328 " -l Match \"label\" string\n"
329 " -p Match \"path\" string\n"
330 " -P Match port number\n"
331 " -r Match \"protocol\" (four-character code)\n"
332 " -s Match \"server\" string\n"
333 " -t Match \"authenticationType\" (four-character code)\n"
334 "If no keychains are specified to search, the default search list is used.",
335 "Delete an internet password item."},
336
337 { "set-internet-password-partition-list", keychain_set_internet_password_partition_list,
338 "[-a account] [-s service] [-S partition-list] [-k keychain password] [options...] [keychain]\n"
339 " -a Match \"account\" string\n"
340 " -c Match \"creator\" (four-character code)\n"
341 " -C Match \"type\" (four-character code)\n"
342 " -d Match \"securityDomain\" string\n"
343 " -D Match \"kind\" string\n"
344 " -j Match \"comment\" string\n"
345 " -l Match \"label\" string\n"
346 " -p Match \"path\" string\n"
347 " -P Match port number\n"
348 " -r Match \"protocol\" (four-character code)\n"
349 " -s Match \"server\" string\n"
350 " -t Match \"authenticationType\" (four-character code)\n"
351 " -S Comma-separated list of allowed partition IDs"
352 " -k password for keychain (required)"
353
354 "If no keychains are specified to search, the default search list is used.",
355 "Set the partition ID list of a internet password item."},
356
357 { "find-certificate", keychain_find_certificate,
358 "[-a] [-c name] [-e emailAddress] [-m] [-p] [-Z] [keychain...]\n"
359 " -a Find all matching certificates, not just the first one\n"
360 " -c Match on \"name\" when searching (optional)\n"
361 " -e Match on \"emailAddress\" when searching (optional)\n"
362 " -m Show the email addresses in the certificate\n"
363 " -p Output certificate in pem format\n"
364 " -Z Print SHA-1 hash of the certificate\n"
365 "If no keychains are specified to search, the default search list is used.",
366 "Find a certificate item."},
367
368 { "find-identity", keychain_find_identity,
369 "[-p policy] [-s string] [-v] [keychain...]\n"
370 " -p Specify policy to evaluate (multiple -p options are allowed)\n"
371 " Supported policies: basic, ssl-client, ssl-server, smime, eap,\n"
372 " ipsec, ichat, codesigning, sys-default, sys-kerberos-kdc, macappstore, appleID\n"
373 " -s Specify optional policy-specific string (e.g. DNS hostname for SSL,\n"
374 " or RFC822 email address for S/MIME)\n"
375 " -v Show valid identities only (default is to show all identities)\n"
376 "If no keychains are specified to search, the default search list is used.",
377 "Find an identity (certificate + private key)."},
378
379 { "delete-certificate", keychain_delete_certificate,
380 "[-c name] [-Z hash] [-t] [keychain...]\n"
381 " -c Specify certificate to delete by its common name\n"
382 " -Z Specify certificate to delete by its SHA-1 hash value\n"
383 " -t Also delete user trust settings for this certificate\n"
384 "The certificate to be deleted must be uniquely specified either by a\n"
385 "string found in its common name, or by its SHA-1 hash.\n"
386 "If no keychains are specified to search, the default search list is used.",
387 "Delete a certificate from a keychain."},
388
389 { "set-identity-preference", set_identity_preference,
390 "[-n] [-c identity] [-s service] [-u keyUsage] [-Z hash] [keychain...]\n"
391 " -n Specify no identity (clears existing preference for service)\n"
392 " -c Specify identity by common name of the certificate\n"
393 " -s Specify service (may be a URL, RFC822 email address, DNS host, or\n"
394 " other name) for which this identity is to be preferred\n"
395 " -u Specify key usage (optional) - see man page for values\n"
396 " -Z Specify identity by SHA-1 hash of certificate (optional)\n",
397 "Set the preferred identity to use for a service."},
398
399 { "get-identity-preference", get_identity_preference,
400 "[-s service] [-u keyUsage] [-p] [-c] [-Z] [keychain...]\n"
401 " -s Specify service (may be a URL, RFC822 email address, DNS host, or\n"
402 " other name)\n"
403 " -u Specify key usage (optional) - see man page for values\n"
404 " -p Output identity certificate in pem format\n"
405 " -c Print common name of the preferred identity certificate\n"
406 " -Z Print SHA-1 hash of the preferred identity certificate\n",
407 "Get the preferred identity to use for a service."},
408
409 { "create-db", db_create,
410 "[-ao0] [-g dl|cspdl] [-m mode] [name]\n"
411 " -a Turn off autocommit\n"
412 " -g Attach to \"guid\" rather than the AppleFileDL\n"
413 " -m Set the inital mode of the created db to \"mode\"\n"
414 " -o Force using openparams argument\n"
415 " -0 Force using version 0 openparams\n"
416 "If no name is provided, ask the user interactively.",
417 "Create a db using the DL." },
418
419 { "export" , keychain_export,
420 "[-k keychain] [-t type] [-f format] [-w] [-p] [-P passphrase] [-o outfile]\n"
421 " -k keychain to export items from\n"
422 " -t Type = certs|allKeys|pubKeys|privKeys|identities|all (Default: all)\n"
423 " -f Format = openssl|openssh1|openssh2|bsafe|pkcs7|pkcs8|pkcs12|pemseq|x509\n"
424 " ...default format is pemseq for aggregate, openssl for single\n"
425 " -w Private keys are wrapped\n"
426 " -p PEM encode the output\n"
427 " -P Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
428 " -o Specify output file (default is stdout)",
429 "Export items from a keychain." },
430
431 { "import", keychain_import,
432 "inputfile [-k keychain] [-t type] [-f format] [-w] [-P passphrase] [options...]\n"
433 " -k Target keychain to import into\n"
434 " -t Type = pub|priv|session|cert|agg\n"
435 " -f Format = openssl|openssh1|openssh2|bsafe|raw|pkcs7|pkcs8|pkcs12|netscape|pemseq\n"
436 " -w Specify that private keys are wrapped and must be unwrapped on import\n"
437 " -x Specify that private keys are non-extractable after being imported\n"
438 " -P Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
439 " -a Specify name and value of extended attribute (can be used multiple times)\n"
440 " -A Allow any application to access the imported key without warning (insecure, not recommended!)\n"
441 " -T Specify an application which may access the imported key (multiple -T options are allowed)\n",
442 "Import items into a keychain." },
443
444 { "cms", cms_util,
445 "[-C|-D|-E|-S] [<options>]\n"
446 " -C create a CMS encrypted message\n"
447 " -D decode a CMS message\n"
448 " -E create a CMS enveloped message\n"
449 " -S create a CMS signed message\n"
450 "\n"
451 "Decoding options:\n"
452 " -c content use this detached content file\n"
453 " -h level generate email headers with info about CMS message\n"
454 " (output level >= 0)\n"
455 " -n suppress output of content\n"
456 "\n"
457 "Encoding options:\n"
458 " -r id,... create envelope for these recipients,\n"
459 " where id can be a certificate nickname or email address\n"
460 " -G include a signing time attribute\n"
461 " -H hash hash = MD2|MD4|MD5|SHA1|SHA256|SHA384|SHA512 (default: SHA1)\n"
462 " -N nick use certificate named \"nick\" for signing\n"
463 " -P include a SMIMECapabilities attribute\n"
464 " -T do not include content in CMS message\n"
465 " -Y nick include an EncryptionKeyPreference attribute with certificate\n"
466 " (use \"NONE\" to omit)\n"
467 " -Z hash find a certificate by subject key ID\n"
468 "\n"
469 "Common options:\n"
470 " -e envelope specify envelope file (valid with -D or -E)\n"
471 " -k keychain specify keychain to use\n"
472 " -i infile use infile as source of data (default: stdin)\n"
473 " -o outfile use outfile as destination of data (default: stdout)\n"
474 " -p password use password as key db password (default: prompt)\n"
475 " -s pass data a single byte at a time to CMS\n"
476 " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n"
477 " -v print debugging information\n"
478 "\n"
479 "Cert usage codes:\n"
480 " 0 - certUsageSSLClient\n"
481 " 1 - certUsageSSLServer\n"
482 " 2 - certUsageSSLServerWithStepUp\n"
483 " 3 - certUsageSSLCA\n"
484 " 4 - certUsageEmailSigner\n"
485 " 5 - certUsageEmailRecipient\n"
486 " 6 - certUsageObjectSigner\n"
487 " 7 - certUsageUserCertImport\n"
488 " 8 - certUsageVerifyCA\n"
489 " 9 - certUsageProtectedObjectSigner\n"
490 " 10 - certUsageStatusResponder\n"
491 " 11 - certUsageAnyCA",
492 "Encode or decode CMS messages." },
493
494 { "install-mds" , mds_install,
495 "", /* no options */
496 "Install (or re-install) the MDS database." },
497
498 { "add-trusted-cert" , trusted_cert_add,
499 " [<options>] [certFile]\n"
500 " -d Add to admin cert store; default is user\n"
501 " -r resultType resultType = trustRoot|trustAsRoot|deny|unspecified;\n"
502 " default is trustRoot\n"
503 " -p policy Specify policy constraint (ssl, smime, codeSign, IPSec, iChat,\n"
504 " basic, swUpdate, pkgSign, pkinitClient, pkinitServer, eap)\n"
505 " -a appPath Specify application constraint\n"
506 " -s policyString Specify policy-specific string\n"
507 " -e allowedError Specify allowed error (certExpired, hostnameMismatch) or integer\n"
508 " -u keyUsage Specify key usage, an integer\n"
509 " -k keychain Specify keychain to which cert is added\n"
510 " -i settingsFileIn Input trust settings file; default is user domain\n"
511 " -o settingsFileOut Output trust settings file; default is user domain\n"
512 " -D Add default setting instead of per-cert setting\n"
513 " certFile Certificate(s)",
514 "Add trusted certificate(s)." },
515
516 { "remove-trusted-cert" , trusted_cert_remove,
517 " [-d] [-D] [certFile]\n"
518 " -d Remove from admin cert store (default is user)\n"
519 " -D Remove default setting instead of per-cert setting\n"
520 " certFile Certificate(s)",
521 "Remove trusted certificate(s)." },
522
523 { "dump-trust-settings" , trusted_cert_dump,
524 " [-s] [-d]\n"
525 " -s Display trusted system certs (default is user)\n"
526 " -d Display trusted admin certs (default is user)\n",
527 "Display contents of trust settings." },
528
529 { "user-trust-settings-enable", user_trust_enable,
530 "[-d] [-e]\n"
531 " -d Disable user-level trust Settings\n"
532 " -e Enable user-level trust Settings\n"
533 "With no parameters, show current enable state of user-level trust settings.",
534 "Display or manipulate user-level trust settings." },
535
536 { "trust-settings-export", trust_settings_export,
537 " [-s] [-d] settings_file\n"
538 " -s Export system trust settings (default is user)\n"
539 " -d Export admin trust settings (default is user)\n",
540 "Export trust settings." },
541
542 { "trust-settings-import", trust_settings_import,
543 " [-d] settings_file\n"
544 " -d Import admin trust settings (default is user)\n",
545 "Import trust settings." },
546
547 { "verify-cert" , verify_cert,
548 " [<options>]\n"
549 " -c certFile Certificate to verify. Can be specified multiple times, leaf first.\n"
550 " -r rootCertFile Root Certificate. Can be specified multiple times.\n"
551 " -p policy Verify Policy (basic, ssl, smime, codeSign, IPSec, iChat, swUpdate,\n"
552 " pkgSign, pkinitClient, pkinitServer, eap, appleID,\n"
553 " macappstore, timestamping); default is basic.\n"
554 " -d date Set date and time to use when verifying certificate,\n"
555 " provided in the form of YYYY-MM-DD-hh:mm:ss (time optional) in GMT.\n"
556 " e.g: 2016-04-25-15:59:59 for April 25, 2016 at 3:59:59 pm in GMT\n"
557 " -k keychain Keychain. Can be called multiple times. Default is default search list.\n"
558 " -n No keychain search list.\n"
559 " -L Local certificates only (do not try to fetch missing CA certs from net).\n"
560 " -l Leaf cert is a CA (normally an error, unless this option is given).\n"
561 " -e emailAddress Email address for smime policy.\n"
562 " -s sslHost SSL host name for ssl policy.\n"
563 " -q Quiet.\n",
564 "Verify certificate(s)." },
565
566 { "authorize" , authorize,
567 "[<options>] <right(s)...>\n"
568 " -u Allow user interaction.\n"
569 " -c Use login name and prompt for password.\n"
570 " -C login Use given login name and prompt for password.\n"
571 " -x Do NOT share -c/-C explicit credentials\n"
572 #ifndef NDEBUG
573 " -E Don't extend rights.\n"
574 #endif
575 " -p Allow returning partial rights.\n"
576 " -d Destroy acquired rights.\n"
577 " -P Pre-authorize rights only.\n"
578 " -l Operate authorizations in least privileged mode.\n"
579 " -i Internalize authref passed on stdin.\n"
580 " -e Externalize authref to stdout.\n"
581 " -w Wait until stdout is closed (to allow reading authref from pipe).\n"
582 "Extend rights flag is passed per default.",
583 "Perform authorization operations." },
584
585 { "authorizationdb" , authorizationdb,
586 "read <right-name>\n"
587 " authorizationdb remove <right-name>\n"
588 " authorizationdb write <right-name> [allow|deny|<rulename>]\n"
589 "If no rulename is specified, write will read a plist from stdin.\n"
590 " authorizationdb merge source [destination]\n"
591 "If no destination path is specified, merge will merge to /etc/authorization.\n"
592 " authorizationdb smartcard <enable|disable|status>\n"
593 "Enables/disables smartcard login support or report current status.",
594 "Make changes to the authorization policy database.\n" },
595
596 { "execute-with-privileges" , execute_with_privileges,
597 "<program> [args...]\n"
598 "On success, stdin will be read and forwarded to the tool.",
599 "Execute tool with privileges." },
600
601 { "leaks", leaks,
602 "[-cycles] [-nocontext] [-nostacks] [-exclude symbol]\n"
603 " -cycles Use a stricter algorithm (\"man leaks\" for details)\n"
604 " -nocontext Withhold hex dumps of the leaked memory\n"
605 " -nostacks Don't show stack traces of leaked memory\n"
606 " -exclude Ignore leaks called from \"symbol\"\n"
607 "(Set the environment variable MallocStackLogging to get symbolic traces.)",
608 "Run /usr/bin/leaks on this process." },
609
610 { "error", display_error_code,
611 "<error code(s)...>\n"
612 "Display an error string for the given security-related error code.\n"
613 "The error can be in decimal or hex, e.g. 1234 or 0x1234. Multiple "
614 "errors can be separated by spaces.",
615 "Display a descriptive message for the given error code(s)." },
616
617 { "create-filevaultmaster-keychain", keychain_createMFV,
618 "[-p password] [keychain name]\n"
619 " -p Use \"password\" as the password for the keychain being created\n"
620 " -s Specify the keysize in bits (default 2048; 1024 & 4096 are allowed)\n"
621 "By default the keychain will be created in ~/Library/Keychains/\n",
622 "Create a keychain containing a key pair for FileVault recovery use."
623 },
624
625 {}
626 };
627
628 /* Global variables. */
629 int do_quiet = 0;
630 int do_verbose = 0;
631
632 /* Return 1 if name matches command. */
633 static int
634 match_command(const char *command, const char *name)
635 {
636 return !strncmp(command, name, strlen(name));
637 }
638
639 /* The help command. */
640 static int
641 help(int argc, char * const *argv)
642 {
643 const command *c;
644
645 if (argc > 1)
646 {
647 char * const *arg;
648 for (arg = argv + 1; *arg; ++arg)
649 {
650 int found = 0;
651
652 for (c = commands; c->c_name; ++c)
653 {
654 if (match_command(c->c_name, *arg))
655 {
656 found = 1;
657 break;
658 }
659 }
660
661 if (found)
662 printf("Usage: %s %s\n", c->c_name, c->c_usage);
663 else
664 {
665 sec_error("%s: no such command: %s", argv[0], *arg);
666 return 1;
667 }
668 }
669 }
670 else
671 {
672 for (c = commands; c->c_name; ++c)
673 printf(" %-17s %s\n", c->c_name, c->c_help);
674 }
675
676 return 0;
677 }
678
679 /* States for split_line parser. */
680 typedef enum
681 {
682 SKIP_WS,
683 READ_ARG,
684 READ_ARG_ESCAPED,
685 QUOTED_ARG,
686 QUOTED_ARG_ESCAPED
687 } parse_state;
688
689 /* Split a line into multiple arguments and return them in *pargc and *pargv. */
690 static void
691 split_line(char *line, int *pargc, char * const **pargv)
692 {
693 static char *argvec[MAX_ARGS + 1];
694 int argc = 0;
695 char *ptr = line;
696 char *dst = line;
697 parse_state state = SKIP_WS;
698 int quote_ch = 0;
699
700 for (ptr = line; *ptr; ++ptr)
701 {
702 if (state == SKIP_WS)
703 {
704 if (isspace(*ptr))
705 continue;
706
707 if (*ptr == '"' || *ptr == '\'')
708 {
709 quote_ch = *ptr;
710 state = QUOTED_ARG;
711 argvec[argc] = dst;
712 continue; /* Skip the quote. */
713 }
714 else
715 {
716 state = READ_ARG;
717 argvec[argc] = dst;
718 }
719 }
720
721 if (state == READ_ARG)
722 {
723 if (*ptr == '\\')
724 {
725 state = READ_ARG_ESCAPED;
726 continue;
727 }
728 else if (isspace(*ptr))
729 {
730 /* 0 terminate each arg. */
731 *dst++ = '\0';
732 argc++;
733 state = SKIP_WS;
734 if (argc >= MAX_ARGS)
735 break;
736 }
737 else
738 *dst++ = *ptr;
739 }
740
741 if (state == QUOTED_ARG)
742 {
743 if (*ptr == '\\')
744 {
745 state = QUOTED_ARG_ESCAPED;
746 continue;
747 }
748 if (*ptr == quote_ch)
749 {
750 /* 0 terminate each arg. */
751 *dst++ = '\0';
752 argc++;
753 state = SKIP_WS;
754 if (argc >= MAX_ARGS)
755 break;
756 }
757 else
758 *dst++ = *ptr;
759 }
760
761 if (state == READ_ARG_ESCAPED)
762 {
763 *dst++ = *ptr;
764 state = READ_ARG;
765 }
766
767 if (state == QUOTED_ARG_ESCAPED)
768 {
769 *dst++ = *ptr;
770 state = QUOTED_ARG;
771 }
772 }
773
774 if (state != SKIP_WS)
775 {
776 /* Terminate last arg. */
777 *dst++ = '\0';
778 argc++;
779 }
780
781 /* Teminate arg vector. */
782 argvec[argc] = NULL;
783
784 *pargv = argvec;
785 *pargc = argc;
786 }
787
788 /* Print a (hopefully) useful usage message. */
789 static int
790 usage(void)
791 {
792 printf(
793 "Usage: %s [-h] [-i] [-l] [-p prompt] [-q] [-v] [command] [opt ...]\n"
794 " -i Run in interactive mode.\n"
795 " -l Run /usr/bin/leaks -nocontext before exiting.\n"
796 " -p Set the prompt to \"prompt\" (implies -i).\n"
797 " -q Be less verbose.\n"
798 " -v Be more verbose about what's going on.\n"
799 "%s commands are:\n", prog_name, prog_name);
800 help(0, NULL);
801 return 2;
802 }
803
804 /* Execute a single command. */
805 static int
806 execute_command(int argc, char * const *argv)
807 {
808 const command *c;
809 int found = 0;
810
811 /* Nothing to do. */
812 if (argc == 0)
813 return 0;
814
815 for (c = commands; c->c_name; ++c)
816 {
817 if (match_command(c->c_name, argv[0]))
818 {
819 found = 1;
820 break;
821 }
822 }
823
824 if (found)
825 {
826 int result;
827
828 /* Reset getopt for command proc. */
829 optind = 1;
830 optreset = 1;
831
832 if (do_verbose)
833 {
834 int ix;
835
836 fprintf(stderr, "%s", c->c_name);
837 for (ix = 1; ix < argc; ++ix)
838 fprintf(stderr, " \"%s\"", argv[ix]);
839 fprintf(stderr, "\n");
840 }
841
842 result = c->c_func(argc, argv);
843 if (result == 2)
844 fprintf(stderr, "Usage: %s %s\n %s\n", c->c_name, c->c_usage, c->c_help);
845
846 return result;
847 }
848 else
849 {
850 sec_error("unknown command \"%s\"", argv[0]);
851 return 1;
852 }
853 }
854
855 static void
856 receive_notifications(void)
857 {
858 /* Run the CFRunloop to get any pending notifications. */
859 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource);
860 }
861
862
863 const char *
864 sec_errstr(int err)
865 {
866 const char *errString;
867 if (IS_SEC_ERROR(err))
868 errString = SECErrorString(err);
869 else
870 errString = cssmErrorString(err);
871 return errString;
872 }
873
874 void
875 sec_error(const char *msg, ...)
876 {
877 va_list args;
878
879 fprintf(stderr, "%s: ", prog_name);
880
881 va_start(args, msg);
882 vfprintf(stderr, msg, args);
883 va_end(args);
884
885 fprintf(stderr, "\n");
886 }
887
888 void
889 sec_perror(const char *msg, int err)
890 {
891 sec_error("%s: %s", msg, sec_errstr(err));
892 }
893
894 int
895 main(int argc, char * const *argv)
896 {
897 int result = 0;
898 int do_help = 0;
899 int do_interactive = 0;
900 int do_leaks = 0;
901 int ch;
902
903
904 /* Remember my name. */
905 prog_name = strrchr(argv[0], '/');
906 prog_name = prog_name ? prog_name + 1 : argv[0];
907
908 /* Do getopt stuff for global options. */
909 optind = 1;
910 optreset = 1;
911 while ((ch = getopt(argc, argv, "hilp:qvR")) != -1)
912 {
913 switch (ch)
914 {
915 case 'h':
916 do_help = 1;
917 break;
918 case 'i':
919 do_interactive = 1;
920 break;
921 case 'l':
922 do_leaks = 1;
923 break;
924 case 'p':
925 do_interactive = 1;
926 prompt_string = optarg;
927 break;
928 case 'q':
929 do_quiet = 1;
930 break;
931 case 'v':
932 do_verbose = 1;
933 break;
934 case 'R':
935 // "Recovery mode", do NOT ask security-checksystem to run when using keychain APIs
936 // NOTE: this is a hidden option (not in the usage message)
937 SecKeychainSystemKeychainCheckWouldDeadlock();
938 break;
939 case '?':
940 default:
941 return usage();
942 }
943 }
944
945 argc -= optind;
946 argv += optind;
947
948 if (do_help)
949 {
950 /* Munge argc/argv so that argv[0] is something. */
951 return help(argc + 1, argv - 1);
952 }
953 else if (argc > 0)
954 {
955 receive_notifications();
956 result = execute_command(argc, argv);
957 receive_notifications();
958 }
959 else if (do_interactive)
960 {
961 /* In interactive mode we just read commands and run them until readline returns NULL. */
962
963 /* Only show prompt string if stdin is a tty. */
964 int show_prompt = isatty(0);
965
966 for (;;)
967 {
968 static char buffer[MAX_LINE_LEN];
969 char * const *av, *input;
970 int ac;
971
972 if (show_prompt)
973 fprintf(stderr, "%s", prompt_string);
974
975 input = readline(buffer, MAX_LINE_LEN);
976 if (!input)
977 break;
978
979 split_line(input, &ac, &av);
980 receive_notifications();
981 result = execute_command(ac, av);
982 receive_notifications();
983 if (result == -1)
984 {
985 result = 0;
986 break;
987 }
988
989 if (result && ! do_quiet)
990 {
991 fprintf(stderr, "%s: returned %d\n", av[0], result);
992 }
993 }
994 }
995 else
996 result = usage();
997
998 if (do_leaks)
999 {
1000 char *const argvec[3] = { "leaks", "-nocontext", NULL };
1001 leaks(2, argvec);
1002 }
1003
1004 return result;
1005 }