/* Reads certificates from the list of known locations into store. Stops when * any location contains any certificates, to prevent spending unnecessary time * adding redundant certificates, e.g. when both a certificate bundle and * individual certificates exist in the same directory. */ static void read_trusted_roots_from_known_locations(HCERTSTORE store) { HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if (from) { DWORD i; BOOL ret = FALSE; #ifdef HAVE_SECURITY_SECURITY_H OSStatus status; CFArrayRef rootCerts; status = SecTrustCopyAnchorCertificates(&rootCerts); if (status == noErr) { int i; for (i = 0; i < CFArrayGetCount(rootCerts); i++) { SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(rootCerts, i); CFDataRef certData; if ((status = SecKeychainItemExport(cert, kSecFormatX509Cert, 0, NULL, &certData)) == noErr) { if (CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, CFDataGetBytePtr(certData), CFDataGetLength(certData), CERT_STORE_ADD_NEW, NULL)) ret = TRUE; else WARN("adding root cert %d failed: %08x\n", i, GetLastError()); CFRelease(certData); } else WARN("could not export certificate %d to X509 format: 0x%08x\n", i, (unsigned int)status); } CFRelease(rootCerts); } #endif for (i = 0; !ret && i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]); i++) ret = import_certs_from_path(CRYPT_knownLocations[i], from, TRUE); check_and_store_certs(from, store); } CertCloseStore(from, 0); }
static int keychain_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { struct ks_keychain *ctx = data; struct iter *iter; iter = calloc(1, sizeof(*iter)); if (iter == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } if (ctx->anchors) { CFArrayRef anchors; int ret; int i; ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, NULL, &iter->certs); if (ret) { free(iter); return ret; } ret = SecTrustCopyAnchorCertificates(&anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ENOMEM, "Can't get trust anchors from Keychain"); return ENOMEM; } for (i = 0; i < CFArrayGetCount(anchors); i++) { SecCertificateRef cr; hx509_cert cert; CSSM_DATA cssm; cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); SecCertificateGetData(cr, &cssm); ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert); if (ret) continue; ret = hx509_certs_add(context, iter->certs, cert); hx509_cert_free(cert); } CFRelease(anchors); } if (iter->certs) { int ret; ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); if (ret) { hx509_certs_free(&iter->certs); free(iter); return ret; } } else { OSStatus ret; ret = SecKeychainSearchCreateFromAttributes(ctx->keychain, kSecCertificateItemClass, NULL, &iter->searchRef); if (ret) { free(iter); hx509_set_error_string(context, 0, ret, "Failed to start search for attributes"); return ENOMEM; } } *cursor = iter; return 0; }
/* * Given a SecIdentityRef, do our best to construct a complete, ordered, and * verified cert chain, returning the result in a CFArrayRef. The result is * suitable for use when calling SSLSetCertificate(). */ OSStatus sslCompleteCertChain( SecIdentityRef identity, SecCertificateRef trustedAnchor, // optional additional trusted anchor bool includeRoot, // include the root in outArray CFArrayRef *outArray) // created and RETURNED { CFMutableArrayRef certArray; SecTrustRef secTrust = NULL; SecPolicyRef policy = NULL; SecPolicySearchRef policySearch = NULL; SecTrustResultType secTrustResult; CSSM_TP_APPLE_EVIDENCE_INFO *dummyEv; // not used CFArrayRef certChain = NULL; // constructed chain CFIndex numResCerts; certArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(certArray, identity); /* * Case 1: identity is a root; we're done. Note that this case * overrides the includeRoot argument. */ SecCertificateRef certRef; OSStatus ortn = SecIdentityCopyCertificate(identity, &certRef); if(ortn) { /* should never happen */ cssmPerror("SecIdentityCopyCertificate", ortn); return ortn; } bool isRoot = isCertRefRoot(certRef); if(isRoot) { *outArray = certArray; CFRelease(certRef); return errSecSuccess; } /* * Now use SecTrust to get a complete cert chain, using all of the * user's keychains to look for intermediate certs. * NOTE this does NOT handle root certs which are not in the system * root cert DB. (The above case, where the identity is a root cert, does.) */ CFMutableArrayRef subjCerts = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); CFArraySetValueAtIndex(subjCerts, 0, certRef); /* the array owns the subject cert ref now */ CFRelease(certRef); /* Get a SecPolicyRef for generic X509 cert chain verification */ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, // value &policySearch); if(ortn) { cssmPerror("SecPolicySearchCreate", ortn); goto errOut; } ortn = SecPolicySearchCopyNext(policySearch, &policy); if(ortn) { cssmPerror("SecPolicySearchCopyNext", ortn); goto errOut; } /* build a SecTrustRef for specified policy and certs */ ortn = SecTrustCreateWithCertificates(subjCerts, policy, &secTrust); if(ortn) { cssmPerror("SecTrustCreateWithCertificates", ortn); goto errOut; } if(trustedAnchor) { /* * Tell SecTrust to trust this one in addition to the current * trusted system-wide anchors. */ CFMutableArrayRef newAnchors; CFArrayRef currAnchors; ortn = SecTrustCopyAnchorCertificates(&currAnchors); if(ortn) { /* should never happen */ cssmPerror("SecTrustCopyAnchorCertificates", ortn); goto errOut; } newAnchors = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(currAnchors) + 1, currAnchors); CFRelease(currAnchors); CFArrayAppendValue(newAnchors, trustedAnchor); ortn = SecTrustSetAnchorCertificates(secTrust, newAnchors); CFRelease(newAnchors); if(ortn) { cssmPerror("SecTrustSetAnchorCertificates", ortn); goto errOut; } } /* evaluate: GO */ ortn = SecTrustEvaluate(secTrust, &secTrustResult); if(ortn) { cssmPerror("SecTrustEvaluate", ortn); goto errOut; } switch(secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ break; default: /* * Cert chain construction failed. * Just go with the single subject cert we were given. */ printf("***Warning: could not construct completed cert chain\n"); ortn = errSecSuccess; goto errOut; } /* get resulting constructed cert chain */ ortn = SecTrustGetResult(secTrust, &secTrustResult, &certChain, &dummyEv); if(ortn) { cssmPerror("SecTrustEvaluate", ortn); goto errOut; } /* * Copy certs from constructed chain to our result array, skipping * the leaf (which is already there, as a SecIdentityRef) and possibly * a root. */ numResCerts = CFArrayGetCount(certChain); if(numResCerts < 2) { /* * Can't happen: if subject was a root, we'd already have returned. * If chain doesn't verify to a root, we'd have bailed after * SecTrustEvaluate(). */ printf("***sslCompleteCertChain screwup: numResCerts %d\n", (int)numResCerts); ortn = errSecSuccess; goto errOut; } if(!includeRoot) { /* skip the last (root) cert) */ numResCerts--; } for(CFIndex dex=1; dex<numResCerts; dex++) { certRef = (SecCertificateRef)CFArrayGetValueAtIndex(certChain, dex); CFArrayAppendValue(certArray, certRef); } errOut: /* clean up */ if(secTrust) { CFRelease(secTrust); } if(subjCerts) { CFRelease(subjCerts); } if(policy) { CFRelease(policy); } if(policySearch) { CFRelease(policySearch); } *outArray = certArray; return ortn; }
static int keychain_iter_start(hx509_context context, hx509_certs certs, void *data, void **cursor) { #ifndef __APPLE_TARGET_EMBEDDED__ struct ks_keychain *ctx = data; #endif struct iter *iter; iter = calloc(1, sizeof(*iter)); if (iter == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } #ifndef __APPLE_TARGET_EMBEDDED__ if (ctx->anchors) { CFArrayRef anchors; int ret; int i; ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, NULL, &iter->certs); if (ret) { free(iter); return ret; } ret = SecTrustCopyAnchorCertificates(&anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ENOMEM, "Can't get trust anchors from Keychain"); return ENOMEM; } for (i = 0; i < CFArrayGetCount(anchors); i++) { SecCertificateRef cr; hx509_cert cert; CFDataRef dataref; cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); dataref = SecCertificateCopyData(cr); if (dataref == NULL) continue; ret = hx509_cert_init_data(context, CFDataGetBytePtr(dataref), CFDataGetLength(dataref), &cert); CFRelease(dataref); if (ret) continue; ret = hx509_certs_add(context, iter->certs, cert); hx509_cert_free(cert); } CFRelease(anchors); if (ret != 0) { hx509_certs_free(&iter->certs); free(iter); hx509_set_error_string(context, 0, ret, "Failed to add cert"); return ret; } } if (iter->certs) { int ret; ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); if (ret) { hx509_certs_free(&iter->certs); free(iter); return ret; } } else #endif { OSStatus ret; const void *keys[] = { kSecClass, kSecReturnRef, kSecMatchLimit }; const void *values[] = { kSecClassCertificate, kCFBooleanTrue, kSecMatchLimitAll }; CFDictionaryRef secQuery; secQuery = CFDictionaryCreate(NULL, keys, values, sizeof(keys) / sizeof(*keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); ret = SecItemCopyMatching(secQuery, (CFTypeRef *)&iter->search); CFRelease(secQuery); if (ret) { free(iter); return ENOMEM; } } *cursor = iter; return 0; }
static int importKeychainToX509_STORE(X509_STORE* verifyStore, char* err, size_t err_len) { int status = 1; CFArrayRef result = NULL; OSStatus osStatus; // This copies all the certificates trusted by the system (regardless of what // keychain they're // attached to) into a CFArray. if ((osStatus = SecTrustCopyAnchorCertificates(&result)) != 0) { CFStringRef statusString = SecCopyErrorMessageString(osStatus, NULL); snprintf(err, err_len, "Error enumerating certificates: %s", CFStringGetCStringPtr(statusString, kCFStringEncodingASCII)); CFRelease(statusString); status = 0; goto CLEANUP; } CFDataRef rawData = NULL; X509* x509Cert = NULL; for (CFIndex i = 0; i < CFArrayGetCount(result); i++) { SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(result, i); rawData = SecCertificateCopyData(cert); if (!rawData) { snprintf(err, err_len, "Error enumerating certificates"); status = 0; goto CLEANUP; } const uint8_t* rawDataPtr = CFDataGetBytePtr(rawData); // Parse an openssl X509 object from each returned certificate x509Cert = d2i_X509(NULL, &rawDataPtr, CFDataGetLength(rawData)); if (!x509Cert) { snprintf(err, err_len, "Error parsing X509 certificate from system keychain: %s", ERR_reason_error_string(ERR_peek_last_error())); status = 0; goto CLEANUP; } // Add the parsed X509 object to the X509_STORE verification store if (X509_STORE_add_cert(verifyStore, x509Cert) != 1) { int check_error_status = checkX509_STORE_error(err, err_len); if (!check_error_status) { status = check_error_status; goto CLEANUP; } } CFRelease(rawData); rawData = NULL; X509_free(x509Cert); x509Cert = NULL; } CLEANUP: if (result != NULL) { CFRelease(result); } if (rawData != NULL) { CFRelease(rawData); } if (x509Cert != NULL) { X509_free(x509Cert); } return status; }
/* Reads certificates from the list of known locations into store. Stops when * any location contains any certificates, to prevent spending unnecessary time * adding redundant certificates, e.g. when both a certificate bundle and * individual certificates exist in the same directory. */ static void read_trusted_roots_from_known_locations(HCERTSTORE store) { HCERTSTORE from = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, 0, CERT_STORE_CREATE_NEW_FLAG, NULL); if (from) { DWORD i; BOOL ret = FALSE; #ifdef __APPLE__ OSStatus status; CFArrayRef rootCerts; KeychainExportFunc exportFunc = NULL; #ifdef HAVE_DLFCN_H /* SecKeychainItemExport is deprecated on OS X 10.7, replaced by SecItemExport. * Since their parameters match, we can simply get the function pointer. */ exportFunc = dlsym(RTLD_DEFAULT, "SecItemExport"); if (exportFunc == NULL) { exportFunc = dlsym(RTLD_DEFAULT, "SecKeychainItemExport"); if (exportFunc == NULL) WARN("unable to load a keychain export function: root certificates not loaded from Keychain\n"); else TRACE("using SecKeychainItemExport()\n"); } else TRACE("using SecItemExport()\n"); #else exportFunc = SecKeychainItemExport; #endif status = SecTrustCopyAnchorCertificates(&rootCerts); if (status == noErr && exportFunc != NULL) { int i; for (i = 0; i < CFArrayGetCount(rootCerts); i++) { SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(rootCerts, i); CFDataRef certData; if ((status = exportFunc(cert, kSecFormatX509Cert, 0, NULL, &certData)) == noErr) { if (CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, CFDataGetBytePtr(certData), CFDataGetLength(certData), CERT_STORE_ADD_NEW, NULL)) ret = TRUE; else WARN("adding root cert %d failed: %08x\n", i, GetLastError()); CFRelease(certData); } else WARN("could not export certificate %d to X509 format: 0x%08x\n", i, (unsigned int)status); } CFRelease(rootCerts); } #endif for (i = 0; !ret && i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]); i++) ret = import_certs_from_path(CRYPT_knownLocations[i], from, TRUE); check_and_store_certs(from, store); } CertCloseStore(from, 0); }