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>
85 #include <utilities/SecCFRelease.h>
87 /* r/w files as CFData */
88 static CFDataRef CF_RETURNS_RETAINED
readFileData(
94 if(readFileSizet(fileName
, &d
, &dLen
)) {
97 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)d
, dLen
);
102 static int writeFileData(
103 const char *fileName
,
106 unsigned long l
= (unsigned long)CFDataGetLength(cfd
);
107 int rtn
= writeFileSizet(fileName
, CFDataGetBytePtr(cfd
), l
);
109 fprintf(stderr
, "Error %d writing to %s\n", rtn
, fileName
);
112 fprintf(stdout
, "...wrote %ld bytes to %s\n", l
, fileName
);
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 */
127 *dict
= CFDictionaryCreateMutable(NULL
,
129 &kCFTypeDictionaryKeyCallBacks
,
130 &kCFTypeDictionaryValueCallBacks
);
133 /* OID string to an OID pointer */
134 const CSSM_OID
*oid
= NULL
;
136 oid
= policyStringToOid(policy
);
138 return SHOW_USAGE_MESSAGE
;
141 /* OID to SecPolicyRef */
142 SecPolicyRef policyRef
= oidToPolicy(oid
);
143 if(policyRef
== NULL
) {
144 return SHOW_USAGE_MESSAGE
;
146 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicy
, policyRef
);
147 CFRelease(policyRef
);
150 /* app string to SecTrustedApplicationRef */
151 if(appPath
!= NULL
) {
152 SecTrustedApplicationRef appRef
;
153 OSStatus ortn
= SecTrustedApplicationCreateFromPath(appPath
, &appRef
);
155 cssmPerror("SecTrustedApplicationCreateFromPath", ortn
);
158 CFDictionaryAddValue(*dict
, kSecTrustSettingsApplication
, appRef
);
162 if(policyStr
!= NULL
) {
163 CFStringRef pstr
= CFStringCreateWithCString(NULL
, policyStr
, kCFStringEncodingUTF8
);
164 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicyString
, pstr
);
169 SInt32 ae
= (SInt32
)allowErr
;
170 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ae
);
171 CFDictionaryAddValue(*dict
, kSecTrustSettingsAllowedError
, cfNum
);
176 SInt32 ku
= (SInt32
)keyUse
;
177 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ku
);
178 CFDictionaryAddValue(*dict
, kSecTrustSettingsKeyUsage
, cfNum
);
182 if(resultType
!= kSecTrustSettingsResultTrustRoot
) {
183 SInt32 rt
= (SInt32
)resultType
;
184 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rt
);
185 CFDictionaryAddValue(*dict
, kSecTrustSettingsResult
, cfNum
);
194 trusted_cert_add(int argc
, char * const *argv
)
200 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
202 SecKeychainRef kcRef
= NULL
;
203 char *certFile
= NULL
;
204 SecCertificateRef certRef
= NULL
;
206 /* for operating in file-based settings */
207 char *settingsFileIn
= NULL
;
208 char *settingsFileOut
= NULL
;
209 CFDataRef settingsIn
= NULL
;
210 CFDataRef settingsOut
= NULL
;
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;
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;
229 return SHOW_USAGE_MESSAGE
;
233 while ((arg
= getopt(argc
, argv
, "dr:a:p:s:e:u:k:i:o:h")) != -1) {
236 domain
= kSecTrustSettingsDomainAdmin
;
239 if(!strcmp(optarg
, "trustRoot")) {
240 resultType
= kSecTrustSettingsResultTrustRoot
;
242 else if(!strcmp(optarg
, "trustAsRoot")) {
243 resultType
= kSecTrustSettingsResultTrustAsRoot
;
245 else if(!strcmp(optarg
, "deny")) {
246 resultType
= kSecTrustSettingsResultDeny
;
248 else if(!strcmp(optarg
, "unspecified")) {
249 resultType
= kSecTrustSettingsResultUnspecified
;
252 return SHOW_USAGE_MESSAGE
;
257 if (policyNameCount
< maxPolicies
) {
258 policyNames
[policyNameCount
++] = optarg
;
260 fprintf(stderr
, "Too many policy arguments.\n");
261 return SHOW_USAGE_MESSAGE
;
270 if (policyStringCount
< maxPolicies
) {
271 policyStrings
[policyStringCount
++] = optarg
;
273 fprintf(stderr
, "Too many policy string arguments.\n");
274 return SHOW_USAGE_MESSAGE
;
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
285 allowErr
= (CSSM_RETURN
)atoi(optarg
);
287 fprintf(stderr
, "Invalid value for allowed error.\n");
288 return SHOW_USAGE_MESSAGE
;
290 allowedErrors
[allowedErrorCount
++] = allowErr
;
292 fprintf(stderr
, "Too many \"allowed error\" arguments.\n");
293 return SHOW_USAGE_MESSAGE
;
298 keyUse
= (SecTrustSettingsKeyUsage
)atoi(optarg
);
302 kcRef
= keychain_open(optarg
);
308 settingsFileIn
= optarg
;
311 settingsFileOut
= optarg
;
315 return SHOW_USAGE_MESSAGE
;
322 switch(argc
- optind
) {
327 certFile
= argv
[optind
];
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");
341 if((settingsFileOut
!= NULL
) && (domain
!= kSecTrustSettingsDomainUser
)) {
342 fprintf(stderr
, "Can't specify both domain and a settingsFile\n");
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");
353 /* build per-policy constraints dictionaries */
354 if(haveConstraints
) {
356 for (i
=0; i
<policyNameCount
; i
++) {
357 if (!trustSettings
) {
358 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
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
,
373 CFArrayAppendValue(trustSettings
, constraintDict
);
374 CFRelease(constraintDict
); // array retains it
377 } else { // no allowed errors
378 CFMutableDictionaryRef constraintDict
= NULL
;
379 ourRtn
= appendConstraintsToDict(appPath
,
382 resultType
, 0, keyUse
,
385 CFArrayAppendValue(trustSettings
, constraintDict
);
386 CFRelease(constraintDict
); // array retains it
391 } else { // no policy strings
392 if (allowedErrorCount
) {
393 for (k
=0; k
<allowedErrorCount
; k
++) {
394 CFMutableDictionaryRef constraintDict
= NULL
;
395 ourRtn
= appendConstraintsToDict(appPath
,
403 CFArrayAppendValue(trustSettings
, constraintDict
);
404 CFRelease(constraintDict
); // array retains it
407 } else { // no allowed errors
408 CFMutableDictionaryRef constraintDict
= NULL
;
409 ourRtn
= appendConstraintsToDict(appPath
,
412 resultType
, 0, keyUse
,
415 CFArrayAppendValue(trustSettings
, constraintDict
);
416 CFRelease(constraintDict
); // array retains it
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
,
434 CFArrayAppendValue(trustSettings
, constraintDict
);
435 CFRelease(constraintDict
); // array retains it
441 /* optional settings file */
443 settingsIn
= readFileData(settingsFileIn
);
444 if(settingsIn
== NULL
) {
445 fprintf(stderr
, "Error reading file %s\n", settingsFileIn
);
450 else if(settingsFileOut
) {
451 /* output file, no input file - start with empty settings */
452 ortn
= SecTrustSettingsSetTrustSettingsExternal(NULL
,
453 NULL
, NULL
, &settingsIn
);
455 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
461 if(certFile
!= NULL
) {
462 if(readCertFile(certFile
, &certRef
)) {
463 fprintf(stderr
, "Error reading file %s\n", certFile
);
468 /* note we do NOT add by default */
469 ortn
= SecCertificateAddToKeychain(certRef
, kcRef
);
472 case errSecDuplicateItem
: /* that's fine */
475 cssmPerror("SecCertificateAddToKeychain", ortn
);
482 /* now manipulate the Trust Settings */
483 if(settingsFileOut
) {
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.
489 if(certRef
!= NULL
) {
490 ortn
= SecTrustSettingsSetTrustSettingsExternal(settingsIn
,
491 certRef
, trustSettings
, &settingsOut
);
493 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
499 /* no cert data: output := input, e.g. create empty settings file */
500 settingsOut
= settingsIn
;
503 ourRtn
= writeFileData(settingsFileOut
, settingsOut
);
505 fprintf(stderr
, "Error writing to %s\n", settingsFileOut
);
510 /* normal "Add this cert to Trust Settings" */
511 if(certRef
== NULL
) {
512 fprintf(stderr
, "Internal error in trusted_cert_add\n");
516 ortn
= SecTrustSettingsSetTrustSettings(certRef
, domain
, trustSettings
);
518 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
523 if(certRef
!= NULL
) {
526 CFRELEASE(trustSettings
);
528 CFRELEASE(settingsIn
);
529 CFRELEASE(settingsOut
);
534 trusted_cert_remove(int argc
, char * const *argv
)
536 OSStatus ortn
= noErr
;
538 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
539 SecCertificateRef certRef
= NULL
;
540 char *certFile
= NULL
;
547 while ((arg
= getopt(argc
, argv
, "dh")) != -1) {
550 domain
= kSecTrustSettingsDomainAdmin
;
554 return SHOW_USAGE_MESSAGE
;
558 switch(argc
- optind
) {
563 certFile
= argv
[optind
];
566 return SHOW_USAGE_MESSAGE
;
569 if(certFile
== NULL
) {
570 fprintf(stderr
, "No cert file specified.\n");
571 return SHOW_USAGE_MESSAGE
;
574 if(readCertFile(certFile
, &certRef
)) {
575 fprintf(stderr
, "Error reading file %s\n", certFile
);
579 ortn
= SecTrustSettingsRemoveTrustSettings(certRef
, domain
);
581 cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn
);
585 if(certRef
!= NULL
) {