/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This 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. */ /* certGroupUtils.cpp Created 10/9/2000 by Doug Mitchell. */ #include #include #include #include #include #include #include "certGroupUtils.h" #include "tpdebugging.h" #include "tpTime.h" #include /* for memcmp */ /* * Copy one CSSM_DATA to another, mallocing destination. */ void tpCopyCssmData( CssmAllocator &alloc, const CSSM_DATA *src, CSSM_DATA_PTR dst) { dst->Data = (uint8 *)alloc.malloc(src->Length); dst->Length = src->Length; memmove(dst->Data, src->Data, src->Length); } /* * Malloc a CSSM_DATA, copy another one to it. */ CSSM_DATA_PTR tpMallocCopyCssmData( CssmAllocator &alloc, const CSSM_DATA *src) { CSSM_DATA_PTR dst = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); tpCopyCssmData(alloc, src, dst); return dst; } /* * Free the data referenced by a CSSM data, and optionally, the struct itself. */ void tpFreeCssmData( CssmAllocator &alloc, CSSM_DATA_PTR data, CSSM_BOOL freeStruct) { if(data == NULL) { return; } if(data->Length != 0) { tpFree(alloc, data->Data); } if(freeStruct) { tpFree(alloc, data); } else { data->Length = 0; data->Data = NULL; } } /* * Compare two CSSM_DATAs, return CSSM_TRUE if identical. */ CSSM_BOOL tpCompareCssmData( const CSSM_DATA *data1, const CSSM_DATA *data2) { if((data1 == NULL) || (data1->Data == NULL) || (data2 == NULL) || (data2->Data == NULL) || (data1->Length != data2->Length)) { return CSSM_FALSE; } if(data1->Length != data2->Length) { return CSSM_FALSE; } if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { return CSSM_TRUE; } else { return CSSM_FALSE; } } /* * Free memory via specified plugin's app-level allocator */ void tpFreePluginMemory( CSSM_HANDLE hand, void *p) { CSSM_API_MEMORY_FUNCS memFuncs; CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); if(crtn) { tpErrorLog("CSSM_GetAPIMemoryFunctions failure\n"); /* oh well, leak and continue */ return; } memFuncs.free_func(p, memFuncs.AllocRef); } /* * Obtain the public key blob from a cert. */ CSSM_DATA_PTR tp_CertGetPublicKey( TPCertInfo *cert, CSSM_DATA_PTR *valueToFree) // used in tp_CertFreePublicKey { CSSM_RETURN crtn; CSSM_DATA_PTR val; CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *keyInfo; *valueToFree = NULL; crtn = cert->fetchField(&CSSMOID_X509V1SubjectPublicKeyCStruct, &val); if(crtn) { tpErrorLog("Error on CSSM_CL_CertGetFirstFieldValue(PublicKeyCStruct)\n"); return NULL; } *valueToFree = val; keyInfo = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)val->Data; return &keyInfo->subjectPublicKey; } void tp_CertFreePublicKey( CSSM_CL_HANDLE clHand, CSSM_DATA_PTR value) { CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SubjectPublicKeyCStruct, value); } /* * Obtain signature algorithm info from a cert. */ CSSM_X509_ALGORITHM_IDENTIFIER_PTR tp_CertGetAlgId( TPCertInfo *cert, CSSM_DATA_PTR *valueToFree) // used in tp_CertFreeAlgId { CSSM_RETURN crtn; CSSM_DATA_PTR val; *valueToFree = NULL; crtn = cert->fetchField(&CSSMOID_X509V1SignatureAlgorithm, &val); if(crtn) { tpErrorLog("Error on fetchField(CSSMOID_X509V1SignatureAlgorithm)\n"); return NULL; } *valueToFree = val; return (CSSM_X509_ALGORITHM_IDENTIFIER_PTR)val->Data; } void tp_CertFreeAlgId( CSSM_CL_HANDLE clHand, CSSM_DATA_PTR value) { CSSM_CL_FreeFieldValue(clHand, &CSSMOID_X509V1SignatureAlgorithm, value); } /* * Determine if two certs - passed in encoded form - are equivalent. */ CSSM_BOOL tp_CompareCerts( const CSSM_DATA *cert1, const CSSM_DATA *cert2) { return tpCompareCssmData(cert1, cert2); } /* * Given a aignature OID, return the corresponding CSSM_ALGID for the * signature the required key. */ CSSM_ALGORITHMS tpOidToAldId( const CSSM_OID *oid, CSSM_ALGORITHMS *keyAlg) // RETURNED { *keyAlg = CSSM_ALGID_RSA; // default if(tpCompareOids(oid, &CSSMOID_MD2WithRSA)) { return CSSM_ALGID_MD2WithRSA; } else if(tpCompareOids(oid, &CSSMOID_MD5WithRSA)) { return CSSM_ALGID_MD5WithRSA; } else if(tpCompareOids(oid, &CSSMOID_SHA1WithRSA)) { return CSSM_ALGID_SHA1WithRSA; } else if(tpCompareOids(oid, &CSSMOID_SHA1WithRSA_OIW)) { return CSSM_ALGID_SHA1WithRSA; } else if(tpCompareOids(oid, &CSSMOID_SHA1WithDSA)) { *keyAlg = CSSM_ALGID_DSA; return CSSM_ALGID_SHA1WithDSA; } else if(tpCompareOids(oid, &CSSMOID_APPLE_FEE_MD5)) { *keyAlg = CSSM_ALGID_FEE; return CSSM_ALGID_FEE_MD5; } else if(tpCompareOids(oid, &CSSMOID_APPLE_FEE_SHA1)) { *keyAlg = CSSM_ALGID_FEE; return CSSM_ALGID_FEE_SHA1; } else if(tpCompareOids(oid, &CSSMOID_APPLE_ECDSA)) { *keyAlg = CSSM_ALGID_FEE; return CSSM_ALGID_SHA1WithECDSA; } else { *keyAlg = CSSM_ALGID_NONE; return CSSM_ALGID_NONE; } } /* * Convert a C string to lower case in place. NULL terminator not needed. */ void tpToLower( char *str, unsigned strLen) { for(unsigned i=0; i= MAX_DNS_COMP_LEN. * Returns true if a component was found. */ static bool tpNextDnsComp( const char *inBuf, uint32 &inBufLen, // IN/OUT char *outBuf, // component RETURNED here uint32 &outBufLen) // RETURNED length of component { outBufLen = 0; if(inBufLen == 0) { return false; } /* skip over leading '.' */ if(*inBuf == '.') { inBuf++; if(--inBufLen == 0) { return false; } } /* copy chars until out of data or next '.' found */ do { if(*inBuf == '.') { break; } *outBuf++ = *inBuf++; inBufLen--; outBufLen++; if(outBufLen >= MAX_DNS_COMP_LEN) { /* abort */ break; } } while(inBufLen != 0); if(outBufLen) { return true; } else { return false; } } /* * Find location of specified substring in given bigstring. Returns * pointer to start of substring in bigstring, else returns NULL. */ static const char *tpSubStr( const char *bigstr, uint32 bigstrLen, const char *substr, uint32 substrLen) { /* stop searching substrLen chars before end of bigstr */ const char *endBigStr = bigstr + bigstrLen - substrLen; for( ; bigstr <= endBigStr; ) { if(*bigstr == *substr) { /* first char match - remainder? */ if(substrLen == 1) { /* don't count on memcmp(a,b,0) */ return bigstr; } if(!memcmp(bigstr+1, substr+1, substrLen - 1)) { return bigstr; } } bigstr++; } return NULL; } /* * Compare two DNS components, with full wildcard check. We assume * that no '.' chars exist (per the processing performed in * tpNextDnsComp()). Returns CSSM_TRUE on match, else CSSM_FALSE. */ static CSSM_BOOL tpCompareComps( const char *hostComp, // no wildcards uint32 hostCompLen, const char *certComp, // wildcards OK here uint32 certCompLen) { const char *endCertComp = certComp + certCompLen; const char *endHostComp = hostComp + hostCompLen; do { /* wild card in cert name? */ const char *wildCard = tpSubStr(certComp, certCompLen, "*", 1); if(wildCard == NULL) { /* no, require perfect literal match right now */ if((hostCompLen == certCompLen) && !memcmp(hostComp, certComp, certCompLen)) { return CSSM_TRUE; } else { return CSSM_FALSE; } } if(wildCard != certComp) { /* * Require literal match of hostComp with certComp * up until (but not including) the wildcard */ uint32 subStrLen = wildCard - certComp; if(subStrLen > hostCompLen) { /* out of host name chars */ return CSSM_FALSE; } if(memcmp(certComp, hostComp, subStrLen)) { return CSSM_FALSE; } /* OK, skip over substring */ hostComp += subStrLen; hostCompLen -= subStrLen; /* start parsing at the wildcard itself */ certComp = wildCard; certCompLen -= subStrLen; continue; } /* * Currently looking at a wildcard. * * Find substring in hostComp which matches from the char after * the wildcard up to whichever of these comes next: * * -- end of certComp * -- another wildcard */ wildCard++; if(wildCard == endCertComp) { /* * -- Wild card at end of cert's DNS * -- nothing else to match - rest of hostComp is the wildcard * match * -- done, success */ return CSSM_TRUE; } const char *afterSubStr; // in certComp afterSubStr = tpSubStr(wildCard, endCertComp - wildCard, "*", 1); if(afterSubStr == NULL) { /* no more wildcards - use end of certComp */ afterSubStr = endCertComp; } uint32 subStrLen = afterSubStr - wildCard; const char *foundSub = tpSubStr(hostComp, hostCompLen, wildCard, subStrLen); if(foundSub == NULL) { /* No match of explicit chars */ return CSSM_FALSE; } /* found it - skip past this substring */ hostComp = foundSub + subStrLen; hostCompLen = endHostComp - hostComp; certComp = afterSubStr; certCompLen = endCertComp - afterSubStr; } while((hostCompLen != 0) || (certCompLen != 0)); if((hostCompLen == 0) && (certCompLen == 0)) { return CSSM_TRUE; } else { /* end of one but not the other */ return CSSM_FALSE; } } /* * Compare hostname, is presented to the TP in * CSSM_APPLE_TP_SSL_OPTIONS.ServerName, to a server name obtained * from the server's cert (i.e., from subjectAltName or commonName). * Limited wildcard checking is performed here. * * The incoming hostname is assumed to have been processed by tpToLower(); * we'll perform that processing on certName here. * * Returns CSSM_TRUE on match, else CSSM_FALSE. */ CSSM_BOOL tpCompareHostNames( const char *hostName, // spec'd by app, tpToLower'd uint32 hostNameLen, char *certName, // from cert, we tpToLower uint32 certNameLen) { tpToLower(certName, certNameLen); /* tolerate optional NULL terminators for both */ if(hostName[hostNameLen - 1] == '\0') { hostNameLen--; } if(certName[certNameLen - 1] == '\0') { certNameLen--; } /* case 1: exact match */ if((certNameLen == hostNameLen) && !memcmp(certName, hostName, certNameLen)) { return CSSM_TRUE; } /* * Case 2: Compare one component at a time, handling wildcards in * cert's server name. The characters implicitly matched by a * wildcard span only one component of a dnsName. */ do { /* get next component from each dnsName */ char hostComp[MAX_DNS_COMP_LEN]; char certComp[MAX_DNS_COMP_LEN]; uint32 hostCompLen; uint32 certCompLen; bool foundHost = tpNextDnsComp(hostName, hostNameLen, hostComp, hostCompLen); bool foundCert = tpNextDnsComp(certName, certNameLen, certComp, certCompLen); if(foundHost != foundCert) { /* unequal number of components */ tpPolicyError("tpCompareHostNames: wildcard mismatch (1)"); return CSSM_FALSE; } if(!foundHost) { /* normal successful termination */ return CSSM_TRUE; } /* compare individual components */ if(!tpCompareComps(hostComp, hostCompLen, certComp, certCompLen)) { tpPolicyError("tpCompareHostNames: wildcard mismatch (2)"); return CSSM_FALSE; } /* skip over this component */ hostName += hostCompLen; certName += certCompLen; } while(1); /* NOT REACHED */ //assert(0): return CSSM_FALSE; } /* * Compare email address, is presented to the TP in * CSSM_APPLE_TP_SMIME_OPTIONS.SenderEmail, to a string obtained * from the sender's cert (i.e., from subjectAltName or Subject DN). * * Returns CSSM_TRUE on match, else CSSM_FALSE. * * Incomiong appEmail string has already been tpNormalizeAddrSpec'd. * We do that for certEmail string here. */ CSSM_BOOL tpCompareEmailAddr( const char *appEmail, // spec'd by app, normalized uint32 appEmailLen, char *certEmail, // from cert, we normalize uint32 certEmailLen) { tpNormalizeAddrSpec(certEmail, certEmailLen); /* tolerate optional NULL terminators for both */ if(appEmail[appEmailLen - 1] == '\0') { appEmailLen--; } if(certEmail[certEmailLen - 1] == '\0') { certEmailLen--; } if((certEmailLen == appEmailLen) && !memcmp(certEmail, appEmail, certEmailLen)) { return CSSM_TRUE; } else { /* mismatch */ tpPolicyError("tpCompareEmailAddr: app/cert email addrs mismatch"); return CSSM_FALSE; } }