/* * 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 #include #include #include #include #include #include #include #include /* 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