]> git.saurik.com Git - apple/security.git/blob - SecurityTool/trusted_cert_add.c
Security-57740.1.18.tar.gz
[apple/security.git] / SecurityTool / trusted_cert_add.c
1 /*
2 * Copyright (c) 2006-2010,2012,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 * trust_cert_add.c
24 */
25
26 /*
27 * This command is fairly versatile and hence the usage might be a bit confusing.
28 * The standard usage of this command is to add one or more certs to a Trust
29 * Settings domain, along with optional usage constraints. Often, but not
30 * necessarily, you'd also add the cert to a keychain while you're adding
31 * it to Trust Settings.
32 *
33 * -- To add someRoot.cer to your login keychain and to your Trust Settings as
34 * an unrestricted root cert:
35 *
36 * % security add-trusted-cert -k login.keychain someRoot.cer
37 *
38 * -- To add anotherRoot.cer to the local admin trust settings, only for policy
39 * ssl, without adding it to a keychain (presumably because it's already in
40 * a keychain somewhere else):
41 *
42 * % security add-trusted-cert -p ssl -d anotherRoot.cer
43 *
44 * The more obscure uses involve default settings and trust settings files.
45 *
46 * Specifying a default trust setting precludes specifying a cert. Other
47 * options apply as usual; note that if the domain for which you are
48 * specifying a default setting already has a default setting, the old default
49 * will be replaced by the new one you specify.
50 *
51 * -- To specify a default of "deny" for policy SMIME for the admin domain:
52 *
53 * % security add-trusted-cert -p smime -r deny -D
54 *
55 * This command can also operate on trust settings as files instead of
56 * modifying an actual on-disk Trust Settings record. One standard use for
57 * this function is in the creation of the system Trust Settings, which
58 * are immutable at runtime via the SecTrustSettings API. You provide a
59 * file name for this option via -f settingsFile. If the file does not
60 * exist, a new empty Trust Settings will be created, and certs and/or
61 * a default will be added to that record, and the record will be written
62 * out to the filename you provide (infile = outfile, always).
63 *
64 * -- To create Trust Settings record with one cert in it, restricted to
65 * policy SSL:
66 *
67 * % security add-trusted-cert -p ssl -f someTrustSettingsFile.plist -r someRoot.cer
68 *
69 * You can also use the -f option and specify no certs, in which case an empty
70 * Trust Settings record will be created. This can be useful if you want to
71 * quickly reset the Trust Settings in a given domain to "empty"; the
72 * empty Trust Settings record can be imported via the trust-settings-import
73 * command.
74 *
75 * -- To reset the admin trust settings to "empty":
76 *
77 * % security add-trusted-cert -f emptySettingsFile.plist
78 * % security trust-settings-import -d emptySettingsFile.plist
79 */
80
81 #include "trusted_cert_add.h"
82 #include "trusted_cert_utils.h"
83 #include "security_tool.h"
84 #include "keychain_utilities.h"
85 #include <Security/Security.h>
86 #include <Security/SecTrust.h>
87 #include <Security/SecTrustSettings.h>
88 #include <Security/SecTrustSettingsPriv.h>
89 #include <Security/oidsalg.h>
90 #include <errno.h>
91 #include <unistd.h>
92 #include <utilities/fileIo.h>
93 #include <CoreFoundation/CoreFoundation.h>
94
95 /* r/w files as CFData */
96 static CFDataRef readFileData(
97 const char *fileName)
98 {
99 unsigned char *d;
100 size_t dLen;
101
102 if(readFileSizet(fileName, &d, &dLen)) {
103 return NULL;
104 }
105 CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)d, dLen);
106 free(d);
107 return cfd;
108 }
109
110 static int writeFileData(
111 const char *fileName,
112 CFDataRef cfd)
113 {
114 unsigned long l = (unsigned long)CFDataGetLength(cfd);
115 int rtn = writeFileSizet(fileName, CFDataGetBytePtr(cfd), l);
116 if(rtn) {
117 fprintf(stderr, "Error %d writing to %s\n", rtn, fileName);
118 }
119 else if(!do_quiet) {
120 fprintf(stdout, "...wrote %ld bytes to %s\n", l, fileName);
121 }
122 return rtn;
123 }
124
125 static int appendConstraintsToDict(
126 const char *appPath, /* optional */
127 const char *policy, /* optional - smime, ssl, etc. */
128 const char *policyStr, /* optional policy string */
129 SecTrustSettingsResult resultType,
130 CSSM_RETURN allowErr, /* optional allowed error */
131 SecTrustSettingsKeyUsage keyUse,/* optional key use */
132 CFMutableDictionaryRef *dict) /* result RETURNED here, created if necessary */
133 {
134 if(*dict == NULL) {
135 *dict = CFDictionaryCreateMutable(NULL,
136 0, // capacity
137 &kCFTypeDictionaryKeyCallBacks,
138 &kCFTypeDictionaryValueCallBacks);
139 }
140
141 /* OID string to an OID pointer */
142 const CSSM_OID *oid = NULL;
143 if(policy != NULL) {
144 oid = policyStringToOid(policy);
145 if(oid == NULL) {
146 return 2;
147 }
148
149 /* OID to SecPolicyRef */
150 SecPolicyRef policyRef = oidToPolicy(oid);
151 if(policyRef == NULL) {
152 return 2;
153 }
154 CFDictionaryAddValue(*dict, kSecTrustSettingsPolicy, policyRef);
155 CFRelease(policyRef);
156 }
157
158 /* app string to SecTrustedApplicationRef */
159 if(appPath != NULL) {
160 SecTrustedApplicationRef appRef;
161 OSStatus ortn = SecTrustedApplicationCreateFromPath(appPath, &appRef);
162 if(ortn) {
163 cssmPerror("SecTrustedApplicationCreateFromPath", ortn);
164 return -1;
165 }
166 CFDictionaryAddValue(*dict, kSecTrustSettingsApplication, appRef);
167 CFRelease(appRef);
168 }
169
170 if(policyStr != NULL) {
171 CFStringRef pstr = CFStringCreateWithCString(NULL, policyStr, kCFStringEncodingUTF8);
172 CFDictionaryAddValue(*dict, kSecTrustSettingsPolicyString, pstr);
173 CFRelease(pstr);
174 }
175
176 if(allowErr) {
177 SInt32 ae = (SInt32)allowErr;
178 CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &ae);
179 CFDictionaryAddValue(*dict, kSecTrustSettingsAllowedError, cfNum);
180 CFRelease(cfNum);
181 }
182
183 if(keyUse != 0) {
184 SInt32 ku = (SInt32)keyUse;
185 CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &ku);
186 CFDictionaryAddValue(*dict, kSecTrustSettingsKeyUsage, cfNum);
187 CFRelease(cfNum);
188 }
189
190 if(resultType != kSecTrustSettingsResultTrustRoot) {
191 SInt32 rt = (SInt32)resultType;
192 CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &rt);
193 CFDictionaryAddValue(*dict, kSecTrustSettingsResult, cfNum);
194 CFRelease(cfNum);
195 }
196
197 return 0;
198 }
199
200
201 int
202 trusted_cert_add(int argc, char * const *argv)
203 {
204 extern char *optarg;
205 extern int optind;
206 OSStatus ortn;
207 int arg;
208 SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
209 int ourRtn = 0;
210 SecKeychainRef kcRef = NULL;
211 int defaultSetting = 0;
212 char *certFile = NULL;
213 SecCertificateRef certRef = NULL;
214
215 /* for operating in file-based settings */
216 char *settingsFileIn = NULL;
217 char *settingsFileOut = NULL;
218 CFDataRef settingsIn = NULL;
219 CFDataRef settingsOut = NULL;
220
221 /* optional usage constraints */
222 // char *policy = NULL;
223 char *appPath = NULL;
224 // char *policyString = NULL;
225 SecTrustSettingsResult resultType = kSecTrustSettingsResultTrustRoot;
226 CSSM_RETURN allowErr = CSSM_OK;
227 SecTrustSettingsKeyUsage keyUse = 0;
228 CFMutableArrayRef trustSettings = NULL;
229 int haveConstraints = 0;
230
231 const int maxPolicies = 16; // upper limit on policies that can be set in one invocation
232 char *policyNames[maxPolicies];
233 char *policyStrings[maxPolicies];
234 int allowedErrors[maxPolicies];
235 int policyNameCount = 0, policyStringCount = 0, allowedErrorCount = 0;
236
237 if(argc < 2) {
238 return 2; /* @@@ Return 2 triggers usage message. */
239 }
240
241 optind = 1;
242 while ((arg = getopt(argc, argv, "dr:a:p:s:e:u:k:i:o:Dh")) != -1) {
243 switch (arg) {
244 case 'd':
245 domain = kSecTrustSettingsDomainAdmin;
246 break;
247 case 'r':
248 if(!strcmp(optarg, "trustRoot")) {
249 resultType = kSecTrustSettingsResultTrustRoot;
250 }
251 else if(!strcmp(optarg, "trustAsRoot")) {
252 resultType = kSecTrustSettingsResultTrustAsRoot;
253 }
254 else if(!strcmp(optarg, "deny")) {
255 resultType = kSecTrustSettingsResultDeny;
256 }
257 else if(!strcmp(optarg, "unspecified")) {
258 resultType = kSecTrustSettingsResultUnspecified;
259 }
260 else {
261 return 2;
262 }
263 haveConstraints = 1;
264 break;
265 case 'p':
266 if (policyNameCount < maxPolicies) {
267 policyNames[policyNameCount++] = optarg;
268 } else {
269 fprintf(stderr, "Too many policy arguments.\n");
270 return 2;
271 }
272 haveConstraints = 1;
273 break;
274 case 'a':
275 appPath = optarg;
276 haveConstraints = 1;
277 break;
278 case 's':
279 if (policyStringCount < maxPolicies) {
280 policyStrings[policyStringCount++] = optarg;
281 } else {
282 fprintf(stderr, "Too many policy string arguments.\n");
283 return 2;
284 }
285 haveConstraints = 1;
286 break;
287 case 'e':
288 if (allowedErrorCount < maxPolicies) {
289 if (!strcmp("certExpired", optarg))
290 allowErr = -2147409654; // 0x8001210A = CSSMERR_TP_CERT_EXPIRED
291 else if (!strcmp("hostnameMismatch", optarg))
292 allowErr = -2147408896; // 0x80012400 = CSSMERR_APPLETP_HOSTNAME_MISMATCH
293 else
294 allowErr = (CSSM_RETURN)atoi(optarg);
295 if (!allowErr) {
296 fprintf(stderr, "Invalid value for allowed error.\n");
297 return 2;
298 }
299 allowedErrors[allowedErrorCount++] = allowErr;
300 } else {
301 fprintf(stderr, "Too many \"allowed error\" arguments.\n");
302 return 2;
303 }
304 haveConstraints = 1;
305 break;
306 case 'u':
307 keyUse = (SecTrustSettingsKeyUsage)atoi(optarg);
308 haveConstraints = 1;
309 break;
310 case 'k':
311 kcRef = keychain_open(optarg);
312 if(kcRef == NULL) {
313 return 1;
314 }
315 break;
316 case 'i':
317 settingsFileIn = optarg;
318 break;
319 case 'o':
320 settingsFileOut = optarg;
321 break;
322 case 'D':
323 defaultSetting = 1;
324 break;
325 default:
326 case 'h':
327 return 2; /* @@@ Return 2 triggers usage message. */
328 }
329 }
330 if(ourRtn) {
331 goto errOut;
332 }
333
334 switch(argc - optind) {
335 case 0:
336 /* no certs */
337 break;
338 case 1:
339 certFile = argv[optind];
340 break;
341 default:
342 ourRtn = 2;
343 goto errOut;
344 }
345
346 /* validate inputs */
347 if(defaultSetting && (certFile != NULL)) {
348 fprintf(stderr, "Can't specify cert when manipulating default setting.\n");
349 ourRtn = 2; /* @@@ Return 2 triggers usage message. */
350 goto errOut;
351 }
352 if((certFile == NULL) && (settingsFileOut == NULL) && !defaultSetting) {
353 /* no cert file - only legal for r/w file or for default settings */
354 fprintf(stderr, "No cert file specified.\n");
355 ourRtn = 2;
356 goto errOut;
357 }
358 if((settingsFileOut != NULL) && (domain != kSecTrustSettingsDomainUser)) {
359 fprintf(stderr, "Can't specify both domain and a settingsFile\n");
360 ourRtn = 2;
361 goto errOut;
362 }
363 if((settingsFileIn != NULL) && (settingsFileOut == NULL)) {
364 /* on the other hand, fileOut with no fileIn is OK */
365 fprintf(stderr, "Can't specify settingsFileIn and no settingsFileOut\n");
366 ourRtn = 2;
367 goto errOut;
368 }
369
370 /* build per-policy constraints dictionaries */
371 if(haveConstraints) {
372 int i, j, k;
373 for (i=0; i<policyNameCount; i++) {
374 if (!trustSettings) {
375 trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
376 }
377 if (policyStringCount) {
378 for (j=0; j<policyStringCount; j++) {
379 if (allowedErrorCount) {
380 for (k=0; k<allowedErrorCount; k++) {
381 CFMutableDictionaryRef constraintDict = NULL;
382 ourRtn = appendConstraintsToDict(appPath,
383 policyNames[i],
384 policyStrings[j],
385 resultType,
386 allowedErrors[k],
387 keyUse,
388 &constraintDict);
389 if (!ourRtn) {
390 CFArrayAppendValue(trustSettings, constraintDict);
391 CFRelease(constraintDict); // array retains it
392 }
393 }
394 } else { // no allowed errors
395 CFMutableDictionaryRef constraintDict = NULL;
396 ourRtn = appendConstraintsToDict(appPath,
397 policyNames[i],
398 policyStrings[j],
399 resultType, 0, keyUse,
400 &constraintDict);
401 if (!ourRtn) {
402 CFArrayAppendValue(trustSettings, constraintDict);
403 CFRelease(constraintDict); // array retains it
404 }
405
406 }
407 }
408 } else { // no policy strings
409 if (allowedErrorCount) {
410 for (k=0; k<allowedErrorCount; k++) {
411 CFMutableDictionaryRef constraintDict = NULL;
412 ourRtn = appendConstraintsToDict(appPath,
413 policyNames[i],
414 NULL,
415 resultType,
416 allowedErrors[k],
417 keyUse,
418 &constraintDict);
419 if (!ourRtn) {
420 CFArrayAppendValue(trustSettings, constraintDict);
421 CFRelease(constraintDict); // array retains it
422 }
423 }
424 } else { // no allowed errors
425 CFMutableDictionaryRef constraintDict = NULL;
426 ourRtn = appendConstraintsToDict(appPath,
427 policyNames[i],
428 NULL,
429 resultType, 0, keyUse,
430 &constraintDict);
431 if (!ourRtn) {
432 CFArrayAppendValue(trustSettings, constraintDict);
433 CFRelease(constraintDict); // array retains it
434 }
435 }
436 }
437 if(ourRtn) {
438 goto errOut;
439 }
440 }
441 }
442
443 /* handle the case where no policies were specified */
444 if(haveConstraints && !trustSettings) {
445 CFMutableDictionaryRef constraintDict = NULL;
446 trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
447 ourRtn = appendConstraintsToDict(appPath, NULL, NULL,
448 resultType, allowErr, keyUse,
449 &constraintDict);
450 if (!ourRtn) {
451 CFArrayAppendValue(trustSettings, constraintDict);
452 CFRelease(constraintDict); // array retains it
453 } else {
454 goto errOut;
455 }
456 }
457
458 /* optional settings file */
459 if(settingsFileIn) {
460 settingsIn = readFileData(settingsFileIn);
461 if(settingsIn == NULL) {
462 fprintf(stderr, "Error reading file %s\n", settingsFileIn);
463 ourRtn = 1;
464 goto errOut;
465 }
466 }
467 else if(settingsFileOut) {
468 /* output file, no input file - start with empty settings */
469 ortn = SecTrustSettingsSetTrustSettingsExternal(NULL,
470 NULL, NULL, &settingsIn);
471 if(ortn) {
472 cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
473 ourRtn = 1;
474 goto errOut;
475 }
476 }
477
478 /* optional cert file */
479 if(defaultSetting) {
480 /* we don't have a cert; use this instead... */
481 certRef = kSecTrustSettingsDefaultRootCertSetting;
482 }
483 else if(certFile != NULL) {
484 if(readCertFile(certFile, &certRef)) {
485 fprintf(stderr, "Error reading file %s\n", certFile);
486 ourRtn = 1;
487 goto errOut;
488 }
489 if(kcRef) {
490 /* note we do NOT add by default */
491 ortn = SecCertificateAddToKeychain(certRef, kcRef);
492 switch(ortn) {
493 case noErr:
494 case errSecDuplicateItem: /* that's fine */
495 break;
496 default:
497 cssmPerror("SecCertificateAddToKeychain", ortn);
498 ourRtn = 1;
499 goto errOut;
500 }
501 }
502 }
503
504 /* now manipulate the Trust Settings */
505 if(settingsFileOut) {
506 /*
507 * Operating on file data, not actual domain.
508 * At this point settingsIn is the current settings data; it
509 * may be empty but it's valid nonetheless.
510 */
511 if(certRef != NULL) {
512 ortn = SecTrustSettingsSetTrustSettingsExternal(settingsIn,
513 certRef, trustSettings, &settingsOut);
514 if(ortn) {
515 cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
516 ourRtn = 1;
517 goto errOut;
518 }
519 }
520 else {
521 /* no cert data: output := input, e.g. create empty settings file */
522 settingsOut = settingsIn;
523 settingsIn = NULL;
524 }
525 ourRtn = writeFileData(settingsFileOut, settingsOut);
526 if(ourRtn) {
527 fprintf(stderr, "Error writing to %s\n", settingsFileOut);
528 goto errOut;
529 }
530 }
531 else {
532 /* normal "Add this cert to Trust Settings" */
533 if(certRef == NULL) {
534 fprintf(stderr, "Internal error in trusted_cert_add\n");
535 ourRtn = 1;
536 goto errOut;
537 }
538 ortn = SecTrustSettingsSetTrustSettings(certRef, domain, trustSettings);
539 if(ortn) {
540 cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
541 ourRtn = 1;
542 }
543 }
544 errOut:
545 if((certRef != NULL) & (certRef != kSecTrustSettingsDefaultRootCertSetting)) {
546 CFRelease(certRef);
547 }
548 CFRELEASE(trustSettings);
549 CFRELEASE(kcRef);
550 CFRELEASE(settingsIn);
551 CFRELEASE(settingsOut);
552 return ourRtn;
553 }
554
555 int
556 trusted_cert_remove(int argc, char * const *argv)
557 {
558 OSStatus ortn = noErr;
559 int ourRtn = 0;
560 SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
561 int defaultSetting = 0;
562 SecCertificateRef certRef = NULL;
563 char *certFile = NULL;
564
565 extern char *optarg;
566 extern int optind;
567 int arg;
568
569 optind = 1;
570 while ((arg = getopt(argc, argv, "dDh")) != -1) {
571 switch (arg) {
572 case 'd':
573 domain = kSecTrustSettingsDomainAdmin;
574 break;
575 case 'D':
576 defaultSetting = 1;
577 break;
578 default:
579 case 'h':
580 return 2; /* @@@ Return 2 triggers usage message. */
581 }
582 }
583
584 switch(argc - optind) {
585 case 0:
586 /* no certs */
587 break;
588 case 1:
589 certFile = argv[optind];
590 break;
591 default:
592 return 2;
593 }
594
595 if((certFile == NULL) && !defaultSetting) {
596 fprintf(stderr, "No cert file specified.\n");
597 return 2;
598 }
599 if((certFile != NULL) && defaultSetting) {
600 fprintf(stderr, "Can't specify cert when manipulating default setting.\n");
601 return 2;
602 }
603
604 if(defaultSetting) {
605 /* we don't have a cert; use this instead... */
606 certRef = kSecTrustSettingsDefaultRootCertSetting;
607 }
608 else {
609 if(readCertFile(certFile, &certRef)) {
610 fprintf(stderr, "Error reading file %s\n", certFile);
611 return 1;
612 }
613 }
614
615 ortn = SecTrustSettingsRemoveTrustSettings(certRef, domain);
616 if(ortn) {
617 cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn);
618 ourRtn = 1;
619 }
620
621 if((certRef != NULL) & (certRef != kSecTrustSettingsDefaultRootCertSetting)) {
622 CFRelease(certRef);
623 }
624
625 return ourRtn;
626 }