// // Load root (anchor) certificates from disk // void TrustStore::loadRootCertificates() { StLock<Mutex> _(mMutex); CFRef<CFArrayRef> anchors; OSStatus ortn; /* * Get the current set of all positively trusted anchors. */ ortn = SecTrustSettingsCopyUnrestrictedRoots( true, true, true, /* all domains */ anchors.take()); if(ortn) { MacOSError::throwMe(ortn); } // how many data bytes do we need? size_t size = 0; CFIndex numCerts = CFArrayGetCount(anchors); CSSM_RETURN crtn; for(CFIndex dex=0; dex<numCerts; dex++) { SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex); CSSM_DATA certData; crtn = SecCertificateGetData(certRef, &certData); if(crtn) { CssmError::throwMe(crtn); } size += certData.Length; } mRootBytes.length(size); // fill CssmData vector while copying data bytes together mRoots.clear(); uint8 *base = mRootBytes.data<uint8>(); for(CFIndex dex=0; dex<numCerts; dex++) { SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex); CSSM_DATA certData; SecCertificateGetData(certRef, &certData); memcpy(base, certData.Data, certData.Length); mRoots.push_back(CssmData(base, certData.Length)); base += certData.Length; } secdebug("anchors", "%ld anchors loaded", (long)numCerts); mRootsValid = true; // ready to roll }
static void writePeerCerts( CFArrayRef peerCerts, const char *fileBase) { CFIndex numCerts; SecCertificateRef certRef; OSStatus ortn; CSSM_DATA certData; CFIndex i; char fileName[100]; if(peerCerts == NULL) { return; } numCerts = CFArrayGetCount(peerCerts); for(i=0; i<numCerts; i++) { sprintf(fileName, "%s%02d.cer", fileBase, i); certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); ortn = SecCertificateGetData(certRef, &certData); if(ortn) { printf("***SecCertificateGetData returned %d\n", ortn); continue; } writeFile(fileName, certData.Data, certData.Length); } printf("...wrote %d certs to fileBase %s\n", numCerts, fileBase); }
static void showPeerCerts( CFArrayRef peerCerts, CSSM_BOOL verbose) { CFIndex numCerts; SecCertificateRef certRef; OSStatus ortn; CSSM_DATA certData; CFIndex i; if(peerCerts == NULL) { return; } numCerts = CFArrayGetCount(peerCerts); for(i=0; i<numCerts; i++) { certRef = (SecCertificateRef)CFArrayGetValueAtIndex(peerCerts, i); ortn = SecCertificateGetData(certRef, &certData); if(ortn) { printf("***SecCertificateGetData returned %d\n", ortn); continue; } printf("\n================== Peer Cert %d ===================\n\n", i); printCert(certData.Data, certData.Length, verbose); printf("\n=============== End of Peer Cert %d ===============\n", i); } }
/* Convert a SecCertificateRef to an SSLCertificate * */ static OSStatus secCertToSslCert( SSLContext *ctx, SecCertificateRef certRef, SSLCertificate **sslCert) { CSSM_DATA certData; // struct is transient, referent owned by // Sec layer OSStatus ortn; SSLCertificate *thisSslCert = NULL; ortn = SecCertificateGetData(certRef, &certData); if(ortn) { sslErrorLog("SecCertificateGetData() returned %d\n", (int)ortn); return ortn; } thisSslCert = (SSLCertificate *)sslMalloc(sizeof(SSLCertificate)); if(thisSslCert == NULL) { return memFullErr; } if(SSLAllocBuffer(&thisSslCert->derCert, certData.Length, ctx)) { return memFullErr; } memcpy(thisSslCert->derCert.data, certData.Data, certData.Length); thisSslCert->derCert.length = certData.Length; *sslCert = thisSslCert; return noErr; }
// // Ditto, given a SecCertificateRef // void hashOfCertificate(SecCertificateRef cert, SHA1::Digest digest) { assert(cert); CSSM_DATA certData; MacOSError::check(SecCertificateGetData(cert, &certData)); hashOfCertificate(certData.Data, certData.Length, digest); }
/* * Given a certificate, obtain the DER-encoded issuer and serial number. Result * is mallocd and must be freed by caller. */ static OSStatus pkinit_get_cert_issuer_sn( SecCertificateRef certRef, CSSM_DATA *issuerSerial) /* mallocd and RETURNED */ { OSStatus ortn; CSSM_DATA certData; krb5_data INIT_KDATA(issuerSerialKrb); krb5_data certDataKrb; krb5_error_code krtn; assert(certRef != NULL); assert(issuerSerial != NULL); ortn = SecCertificateGetData(certRef, &certData); if(ortn) { pkiCssmErr("SecCertificateGetData", ortn); return ortn; } PKI_CSSM_TO_KRB_DATA(&certData, &certDataKrb); krtn = krb5int_pkinit_get_issuer_serial(&certDataKrb, &issuerSerialKrb); if(krtn) { return CSSMERR_CL_INVALID_DATA; } PKI_KRB_TO_CSSM_DATA(&issuerSerialKrb, issuerSerial); return noErr; }
/* * Convert CFArray of SecCertificateRefs to a mallocd array of krb5_datas. */ static krb5_error_code pkiCertArrayToKrb5Data( CFArrayRef cf_certs, unsigned *num_all_certs, krb5_data **all_certs) { CFIndex num_certs; krb5_data *allCerts = NULL; krb5_error_code krtn = 0; unsigned dex; if(cf_certs == NULL) { *all_certs = NULL; return 0; } num_certs = CFArrayGetCount(cf_certs); *num_all_certs = (unsigned)num_certs; if(num_certs == 0) { *all_certs = NULL; return 0; } allCerts = (krb5_data *)malloc(sizeof(krb5_data) * num_certs); if(allCerts == NULL) { return ENOMEM; } for(dex=0; dex<num_certs; dex++) { CSSM_DATA cert_data; OSStatus ortn; SecCertificateRef sec_cert; sec_cert = (SecCertificateRef)CFArrayGetValueAtIndex(cf_certs, dex); ortn = SecCertificateGetData(sec_cert, &cert_data); if(ortn) { pkiCssmErr("SecCertificateGetData", ortn); krtn = KRB5_PARSE_MALFORMED; break; } krtn = pkiCssmDataToKrb5Data(&cert_data, &allCerts[dex]); if(krtn) { break; } } if(krtn) { if(allCerts) { free(allCerts); } } else { *all_certs = allCerts; } return krtn; }
OSStatus CertParser::initWithSecCert( SecCertificateRef secCert) { OSStatus ortn; CSSM_DATA certData; assert(mClHand == 0); ortn = SecCertificateGetCLHandle(secCert, &mClHand); if(ortn) { return ortn; } ortn = SecCertificateGetData(secCert, &certData); if(ortn) { return ortn; } return (OSStatus)initWithData(certData); }
void extract_certificate_from_identity(const void *value, void *context) { if (!context || !value) return; CSSM_DATA certData = {0,}; SecCertificateRef certificateRef; OSStatus status = SecIdentityCopyCertificate((SecIdentityRef)value, &certificateRef); if (!status) { status = SecCertificateGetData(certificateRef, &certData); CFRelease(certificateRef); if (!status) { CFDataRef cert = CFDataCreate(kCFAllocatorDefault, (UInt8 *)certData.Data, certData.Length); CFArrayAppendValue((CFMutableArrayRef)context, cert); CFRelease(cert); if (certData.Data) free(certData.Data); } } }
/* * Parse a ContentInfo as best we can. All return fields are optional. * If signer_cert_status is NULL on entry, NO signature or cert evaluation * will be performed. */ krb5_error_code krb5int_pkinit_parse_cms_msg( const krb5_data *content_info, krb5_pkinit_cert_db_t cert_db, /* may be required for SignedData */ krb5_boolean is_client_msg, /* TRUE : msg is from client */ krb5_boolean *is_signed, /* RETURNED */ krb5_boolean *is_encrypted, /* RETURNED */ krb5_data *raw_data, /* RETURNED */ krb5int_cms_content_type *inner_content_type,/* Returned, ContentType of */ /* EncapsulatedData */ krb5_data *signer_cert, /* RETURNED */ krb5int_cert_sig_status *signer_cert_status,/* RETURNED */ unsigned *num_all_certs, /* size of *all_certs RETURNED */ krb5_data **all_certs) /* entire cert chain RETURNED */ { SecPolicySearchRef policy_search = NULL; SecPolicyRef policy = NULL; OSStatus ortn; krb5_error_code krtn = 0; CMSDecoderRef decoder = NULL; size_t num_signers; CMSSignerStatus signer_status; OSStatus cert_verify_status; CFArrayRef cf_all_certs = NULL; int msg_is_signed = 0; if(content_info == NULL) { pkiDebug("krb5int_pkinit_parse_cms_msg: no ContentInfo\n"); return KRB5_CRYPTO_INTERNAL; } ortn = CMSDecoderCreate(&decoder); if(ortn) { return ENOMEM; } ortn = CMSDecoderUpdateMessage(decoder, content_info->data, content_info->length); if(ortn) { /* no verify yet, must be bad message */ krtn = KRB5_PARSE_MALFORMED; goto errOut; } ortn = CMSDecoderFinalizeMessage(decoder); if(ortn) { pkiCssmErr("CMSDecoderFinalizeMessage", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } /* expect zero or one signers */ ortn = CMSDecoderGetNumSigners(decoder, &num_signers); switch(num_signers) { case 0: msg_is_signed = 0; break; case 1: msg_is_signed = 1; break; default: krtn = KRB5_PARSE_MALFORMED; goto errOut; } /* * We need a cert verify policy even if we're not actually evaluating * the cert due to requirements in libsecurity_smime. */ ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, is_client_msg ? &CSSMOID_APPLE_TP_PKINIT_CLIENT : &CSSMOID_APPLE_TP_PKINIT_SERVER, NULL, &policy_search); if(ortn) { pkiCssmErr("SecPolicySearchCreate", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ortn = SecPolicySearchCopyNext(policy_search, &policy); if(ortn) { pkiCssmErr("SecPolicySearchCopyNext", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } /* get some basic status that doesn't need heavyweight evaluation */ if(msg_is_signed) { if(is_signed) { *is_signed = TRUE; } if(inner_content_type) { CSSM_OID ec_oid = {0, NULL}; CFDataRef ec_data = NULL; krb5int_cms_content_type ctype; ortn = CMSDecoderCopyEncapsulatedContentType(decoder, &ec_data); if(ortn || (ec_data == NULL)) { pkiCssmErr("CMSDecoderCopyEncapsulatedContentType", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ec_oid.Data = (uint8 *)CFDataGetBytePtr(ec_data); ec_oid.Length = CFDataGetLength(ec_data); if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_Data)) { ctype = ECT_Data; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_SignedData)) { ctype = ECT_SignedData; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EnvelopedData)) { ctype = ECT_EnvelopedData; } else if(pkiCompareCssmData(&ec_oid, &CSSMOID_PKCS7_EncryptedData)) { ctype = ECT_EncryptedData; } else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_AUTH_DATA)) { ctype = ECT_PkAuthData; } else if(pkiCompareCssmData(&ec_oid, &_CSSMOID_PKINIT_RKEY_DATA)) { ctype = ECT_PkReplyKeyKata; } else { ctype = ECT_Other; } *inner_content_type = ctype; CFRelease(ec_data); } /* * Get SignedData's certs if the caller wants them */ if(all_certs) { ortn = CMSDecoderCopyAllCerts(decoder, &cf_all_certs); if(ortn) { pkiCssmErr("CMSDecoderCopyAllCerts", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } krtn = pkiCertArrayToKrb5Data(cf_all_certs, num_all_certs, all_certs); if(krtn) { goto errOut; } } /* optional signer cert */ if(signer_cert) { SecCertificateRef sec_signer_cert = NULL; CSSM_DATA cert_data; ortn = CMSDecoderCopySignerCert(decoder, 0, &sec_signer_cert); if(ortn) { /* should never happen if it's signed */ pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } ortn = SecCertificateGetData(sec_signer_cert, &cert_data); if(ortn) { pkiCssmErr("SecCertificateGetData", ortn); CFRelease(sec_signer_cert); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } krtn = pkiDataToKrb5Data(cert_data.Data, cert_data.Length, signer_cert); CFRelease(sec_signer_cert); if(krtn) { goto errOut; } } } else { /* not signed */ if(is_signed) { *is_signed = FALSE; } if(inner_content_type) { *inner_content_type = ECT_Other; } if(signer_cert) { signer_cert->data = NULL; signer_cert->length = 0; } if(signer_cert_status) { *signer_cert_status = pki_not_signed; } if(num_all_certs) { *num_all_certs = 0; } if(all_certs) { *all_certs = NULL; } } if(is_encrypted) { Boolean bencr; ortn = CMSDecoderIsContentEncrypted(decoder, &bencr); if(ortn) { pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_CRYPTO_INTERNAL; goto errOut; } *is_encrypted = bencr ? TRUE : FALSE; } /* * Verify signature and cert. The actual verify operation is optional, * per our signer_cert_status argument, but we do this anyway if we need * to get the signer cert. */ if((signer_cert_status != NULL) || (signer_cert != NULL)) { ortn = CMSDecoderCopySignerStatus(decoder, 0, /* signerIndex */ policy, signer_cert_status ? TRUE : FALSE, /* evaluateSecTrust */ &signer_status, NULL, /* secTrust - not needed */ &cert_verify_status); if(ortn) { /* gross error - subsequent processing impossible */ pkiCssmErr("CMSDecoderCopySignerStatus", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } } /* obtain & return status */ if(signer_cert_status) { *signer_cert_status = pkiInferSigStatus(signer_status, cert_verify_status); } /* finally, the payload */ if(raw_data) { CFDataRef cf_content = NULL; ortn = CMSDecoderCopyContent(decoder, &cf_content); if(ortn) { pkiCssmErr("CMSDecoderCopyContent", ortn); krtn = KRB5_PARSE_MALFORMED; goto errOut; } krtn = pkiCfDataToKrb5Data(cf_content, raw_data); CFRELEASE(cf_content); } errOut: CFRELEASE(policy_search); CFRELEASE(policy); CFRELEASE(cf_all_certs); CFRELEASE(decoder); return krtn; }
/* * Returns true if we are to allow/trust the specified * cert as a PKINIT-only anchor. */ static bool tpCheckPkinitServerCert( TPCertGroup &certGroup) { /* * Basic requirement: exactly one cert, self-signed. * The numCerts == 1 requirement might change... */ unsigned numCerts = certGroup.numCerts(); if(numCerts != 1) { tpDebug("tpCheckPkinitServerCert: too many certs"); return false; } /* end of chain... */ TPCertInfo *theCert = certGroup.certAtIndex(numCerts - 1); if(!theCert->isSelfSigned()) { tpDebug("tpCheckPkinitServerCert: 1 cert, not self-signed"); return false; } const CSSM_DATA *subjectName = theCert->subjectName(); /* * Open the magic keychain. * We're going up and over the Sec layer here, not generally * kosher, but this is a temp hack. */ OSStatus ortn; SecKeychainRef kcRef = NULL; string fullPathName; const char *homeDir = getenv("HOME"); if (homeDir == NULL) { // If $HOME is unset get the current user's home directory // from the passwd file. uid_t uid = geteuid(); if (!uid) uid = getuid(); struct passwd *pw = getpwuid(uid); if (!pw) { return false; } homeDir = pw->pw_dir; } fullPathName = homeDir; fullPathName += "/Library/Application Support/PKINIT/TrustedServers.keychain"; ortn = SecKeychainOpen(fullPathName.c_str(), &kcRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (1)"); return false; } /* subsequent errors to errOut: */ bool ourRtn = false; SecKeychainStatus kcStatus; CSSM_DATA_PTR subjSerial = NULL; CSSM_RETURN crtn; SecKeychainSearchRef srchRef = NULL; SecKeychainAttributeList attrList; SecKeychainAttribute attrs[2]; SecKeychainItemRef foundItem = NULL; ortn = SecKeychainGetStatus(kcRef, &kcStatus); if(ortn) { tpDebug("tpCheckPkinitServerCert: keychain not found (2)"); goto errOut; } /* * We already have this cert's normalized name; get its * serial number. */ crtn = theCert->fetchField(&CSSMOID_X509V1SerialNumber, &subjSerial); if(crtn) { /* should never happen */ tpDebug("tpCheckPkinitServerCert: error fetching serial number"); goto errOut; } attrs[0].tag = kSecSubjectItemAttr; attrs[0].length = subjectName->Length; attrs[0].data = subjectName->Data; attrs[1].tag = kSecSerialNumberItemAttr; attrs[1].length = subjSerial->Length; attrs[1].data = subjSerial->Data; attrList.count = 2; attrList.attr = attrs; ortn = SecKeychainSearchCreateFromAttributes(kcRef, kSecCertificateItemClass, &attrList, &srchRef); if(ortn) { tpDebug("tpCheckPkinitServerCert: search failure"); goto errOut; } for(;;) { ortn = SecKeychainSearchCopyNext(srchRef, &foundItem); if(ortn) { tpDebug("tpCheckPkinitServerCert: end search"); break; } /* found a matching cert; do byte-for-byte compare */ CSSM_DATA certData; ortn = SecCertificateGetData((SecCertificateRef)foundItem, &certData); if(ortn) { tpDebug("tpCheckPkinitServerCert: SecCertificateGetData failure"); continue; } if(tpCompareCssmData(&certData, theCert->itemData())){ tpDebug("tpCheckPkinitServerCert: FOUND CERT"); ourRtn = true; break; } tpDebug("tpCheckPkinitServerCert: skipping matching cert"); CFRelease(foundItem); foundItem = NULL; } errOut: CFRELEASE(kcRef); CFRELEASE(srchRef); CFRELEASE(foundItem); if(subjSerial != NULL) { theCert->freeField(&CSSMOID_X509V1SerialNumber, subjSerial); } return ourRtn; }
/* * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData * after all the encapsulated data was passed through the encoder. * * In detail: * - create the signatures in all the SignerInfos * * Please note that nothing is done to the Certificates and CRLs in the message - this * is entirely the responsibility of our callers. */ OSStatus SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd) { SecCmsSignerInfoRef *signerinfos, signerinfo; SecCmsContentInfoRef cinfo; SECOidTag digestalgtag; OSStatus ret = SECFailure; OSStatus rv; CSSM_DATA_PTR contentType; int certcount; int i, ci, n, rci, si; PLArenaPool *poolp; CFArrayRef certlist; extern const SecAsn1Template SecCmsSignerInfoTemplate[]; poolp = sigd->cmsg->poolp; cinfo = &(sigd->contentInfo); /* did we have digest calculation going on? */ if (cinfo->digcx) { rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests)); if (rv != SECSuccess) goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ cinfo->digcx = NULL; } signerinfos = sigd->signerInfos; certcount = 0; /* prepare all the SignerInfos (there may be none) */ for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); /* find correct digest for this signerinfo */ digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) { /* oops - digest not found */ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); goto loser; } /* XXX if our content is anything else but data, we need to force the * presence of signed attributes (RFC2630 5.3 "signedAttributes is a * collection...") */ /* pass contentType here as we want a contentType attribute */ if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL) goto loser; /* sign the thing */ rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType); if (rv != SECSuccess) goto loser; /* while we're at it, count number of certs in certLists */ certlist = SecCmsSignerInfoGetCertList(signerinfo); if (certlist) certcount += CFArrayGetCount(certlist); } /* Now we can get a timestamp, since we have all the digests */ // We force the setting of a callback, since this is the most usual case if (!sigd->cmsg->tsaCallback) SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback); if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext) { CSSM_DATA tsaResponse = {0,}; SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}}; // <rdar://problem/11073466> Add nonce support for timestamping client uint64_t nonce = 0; require_noerr(getRandomNonce(&nonce), tsxit); dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd)); // Calculate hash of encDigest and put in messageImprint.hashedMessage SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0); // NB - assume 1 signer only! CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo); require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit); // Callback to fire up XPC service to talk to TimeStamping server, etc. require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint, nonce, &tsaResponse), tsxit); require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit); /* It is likely that every occurrence of "goto loser" in this file should also do a PORT_SetError. Since it is not clear what might depend on this behavior, we just do this in the timestamping case. */ tsxit: if (rv) { dprintf("Original timestamp error: %d\n", (int)rv); rv = remapTimestampError(rv); PORT_SetError(rv); goto loser; } } /* this is a SET OF, so we need to sort them guys */ rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL); if (rv != SECSuccess) goto loser; /* * now prepare certs & crls */ /* count the rest of the certs */ if (sigd->certs != NULL) certcount += CFArrayGetCount(sigd->certs); if (certcount == 0) { sigd->rawCerts = NULL; } else { /* * Combine all of the certs and cert chains into rawcerts. * Note: certcount is an upper bound; we may not need that many slots * but we will allocate anyway to avoid having to do another pass. * (The temporary space saving is not worth it.) * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR)); if (sigd->rawCerts == NULL) return SECFailure; /* * XXX Want to check for duplicates and not add *any* cert that is * already in the set. This will be more important when we start * dealing with larger sets of certs, dual-key certs (signing and * encryption), etc. For the time being we can slide by... * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ rci = 0; if (signerinfos != NULL) { for (si = 0; signerinfos[si] != NULL; si++) { signerinfo = signerinfos[si]; for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } } if (sigd->certs != NULL) { for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } sigd->rawCerts[rci] = NULL; /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); } ret = SECSuccess; loser: dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv); return ret; }
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; }
// Extract the issuer and serial number from a certificate SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert) { OSStatus status; SecCmsIssuerAndSN *certIssuerAndSN; CSSM_CL_HANDLE clHandle; CSSM_DATA_PTR serialNumber = 0; CSSM_DATA_PTR issuer = 0; CSSM_DATA certData = {}; CSSM_HANDLE resultsHandle = 0; uint32 numberOfFields = 0; CSSM_RETURN result; void *mark; mark = PORT_ArenaMark(pl); status = SecCertificateGetCLHandle(cert, &clHandle); if (status) goto loser; status = SecCertificateGetData(cert, &certData); if (status) goto loser; /* Get the issuer from the cert. */ result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, &OID_X509V1IssuerNameStd, &resultsHandle, &numberOfFields, &issuer); if (result || numberOfFields < 1) goto loser; result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle); if (result) goto loser; /* Get the serialNumber from the cert. */ result = CSSM_CL_CertGetFirstFieldValue(clHandle, &certData, &CSSMOID_X509V1SerialNumber, &resultsHandle, &numberOfFields, &serialNumber); if (result || numberOfFields < 1) goto loser; result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle); if (result) goto loser; /* Allocate the SecCmsIssuerAndSN struct. */ certIssuerAndSN = (SecCmsIssuerAndSN *)PORT_ArenaZAlloc (pl, sizeof(SecCmsIssuerAndSN)); if (certIssuerAndSN == NULL) goto loser; /* Copy the issuer. */ certIssuerAndSN->derIssuer.Data = (uint8 *) PORT_ArenaAlloc(pl, issuer->Length); if (!certIssuerAndSN->derIssuer.Data) goto loser; PORT_Memcpy(certIssuerAndSN->derIssuer.Data, issuer->Data, issuer->Length); certIssuerAndSN->derIssuer.Length = issuer->Length; /* Copy the serialNumber. */ certIssuerAndSN->serialNumber.Data = (uint8 *) PORT_ArenaAlloc(pl, serialNumber->Length); if (!certIssuerAndSN->serialNumber.Data) goto loser; PORT_Memcpy(certIssuerAndSN->serialNumber.Data, serialNumber->Data, serialNumber->Length); certIssuerAndSN->serialNumber.Length = serialNumber->Length; PORT_ArenaUnmark(pl, mark); CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber); CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer); return certIssuerAndSN; loser: PORT_ArenaRelease(pl, mark); if (serialNumber) CSSM_CL_FreeFieldValue(clHandle, &CSSMOID_X509V1SerialNumber, serialNumber); if (issuer) CSSM_CL_FreeFieldValue(clHandle, &OID_X509V1IssuerNameStd, issuer); PORT_SetError(SEC_INTERNAL_ONLY); return NULL; }
// find_unique_certificate // // Returns a SecKeychainItemRef for the certificate // in the specified keychain (or keychain list) // which is a unique match for either the specified name // or SHA-1 hash. If more than one match exists, the // certificate is not unique and none are returned. Caller is // responsible for releasing the item (with CFRelease). // SecKeychainItemRef find_unique_certificate(CFTypeRef keychainOrArray, const char *name, const char *hash) { OSStatus status = noErr; SecKeychainSearchRef searchRef = NULL; SecKeychainItemRef uniqueItemRef = NULL; status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); if (status) { return uniqueItemRef; } // check input hash string and convert to data CSSM_DATA hashData = { 0, NULL }; if (hash) { CSSM_SIZE len = strlen(hash)/2; hashData.Length = len; hashData.Data = (uint8 *)malloc(hashData.Length); fromHex(hash, &hashData); } // filter candidates against the hash (or the name, if no hash provided) CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; Boolean exactMatch = FALSE; CSSM_DATA certData = { 0, NULL }; SecKeychainItemRef candidate = NULL; while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) { SecCertificateRef cert = (SecCertificateRef)candidate; if (SecCertificateGetData(cert, &certData) != noErr) { safe_CFRelease(&candidate); continue; } if (hash) { uint8 candidate_sha1_hash[20]; CSSM_DATA digest; digest.Length = sizeof(candidate_sha1_hash); digest.Data = candidate_sha1_hash; if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) && (hashData.Length == digest.Length) && (!memcmp(hashData.Data, digest.Data, digest.Length))) { exactMatch = TRUE; uniqueItemRef = candidate; // currently retained break; // we're done - can't get more exact than this } } else { // copy certificate name CFStringRef nameRef = NULL; if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) { safe_CFRelease(&candidate); continue; // no name, so no match is possible } CFIndex nameLen = CFStringGetLength(nameRef); CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8); char *nameBuf = (char *)malloc(bufLen); if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8)) nameBuf[0]=0; CFRange find = { kCFNotFound, 0 }; if (nameRef && matchRef) find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); Boolean isExact = (find.location == 0 && find.length == nameLen); if (find.location == kCFNotFound) { free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; // no match } if (uniqueItemRef) { // got two matches if (exactMatch && !isExact) { // prior is better; ignore this one free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; } if (exactMatch == isExact) { // same class of match if (CFEqual(uniqueItemRef, candidate)) { // same certificate free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; } // ambiguity - must fail sec_error("\"%s\" is ambiguous, matches more than one certificate", name); free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); safe_CFRelease(&uniqueItemRef); break; } safe_CFRelease(&uniqueItemRef); // about to replace with this one } uniqueItemRef = candidate; // currently retained exactMatch = isExact; free(nameBuf); safe_CFRelease(&nameRef); } } safe_CFRelease(&searchRef); safe_CFRelease(&matchRef); if (hashData.Data) { free(hashData.Data); } return uniqueItemRef; }
static int do_keychain_find_certificate(CFTypeRef keychainOrArray, const char *name, const char *emailAddress, Boolean print_hash, Boolean output_pem, Boolean find_all, Boolean print_email) { OSStatus result = noErr; SecCertificateRef certificateRef = NULL; SecKeychainSearchRef searchRef = NULL; CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; if (find_all && emailAddress) { result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef); if (result) { sec_perror("SecKeychainSearchCreateForCertificateByEmail", result); goto cleanup; } } else { result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); if (result) { sec_perror("SecKeychainSearchCreateFromAttributes", result); goto cleanup; } } do { if (find_all) { SecKeychainItemRef itemRef = NULL; result = SecKeychainSearchCopyNext(searchRef, &itemRef); if (result == errSecItemNotFound) { result = 0; break; } else if (result) { sec_perror("SecKeychainSearchCopyNext", result); goto cleanup; } if (!emailAddress && name) { // match name in common name CFStringRef nameRef = NULL; if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { safe_CFRelease(&itemRef); continue; // no name, so no match is possible } CFRange find = { kCFNotFound, 0 }; if (nameRef && matchRef) find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); if (find.location == kCFNotFound) { safe_CFRelease(&nameRef); safe_CFRelease(&itemRef); continue; // no match } safe_CFRelease(&nameRef); } safe_CFRelease(&certificateRef); certificateRef = (SecCertificateRef) itemRef; } else { // only want the first match if (emailAddress) { result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef); if (result) { sec_perror("SecCertificateFindByEmail", result); goto cleanup; } } else { SecKeychainItemRef itemRef = NULL; while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) { if (name) { // match name in common name CFStringRef nameRef = NULL; if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) { safe_CFRelease(&itemRef); continue; // no name, so no match is possible } CFRange find = { kCFNotFound, 0 }; if (nameRef && matchRef) find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); if (find.location == kCFNotFound) { safe_CFRelease(&nameRef); safe_CFRelease(&itemRef); continue; // no match } safe_CFRelease(&nameRef); } break; // we have a match! } if (result == errSecItemNotFound) { sec_perror("SecKeychainSearchCopyNext", result); goto cleanup; } certificateRef = (SecCertificateRef) itemRef; } } // process the found certificate if (print_hash) { uint8 sha1_hash[20]; CSSM_DATA data; CSSM_DATA digest; digest.Length = sizeof(sha1_hash); digest.Data = sha1_hash; if ((SecCertificateGetData(certificateRef, &data) == noErr) && (SecDigestGetData(CSSM_ALGID_SHA1, &digest, &data) == CSSM_OK)) { unsigned int i; uint32 len = digest.Length; uint8 *cp = digest.Data; fprintf(stdout, "SHA-1 hash: "); for(i=0; i<len; i++) { fprintf(stdout, "%02X", ((unsigned char *)cp)[i]); } fprintf(stdout, "\n"); } } if (print_email) { CFArrayRef emailAddresses = NULL; CFIndex ix, count; result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses); if (result) { sec_perror("SecCertificateCopyEmailAddresses", result); goto cleanup; } count = CFArrayGetCount(emailAddresses); fputs("email addresses: ", stdout); for (ix = 0; ix < count; ++ix) { CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix); const char *addr; char buffer[256]; if (ix) fputs(", ", stdout); addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8); if (!addr) { if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8)) addr = buffer; } fprintf(stdout, "%s", addr); } fputc('\n', stdout); CFRelease(emailAddresses); } if (output_pem) { CSSM_DATA certData = {}; result = SecCertificateGetData(certificateRef, &certData); if (result) { sec_perror("SecCertificateGetData", result); goto cleanup; } print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data); } else { print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE); } } while (find_all); cleanup: safe_CFRelease(&searchRef); safe_CFRelease(&certificateRef); safe_CFRelease(&matchRef); return result; }
int main(int argc, char **argv) { if(argc < 2) { usage(argv); } bool print_cert = false; bool allCerts = false; const char *emailAddress = NULL; bool addToKC = false; extern int optind; optind = 1; if(argv[1][0] != '-') { /* normal case, email address specified */ emailAddress = argv[1]; optind++; } extern char *optarg; int arg; while ((arg = getopt(argc, argv, "aphA")) != -1) { switch (arg) { case 'p': print_cert = true; break; case 'a': allCerts = true; break; case 'A': addToKC = true; break; case 'h': default: usage(argv); } } if(optind != argc) { usage(argv); } if(!allCerts && (emailAddress == NULL)) { printf("***You must specify either an email address or the -a option.\n"); exit(1); } OSStatus ortn; SecKeychainSearchRef srch; SecKeychainAttributeList attrList; SecKeychainAttribute attr; unsigned numCerts = 0; if(emailAddress) { attr.tag = kSecAlias; // i.e., email address attr.length = strlen(emailAddress); attr.data = (void *)emailAddress; attrList.count = 1; attrList.attr = &attr; } else { attrList.count = 0; attrList.attr = NULL; } ortn = SecKeychainSearchCreateFromAttributes(NULL, // default search list kSecCertificateItemClass, &attrList, &srch); if(ortn) { cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); exit(1); } do { SecCertificateRef certRef = NULL; CSSM_DATA certData; ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)&certRef); if(ortn) { break; } ortn = SecCertificateGetData(certRef, &certData); if(ortn) { cssmPerror("SecCertificateGetData", ortn); continue; } printf("=== Cert %u ===\n", numCerts); printCertName(certData.Data, certData.Length, NameBoth); if(print_cert) { printCert(certData.Data, certData.Length, CSSM_FALSE); } if(addToKC) { /* * Can't call SecCertificateAddToKeychain directly since this * cert already has a keychain. */ SecCertificateRef newCertRef = NULL; ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &newCertRef); if(ortn) { cssmPerror("SecCertificateCreateFromData", ortn); printf("***Error adding this cert to default keychain.\n"); } else { ortn = SecCertificateAddToKeychain(newCertRef, NULL); if(ortn) { cssmPerror("SecCertificateAddToKeychain", ortn); printf("***Error adding this cert to default keychain.\n"); } else { printf("...cert added to default keychain.\n"); } CFRelease(newCertRef); } } CFRelease(certRef); numCerts++; } while(ortn == noErr); printf("...%u certs found matching email address \'%s\'\n", numCerts, emailAddress ? emailAddress : "<any>"); CFRelease(srch); return 0; }