/* * 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; }