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