X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_smime/lib/cert.c diff --git a/OSX/libsecurity_smime/lib/cert.c b/OSX/libsecurity_smime/lib/cert.c new file mode 100644 index 00000000..d00935a2 --- /dev/null +++ b/OSX/libsecurity_smime/lib/cert.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2003,2011-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@ + */ + + +#include "cert.h" +#include "cmstpriv.h" +#include "cmslocal.h" +#include "secitem.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* for errKCDuplicateItem */ +#include + +#define CERT_DEBUG 0 +#if CERT_DEBUG +#define dprintf(args...) fprintf(stderr, args) +#else +#define dprintf(args...) +#endif + +/* @@@ Remove this once it's back in the appropriate header. */ +static const uint8 X509V1IssuerNameStd[] = {INTEL_X509V3_CERT_R08, 23}; +static const CSSM_OID OID_X509V1IssuerNameStd = {INTEL_X509V3_CERT_R08_LENGTH+1, (uint8 *)X509V1IssuerNameStd}; + +/* + * Normalize a Printable String. Per RFC2459 (4.1.2.4), printable strings are case + * insensitive and we're supposed to ignore leading and trailing + * whitespace, and collapse multiple whitespace characters into one. + */ +static void +CERT_NormalizeString(CSSM_DATA_PTR string) +{ + char *pD, *pCh, *pEos; + + if (!string->Length) + return; + + pD = pCh = (char *)string->Data; + pEos = pCh + string->Length - 1; + + /* Strip trailing NULL terminators */ + while(*pEos == 0) + pEos--; + + /* Remove trailing spaces */ + while(isspace(*pEos)) + pEos--; + + /* Point to one past last non-space character */ + pEos++; + + /* skip all leading whitespace */ + while(isspace(*pCh) && (pCh < pEos)) + pCh++; + + /* Eliminate multiple whitespace and convent to upper case. + * pCh points to first non-white char. + * pD still points to start of string. */ + while(pCh < pEos) + { + char ch = *pCh++; + *pD++ = toupper(ch); + if(isspace(ch)) + { + /* skip 'til next nonwhite */ + while(isspace(*pCh) && (pCh < pEos)) + pCh++; + } + } + + string->Length = pD - (char *)string->Data; +} + +/* + * Normalize an RDN. Per RFC2459 (4.1.2.4), printable strings are case + * insensitive and we're supposed to ignore leading and trailing + * whitespace, and collapse multiple whitespace characters into one. + * + * Incoming NSS_Name is assumed to be entirely within specifed coder's + * address space; we'll be munging some of that and possibly replacing + * some pointers with others allocated from the same space. + */ +void +CERT_NormalizeX509NameNSS(NSS_Name *nssName) +{ + NSS_RDN *rdn; + + for (rdn = *nssName->rdns; rdn; ++rdn) + { + NSS_ATV *attr; + for (attr = *rdn->atvs; attr; ++attr) + { + /* + * attr->value is an ASN_ANY containing an encoded + * string. We only normalize Prinatable String types. + * If we find one, decode it, normalize it, encode the + * result, and put the encoding back in attr->value. + * We temporarily "leak" the original string, which only + * has a lifetime of the incoming SecNssCoder. + */ + NSS_TaggedItem *attrVal = &attr->value; + if(attrVal->tag != SEC_ASN1_PRINTABLE_STRING) + continue; + + CERT_NormalizeString(&attrVal->item); + } + } +} + +SecCertificateRef CERT_FindCertByNicknameOrEmailAddr(SecKeychainRef keychainOrArray, char *name) +{ + SecCertificateRef certificate; + OSStatus status=SecCertificateFindByEmail(keychainOrArray,name,&certificate); + return status==noErr?certificate:NULL; +} + +SecPublicKeyRef SECKEY_CopyPublicKey(SecPublicKeyRef pubKey) +{ + CFRetain(pubKey); + return pubKey; +} + +void SECKEY_DestroyPublicKey(SecPublicKeyRef pubKey) +{ + CFRelease(pubKey); +} + +SecPublicKeyRef SECKEY_CopyPrivateKey(SecPublicKeyRef privKey) +{ + CFRetain(privKey); + return privKey; +} + +void SECKEY_DestroyPrivateKey(SecPublicKeyRef privKey) +{ + CFRelease(privKey); +} + +void CERT_DestroyCertificate(SecCertificateRef cert) +{ + CFRelease(cert); +} + +SecCertificateRef CERT_DupCertificate(SecCertificateRef cert) +{ + CFRetain(cert); + return cert; +} + +SecIdentityRef CERT_FindIdentityByUsage(SecKeychainRef keychainOrArray, + char *nickname, SECCertUsage usage, Boolean validOnly, void *proto_win) +{ + SecIdentityRef identityRef = NULL; + SecCertificateRef cert = CERT_FindCertByNicknameOrEmailAddr(keychainOrArray, nickname); + if (!cert) + return NULL; + + SecIdentityCreateWithCertificate(keychainOrArray, cert, &identityRef); + CFRelease(cert); + + return identityRef; +} + +SecCertificateRef CERT_FindUserCertByUsage(SecKeychainRef keychainOrArray, + char *nickname,SECCertUsage usage,Boolean validOnly,void *proto_win) +{ + SecItemClass itemClass = kSecCertificateItemClass; + SecKeychainSearchRef searchRef; + SecKeychainItemRef itemRef = NULL; + OSStatus status; + SecKeychainAttribute attrs[1]; + const char *serialNumber = "12345678"; + // const SecKeychainAttributeList attrList; +#if 0 + attrs[0].tag = kSecLabelItemAttr; + attrs[0].length = strlen(nickname)+1; + attrs[0].data = nickname; +#else + attrs[0].tag = kSecSerialNumberItemAttr; + attrs[0].length = (UInt32)strlen(serialNumber)+1; + attrs[0].data = (uint8 *)serialNumber; +#endif + SecKeychainAttributeList attrList = { 0, attrs }; + // 12 34 56 78 + status = SecKeychainSearchCreateFromAttributes(keychainOrArray,itemClass,&attrList,&searchRef); + if (status) + { + printf("CERT_FindUserCertByUsage: SecKeychainSearchCreateFromAttributes:%d",(int)status); + return NULL; + } + status = SecKeychainSearchCopyNext(searchRef,&itemRef); + if (status) + printf("CERT_FindUserCertByUsage: SecKeychainSearchCopyNext:%d",(int)status); + CFRelease(searchRef); + return (SecCertificateRef)itemRef; +} + +/* +startNewClass(X509Certificate) +CertType, kSecCertTypeItemAttr, "CertType", 0, NULL, UINT32) +CertEncoding, kSecCertEncodingItemAttr, "CertEncoding", 0, NULL, UINT32) +PrintName, kSecLabelItemAttr, "PrintName", 0, NULL, BLOB) +Alias, kSecAlias, "Alias", 0, NULL, BLOB) +Subject, kSecSubjectItemAttr, "Subject", 0, NULL, BLOB) +Issuer, kSecIssuerItemAttr, "Issuer", 0, NULL, BLOB) +SerialNumber, kSecSerialNumberItemAttr, "SerialNumber", 0, NULL, BLOB) +SubjectKeyIdentifier, kSecSubjectKeyIdentifierItemAttr, "SubjectKeyIdentifier", 0, NULL, BLOB) +PublicKeyHash, kSecPublicKeyHashItemAttr, "PublicKeyHash", 0, NULL, BLOB) +endNewClass() +*/ + +CFArrayRef CERT_CertChainFromCert(SecCertificateRef cert, SECCertUsage usage, Boolean includeRoot) +{ + SecPolicySearchRef searchRef = NULL; + SecPolicyRef policy = NULL; + CFArrayRef wrappedCert = NULL; + SecTrustRef trust = NULL; + CFArrayRef certChain = NULL; + CSSM_TP_APPLE_EVIDENCE_INFO *statusChain; + CFDataRef actionData = NULL; + OSStatus status = 0; + + if (!cert) + goto loser; + + status = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); + if (status) + goto loser; + status = SecPolicySearchCopyNext(searchRef, &policy); + if (status) + goto loser; + + wrappedCert = CERT_CertListFromCert(cert); + status = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); + if (status) + goto loser; + + /* Tell SecTrust that we don't care if any certs in the chain have expired, + nor do we want to stop when encountering a cert with a trust setting; + we always want to build the full chain. + */ + CSSM_APPLE_TP_ACTION_DATA localActionData = { + CSSM_APPLE_TP_ACTION_VERSION, + CSSM_TP_ACTION_ALLOW_EXPIRED | CSSM_TP_ACTION_ALLOW_EXPIRED_ROOT + }; + actionData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)&localActionData, sizeof(localActionData), kCFAllocatorNull); + if (!actionData) + goto loser; + + status = SecTrustSetParameters(trust, CSSM_TP_ACTION_DEFAULT, actionData); + if (status) + goto loser; + + status = SecTrustEvaluate(trust, NULL); + if (status) + goto loser; + + status = SecTrustGetResult(trust, NULL, &certChain, &statusChain); + if (status) + goto loser; + + /* We don't drop the root if there is only 1 (self signed) certificate in the chain. */ + if (!includeRoot && CFArrayGetCount(certChain) > 1) + { + CFMutableArrayRef subChain = CFArrayCreateMutableCopy(NULL, 0, certChain); + CFRelease(certChain); + certChain = subChain; + if (subChain) + CFArrayRemoveValueAtIndex(subChain, CFArrayGetCount(subChain) - 1); + } + +loser: + if (searchRef) + CFRelease(searchRef); + if (policy) + CFRelease(policy); + if (wrappedCert) + CFRelease(wrappedCert); + if (trust) + CFRelease(trust); + if (actionData) + CFRelease(actionData); + if (certChain && status) + { + CFRelease(certChain); + certChain = NULL; + } + + return certChain; +} + +CFArrayRef CERT_CertListFromCert(SecCertificateRef cert) +{ + const void *value = cert; + return cert ? CFArrayCreate(NULL, &value, 1, &kCFTypeArrayCallBacks) : NULL; +} + +CFArrayRef CERT_DupCertList(CFArrayRef oldList) +{ + CFRetain(oldList); + return oldList; +} + +// Extract a public key object from a SubjectPublicKeyInfo +SecPublicKeyRef CERT_ExtractPublicKey(SecCertificateRef cert) +{ + SecPublicKeyRef keyRef = NULL; + SecCertificateCopyPublicKey(cert,&keyRef); + return keyRef; +} + +SECStatus CERT_CheckCertUsage (SecCertificateRef cert,unsigned char usage) +{ + // abort(); + // @@@ It's all good, it's ok. + return SECSuccess; +} + +// Find a certificate in the database by a email address +// "emailAddr" is the email address to look up +SecCertificateRef CERT_FindCertByEmailAddr(SecKeychainRef keychainOrArray, char *emailAddr) +{ + abort(); + return NULL; +} + +// Find a certificate in the database by a DER encoded certificate +// "derCert" is the DER encoded certificate +SecCertificateRef CERT_FindCertByDERCert(SecKeychainRef keychainOrArray, const SECItem *derCert) +{ + // @@@ Technically this should look though keychainOrArray for a cert matching this one I guess. + SecCertificateRef cert = NULL; + OSStatus rv; + + rv = SecCertificateCreateFromData(derCert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert); + if (rv && cert) + { + PORT_SetError(SEC_ERROR_NO_EMAIL_CERT); + CFRelease(cert); + cert = NULL; + } + + return cert; +} + +static int compareCssmData( + const CSSM_DATA *d1, + const CSSM_DATA *d2) +{ + if((d1 == NULL) || (d2 == NULL)) { + return 0; + } + if(d1->Length != d2->Length) { + return 0; + } + if(memcmp(d1->Data, d2->Data, d1->Length)) { + return 0; + } + return 1; +} + +// Generate a certificate key from the issuer and serialnumber, then look it up in the database. +// Return the cert if found. "issuerAndSN" is the issuer and serial number to look for +SecCertificateRef CERT_FindCertByIssuerAndSN (CFTypeRef keychainOrArray, + CSSM_DATA_PTR *rawCerts, PRArenaPool *pl, const SecCmsIssuerAndSN *issuerAndSN) +{ + SecCertificateRef certificate; + int numRawCerts = SecCmsArrayCount((void **)rawCerts); + int dex; + OSStatus ortn; + + /* + * First search the rawCerts array. + */ + for(dex=0; dexderIssuer, &issuerAndSN->derIssuer)) { + CFRelease(certificate); + continue; + } + if(!compareCssmData(&isn->serialNumber, &issuerAndSN->serialNumber)) { + CFRelease(certificate); + continue; + } + /* got it */ + dprintf("CERT_FindCertByIssuerAndSN: found cert %p\n", certificate); + return certificate; + } + + /* now search keychain(s) */ + OSStatus status = SecCertificateFindByIssuerAndSN(keychainOrArray, &issuerAndSN->derIssuer, + &issuerAndSN->serialNumber, &certificate); + if (status) + { + PORT_SetError(SEC_ERROR_NO_EMAIL_CERT); + certificate = NULL; + } + + return certificate; +} + +SecCertificateRef CERT_FindCertBySubjectKeyID (CFTypeRef keychainOrArray, + CSSM_DATA_PTR *rawCerts, const SECItem *subjKeyID) +{ + SecCertificateRef certificate; + int numRawCerts = SecCmsArrayCount((void **)rawCerts); + int dex; + OSStatus ortn; + SECItem skid; + + /* + * First search the rawCerts array. + */ + for(dex=0; dexdata. +SECStatus CERT_FindSubjectKeyIDExtension (SecCertificateRef cert, SECItem *retItem) +{ + CSSM_DATA_PTR fieldValue = NULL; + OSStatus ortn; + CSSM_X509_EXTENSION *extp; + CE_SubjectKeyID *skid; + + ortn = SecCertificateCopyFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier, + &fieldValue); + if(ortn || (fieldValue == NULL)) { + /* this cert doesn't have that extension */ + return SECFailure; + } + extp = (CSSM_X509_EXTENSION *)fieldValue->Data; + skid = (CE_SubjectKeyID *)extp->value.parsedValue; + retItem->Data = (uint8 *)PORT_Alloc(skid->Length); + retItem->Length = skid->Length; + memmove(retItem->Data, skid->Data, retItem->Length); + SecCertificateReleaseFirstFieldValue(cert, &CSSMOID_SubjectKeyIdentifier, + fieldValue); + return SECSuccess; +} + +// Extract the issuer and serial number from a certificate +SecCmsIssuerAndSN *CERT_GetCertIssuerAndSN(PRArenaPool *pl, SecCertificateRef cert) +{ + OSStatus status; + SecCmsIssuerAndSN *certIssuerAndSN; + SecCertificateRef certRef; + SecCertificateRef itemImplRef = NULL; + 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); + + /* Retain input cert and get pointer to its data */ + certRef = (SecCertificateRef)((cert) ? CFRetain(cert) : NULL); + status = SecCertificateGetData(certRef, &certData); + if (status) + goto loser; +#if 1 + // Convert unified input certRef to itemImpl instance. + // note: must not release this instance while we're using its CL handle! + itemImplRef = SecCertificateCreateItemImplInstance(cert); + status = SecCertificateGetCLHandle_legacy(itemImplRef, &clHandle); +#else + status = SecCertificateGetCLHandle(certRef, &clHandle); +#endif + 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); + + if (itemImplRef) + CFRelease(itemImplRef); + if (certRef) + CFRelease(certRef); + + 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); + if (itemImplRef) + CFRelease(itemImplRef); + if (certRef) + CFRelease(certRef); + + PORT_SetError(SEC_INTERNAL_ONLY); + return NULL; +} + +// import a collection of certs into the temporary or permanent cert database +SECStatus CERT_ImportCerts(SecKeychainRef keychain, SECCertUsage usage, unsigned int ncerts, + SECItem **derCerts, SecCertificateRef **retCerts, Boolean keepCerts, Boolean caOnly, char *nickname) +{ + OSStatus rv = SECFailure; + SecCertificateRef cert; + unsigned int ci; + + // @@@ Do something with caOnly and nickname + if (caOnly || nickname) + abort(); + + for (ci = 0; ci < ncerts; ++ci) + { + rv = SecCertificateCreateFromData(derCerts[ci], CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cert); + if (rv) + break; + if (keepCerts) + { + rv = SecCertificateAddToKeychain(cert, keychain); + if (rv) + { + if (rv == errKCDuplicateItem) + rv = noErr; + else + { + CFRelease(cert); + break; + } + } + } + + if (retCerts) + { + // @@@ not yet + abort(); + } + else + CFRelease(cert); + } + + return rv; +} + +SECStatus CERT_SaveSMimeProfile(SecCertificateRef cert, SECItem *emailProfile,SECItem *profileTime) +{ + fprintf(stderr, "WARNING: CERT_SaveSMimeProfile unimplemented\n"); + return SECSuccess; +} + +// Check the hostname to make sure that it matches the shexp that +// is given in the common name of the certificate. +SECStatus CERT_VerifyCertName(SecCertificateRef cert, const char *hostname) +{ + fprintf(stderr, "WARNING: CERT_VerifyCertName unimplemented\n"); + return SECSuccess; +} + +/* +** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE +** verify a certificate by checking validity times against a certain time, +** that we trust the issuer, and that the signature on the certificate is +** valid. +** "cert" the certificate to verify +** "checkSig" only check signatures if true +*/ +SECStatus +CERT_VerifyCert(SecKeychainRef keychainOrArray, SecCertificateRef cert, + const CSSM_DATA_PTR *otherCerts, /* intermediates */ + CFTypeRef policies, CFAbsoluteTime stime, SecTrustRef *trustRef) +{ + CFMutableArrayRef certificates = NULL; + SecTrustRef trust = NULL; + OSStatus rv; + int numOtherCerts = SecCmsArrayCount((void **)otherCerts); + int dex; + + /* + * Certs to evaluate: first the leaf - our cert - then all the rest we know + * about. It's OK for otherCerts to contain a copy of the leaf. + */ + certificates = CFArrayCreateMutable(NULL, numOtherCerts + 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(certificates, cert); + for(dex=0; dex