2 * Copyright (c) 2006-2010,2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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.
33 * -- To add someRoot.cer to your login keychain and to your Trust Settings as
34 * an unrestricted root cert:
36 * % security add-trusted-cert -k login.keychain someRoot.cer
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):
42 * % security add-trusted-cert -p ssl -d anotherRoot.cer
44 * The more obscure uses involve default settings and trust settings files.
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.
51 * -- To specify a default of "deny" for policy SMIME for the admin domain:
53 * % security add-trusted-cert -p smime -r deny -D
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).
64 * -- To create Trust Settings record with one cert in it, restricted to
67 * % security add-trusted-cert -p ssl -f someTrustSettingsFile.plist -r someRoot.cer
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
75 * -- To reset the admin trust settings to "empty":
77 * % security add-trusted-cert -f emptySettingsFile.plist
78 * % security trust-settings-import -d emptySettingsFile.plist
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>
92 #include <utilities/fileIo.h>
93 #include <CoreFoundation/CoreFoundation.h>
95 /* r/w files as CFData */
96 static CFDataRef
readFileData(
102 if(readFileSizet(fileName
, &d
, &dLen
)) {
105 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)d
, dLen
);
110 static int writeFileData(
111 const char *fileName
,
114 unsigned long l
= (unsigned long)CFDataGetLength(cfd
);
115 int rtn
= writeFileSizet(fileName
, CFDataGetBytePtr(cfd
), l
);
117 fprintf(stderr
, "Error %d writing to %s\n", rtn
, fileName
);
120 fprintf(stdout
, "...wrote %ld bytes to %s\n", l
, fileName
);
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 */
135 *dict
= CFDictionaryCreateMutable(NULL
,
137 &kCFTypeDictionaryKeyCallBacks
,
138 &kCFTypeDictionaryValueCallBacks
);
141 /* OID string to an OID pointer */
142 const CSSM_OID
*oid
= NULL
;
144 oid
= policyStringToOid(policy
);
149 /* OID to SecPolicyRef */
150 SecPolicyRef policyRef
= oidToPolicy(oid
);
151 if(policyRef
== NULL
) {
154 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicy
, policyRef
);
155 CFRelease(policyRef
);
158 /* app string to SecTrustedApplicationRef */
159 if(appPath
!= NULL
) {
160 SecTrustedApplicationRef appRef
;
161 OSStatus ortn
= SecTrustedApplicationCreateFromPath(appPath
, &appRef
);
163 cssmPerror("SecTrustedApplicationCreateFromPath", ortn
);
166 CFDictionaryAddValue(*dict
, kSecTrustSettingsApplication
, appRef
);
170 if(policyStr
!= NULL
) {
171 CFStringRef pstr
= CFStringCreateWithCString(NULL
, policyStr
, kCFStringEncodingUTF8
);
172 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicyString
, pstr
);
177 SInt32 ae
= (SInt32
)allowErr
;
178 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ae
);
179 CFDictionaryAddValue(*dict
, kSecTrustSettingsAllowedError
, cfNum
);
184 SInt32 ku
= (SInt32
)keyUse
;
185 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ku
);
186 CFDictionaryAddValue(*dict
, kSecTrustSettingsKeyUsage
, cfNum
);
190 if(resultType
!= kSecTrustSettingsResultTrustRoot
) {
191 SInt32 rt
= (SInt32
)resultType
;
192 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rt
);
193 CFDictionaryAddValue(*dict
, kSecTrustSettingsResult
, cfNum
);
202 trusted_cert_add(int argc
, char * const *argv
)
208 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
210 SecKeychainRef kcRef
= NULL
;
211 int defaultSetting
= 0;
212 char *certFile
= NULL
;
213 SecCertificateRef certRef
= NULL
;
215 /* for operating in file-based settings */
216 char *settingsFileIn
= NULL
;
217 char *settingsFileOut
= NULL
;
218 CFDataRef settingsIn
= NULL
;
219 CFDataRef settingsOut
= NULL
;
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;
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;
238 return 2; /* @@@ Return 2 triggers usage message. */
242 while ((arg
= getopt(argc
, argv
, "dr:a:p:s:e:u:k:i:o:Dh")) != -1) {
245 domain
= kSecTrustSettingsDomainAdmin
;
248 if(!strcmp(optarg
, "trustRoot")) {
249 resultType
= kSecTrustSettingsResultTrustRoot
;
251 else if(!strcmp(optarg
, "trustAsRoot")) {
252 resultType
= kSecTrustSettingsResultTrustAsRoot
;
254 else if(!strcmp(optarg
, "deny")) {
255 resultType
= kSecTrustSettingsResultDeny
;
257 else if(!strcmp(optarg
, "unspecified")) {
258 resultType
= kSecTrustSettingsResultUnspecified
;
266 if (policyNameCount
< maxPolicies
) {
267 policyNames
[policyNameCount
++] = optarg
;
269 fprintf(stderr
, "Too many policy arguments.\n");
279 if (policyStringCount
< maxPolicies
) {
280 policyStrings
[policyStringCount
++] = optarg
;
282 fprintf(stderr
, "Too many policy string arguments.\n");
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
294 allowErr
= (CSSM_RETURN
)atoi(optarg
);
296 fprintf(stderr
, "Invalid value for allowed error.\n");
299 allowedErrors
[allowedErrorCount
++] = allowErr
;
301 fprintf(stderr
, "Too many \"allowed error\" arguments.\n");
307 keyUse
= (SecTrustSettingsKeyUsage
)atoi(optarg
);
311 kcRef
= keychain_open(optarg
);
317 settingsFileIn
= optarg
;
320 settingsFileOut
= optarg
;
327 return 2; /* @@@ Return 2 triggers usage message. */
334 switch(argc
- optind
) {
339 certFile
= argv
[optind
];
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. */
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");
358 if((settingsFileOut
!= NULL
) && (domain
!= kSecTrustSettingsDomainUser
)) {
359 fprintf(stderr
, "Can't specify both domain and a settingsFile\n");
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");
370 /* build per-policy constraints dictionaries */
371 if(haveConstraints
) {
373 for (i
=0; i
<policyNameCount
; i
++) {
374 if (!trustSettings
) {
375 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
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
,
390 CFArrayAppendValue(trustSettings
, constraintDict
);
391 CFRelease(constraintDict
); // array retains it
394 } else { // no allowed errors
395 CFMutableDictionaryRef constraintDict
= NULL
;
396 ourRtn
= appendConstraintsToDict(appPath
,
399 resultType
, 0, keyUse
,
402 CFArrayAppendValue(trustSettings
, constraintDict
);
403 CFRelease(constraintDict
); // array retains it
408 } else { // no policy strings
409 if (allowedErrorCount
) {
410 for (k
=0; k
<allowedErrorCount
; k
++) {
411 CFMutableDictionaryRef constraintDict
= NULL
;
412 ourRtn
= appendConstraintsToDict(appPath
,
420 CFArrayAppendValue(trustSettings
, constraintDict
);
421 CFRelease(constraintDict
); // array retains it
424 } else { // no allowed errors
425 CFMutableDictionaryRef constraintDict
= NULL
;
426 ourRtn
= appendConstraintsToDict(appPath
,
429 resultType
, 0, keyUse
,
432 CFArrayAppendValue(trustSettings
, constraintDict
);
433 CFRelease(constraintDict
); // array retains it
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
,
451 CFArrayAppendValue(trustSettings
, constraintDict
);
452 CFRelease(constraintDict
); // array retains it
458 /* optional settings file */
460 settingsIn
= readFileData(settingsFileIn
);
461 if(settingsIn
== NULL
) {
462 fprintf(stderr
, "Error reading file %s\n", settingsFileIn
);
467 else if(settingsFileOut
) {
468 /* output file, no input file - start with empty settings */
469 ortn
= SecTrustSettingsSetTrustSettingsExternal(NULL
,
470 NULL
, NULL
, &settingsIn
);
472 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
478 /* optional cert file */
480 /* we don't have a cert; use this instead... */
481 certRef
= kSecTrustSettingsDefaultRootCertSetting
;
483 else if(certFile
!= NULL
) {
484 if(readCertFile(certFile
, &certRef
)) {
485 fprintf(stderr
, "Error reading file %s\n", certFile
);
490 /* note we do NOT add by default */
491 ortn
= SecCertificateAddToKeychain(certRef
, kcRef
);
494 case errSecDuplicateItem
: /* that's fine */
497 cssmPerror("SecCertificateAddToKeychain", ortn
);
504 /* now manipulate the Trust Settings */
505 if(settingsFileOut
) {
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.
511 if(certRef
!= NULL
) {
512 ortn
= SecTrustSettingsSetTrustSettingsExternal(settingsIn
,
513 certRef
, trustSettings
, &settingsOut
);
515 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
521 /* no cert data: output := input, e.g. create empty settings file */
522 settingsOut
= settingsIn
;
525 ourRtn
= writeFileData(settingsFileOut
, settingsOut
);
527 fprintf(stderr
, "Error writing to %s\n", settingsFileOut
);
532 /* normal "Add this cert to Trust Settings" */
533 if(certRef
== NULL
) {
534 fprintf(stderr
, "Internal error in trusted_cert_add\n");
538 ortn
= SecTrustSettingsSetTrustSettings(certRef
, domain
, trustSettings
);
540 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
545 if((certRef
!= NULL
) & (certRef
!= kSecTrustSettingsDefaultRootCertSetting
)) {
548 CFRELEASE(trustSettings
);
550 CFRELEASE(settingsIn
);
551 CFRELEASE(settingsOut
);
556 trusted_cert_remove(int argc
, char * const *argv
)
558 OSStatus ortn
= noErr
;
560 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
561 int defaultSetting
= 0;
562 SecCertificateRef certRef
= NULL
;
563 char *certFile
= NULL
;
570 while ((arg
= getopt(argc
, argv
, "dDh")) != -1) {
573 domain
= kSecTrustSettingsDomainAdmin
;
580 return 2; /* @@@ Return 2 triggers usage message. */
584 switch(argc
- optind
) {
589 certFile
= argv
[optind
];
595 if((certFile
== NULL
) && !defaultSetting
) {
596 fprintf(stderr
, "No cert file specified.\n");
599 if((certFile
!= NULL
) && defaultSetting
) {
600 fprintf(stderr
, "Can't specify cert when manipulating default setting.\n");
605 /* we don't have a cert; use this instead... */
606 certRef
= kSecTrustSettingsDefaultRootCertSetting
;
609 if(readCertFile(certFile
, &certRef
)) {
610 fprintf(stderr
, "Error reading file %s\n", certFile
);
615 ortn
= SecTrustSettingsRemoveTrustSettings(certRef
, domain
);
617 cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn
);
621 if((certRef
!= NULL
) & (certRef
!= kSecTrustSettingsDefaultRootCertSetting
)) {