2 * Copyright (c) 2006-2010,2012,2014-2019 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>
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 */
92 /* r/w files as CFData */
93 static CFDataRef CF_RETURNS_RETAINED
readFileData(
99 if(readFileSizet(fileName
, &d
, &dLen
)) {
102 CFDataRef cfd
= CFDataCreate(NULL
, (const UInt8
*)d
, dLen
);
107 static int writeFileData(
108 const char *fileName
,
111 unsigned long l
= (unsigned long)CFDataGetLength(cfd
);
112 int rtn
= writeFileSizet(fileName
, CFDataGetBytePtr(cfd
), l
);
114 fprintf(stderr
, "Error %d writing to %s\n", rtn
, fileName
);
117 fprintf(stdout
, "...wrote %ld bytes to %s\n", l
, fileName
);
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 */
132 *dict
= CFDictionaryCreateMutable(NULL
,
134 &kCFTypeDictionaryKeyCallBacks
,
135 &kCFTypeDictionaryValueCallBacks
);
138 /* OID string to an OID pointer */
139 const CSSM_OID
*oid
= NULL
;
142 oid
= policyStringToOid(policy
, &tls
);
144 return SHOW_USAGE_MESSAGE
;
147 /* OID to SecPolicyRef */
148 SecPolicyRef policyRef
= oidToPolicy(oid
);
149 if(policyRef
== NULL
) {
150 return SHOW_USAGE_MESSAGE
;
152 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicy
, policyRef
);
153 CFRelease(policyRef
);
156 /* app string to SecTrustedApplicationRef */
157 if(appPath
!= NULL
) {
158 SecTrustedApplicationRef appRef
;
159 OSStatus ortn
= SecTrustedApplicationCreateFromPath(appPath
, &appRef
);
161 cssmPerror("SecTrustedApplicationCreateFromPath", ortn
);
164 CFDictionaryAddValue(*dict
, kSecTrustSettingsApplication
, appRef
);
168 if(policyStr
!= NULL
) {
169 CFStringRef pstr
= CFStringCreateWithCString(NULL
, policyStr
, kCFStringEncodingUTF8
);
170 CFDictionaryAddValue(*dict
, kSecTrustSettingsPolicyString
, pstr
);
175 SInt32 ae
= (SInt32
)allowErr
;
176 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ae
);
177 CFDictionaryAddValue(*dict
, kSecTrustSettingsAllowedError
, cfNum
);
182 SInt32 ku
= (SInt32
)keyUse
;
183 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &ku
);
184 CFDictionaryAddValue(*dict
, kSecTrustSettingsKeyUsage
, cfNum
);
188 if(resultType
!= kSecTrustSettingsResultTrustRoot
) {
189 SInt32 rt
= (SInt32
)resultType
;
190 CFNumberRef cfNum
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &rt
);
191 CFDictionaryAddValue(*dict
, kSecTrustSettingsResult
, cfNum
);
200 trusted_cert_add(int argc
, char * const *argv
)
203 return SHOW_USAGE_MESSAGE
;
209 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
211 SecKeychainRef kcRef
= NULL
;
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 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");
242 while ((arg
= getopt(argc
, argv
, "dr:a:p:s:e:u:k:i:o:h")) != -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
;
261 ourRtn
= SHOW_USAGE_MESSAGE
;
267 if (policyNameCount
< MAX_POLICIES
) {
268 policyNames
[policyNameCount
++] = optarg
;
270 fprintf(stderr
, "Too many policy arguments.\n");
271 ourRtn
= SHOW_USAGE_MESSAGE
;
281 if (policyStringCount
< MAX_STRINGS
) {
282 policyStrings
[policyStringCount
++] = optarg
;
284 fprintf(stderr
, "Too many policy string arguments.\n");
285 ourRtn
= SHOW_USAGE_MESSAGE
;
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
297 allowErr
= (CSSM_RETURN
)atoi(optarg
);
299 fprintf(stderr
, "Invalid value for allowed error.\n");
300 ourRtn
= SHOW_USAGE_MESSAGE
;
303 allowedErrors
[allowedErrorCount
++] = allowErr
;
305 fprintf(stderr
, "Too many \"allowed error\" arguments.\n");
306 ourRtn
= SHOW_USAGE_MESSAGE
;
312 keyUse
= (SecTrustSettingsKeyUsage
)atoi(optarg
);
316 kcRef
= keychain_open(optarg
);
323 settingsFileIn
= optarg
;
326 settingsFileOut
= optarg
;
330 ourRtn
= SHOW_USAGE_MESSAGE
;
338 switch(argc
- optind
) {
343 certFile
= argv
[optind
];
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");
357 if((settingsFileOut
!= NULL
) && (domain
!= kSecTrustSettingsDomainUser
)) {
358 fprintf(stderr
, "Can't specify both domain and a settingsFile\n");
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");
369 /* build per-policy constraints dictionaries */
370 if(haveConstraints
) {
372 for (i
=0; i
<policyNameCount
; i
++) {
373 if (!trustSettings
) {
374 trustSettings
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
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
,
389 CFArrayAppendValue(trustSettings
, constraintDict
);
390 CFRelease(constraintDict
); // array retains it
393 } else { // no allowed errors
394 CFMutableDictionaryRef constraintDict
= NULL
;
395 ourRtn
= appendConstraintsToDict(appPath
,
398 resultType
, 0, keyUse
,
401 CFArrayAppendValue(trustSettings
, constraintDict
);
402 CFRelease(constraintDict
); // array retains it
407 } else { // no policy strings
408 if (allowedErrorCount
) {
409 for (k
=0; k
<allowedErrorCount
; k
++) {
410 CFMutableDictionaryRef constraintDict
= NULL
;
411 ourRtn
= appendConstraintsToDict(appPath
,
419 CFArrayAppendValue(trustSettings
, constraintDict
);
420 CFRelease(constraintDict
); // array retains it
423 } else { // no allowed errors
424 CFMutableDictionaryRef constraintDict
= NULL
;
425 ourRtn
= appendConstraintsToDict(appPath
,
428 resultType
, 0, keyUse
,
431 CFArrayAppendValue(trustSettings
, constraintDict
);
432 CFRelease(constraintDict
); // array retains it
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
,
450 CFArrayAppendValue(trustSettings
, constraintDict
);
451 CFRelease(constraintDict
); // array retains it
457 /* optional settings file */
459 settingsIn
= readFileData(settingsFileIn
);
460 if(settingsIn
== NULL
) {
461 fprintf(stderr
, "Error reading file %s\n", settingsFileIn
);
466 else if(settingsFileOut
) {
467 /* output file, no input file - start with empty settings */
468 ortn
= SecTrustSettingsSetTrustSettingsExternal(NULL
,
469 NULL
, NULL
, &settingsIn
);
471 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
477 if(certFile
!= NULL
) {
478 if(readCertFile(certFile
, &certRef
)) {
479 fprintf(stderr
, "Error reading file %s\n", certFile
);
484 /* note we do NOT add by default */
485 ortn
= SecCertificateAddToKeychain(certRef
, kcRef
);
488 case errSecDuplicateItem
: /* that's fine */
491 cssmPerror("SecCertificateAddToKeychain", ortn
);
498 /* now manipulate the Trust Settings */
499 if(settingsFileOut
) {
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.
505 if(certRef
!= NULL
) {
506 ortn
= SecTrustSettingsSetTrustSettingsExternal(settingsIn
,
507 certRef
, trustSettings
, &settingsOut
);
509 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
515 /* no cert data: output := input, e.g. create empty settings file */
516 settingsOut
= settingsIn
;
519 ourRtn
= writeFileData(settingsFileOut
, settingsOut
);
521 fprintf(stderr
, "Error writing to %s\n", settingsFileOut
);
526 /* normal "Add this cert to Trust Settings" */
527 if(certRef
== NULL
) {
528 fprintf(stderr
, "Internal error in trusted_cert_add\n");
532 ortn
= SecTrustSettingsSetTrustSettings(certRef
, domain
, trustSettings
);
534 cssmPerror("SecTrustSettingsSetTrustSettings", ortn
);
539 if(certRef
!= NULL
) {
542 CFRELEASE(trustSettings
);
544 CFRELEASE(settingsIn
);
545 CFRELEASE(settingsOut
);
553 trusted_cert_remove(int argc
, char * const *argv
)
555 OSStatus ortn
= noErr
;
557 SecTrustSettingsDomain domain
= kSecTrustSettingsDomainUser
;
558 SecCertificateRef certRef
= NULL
;
559 char *certFile
= NULL
;
566 while ((arg
= getopt(argc
, argv
, "dh")) != -1) {
569 domain
= kSecTrustSettingsDomainAdmin
;
573 return SHOW_USAGE_MESSAGE
;
577 switch(argc
- optind
) {
582 certFile
= argv
[optind
];
585 return SHOW_USAGE_MESSAGE
;
588 if(certFile
== NULL
) {
589 fprintf(stderr
, "No cert file specified.\n");
590 return SHOW_USAGE_MESSAGE
;
593 if(readCertFile(certFile
, &certRef
)) {
594 fprintf(stderr
, "Error reading file %s\n", certFile
);
598 ortn
= SecTrustSettingsRemoveTrustSettings(certRef
, domain
);
600 cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn
);
604 if(certRef
!= NULL
) {