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