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