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 trust settings files.
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).
55 * -- To create Trust Settings record with one cert in it, restricted to
58 * % security add-trusted-cert -p ssl -f someTrustSettingsFile.plist -r someRoot.cer
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
66 * -- To reset the admin trust settings to "empty":
68 * % security add-trusted-cert -f emptySettingsFile.plist
69 * % security trust-settings-import -d emptySettingsFile.plist
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>
83 #include <utilities/fileIo.h>
84 #include <CoreFoundation/CoreFoundation.h>
86 /* r/w files as CFData */
87 static CFDataRef
readFileData(
93 if(readFileSizet(fileName
, &d
, &dLen
)) {
96 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)d
, dLen
);
101 static int writeFileData(
102 const char *fileName
,
105 unsigned long l
= (unsigned long)CFDataGetLength(cfd
);
106 int rtn
= writeFileSizet(fileName
, CFDataGetBytePtr(cfd
), l
);
108 fprintf(stderr
, "Error %d writing to %s\n", rtn
, fileName
);
111 fprintf(stdout
, "...wrote %ld bytes to %s\n", l
, fileName
);
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 */
126 *dict
= CFDictionaryCreateMutable(NULL
,
128 &kCFTypeDictionaryKeyCallBacks
,
129 &kCFTypeDictionaryValueCallBacks
);
132 /* OID string to an OID pointer */
133 const CSSM_OID
*oid
= NULL
;
135 oid
= policyStringToOid(policy
);
140 /* OID to SecPolicyRef */
141 SecPolicyRef policyRef
= oidToPolicy(oid
);
142 if(policyRef
== NULL
) {
145 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicy
, policyRef
);
146 CFRelease(policyRef
);
149 /* app string to SecTrustedApplicationRef */
150 if(appPath
!= NULL
) {
151 SecTrustedApplicationRef appRef
;
152 OSStatus ortn
= SecTrustedApplicationCreateFromPath(appPath
, &appRef
);
154 cssmPerror("SecTrustedApplicationCreateFromPath", ortn
);
157 CFDictionaryAddValue(*dict
, kSecTrustSettingsApplication
, appRef
);
161 if(policyStr
!= NULL
) {
162 CFStringRef pstr
= CFStringCreateWithCString(NULL
, policyStr
, kCFStringEncodingUTF8
);
163 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicyString
, pstr
);
168 SInt32 ae
= (SInt32
)allowErr
;
169 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ae
);
170 CFDictionaryAddValue(*dict
, kSecTrustSettingsAllowedError
, cfNum
);
175 SInt32 ku
= (SInt32
)keyUse
;
176 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ku
);
177 CFDictionaryAddValue(*dict
, kSecTrustSettingsKeyUsage
, cfNum
);
181 if(resultType
!= kSecTrustSettingsResultTrustRoot
) {
182 SInt32 rt
= (SInt32
)resultType
;
183 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rt
);
184 CFDictionaryAddValue(*dict
, kSecTrustSettingsResult
, cfNum
);
193 trusted_cert_add(int argc
, char * const *argv
)
199 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
201 SecKeychainRef kcRef
= NULL
;
202 char *certFile
= NULL
;
203 SecCertificateRef certRef
= NULL
;
205 /* for operating in file-based settings */
206 char *settingsFileIn
= NULL
;
207 char *settingsFileOut
= NULL
;
208 CFDataRef settingsIn
= NULL
;
209 CFDataRef settingsOut
= NULL
;
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;
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;
228 return 2; /* @@@ Return 2 triggers usage message. */
232 while ((arg
= getopt(argc
, argv
, "dr:a:p:s:e:u:k:i:o:h")) != -1) {
235 domain
= kSecTrustSettingsDomainAdmin
;
238 if(!strcmp(optarg
, "trustRoot")) {
239 resultType
= kSecTrustSettingsResultTrustRoot
;
241 else if(!strcmp(optarg
, "trustAsRoot")) {
242 resultType
= kSecTrustSettingsResultTrustAsRoot
;
244 else if(!strcmp(optarg
, "deny")) {
245 resultType
= kSecTrustSettingsResultDeny
;
247 else if(!strcmp(optarg
, "unspecified")) {
248 resultType
= kSecTrustSettingsResultUnspecified
;
256 if (policyNameCount
< maxPolicies
) {
257 policyNames
[policyNameCount
++] = optarg
;
259 fprintf(stderr
, "Too many policy arguments.\n");
269 if (policyStringCount
< maxPolicies
) {
270 policyStrings
[policyStringCount
++] = optarg
;
272 fprintf(stderr
, "Too many policy string arguments.\n");
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
284 allowErr
= (CSSM_RETURN
)atoi(optarg
);
286 fprintf(stderr
, "Invalid value for allowed error.\n");
289 allowedErrors
[allowedErrorCount
++] = allowErr
;
291 fprintf(stderr
, "Too many \"allowed error\" arguments.\n");
297 keyUse
= (SecTrustSettingsKeyUsage
)atoi(optarg
);
301 kcRef
= keychain_open(optarg
);
307 settingsFileIn
= optarg
;
310 settingsFileOut
= optarg
;
314 return 2; /* @@@ Return 2 triggers usage message. */
321 switch(argc
- optind
) {
326 certFile
= argv
[optind
];
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");
340 if((settingsFileOut
!= NULL
) && (domain
!= kSecTrustSettingsDomainUser
)) {
341 fprintf(stderr
, "Can't specify both domain and a settingsFile\n");
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");
352 /* build per-policy constraints dictionaries */
353 if(haveConstraints
) {
355 for (i
=0; i
<policyNameCount
; i
++) {
356 if (!trustSettings
) {
357 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
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
,
372 CFArrayAppendValue(trustSettings
, constraintDict
);
373 CFRelease(constraintDict
); // array retains it
376 } else { // no allowed errors
377 CFMutableDictionaryRef constraintDict
= NULL
;
378 ourRtn
= appendConstraintsToDict(appPath
,
381 resultType
, 0, keyUse
,
384 CFArrayAppendValue(trustSettings
, constraintDict
);
385 CFRelease(constraintDict
); // array retains it
390 } else { // no policy strings
391 if (allowedErrorCount
) {
392 for (k
=0; k
<allowedErrorCount
; k
++) {
393 CFMutableDictionaryRef constraintDict
= NULL
;
394 ourRtn
= appendConstraintsToDict(appPath
,
402 CFArrayAppendValue(trustSettings
, constraintDict
);
403 CFRelease(constraintDict
); // array retains it
406 } else { // no allowed errors
407 CFMutableDictionaryRef constraintDict
= NULL
;
408 ourRtn
= appendConstraintsToDict(appPath
,
411 resultType
, 0, keyUse
,
414 CFArrayAppendValue(trustSettings
, constraintDict
);
415 CFRelease(constraintDict
); // array retains it
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
,
433 CFArrayAppendValue(trustSettings
, constraintDict
);
434 CFRelease(constraintDict
); // array retains it
440 /* optional settings file */
442 settingsIn
= readFileData(settingsFileIn
);
443 if(settingsIn
== NULL
) {
444 fprintf(stderr
, "Error reading file %s\n", settingsFileIn
);
449 else if(settingsFileOut
) {
450 /* output file, no input file - start with empty settings */
451 ortn
= SecTrustSettingsSetTrustSettingsExternal(NULL
,
452 NULL
, NULL
, &settingsIn
);
454 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
460 if(certFile
!= NULL
) {
461 if(readCertFile(certFile
, &certRef
)) {
462 fprintf(stderr
, "Error reading file %s\n", certFile
);
467 /* note we do NOT add by default */
468 ortn
= SecCertificateAddToKeychain(certRef
, kcRef
);
471 case errSecDuplicateItem
: /* that's fine */
474 cssmPerror("SecCertificateAddToKeychain", ortn
);
481 /* now manipulate the Trust Settings */
482 if(settingsFileOut
) {
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.
488 if(certRef
!= NULL
) {
489 ortn
= SecTrustSettingsSetTrustSettingsExternal(settingsIn
,
490 certRef
, trustSettings
, &settingsOut
);
492 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
498 /* no cert data: output := input, e.g. create empty settings file */
499 settingsOut
= settingsIn
;
502 ourRtn
= writeFileData(settingsFileOut
, settingsOut
);
504 fprintf(stderr
, "Error writing to %s\n", settingsFileOut
);
509 /* normal "Add this cert to Trust Settings" */
510 if(certRef
== NULL
) {
511 fprintf(stderr
, "Internal error in trusted_cert_add\n");
515 ortn
= SecTrustSettingsSetTrustSettings(certRef
, domain
, trustSettings
);
517 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
522 if(certRef
!= NULL
) {
525 CFRELEASE(trustSettings
);
527 CFRELEASE(settingsIn
);
528 CFRELEASE(settingsOut
);
533 trusted_cert_remove(int argc
, char * const *argv
)
535 OSStatus ortn
= noErr
;
537 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
538 SecCertificateRef certRef
= NULL
;
539 char *certFile
= NULL
;
546 while ((arg
= getopt(argc
, argv
, "dh")) != -1) {
549 domain
= kSecTrustSettingsDomainAdmin
;
553 return 2; /* @@@ Return 2 triggers usage message. */
557 switch(argc
- optind
) {
562 certFile
= argv
[optind
];
568 if(certFile
== NULL
) {
569 fprintf(stderr
, "No cert file specified.\n");
573 if(readCertFile(certFile
, &certRef
)) {
574 fprintf(stderr
, "Error reading file %s\n", certFile
);
578 ortn
= SecTrustSettingsRemoveTrustSettings(certRef
, domain
);
580 cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn
);
584 if(certRef
!= NULL
) {