X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/rootStoreTool/rootStoreTool.cpp diff --git a/SecurityTests/clxutils/rootStoreTool/rootStoreTool.cpp b/SecurityTests/clxutils/rootStoreTool/rootStoreTool.cpp new file mode 100644 index 00000000..eb54e571 --- /dev/null +++ b/SecurityTests/clxutils/rootStoreTool/rootStoreTool.cpp @@ -0,0 +1,932 @@ +/* + * rootStoreTool.cpp - exercise SecTrustSettings API + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "parseTrustedRootList.h" +#include /* private header */ +#include "rootUtils.h" +#include +#include +#include + +static void usage(char **argv) +{ + printf("usage: %s op [options]\n", argv[0]); + printf("Op values:\n"); + printf(" a -- add cert\n"); + printf(" p -- parse TrustSettings record\n"); + printf(" r -- get certs from TS & display\n"); + printf(" d -- delete entries from TS interactively\n"); + printf(" D -- delete ALL certs from TS (requires -R argument)\n"); + printf(" R -- remove legacy User Trust setting\n"); + + printf("Options:\n"); + printf(" -c certFile -- specify cert\n"); + printf(" -s -- system TrustSettings; default is user\n"); + printf(" -d -- Admin TrustSettings; default is user\n"); + printf(" -t settingsFile -- settings from file; default is user\n"); + printf(" -T settingsFileOut -- settings to file\n"); + printf(" -a appPath -- specify app constraints\n"); + printf(" -p policy -- specify policy constraint\n"); + printf(" policy = ssl, smime, swuSign, codeSign, IPSec, iChat\n"); + printf(" -P appPath policy -- specify app AND policy constraint\n"); + printf(" -e emailAddress -- specify SMIME policy plus email address\n"); + printf(" -L hostname -- specify SSL policy plus hostname\n"); + printf(" -r resultType -- resultType = trust, trustAsRoot, deny, unspecified\n"); + printf(" -w allowErr -- allowed error, an integer; implies result unspecified\n"); + printf(" -W allowErr policy -- allowed error AND policy AND implies result unspecified\n"); + printf(" -u keyUsage -- key usage, an integer\n"); + printf(" -k keychain -- Default is default keychain.\n"); + printf(" -R -- Really. For Delete All op.\n"); + printf(" -v -- verbose cert display\n"); + printf(" -A -- add cert to keychain\n"); + printf(" -U -- use SecTrustSetUserTrust\n"); + printf(" -2 -- use SecTrustSetUserTrustLegacy\n"); + printf(" -l -- loop and pause for malloc debug\n"); + printf(" -h -- help\n"); + exit(1); +} + +/* + * Start up a CFRunLoop. This is needed to field keychain event callbacks, used + * to maintain root cert cache coherency. This operation is only needed in command + * line tools; regular GUI apps already have a CFRunLoop. + */ + +/* first we need something to register so we *have* a run loop */ +static OSStatus kcCacheCallback ( + SecKeychainEvent keychainEvent, + SecKeychainCallbackInfo *info, + void *context) +{ + return noErr; +} + +/* main thread has to wait for this to be set to know a run loop has been set up */ +static int runLoopInitialized = 0; + +/* this is the thread which actually runs the CFRunLoop */ +void *cfRunLoopThread(void *arg) +{ + OSStatus ortn = SecKeychainAddCallback(kcCacheCallback, + kSecTrustSettingsChangedEventMask, NULL); + if(ortn) { + printf("registerCacheCallbacks: SecKeychainAddCallback returned %ld", ortn); + /* Not sure how this could ever happen - maybe if there is no run loop active? */ + return NULL; + } + runLoopInitialized = 1; + CFRunLoopRun(); + /* should not be reached */ + printf("\n*** Hey! CFRunLoopRun() exited!***\n"); + return NULL; +} + +static int startCFRunLoop() +{ + pthread_t runLoopThread; + + int result = pthread_create(&runLoopThread, NULL, cfRunLoopThread, NULL); + if(result) { + printf("***pthread_create returned %d, aborting\n", result); + return -1; + } + return 0; +} + +static SecCertificateRef certFromFile( + const char *fileName) +{ + unsigned char *cp = NULL; + unsigned len = 0; + if(readFile(fileName, &cp, &len)) { + printf("***Error reading file %s\n", fileName); + return NULL; + } + SecCertificateRef certRef; + CSSM_DATA certData = {len, cp}; + OSStatus ortn = SecCertificateCreateFromData(&certData, + CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef); + if(ortn) { + cssmPerror("SecCertificateCreateFromData", ortn); + return NULL; + } + free(cp); + return certRef; +} + +/* + * Display usage constraints array as obtained from + * SecTrustSettingsCopyTrustSettings(). + */ +static int displayTrustSettings( + CFArrayRef trustSettings, + OidParser &parser) +{ + /* must always be there though it may be empty */ + if(trustSettings == NULL) { + printf("***displayTrustSettings: missing trust settings array"); + return -1; + } + if(CFGetTypeID(trustSettings) != CFArrayGetTypeID()) { + printf("***displayTrustSettings: malformed trust settings array"); + return -1; + } + + int ourRtn = 0; + CFIndex numUseConstraints = CFArrayGetCount(trustSettings); + indentIncr(); + indent(); printf("Number of trust settings : %ld\n", numUseConstraints); + OSStatus ortn; + SecPolicyRef certPolicy; + SecTrustedApplicationRef certApp; + CFDictionaryRef ucDict; + CFStringRef policyStr; + CFNumberRef cfNum; + + /* grind thru the trust settings dictionaries */ + for(CFIndex ucDex=0; ucDex appPath; + ortn = SecTrustedApplicationCopyData(certApp, appPath.take()); + if(ortn) { + cssmPerror("SecTrustedApplicationCopyData", ortn); + ourRtn = -1; + goto nextAp; + } + indent(); printf("Application : %s", CFDataGetBytePtr(appPath)); + printf("\n"); + } + + /* policy string */ + policyStr = (CFStringRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsPolicyString); + if(policyStr != NULL) { + if(CFGetTypeID(policyStr) != CFStringGetTypeID()) { + printf("***displayTrustSettings: malformed policyStr"); + ourRtn = -1; + goto nextAp; + } + indent(); printf("Policy String : "); + printCfStr(policyStr); printf("\n"); + } + + /* Allowed error */ + cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsAllowedError); + if(cfNum != NULL) { + if(CFGetTypeID(cfNum) != CFNumberGetTypeID()) { + printf("***displayTrustSettings: malformed allowedError"); + ourRtn = -1; + goto nextAp; + } + indent(); printf("Allowed Error : "); + printCssmErr(cfNum); printf("\n"); + } + + /* Result */ + cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsResult); + if(cfNum != NULL) { + if(CFGetTypeID(cfNum) != CFNumberGetTypeID()) { + printf("***displayTrustSettings: malformed Result"); + ourRtn = -1; + goto nextAp; + } + indent(); printf("Result Type : "); + printResult(cfNum); printf("\n"); + } + + /* key usage */ + cfNum = (CFNumberRef)CFDictionaryGetValue(ucDict, kSecTrustSettingsKeyUsage); + if(cfNum != NULL) { + if(CFGetTypeID(cfNum) != CFNumberGetTypeID()) { + printf("***displayTrustSettings: malformed keyUsage"); + ourRtn = -1; + goto nextAp; + } + indent(); printf("Key Usage : "); + printKeyUsage(cfNum); printf("\n"); + } + + nextAp: + indentDecr(); + } + indentDecr(); + return ourRtn; +} + +/* convert an OID to a SecPolicyRef */ +static SecPolicyRef oidToPolicy( + const CSSM_OID &oid) +{ + SecPolicyRef policyRef = NULL; + + OSStatus ortn = SecPolicyCopy(CSSM_CERT_X_509v3, &oid, &policyRef); + if(ortn) { + cssmPerror("SecPolicyCopy", ortn); + return NULL; + } + return policyRef; +} + +/* Convert cmdline policy string to SecPolicyRef */ +static SecPolicyRef policyStringToPolicy( + const char *policy) +{ + if(policy == NULL) { + return NULL; + } + const CSSM_OID *oid = NULL; + if(!strcmp(policy, "ssl")) { + oid = &CSSMOID_APPLE_TP_SSL; + } + else if(!strcmp(policy, "smime")) { + oid = &CSSMOID_APPLE_TP_SMIME; + } + else if(!strcmp(policy, "codeSign")) { + oid = &CSSMOID_APPLE_TP_CODE_SIGNING; + } + else if(!strcmp(policy, "swuSign")) { + oid = &CSSMOID_APPLE_TP_SW_UPDATE_SIGNING; + } + else if(!strcmp(policy, "IPSec")) { + oid = &CSSMOID_APPLE_TP_IP_SEC; + } + else if(!strcmp(policy, "iChat")) { + oid = &CSSMOID_APPLE_TP_ICHAT; + } + else { + printf("***Unknown policy string (%s)\n", policy); + return NULL; + } + + /* OID to SecPolicyRef */ + return oidToPolicy(*oid); +} + +static int appendConstraintToArray( + const char *appPath, /* optional, "-" means ensure apArray is nonempty */ + const char *policy, /* optional (ssl/smime), "-" as above */ + const char *policyStr, /* optional policy string */ + const SInt32 *allowErr, /* optional allowed error */ + const char *resultType, /* optional allow/confirm/deny */ + SecTrustSettingsKeyUsage keyUse, /* optional key use */ + CFMutableArrayRef &array) /* result RETURNED here, created if necessary */ +{ + if(array == NULL) { + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + } + + CFMutableDictionaryRef outDict = CFDictionaryCreateMutable(NULL, + 0, // capacity + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if((policy != NULL) && (strcmp(policy, "-"))) { + + /* policy string to SecPolicyRef */ + SecPolicyRef policyRef = policyStringToPolicy(policy); + if(policyRef == NULL) { + return -1; + } + CFDictionaryAddValue(outDict, kSecTrustSettingsPolicy, policyRef); + CFRelease(policyRef); + } + + /* app string to SecTrustedApplicationRef */ + if((appPath != NULL) && (strcmp(appPath, "-"))) { + SecTrustedApplicationRef appRef; + OSStatus ortn = SecTrustedApplicationCreateFromPath(appPath, &appRef); + if(ortn) { + cssmPerror("SecTrustedApplicationCreateFromPath", ortn); + return -1; + } + CFDictionaryAddValue(outDict, kSecTrustSettingsApplication, appRef); + CFRelease(appRef); + } + + if(policyStr != NULL) { + CFStringRef pstr = CFStringCreateWithCString(NULL, policyStr, kCFStringEncodingASCII); + CFDictionaryAddValue(outDict, kSecTrustSettingsPolicyString, pstr); + CFRelease(pstr); + } + + if(allowErr != NULL) { + CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, allowErr); + CFDictionaryAddValue(outDict, kSecTrustSettingsAllowedError, cfNum); + CFRelease(cfNum); + } + + if(keyUse != 0) { + SInt32 ku = (SInt32)ku; + CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &ku); + CFDictionaryAddValue(outDict, kSecTrustSettingsKeyUsage, cfNum); + CFRelease(cfNum); + } + + if(resultType != NULL) { + SInt32 n; + + if(!strcmp(resultType, "trust")) { + n = kSecTrustSettingsResultTrustRoot; + } + else if(!strcmp(resultType, "trustAsRoot")) { + n = kSecTrustSettingsResultTrustAsRoot; + } + else if(!strcmp(resultType, "deny")) { + n = kSecTrustSettingsResultDeny; + } + else if(!strcmp(resultType, "unspecified")) { + n = kSecTrustSettingsResultUnspecified; + } + else { + printf("***unknown resultType spec (%s)\n", resultType); + return -1; + } + CFNumberRef cfNum = CFNumberCreate(NULL, kCFNumberSInt32Type, &n); + CFDictionaryAddValue(outDict, kSecTrustSettingsResult, cfNum); + CFRelease(cfNum); + } + + /* append dictionary to output */ + CFArrayAppendValue(array, outDict); + /* array owns the dictionary now */ + CFRelease(outDict); + return 0; +} + +/* read a file --> CFDataRef */ +CFDataRef readFileCFData( + const char *fileName) +{ + int rtn; + unsigned char *fileData = NULL; + unsigned fileDataLen = 0; + + rtn = readFile(fileName, &fileData, &fileDataLen); + if(rtn) { + printf("Error (%d) reading %s.\n", rtn, fileName); + return NULL; + } + CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)fileData, fileDataLen); + free(fileData); + return cfd; +} + +static int fetchParseTrustRecord( + SecTrustSettingsDomain domain, + char *settingsFile) /* optional, ignore domain if present */ +{ + CFDataRef trustSettings = NULL; + + if(settingsFile) { + trustSettings = readFileCFData(settingsFile); + if(trustSettings == NULL) { + return -1; + } + } + else { + OSStatus ortn = SecTrustSettingsCreateExternalRepresentation(domain, &trustSettings); + if(ortn) { + cssmPerror("SecTrustSettingsCreateExternalRepresentation", ortn); + return -1; + } + } + int rtn = parseTrustedRootList(trustSettings); + CFRelease(trustSettings); + return rtn; +} + +static int copyCertsAndDisplay( + bool verbose, + SecTrustSettingsDomain domain) +{ + OSStatus ortn; + + auto_ptr parser(NULL); + + if(verbose) { + parser.reset(new OidParser); + } + + CFArrayRef certArray = NULL; + ortn = SecTrustSettingsCopyCertificates(domain, &certArray); + if(ortn) { + cssmPerror("SecTrustSettingsCopyCertificates", ortn); + return ortn; + } + + CFIndex numCerts = CFArrayGetCount(certArray); + indent(); + printf("Num certs = %ld\n", numCerts); + int ourRtn = 0; + for(CFIndex dex=0; dex appPolicies; + ortn = SecTrustSettingsCopyTrustSettings(certRef, domain, appPolicies.take()); + if(ortn) { + cssmPerror("SecRootCertificateCopyAppPolicyConstraints", ortn); + ourRtn = -1; + continue; + } + if(displayTrustSettings(appPolicies, *parser.get())) { + ourRtn = -1; + } + } + } + CFRelease(certArray); + return ourRtn; +} + +static int deleteCerts( + SecTrustSettingsDomain domain, + bool deleteAll) +{ + OSStatus ortn; + + CFArrayRef certArray = NULL; + ortn = SecTrustSettingsCopyCertificates(domain, &certArray); + if(ortn) { + cssmPerror("SecTrustSettingsCopyCertificates", ortn); + return ortn; + } + + CFIndex numCerts = CFArrayGetCount(certArray); + unsigned numDeleted = 0; + + for(CFIndex dex=0; dex")); + } + if(settingsIn) { + ortn = SecTrustSettingsSetTrustSettingsExternal(settingsIn, + certRef, trustSettings, settingsOut); + if(ortn) { + cssmPerror("SecTrustSettingsSetTrustSettingsExternal", ortn); + return -1; + } + } + else { + ortn = SecTrustSettingsSetTrustSettings(certRef, domain, trustSettings); + if(ortn) { + cssmPerror("SecTrustSettingsSetTrustSettings", ortn); + return -1; + } + printf("...cert added to %s TrustList.\n", domainName); + } + return 0; +} + +static int addCertLegacy( + SecCertificateRef certRef, + const char *policy, + const char *resultStr, + bool useLegacy) +{ + /* OID string to an OID pointer */ + if(policy == NULL) { + printf("***You must specify a policy to set legacy User Trust\n"); + return 1; + } + SecPolicyRef policyRef = policyStringToPolicy(policy); + if(policyRef == NULL) { + return -1; + } + + /* result string to legacy SecTrustUserSetting */ + SecTrustUserSetting setting = kSecTrustResultInvalid; + if(resultStr == NULL) { + setting = kSecTrustResultProceed; + } + else if(!strcmp(resultStr, "trust")) { + setting = kSecTrustResultProceed; + } + else if(!strcmp(resultStr, "trustAsRoot")) { + setting = kSecTrustResultProceed; + } + else if(!strcmp(resultStr, "deny")) { + setting = kSecTrustResultDeny; + } + else if (!strcmp(resultStr, "unspecified")) { + setting = kSecTrustResultUnspecified; + } + else { + printf("***Can't map %s to a SecTrustUserSetting\n", resultStr); + return -1; + } + OSStatus ortn; + if(useLegacy) { + ortn = SecTrustSetUserTrustLegacy(certRef, policyRef, setting); + if(ortn) { + cssmPerror("SecTrustSetUserTrustLegacy", ortn); + } + else { + if(setting == kSecTrustResultUnspecified) { + printf("...User Trust removed via SecTrustSetUserTrustLegacy().\n"); + } + else { + printf("...User Trust set via SecTrustSetUserTrustLegacy().\n"); + } + } + } + else { + #if 1 + printf("...Legacy implementation needs Makefile work to avoid deprecation error\n"); + exit(1); + #else + ortn = SecTrustSetUserTrust(certRef, policyRef, setting); + if(ortn) { + cssmPerror("SecTrustSetUserTrust", ortn); + } + else { + printf("...trust setting set via SecTrustSetUserTrust().\n"); + } + #endif + } + if(policyRef != NULL) { + CFRelease(policyRef); + } + return ortn; +} + +int main(int argc, char **argv) +{ + int arg; + CFMutableArrayRef appPolicies = NULL; + CFDataRef settingsIn = NULL; + CFDataRef settingsOut = NULL; + + /* user-spec'd variables */ + bool loopPause = false; + bool really = false; + bool verbose = false; + char *kcName = NULL; + SecTrustSettingsDomain domain = kSecTrustSettingsDomainUser; + SecCertificateRef certRef = NULL; + bool addToKeychain = false; + char *settingsFileIn = NULL; + char *settingsFileOut = NULL; + bool userTrustLegacy = false; + char *policyStr = NULL; + char *resultStr = NULL; + bool userTrust = false; + + extern char *optarg; + extern int optind; + optind = 2; + while ((arg = getopt(argc, argv, "c:sdt:T:a:p:P:e:L:r:w:W:k:u:RvAU2lh")) != -1) { + switch (arg) { + case 'c': + if(certRef) { + printf("***Only one cert at a time, please.\n"); + usage(argv); + } + certRef = certFromFile(optarg); + if(certRef == NULL) { + exit(1); + } + break; + case 's': + domain = kSecTrustSettingsDomainSystem; + break; + case 'd': + domain = kSecTrustSettingsDomainAdmin; + break; + case 't': + settingsFileIn = optarg; + break; + case 'T': + settingsFileOut = optarg; + break; + case 'a': + if(appendConstraintToArray(optarg, NULL, NULL, NULL, NULL, 0, appPolicies)) { + exit(1); + } + break; + case 'p': + if(appendConstraintToArray(NULL, optarg, NULL, NULL, NULL, 0, appPolicies)) { + exit(1); + } + policyStr = optarg; + break; + case 'P': + /* this takes an additional argument */ + if(optind > (argc - 1)) { + usage(argv); + } + if(appendConstraintToArray(optarg, argv[optind], NULL, NULL, NULL, + 0, appPolicies)) { + exit(1); + } + optind++; + break; + case 'e': + if(appendConstraintToArray(NULL, "smime", optarg, NULL, NULL, + 0, appPolicies)) { + exit(1); + } + policyStr = "smime"; + break; + case 'L': + if(appendConstraintToArray(NULL, "ssl", optarg, NULL, NULL, 0, appPolicies)) { + exit(1); + } + policyStr = "ssl"; + break; + case 'r': + if(appendConstraintToArray(NULL, NULL, NULL, NULL, optarg, 0, appPolicies)) { + exit(1); + } + resultStr = optarg; + break; + case 'w': + { + SInt32 l = atol(optarg); + if(appendConstraintToArray(NULL, NULL, NULL, &l, "unspecified", 0, appPolicies)) { + exit(1); + } + break; + } + case 'W': + { + /* this takes an additional argument */ + if(optind > (argc - 1)) { + usage(argv); + } + SInt32 l = atol(optarg); + if(appendConstraintToArray(NULL, argv[optind], NULL, &l, "unspecified", 0, + appPolicies)) { + exit(1); + } + optind++; + break; + } + case 'u': + { + SInt32 l = atol(optarg); + SecTrustSettingsKeyUsage ku = (SecTrustSettingsKeyUsage)l; + if(appendConstraintToArray(NULL, NULL, NULL, NULL, NULL, ku, appPolicies)) { + exit(1); + } + break; + } + case 'k': + kcName = optarg; + break; + case 'R': + really = true; + break; + case 'v': + verbose = true; + break; + case 'A': + addToKeychain = true; + break; + case 'l': + loopPause = true; + break; + case '2': + userTrustLegacy = true; + break; + case 'U': + userTrust = true; + break; + default: + case 'h': + usage(argv); + } + } + if(optind != argc) { + usage(argv); + } + if(startCFRunLoop()) { + /* enable reception of KC event messages */ + exit(1); + } + + /* give that thread a chance right now */ + while(!runLoopInitialized) { + usleep(1000); + }; + + int ortn = 0; + do { + switch(argv[1][0]) { + case 'a': + if(certRef == NULL) { + printf("You must supply a cert.\n"); + usage(argv); + } + if(settingsFileIn) { + if(!settingsFileOut) { + printf("Modifying trust settings as file requires output file\n"); + return -1; + } + settingsIn = readFileCFData(settingsFileIn); + if(!settingsIn) { + return -1; + } + } + if(userTrustLegacy || userTrust) { + ortn = addCertLegacy(certRef, policyStr, resultStr, userTrustLegacy); + } + else { + ortn = addCert(certRef, domain, addToKeychain, kcName, appPolicies, + settingsIn, &settingsOut); + if((ortn == noErr) && (settingsOut != NULL)) { + unsigned len = CFDataGetLength(settingsOut); + if(writeFile(settingsFileOut, CFDataGetBytePtr(settingsOut), len)) { + printf("***Error writing settings to %s\n", settingsFileOut); + } + else { + printf("...wrote %u bytes to %s\n", len, settingsFileOut); + } + } + } + if(settingsIn) { + CFRelease(settingsIn); + } + if(settingsOut) { + CFRelease(settingsOut); + } + break; + case 'p': + ortn = fetchParseTrustRecord(domain, settingsFileIn); + break; + case 'r': + ortn = copyCertsAndDisplay(verbose, domain); + break; + case 'd': + ortn = deleteCerts(domain, false); + break; + case 'D': + if(!really) { + printf("I do not believe you. Specify -D option to delete all roots.\n"); + exit(1); + } + ortn = deleteCerts(domain, true); + break; + case 'R': + ortn = addCertLegacy(certRef, policyStr, "unspecified", true); + break; + default: + usage(argv); + } + if(loopPause) { + fpurge(stdin); + printf("Pausing for MallocDebug. Hit CR to continue: "); + fflush(stdout); + getchar(); + } + } while(loopPause); + return (int)ortn; +}