]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTool/trusted_cert_add.c
Security-57031.1.35.tar.gz
[apple/security.git] / SecurityTool / trusted_cert_add.c
diff --git a/SecurityTool/trusted_cert_add.c b/SecurityTool/trusted_cert_add.c
new file mode 100644 (file)
index 0000000..7c0b9cd
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 2006-2010,2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * trust_cert_add.c
+ */
+
+/*
+ * This command is fairly versatile and hence the usage might be a bit confusing.
+ * The standard usage of this command is to add one or more certs to a Trust
+ * Settings domain, along with optional usage constraints. Often, but not
+ * necessarily, you'd also add the cert to a keychain while you're adding
+ * it to Trust Settings.
+ *
+ * -- To add someRoot.cer to your login keychain and to your Trust Settings as
+ *    an unrestricted root cert:
+ *
+ *    % security add-trusted-cert -k login.keychain someRoot.cer
+ *
+ * -- To add anotherRoot.cer to the local admin trust settings, only for policy
+ *    ssl, without adding it to a keychain (presumably because it's already in
+ *    a keychain somewhere else):
+ *
+ *    % security add-trusted-cert -p ssl -d anotherRoot.cer
+ *
+ * The more obscure uses involve default settings and trust settings files.
+ *
+ * Specifying a default trust setting precludes specifying a cert. Other
+ * options apply as usual; note that if the domain for which you are
+ * specifying a default setting already has a default setting, the old default
+ * will be replaced by the new one you specify.
+ *
+ * -- To specify a default of "deny" for policy SMIME for the admin domain:
+ *
+ *    % security add-trusted-cert -p smime -r deny -D
+ *
+ * This command can also operate on trust settings as files instead of
+ * modifying an actual on-disk Trust Settings record. One standard use for
+ * this function is in the creation of the system Trust Settings, which
+ * are immutable at runtime via the SecTrustSettings API. You provide a
+ * file name for this option via -f settingsFile. If the file does not
+ * exist, a new empty Trust Settings will be created, and certs and/or
+ * a default will be added to that record, and the record will be written
+ * out to the filename you provide (infile = outfile, always).
+ *
+ * -- To create Trust Settings record with one cert in it, restricted to
+ *    policy SSL:
+ *
+ *    % security add-trusted-cert -p ssl -f someTrustSettingsFile.plist -r someRoot.cer
+ *
+ * You can also use the -f option and specify no certs, in which case an empty
+ * Trust Settings record will be created. This can be useful if you want to
+ * quickly reset the Trust Settings in a given domain to "empty"; the
+ * empty Trust Settings record can be imported via the trust-settings-import
+ * command.
+ *
+ * -- To reset the admin trust settings to "empty":
+ *
+ *    % security add-trusted-cert -f emptySettingsFile.plist
+ *    % security trust-settings-import -d emptySettingsFile.plist
+ */
+
+#include "trusted_cert_add.h"
+#include "trusted_cert_utils.h"
+#include "security.h"
+#include "keychain_utilities.h"
+#include <Security/Security.h>
+#include <Security/SecTrust.h>
+#include <Security/SecTrustSettings.h>
+#include <Security/SecTrustSettingsPriv.h>
+#include <Security/oidsalg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+/* r/w files as CFData */
+static CFDataRef readFileData(
+       const char *fileName)
+{
+       unsigned char *d;
+       unsigned dLen;
+
+       if(readFile(fileName, &d, &dLen)) {
+               return NULL;
+       }
+       CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)d, dLen);
+       free(d);
+       return cfd;
+}
+
+static int writeFileData(
+       const char *fileName,
+       CFDataRef cfd)
+{
+       unsigned long l = (unsigned long)CFDataGetLength(cfd);
+       int rtn = writeFile(fileName, CFDataGetBytePtr(cfd), l);
+       if(rtn) {
+               fprintf(stderr, "Error %d writing to %s\n", rtn, fileName);
+       }
+       else if(!do_quiet) {
+               fprintf(stdout, "...wrote %ld bytes to %s\n", l, fileName);
+       }
+       return rtn;
+}
+
+static int appendConstraintsToDict(
+       const char *appPath,                    /* optional */
+       const char *policy,                             /* optional - smime, ssl, etc. */
+       const char *policyStr,                  /* optional policy string */
+       SecTrustSettingsResult resultType,
+       CSSM_RETURN allowErr,                   /* optional allowed error */
+       SecTrustSettingsKeyUsage keyUse,/* optional key use */
+       CFMutableDictionaryRef *dict)   /* result RETURNED here, created if necessary */
+{
+       if(*dict == NULL) {
+               *dict = CFDictionaryCreateMutable(NULL,
+                       0,              // capacity
+                       &kCFTypeDictionaryKeyCallBacks,
+                       &kCFTypeDictionaryValueCallBacks);
+       }
+
+       /* OID string to an OID pointer */
+       const CSSM_OID *oid = NULL;
+       if(policy != NULL) {
+               oid = policyStringToOid(policy);
+               if(oid == NULL) {
+                       return 2;
+               }
+
+               /* OID to SecPolicyRef */
+               SecPolicyRef policyRef = oidToPolicy(oid);
+               if(policyRef == NULL) {
+                       return 2;
+               }
+               CFDictionaryAddValue(*dict, kSecTrustSettingsPolicy, policyRef);
+               CFRelease(policyRef);
+       }
+
+       /* app string to SecTrustedApplicationRef */
+       if(appPath != NULL) {
+               SecTrustedApplicationRef appRef;
+               OSStatus ortn = SecTrustedApplicationCreateFromPath(appPath, &appRef);
+               if(ortn) {
+                       cssmPerror("SecTrustedApplicationCreateFromPath", ortn);
+                       return -1;
+               }
+               CFDictionaryAddValue(*dict, kSecTrustSettingsApplication, appRef);
+               CFRelease(appRef);
+       }
+
+       if(policyStr != NULL) {
+               CFStringRef pstr = CFStringCreateWithCString(NULL, policyStr, kCFStringEncodingUTF8);
+               CFDictionaryAddValue(*dict, kSecTrustSettingsPolicyString, pstr);
+               CFRelease(pstr);
+       }
+
+       if(allowErr) {
+               SInt32 ae = (SInt32)allowErr;
+               CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &ae);
+               CFDictionaryAddValue(*dict, kSecTrustSettingsAllowedError, cfNum);
+               CFRelease(cfNum);
+       }
+
+       if(keyUse != 0) {
+               SInt32 ku = (SInt32)keyUse;
+               CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &ku);
+               CFDictionaryAddValue(*dict, kSecTrustSettingsKeyUsage, cfNum);
+               CFRelease(cfNum);
+       }
+
+       if(resultType != kSecTrustSettingsResultTrustRoot) {
+               SInt32 rt = (SInt32)resultType;
+               CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &rt);
+               CFDictionaryAddValue(*dict, kSecTrustSettingsResult, cfNum);
+               CFRelease(cfNum);
+       }
+
+       return 0;
+}
+
+
+int
+trusted_cert_add(int argc, char * const *argv)
+{
+       extern char *optarg;
+       extern int optind;
+       OSStatus ortn;
+       int arg;
+       SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
+       int ourRtn = 0;
+       SecKeychainRef kcRef = NULL;
+       int defaultSetting = 0;
+       char *certFile = NULL;
+       SecCertificateRef certRef = NULL;
+
+       /* for operating in file-based settings */
+       char *settingsFileIn = NULL;
+       char *settingsFileOut = NULL;
+       CFDataRef settingsIn = NULL;
+       CFDataRef settingsOut = NULL;
+
+       /* optional usage constraints */
+//     char *policy = NULL;
+       char *appPath = NULL;
+//     char *policyString = NULL;
+       SecTrustSettingsResult resultType = kSecTrustSettingsResultTrustRoot;
+       CSSM_RETURN allowErr = CSSM_OK;
+       SecTrustSettingsKeyUsage keyUse = 0;
+       CFMutableArrayRef trustSettings = NULL;
+       int haveConstraints = 0;
+
+       const int maxPolicies = 16; // upper limit on policies that can be set in one invocation
+       char *policyNames[maxPolicies];
+       char *policyStrings[maxPolicies];
+       int allowedErrors[maxPolicies];
+       int policyNameCount = 0, policyStringCount = 0, allowedErrorCount = 0;
+
+       if(argc < 2) {
+               return 2; /* @@@ Return 2 triggers usage message. */
+       }
+
+       optind = 1;
+       while ((arg = getopt(argc, argv, "dr:a:p:s:e:u:k:i:o:Dh")) != -1) {
+               switch (arg) {
+                       case 'd':
+                               domain = kSecTrustSettingsDomainAdmin;
+                               break;
+                       case 'r':
+                               if(!strcmp(optarg, "trustRoot")) {
+                                       resultType = kSecTrustSettingsResultTrustRoot;
+                               }
+                               else if(!strcmp(optarg, "trustAsRoot")) {
+                                       resultType = kSecTrustSettingsResultTrustAsRoot;
+                               }
+                               else if(!strcmp(optarg, "deny")) {
+                                       resultType = kSecTrustSettingsResultDeny;
+                               }
+                               else if(!strcmp(optarg, "unspecified")) {
+                                       resultType = kSecTrustSettingsResultUnspecified;
+                               }
+                               else {
+                                       return 2;
+                               }
+                               haveConstraints = 1;
+                               break;
+                       case 'p':
+                               if (policyNameCount < maxPolicies) {
+                                       policyNames[policyNameCount++] = optarg;
+                               } else {
+                                       fprintf(stderr, "Too many policy arguments.\n");
+                                       return 2;
+                               }
+                               haveConstraints = 1;
+                               break;
+                       case 'a':
+                               appPath = optarg;
+                               haveConstraints = 1;
+                               break;
+                       case 's':
+                               if (policyStringCount < maxPolicies) {
+                                       policyStrings[policyStringCount++] = optarg;
+                               } else {
+                                       fprintf(stderr, "Too many policy string arguments.\n");
+                                       return 2;
+                               }
+                               haveConstraints = 1;
+                               break;
+                       case 'e':
+                               if (allowedErrorCount < maxPolicies) {
+                                       if (!strcmp("certExpired", optarg))
+                                               allowErr = -2147409654; // 0x8001210A = CSSMERR_TP_CERT_EXPIRED
+                                       else if (!strcmp("hostnameMismatch", optarg))
+                                               allowErr = -2147408896; // 0x80012400 = CSSMERR_APPLETP_HOSTNAME_MISMATCH
+                                       else
+                                               allowErr = (CSSM_RETURN)atoi(optarg);
+                                       if (!allowErr) {
+                                               fprintf(stderr, "Invalid value for allowed error.\n");
+                                               return 2;
+                                       }
+                                       allowedErrors[allowedErrorCount++] = allowErr;
+                               } else {
+                                       fprintf(stderr, "Too many \"allowed error\" arguments.\n");
+                                       return 2;
+                               }
+                               haveConstraints = 1;
+                               break;
+                       case 'u':
+                               keyUse = (SecTrustSettingsKeyUsage)atoi(optarg);
+                               haveConstraints = 1;
+                               break;
+                       case 'k':
+                               kcRef = keychain_open(optarg);
+                               if(kcRef == NULL) {
+                                       return 1;
+                               }
+                               break;
+                       case 'i':
+                               settingsFileIn = optarg;
+                               break;
+                       case 'o':
+                               settingsFileOut = optarg;
+                               break;
+                       case 'D':
+                               defaultSetting = 1;
+                               break;
+                       default:
+                       case 'h':
+                               return 2; /* @@@ Return 2 triggers usage message. */
+               }
+       }
+       if(ourRtn) {
+               goto errOut;
+       }
+
+       switch(argc - optind) {
+               case 0:
+                       /* no certs */
+                       break;
+               case 1:
+                       certFile = argv[optind];
+                       break;
+               default:
+                       ourRtn = 2;
+                       goto errOut;
+       }
+
+       /* validate inputs */
+       if(defaultSetting && (certFile != NULL)) {
+               fprintf(stderr, "Can't specify cert when manipulating default setting.\n");
+               ourRtn = 2; /* @@@ Return 2 triggers usage message. */
+               goto errOut;
+       }
+       if((certFile == NULL) && (settingsFileOut == NULL) && !defaultSetting) {
+               /* no cert file - only legal for r/w file or for default settings */
+               fprintf(stderr, "No cert file specified.\n");
+               ourRtn = 2;
+               goto errOut;
+       }
+       if((settingsFileOut != NULL) && (domain != kSecTrustSettingsDomainUser)) {
+               fprintf(stderr, "Can't specify both domain and a settingsFile\n");
+               ourRtn = 2;
+               goto errOut;
+       }
+       if((settingsFileIn != NULL) && (settingsFileOut == NULL)) {
+               /* on the other hand, fileOut with no fileIn is OK */
+               fprintf(stderr, "Can't specify settingsFileIn and no settingsFileOut\n");
+               ourRtn = 2;
+               goto errOut;
+       }
+
+       /* build per-policy constraints dictionaries */
+       if(haveConstraints) {
+               int i, j, k;
+               for (i=0; i<policyNameCount; i++) {
+                       if (!trustSettings) {
+                               trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+                       }
+                       if (policyStringCount) {
+                               for (j=0; j<policyStringCount; j++) {
+                                       if (allowedErrorCount) {
+                                               for (k=0; k<allowedErrorCount; k++) {
+                                                       CFMutableDictionaryRef constraintDict = NULL;
+                                                       ourRtn = appendConstraintsToDict(appPath,
+                                                                                                                        policyNames[i],
+                                                                                                                        policyStrings[j],
+                                                                                                                        resultType,
+                                                                                                                        allowedErrors[k],
+                                                                                                                        keyUse,
+                                                                                                                        &constraintDict);
+                                                       if (!ourRtn) {
+                                                               CFArrayAppendValue(trustSettings, constraintDict);
+                                                               CFRelease(constraintDict); // array retains it
+                                                       }
+                                               }
+                                       } else { // no allowed errors
+                                               CFMutableDictionaryRef constraintDict = NULL;
+                                               ourRtn = appendConstraintsToDict(appPath,
+                                                                                                                policyNames[i],
+                                                                                                                policyStrings[j],
+                                                                                                                resultType, 0, keyUse,
+                                                                                                                &constraintDict);
+                                               if (!ourRtn) {
+                                                       CFArrayAppendValue(trustSettings, constraintDict);
+                                                       CFRelease(constraintDict); // array retains it
+                                               }
+
+                                       }
+                               }
+                       } else { // no policy strings
+                               if (allowedErrorCount) {
+                                       for (k=0; k<allowedErrorCount; k++) {
+                                               CFMutableDictionaryRef constraintDict = NULL;
+                                               ourRtn = appendConstraintsToDict(appPath,
+                                                                                                                policyNames[i],
+                                                                                                                NULL,
+                                                                                                                resultType,
+                                                                                                                allowedErrors[k],
+                                                                                                                keyUse,
+                                                                                                                &constraintDict);
+                                               if (!ourRtn) {
+                                                       CFArrayAppendValue(trustSettings, constraintDict);
+                                                       CFRelease(constraintDict); // array retains it
+                                               }
+                                       }
+                               } else { // no allowed errors
+                                       CFMutableDictionaryRef constraintDict = NULL;
+                                       ourRtn = appendConstraintsToDict(appPath,
+                                                                                                        policyNames[i],
+                                                                                                        NULL,
+                                                                                                        resultType, 0, keyUse,
+                                                                                                        &constraintDict);
+                                       if (!ourRtn) {
+                                               CFArrayAppendValue(trustSettings, constraintDict);
+                                               CFRelease(constraintDict); // array retains it
+                                       }
+                               }
+                       }
+                       if(ourRtn) {
+                               goto errOut;
+                       }
+               }
+       }
+
+       /* handle the case where no policies were specified */
+       if(haveConstraints && !trustSettings) {
+               CFMutableDictionaryRef constraintDict = NULL;
+               trustSettings = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+               ourRtn = appendConstraintsToDict(appPath, NULL, NULL,
+                                                                                resultType, allowErr, keyUse,
+                                                                                &constraintDict);
+               if (!ourRtn) {
+                       CFArrayAppendValue(trustSettings, constraintDict);
+                       CFRelease(constraintDict); // array retains it
+               } else {
+                       goto errOut;
+               }
+       }
+
+       /* optional settings file */
+       if(settingsFileIn) {
+               settingsIn = readFileData(settingsFileIn);
+               if(settingsIn == NULL) {
+                       fprintf(stderr, "Error reading file %s\n", settingsFileIn);
+                       ourRtn = 1;
+                       goto errOut;
+               }
+       }
+       else if(settingsFileOut) {
+               /* output file, no input file - start with empty settings */
+               ortn = SecTrustSettingsSetTrustSettingsExternal(NULL,
+                       NULL, NULL, &settingsIn);
+               if(ortn) {
+                       cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
+                       ourRtn = 1;
+                       goto errOut;
+               }
+       }
+
+       /* optional cert file */
+       if(defaultSetting) {
+               /* we don't have a cert; use this instead... */
+               certRef = kSecTrustSettingsDefaultRootCertSetting;
+       }
+       else if(certFile != NULL) {
+               if(readCertFile(certFile, &certRef)) {
+                       fprintf(stderr, "Error reading file %s\n", certFile);
+                       ourRtn = 1;
+                       goto errOut;
+               }
+               if(kcRef) {
+                       /* note we do NOT add by default */
+                       ortn = SecCertificateAddToKeychain(certRef, kcRef);
+                       switch(ortn) {
+                               case noErr:
+                               case errSecDuplicateItem:               /* that's fine */
+                                       break;
+                               default:
+                                       cssmPerror("SecCertificateAddToKeychain", ortn);
+                                       ourRtn = 1;
+                                       goto errOut;
+                       }
+               }
+       }
+
+       /* now manipulate the Trust Settings */
+       if(settingsFileOut) {
+               /*
+                * Operating on file data, not actual domain.
+                * At this point settingsIn is the current settings data; it
+                * may be empty but it's valid nonetheless.
+                */
+               if(certRef != NULL) {
+                       ortn = SecTrustSettingsSetTrustSettingsExternal(settingsIn,
+                               certRef, trustSettings, &settingsOut);
+                       if(ortn) {
+                               cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
+                               ourRtn = 1;
+                               goto errOut;
+                       }
+               }
+               else {
+                       /* no cert data: output := input, e.g. create empty settings file */
+                       settingsOut = settingsIn;
+                       settingsIn = NULL;
+               }
+               ourRtn = writeFileData(settingsFileOut, settingsOut);
+               if(ourRtn) {
+                       fprintf(stderr, "Error writing to %s\n", settingsFileOut);
+                       goto errOut;
+               }
+       }
+       else {
+               /* normal "Add this cert to Trust Settings" */
+               if(certRef == NULL) {
+                       fprintf(stderr, "Internal error in trusted_cert_add\n");
+                       ourRtn = 1;
+                       goto errOut;
+               }
+               ortn = SecTrustSettingsSetTrustSettings(certRef, domain, trustSettings);
+               if(ortn) {
+                       cssmPerror("SecTrustSettingsSetTrustSettings", ortn);
+                       ourRtn = 1;
+               }
+       }
+errOut:
+       if((certRef != NULL) & (certRef != kSecTrustSettingsDefaultRootCertSetting)) {
+               CFRelease(certRef);
+       }
+       CFRELEASE(trustSettings);
+       CFRELEASE(kcRef);
+       CFRELEASE(settingsIn);
+       CFRELEASE(settingsOut);
+       return ourRtn;
+}
+
+int
+trusted_cert_remove(int argc, char * const *argv)
+{
+       OSStatus ortn = noErr;
+       int ourRtn = 0;
+       SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser;
+       int defaultSetting = 0;
+       SecCertificateRef certRef = NULL;
+       char *certFile = NULL;
+
+       extern char *optarg;
+       extern int optind;
+       int arg;
+
+       optind = 1;
+       while ((arg = getopt(argc, argv, "dDh")) != -1) {
+               switch (arg) {
+                       case 'd':
+                               domain = kSecTrustSettingsDomainAdmin;
+                               break;
+                       case 'D':
+                               defaultSetting = 1;
+                               break;
+                       default:
+                       case 'h':
+                               return 2; /* @@@ Return 2 triggers usage message. */
+               }
+       }
+
+       switch(argc - optind) {
+               case 0:
+                       /* no certs */
+                       break;
+               case 1:
+                       certFile = argv[optind];
+                       break;
+               default:
+                       return 2;
+       }
+
+       if((certFile == NULL) && !defaultSetting) {
+               fprintf(stderr, "No cert file specified.\n");
+               return 2;
+       }
+       if((certFile != NULL) && defaultSetting) {
+               fprintf(stderr, "Can't specify cert when manipulating default setting.\n");
+               return 2;
+       }
+
+       if(defaultSetting) {
+               /* we don't have a cert; use this instead... */
+               certRef = kSecTrustSettingsDefaultRootCertSetting;
+       }
+       else {
+               if(readCertFile(certFile, &certRef)) {
+                       fprintf(stderr, "Error reading file %s\n", certFile);
+                       return 1;
+               }
+       }
+
+       ortn = SecTrustSettingsRemoveTrustSettings(certRef, domain);
+       if(ortn) {
+               cssmPerror("SecTrustSettingsRemoveTrustSettings", ortn);
+               ourRtn = 1;
+       }
+
+       if((certRef != NULL) & (certRef != kSecTrustSettingsDefaultRootCertSetting)) {
+               CFRelease(certRef);
+       }
+
+       return ourRtn;
+}