/* * 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. */ /* * CLCertExtensions.cpp - extensions support. A major component of DecodedCert. * * Created 9/8/2000 by Doug Mitchell. * Copyright (c) 2000 by Apple Computer. * */ #include "DecodedCert.h" #include "cldebugging.h" #include "CertBuilder.h" #include "CLCertExtensions.h" #include "SnaccUtils.h" #include #include #include #include #include #include #include #include static AsnType *oidToSnaccObj( const AsnOid &extnId); #define MIN_EXTENSIONS 4 // initial size of *mExtensions /* * AsnOid "constants" which we construct and cache on demand to avoid the * somewhat expensive op of constructing them every time we test for equality * in oidToSnaccObj(). */ class ExtOidCache { public: ExtOidCache() : mId_ce_keyUsage(id_ce_keyUsage_arc), mId_ce_basicConstraints(id_ce_basicConstraints_arc), mId_ce_extKeyUsage(id_ce_extKeyUsage_arc), mId_ce_subjectKeyIdentifier(id_ce_subjectKeyIdentifier_arc), mId_ce_authorityKeyIdentifier(id_ce_authorityKeyIdentifier_arc), mId_ce_subjectAltName(id_ce_subjectAltName_arc), mId_ce_certificatePolicies(id_ce_certificatePolicies_arc), mId_netscape_cert_type(id_netscape_cert_type_arc) { } AsnOid mId_ce_keyUsage; AsnOid mId_ce_basicConstraints; AsnOid mId_ce_extKeyUsage; AsnOid mId_ce_subjectKeyIdentifier; AsnOid mId_ce_authorityKeyIdentifier; AsnOid mId_ce_subjectAltName; AsnOid mId_ce_certificatePolicies; AsnOid mId_netscape_cert_type; }; static ModuleNexus extOidCache; /* * Decode tbs->Extensions into mExtensions. This involves figuring out * what kind of object is represented in the octet string in the * extension, decoding it, and placing the resulting AsnType in a * new DecodedExten struct. * * Called when decoding either a cert (for caching it or getting its fields) * or a template (only via CertGetAllTemplateFields()). */ void DecodedCert::decodeExtensions() { CASSERT(certificateToSign != NULL); Extensions *extensions = certificateToSign->extensions; if(extensions == NULL) { /* OK, no extensions present */ return; } Extension *snaccExten = extensions->First(); extensions->SetCurrToFirst(); /* traverse extension list */ while(snaccExten != 0) { /* * For this extension->extnId, cook up an approppriate * AsnType (KeyUsage, etc.); */ AsnOid &extnId = snaccExten->extnId; bool berEncoded = false; AsnType *snaccObj = oidToSnaccObj(extnId); if(snaccObj == NULL) { /* * We don't know how to deal with this, just take the * raw bytes, copied from snaccExte. */ snaccObj = new AsnOcts(snaccExten->extnValue); berEncoded = true; } else { /* * We have a snacc-style object specific to this extension. * Decode the extensions's extnValue into that object. We don't * have to know what kind of object it is anymore. */ CssmData cData(snaccExten->extnValue, snaccExten->extnValue.Len()); try { SC_decodeAsnObj(cData, *snaccObj); } catch (...) { /* * FIXME - what do we do here? Is it safe to just ignore this * extension, or is the whole cert invalid? */ errorLog0("decodeExtensions: extension decode error\n"); delete snaccObj; snaccObj = NULL; } } if(snaccObj != NULL) { /* add to mExtensions if the decode was successful */ bool critical = false; // default if(snaccExten->critical != NULL) { critical = *snaccExten->critical; } addExtension(snaccObj, snaccExten->extnId, critical, berEncoded); } extensions->GoNext(); snaccExten = extensions->Curr(); } } /* * Encode mExtensions into tbs->Extensions. * * Each extension object, currently stored as some AsnType subclass, * is BER-encoded and the reesult is stored as an octet string * (AsnOcts) in a new Extension object in the TBS. * * Only called from CertCreateTemplate via encodeTbs(). */ #define MAX_EXTEN_SIZE (4 * 1024) /* SWAG for max encoded size */ void DecodedCert::encodeExtensions() { CertificateToSign *tbs = certificateToSign; CASSERT(mState == CS_Building); CASSERT((tbs != NULL) && (tbs->extensions == NULL)); if(mNumExtensions == 0) { /* no extensions, no error */ return; } tbs->extensions = new Extensions; Extensions *extns = tbs->extensions; /* for each of our DecodedExtens, append an Extension object to * tbs->extensions */ unsigned extenDex; for(extenDex=0; extenDexAppend(); DecodedExten *decodedExt = &mExtensions[extenDex]; /* BER-encode the extension object if appropriate */ if(decodedExt->berEncoded) { /* unknown extension type, it's already encoded */ /* A failure of this dynamic cast is a fata internal error */ AsnOcts *rawOcts = dynamic_cast(decodedExt->snaccObj); if(rawOcts == NULL) { errorLog0("encodeExtensions: dynamic_cast failure!\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } snaccExt->extnValue.Set(*rawOcts); } else { CssmAutoData aData(alloc); try { SC_encodeAsnObj(*decodedExt->snaccObj, aData, MAX_EXTEN_SIZE); } catch (...) { errorLog0("encodeExtensions: extension encode error\n"); throw; } CssmData &cData = aData.get(); snaccExt->extnValue.Set((char *)cData.data(), cData.length()); } snaccExt->critical = new AsnBool(decodedExt->critical); snaccExt->extnId.Set(*decodedExt->extnId); } } /* * Add a new DecodedExten to mExtensions. * Called from decodeExtensions and setField*. * At this point, the actual extenmsion data is represented by some subclass * of AsnType - either a specific extension type (e.g. KeyUsage), or as an * octet string (AsnOcts) for extension types we don't understand (and which * are encoded by the app). */ void DecodedCert::addExtension( AsnType *snaccObj, // e.g. KeyUsage const AsnOid &extnId, bool critical, bool berEncoded) // i.e., we don't know how to parse { /* cook up a new DecodedExten, reallocing mExtensions is necessary */ if(mNumExtensions == mSizeofExtensions) { /* expand by doubling, or initial malloc */ mSizeofExtensions = mNumExtensions ? (2 * mNumExtensions) : MIN_EXTENSIONS; mExtensions = (DecodedExten *)alloc.realloc( mExtensions, mSizeofExtensions * sizeof(DecodedExten)); } DecodedExten *decodedExt = &mExtensions[mNumExtensions++]; decodedExt->extnId = new AsnOid(extnId); decodedExt->critical = critical; decodedExt->snaccObj = snaccObj; decodedExt->berEncoded = berEncoded; } /* * Search for DecodedExten by AsnOid or "any unknown extension". * Called from getField*() and inferKeyUsage. * Returns NULL if specified extension not found. */ DecodedExten *DecodedCert::findDecodedExt( const AsnOid &extnId, // for known extensions bool unknown, // otherwise uint32 index, uint32 &numFields) const { unsigned dex; DecodedExten *decodedExt; DecodedExten *rtnExt = NULL; unsigned found = 0; for(dex=0; dexextnId == extnId)) || (unknown && decodedExt->berEncoded)) { if(found++ == index) { /* the one we want */ rtnExt = decodedExt; } if((rtnExt != NULL) && (index != 0)) { /* only determine numFields on search for first one */ break; } } } if(rtnExt != NULL) { /* sucessful return */ if(index == 0) { numFields = found; } return rtnExt; } else { return NULL; } } /* * Common code to pass info from a DecodedExten back to app. * Called from getField*(). */ static void getFieldExtenCommon( void *cdsaObj, // e.g. CE_KeyUsage // CSSM_DATA_PTR for berEncoded const DecodedExten &decodedExt, CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt; CssmAllocator &alloc = fieldValue.allocator; CssmData &fdata = fieldValue.get(); cssmExt = (CSSM_X509_EXTENSION_PTR)alloc.malloc(sizeof(CSSM_X509_EXTENSION)); fdata.Data = (uint8 *)cssmExt; fdata.Length = sizeof(CSSM_X509_EXTENSION); CL_snaccOidToCssm(*decodedExt.extnId, CssmOid::overlay(cssmExt->extnId), alloc); if(decodedExt.critical != NULL) { cssmExt->critical = decodedExt.critical ? CSSM_TRUE : CSSM_FALSE; } else { /* default */ cssmExt->critical = false; } if(decodedExt.berEncoded) { /* an extension we never parsed or understood */ cssmExt->format = CSSM_X509_DATAFORMAT_ENCODED; cssmExt->value.parsedValue = NULL; cssmExt->BERvalue = *(reinterpret_cast(cdsaObj)); } else { cssmExt->format = CSSM_X509_DATAFORMAT_PARSED; cssmExt->value.parsedValue = cdsaObj; cssmExt->BERvalue.Data = NULL; cssmExt->BERvalue.Length = 0; } } /* * Common code for top of setField* and freeField*(). */ static CSSM_X509_EXTENSION_PTR verifySetFreeExtension( const CssmData &fieldValue, bool berEncoded) // false: value in value.parsedValue // true : value in BERValue { if(fieldValue.length() != sizeof(CSSM_X509_EXTENSION)) { errorLog2("Set/FreeExtension: bad length : exp %d got %d\n", (int)sizeof(CSSM_X509_EXTENSION), (int)fieldValue.length()); CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } CSSM_X509_EXTENSION_PTR cssmExt = reinterpret_cast(fieldValue.data()); if(berEncoded) { if((cssmExt->value.parsedValue != NULL) || (cssmExt->BERvalue.Data == NULL)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } } else { if((cssmExt->value.parsedValue == NULL) || (cssmExt->BERvalue.Data != NULL)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } } return cssmExt; } /* * Common free code for all extensions. Extension-specific code must * free anything beyond cdsaExt->Value.parsedValue, then we free everything * else (except the extension struct itself, which is freed by * DecodedCert::freeCertFieldData()). */ static void freeFieldExtenCommon( CSSM_X509_EXTENSION_PTR exten, CssmAllocator &alloc) { alloc.free(exten->extnId.Data); alloc.free(exten->BERvalue.Data); // may be NULL alloc.free(exten->value.parsedValue); // may be NULL } /* * Cook up an AsnType associated with specified extnId * When adding oid/type pairs here, also add to: * -- fieldFuncs[] in CertFields.cpp * -- and the get/set/free functions in this file. */ static AsnType *oidToSnaccObj( const AsnOid &extnId) { ExtOidCache &oc = extOidCache(); if(extnId == oc.mId_ce_keyUsage) { return new KeyUsage; } else if(extnId == oc.mId_ce_basicConstraints) { return new BasicConstraintsSyntax; } else if(extnId == oc.mId_ce_extKeyUsage) { return new ExtKeyUsageSyntax; } else if(extnId == oc.mId_ce_subjectKeyIdentifier) { return new AsnOcts; } else if(extnId == oc.mId_ce_authorityKeyIdentifier) { return new AuthorityKeyIdentifier; } else if(extnId == oc.mId_ce_subjectAltName) { return new GeneralNames; } else if(extnId == oc.mId_ce_certificatePolicies) { return new CertificatePoliciesSyntax; } else if(extnId == oc.mId_netscape_cert_type) { return new AsnBits; } else { return NULL; } } /* common code for top of getField* */ template bool GetFieldTop( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmAllocator &alloc, const AsnOid &fieldId, SnaccType *&snaccObj, CdsaType *&cdsaObj, DecodedExten *&decodedExt) { /* See if we have one of these in our list of DecodedExtens */ decodedExt = cert.findDecodedExt(fieldId, false, index, numFields); if(decodedExt == NULL) { return false; } /* failure of this dynamic_cast is fatal */ snaccObj = dynamic_cast(decodedExt->snaccObj); if(snaccObj == NULL) { errorLog0("GetFieldTop: dynamic_cast failure\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } cdsaObj = (CdsaType *)alloc.malloc(sizeof(CdsaType)); memset(cdsaObj, 0, sizeof(CdsaType)); return true; } /*** *** get/set/free functions called out from CertFields.cpp ***/ /* * One common free for extensions whose parsed value doesn't go any deeper * than cssmExt->value.parsedValue. */ void freeFieldSimpleExtension ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); freeFieldExtenCommon(cssmExt, fieldValue.allocator); } /*** *** KeyUsage *** CDSA format CE_KeyUsage *** SNACC format KeyUsage *** OID CSSMOID_KeyUsage ***/ void setFieldKeyUsage( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CE_KeyUsage *cdsaObj = (CE_KeyUsage *)cssmExt->value.parsedValue; /* brute-force cdsaObj --> snaccObj */ char bits[sizeof(CE_KeyUsage)]; bits[0] = static_cast((*cdsaObj) >> 8); bits[1] = static_cast(*cdsaObj); memmove(bits, cdsaObj, sizeof(CE_KeyUsage)); KeyUsage *snaccObj = new KeyUsage(bits, sizeof(CE_KeyUsage) * 8); /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldKeyUsage( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; KeyUsage *snaccObj; CE_KeyUsage *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_keyUsage, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } unsigned toCopy = (snaccObj->BitLen() + 7) / 8; if(toCopy > 2) { /* I hope I never see this... */ errorLog0("getFieldKeyUsage: KeyUsage larger than 2 bytes!\n"); toCopy = 2; } unsigned char bits[2] = {0, 0}; memmove(bits, snaccObj->BitOcts(), toCopy); *cdsaObj = (((unsigned)bits[0]) << 8) | bits[1]; /* pass back to caller */ getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } /*** *** Basic Constraints *** CDSA format: CE_BasicConstraints *** SNACC format BasicConstraintsSyntax *** OID CSSMOID_BasicConstraints ***/ void setFieldBasicConstraints( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); BasicConstraintsSyntax *snaccObj = new BasicConstraintsSyntax; CE_BasicConstraints *cdsaObj = (CE_BasicConstraints *)cssmExt->value.parsedValue; /* brute-force cdsaObj --> snaccObj */ snaccObj->cA = new AsnBool(cdsaObj->cA ? true : false); if(cdsaObj->pathLenConstraintPresent) { AsnIntType val = cdsaObj->pathLenConstraint; snaccObj->pathLenConstraint = new AsnInt(val); } /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldBasicConstraints( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; BasicConstraintsSyntax *snaccObj; CE_BasicConstraints *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_basicConstraints, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } if(snaccObj->cA == NULL) { /* default */ cdsaObj->cA = CSSM_FALSE; } else { bool val = *snaccObj->cA; cdsaObj->cA = val ? CSSM_TRUE : CSSM_FALSE; } if(snaccObj->pathLenConstraint == NULL) { cdsaObj->pathLenConstraintPresent = CSSM_FALSE; cdsaObj->pathLenConstraint = 0; } else { cdsaObj->pathLenConstraintPresent = CSSM_TRUE; AsnIntType val = *snaccObj->pathLenConstraint; cdsaObj->pathLenConstraint = val; } /* pass back to caller */ getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } /*** *** Extended Key Usage *** CDSA format: CE_ExtendedKeyUsage *** SNACC format ExtKeyUsageSyntax *** OID CSSMOID_ExtendedKeyUsage ***/ void setFieldExtKeyUsage( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); ExtKeyUsageSyntax *snaccObj = new ExtKeyUsageSyntax; CE_ExtendedKeyUsage *cdsaObj = (CE_ExtendedKeyUsage *)cssmExt->value.parsedValue; /* brute-force cdsaObj --> snaccObj, one 'purpose' (OID) at a time */ unsigned oidDex; for(oidDex=0; oidDexnumPurposes; oidDex++) { KeyPurposeId *snaccPurp = snaccObj->Append(); CSSM_OID_PTR cdsaPurp = &cdsaObj->purposes[oidDex]; snaccPurp->Set(reinterpret_cast(cdsaPurp->Data), cdsaPurp->Length); } /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldExtKeyUsage( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; ExtKeyUsageSyntax *snaccObj; CE_ExtendedKeyUsage *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_extKeyUsage, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } /* brute force snaccObj --> cdsaObj, one purpose at a time */ CssmAllocator &alloc = fieldValue.allocator; cdsaObj->numPurposes = snaccObj->Count(); cdsaObj->purposes = (CSSM_OID_PTR)alloc.malloc(cdsaObj->numPurposes * sizeof(CSSM_OID)); snaccObj->SetCurrToFirst(); unsigned oidDex; for(oidDex=0; oidDexnumPurposes; oidDex++) { CL_snaccOidToCssm(*snaccObj->Curr(), CssmOid::overlay(cdsaObj->purposes[oidDex]), alloc); snaccObj->GoNext(); } getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } void freeFieldExtKeyUsage( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; CE_ExtendedKeyUsage *cdsaObj = (CE_ExtendedKeyUsage *)cssmExt->value.parsedValue; unsigned oidDex; for(oidDex=0; oidDexnumPurposes; oidDex++) { alloc.free(cdsaObj->purposes[oidDex].Data); } alloc.free(cdsaObj->purposes); freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } /*** *** Subject Key Identifier *** CDSA format: CE_SubjectKeyID, which is just a CSSM_DATA *** SNACC format AsnOcts *** OID CSSMOID_SubjectKeyIdentifier ***/ void setFieldSubjectKeyId( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CE_SubjectKeyID *cdsaObj = (CE_SubjectKeyID *)cssmExt->value.parsedValue; AsnOcts *snaccObj = new AsnOcts((char *)cdsaObj->Data, cdsaObj->Length); cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldSubjectKeyId( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; AsnOcts *snaccObj; CE_SubjectKeyID *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_subjectKeyIdentifier, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } CL_AsnOctsToCssmData(*snaccObj, *cdsaObj, fieldValue.allocator); getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } void freeFieldSubjectKeyId ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; CE_SubjectKeyID *cdsaObj = (CE_SubjectKeyID *)cssmExt->value.parsedValue; alloc.free(cdsaObj->Data); freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } /*** *** Authority Key Identifier *** CDSA format: CE_AuthorityKeyID *** SNACC format AuthorityKeyIdentifier *** OID CSSMOID_AuthorityKeyIdentifier ***/ void setFieldAuthorityKeyId( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CE_AuthorityKeyID *cdsaObj = (CE_AuthorityKeyID *)cssmExt->value.parsedValue; AuthorityKeyIdentifier *snaccObj = new AuthorityKeyIdentifier; /* * brute-force a CDSA-style CE_AuthorityKeyID into snacc-style * AuthorityKeyIdentifier */ if(cdsaObj->keyIdentifierPresent) { /* Just an AsnOcts */ snaccObj->keyIdentifier = new AsnOcts((char *)cdsaObj->keyIdentifier.Data, cdsaObj->keyIdentifier.Length); } if(cdsaObj->generalNamesPresent ) { /* GeneralNames, the hard one */ snaccObj->authorityCertIssuer = CL_cdsaGeneralNamesToSnacc( *cdsaObj->generalNames); } if(cdsaObj->serialNumberPresent) { /* boils down to BigIntegerStr, or AsnOcts */ snaccObj->authorityCertSerialNumber = new CertificateSerialNumber((char *)cdsaObj->serialNumber.Data, cdsaObj->serialNumber.Length); } cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldAuthorityKeyId( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; AuthorityKeyIdentifier *snaccObj; CE_AuthorityKeyID *cdsaObj; bool brtn; CssmAllocator &alloc = fieldValue.allocator; brtn = GetFieldTop( cert, index, numFields, alloc, id_ce_authorityKeyIdentifier, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } /* brute-force a snacc-style AuthorityKeyIdentifier into CDSA format */ if(snaccObj->keyIdentifier != NULL) { /* Just an AsnOcts */ cdsaObj->keyIdentifierPresent = CSSM_TRUE; CL_AsnOctsToCssmData(*snaccObj->keyIdentifier, cdsaObj->keyIdentifier, alloc); } if(snaccObj->authorityCertIssuer != NULL) { /* GeneralNames, the hard one */ cdsaObj->generalNamesPresent = CSSM_TRUE; cdsaObj->generalNames = (CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames)); CL_snaccGeneralNamesToCdsa(*snaccObj->authorityCertIssuer, *cdsaObj->generalNames, alloc); } if(snaccObj->authorityCertSerialNumber != NULL) { /* boils down to BigIntegerStr, or AsnOcts */ cdsaObj->serialNumberPresent = CSSM_TRUE; CL_AsnOctsToCssmData(*snaccObj->authorityCertSerialNumber, cdsaObj->serialNumber, alloc); } getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } static void freeFieldGeneralNames( CE_GeneralNames *cdsaObj, CssmAllocator &alloc) { if(cdsaObj == NULL) { return; } for(unsigned i=0; inumNames; i++) { alloc.free(cdsaObj->generalName[i].name.Data); } if(cdsaObj->numNames) { memset(cdsaObj->generalName, 0, cdsaObj->numNames * sizeof(CE_GeneralName)); alloc.free(cdsaObj->generalName); } memset(cdsaObj, 0, sizeof(CE_GeneralNames)); } void freeFieldAuthorityKeyId ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; CE_AuthorityKeyID *cdsaObj = (CE_AuthorityKeyID *)cssmExt->value.parsedValue; alloc.free(cdsaObj->keyIdentifier.Data); freeFieldGeneralNames(cdsaObj->generalNames, alloc); alloc.free(cdsaObj->generalNames); alloc.free(cdsaObj->serialNumber.Data); memset(cdsaObj, 0, sizeof(CE_AuthorityKeyID)); freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } /*** *** Subject alternate name *** CDSA Format: CE_GeneralNames *** SNACC format: GeneralNames *** OID: CSSMOID_SubjectAltName ***/ void setFieldSubjAltName( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CE_GeneralNames *cdsaObj = (CE_GeneralNames *)cssmExt->value.parsedValue; GeneralNames *snaccObj = CL_cdsaGeneralNamesToSnacc(*cdsaObj); cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldSubjAltName( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; GeneralNames *snaccObj; CE_GeneralNames *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_subjectAltName, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } CL_snaccGeneralNamesToCdsa(*snaccObj, *cdsaObj, fieldValue.allocator); getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } void freeFieldSubjAltName ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; CE_GeneralNames *cdsaObj = (CE_GeneralNames *)cssmExt->value.parsedValue; freeFieldGeneralNames(cdsaObj, alloc); freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } /*** *** Certificate Policies *** CDSA Format: CE_CertPolicies *** SNACC format: CertificatePoliciesSyntax *** OID: CSSMOID_CertificatePolicies ***/ #define MAX_IA5_NAME_SIZE 1024 void setFieldCertPolicies( DecodedCert &cert, const CssmData &fieldValue) { CssmAllocator &alloc = CssmAllocator::standard(); CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CertificatePoliciesSyntax *snaccObj = new CertificatePoliciesSyntax; CE_CertPolicies *cdsaObj = (CE_CertPolicies *)cssmExt->value.parsedValue; /* brute-force cdsaObj --> snaccObj */ for(unsigned polDex=0; polDexnumPolicies; polDex++) { CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex]; PolicyInformation *sPolInfo = snaccObj->Append(); sPolInfo->policyIdentifier.Set((char *)cPolInfo->certPolicyId.Data, cPolInfo->certPolicyId.Length); if(cPolInfo->numPolicyQualifiers != 0) { sPolInfo->policyQualifiers = new PolicyInformationSeqOf; } for(unsigned qualDex=0; qualDexnumPolicyQualifiers; qualDex++) { CE_PolicyQualifierInfo *cQualInfo = &cPolInfo->policyQualifiers[qualDex]; PolicyQualifierInfo *sQualInfo = sPolInfo->policyQualifiers->Append(); /* OK we're at the lowest level. * policyQualifierId == id_qt_cps: qualifier is an IA5 string, * incoming data is its contents. Else incoming data is an encoded * blob we pass on directly. */ sQualInfo->policyQualifierId.Set( (char *)cQualInfo->policyQualifierId.Data, cQualInfo->policyQualifierId.Length); /* we'll convert this incoming ptr/len.... */ uint8 *rawData = cQualInfo->qualifier.Data; unsigned rawDataLen = cQualInfo->qualifier.Length; /* to this, somehow; it'll be used to build the required AsnAny. */ CssmAutoData aData(alloc); if(sQualInfo->policyQualifierId == id_qt_cps) { /* build & encode an IA5String */ IA5String *ia5 = new IA5String((char *)rawData, rawDataLen); SC_encodeAsnObj(*ia5, aData, MAX_IA5_NAME_SIZE); delete ia5; } else { /* copy over directly */ aData.copy(rawData, rawDataLen); } /* install the result into CSM_Buffer, which mallocs & copies */ sQualInfo->qualifier = new AsnAny; char *cp = aData; sQualInfo->qualifier->value = new CSM_Buffer(cp, aData.length()); aData.reset(); } /* for each qualifier */ } /* for each policy */ /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldCertPolicies( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; CertificatePoliciesSyntax *snaccObj; CE_CertPolicies *cdsaObj; bool brtn; CssmAllocator &alloc = fieldValue.allocator; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_ce_certificatePolicies, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } /* brute force CertificatePoliciesSyntax --> CE_CertPolicies */ cdsaObj->numPolicies = snaccObj->Count(); unsigned sz = cdsaObj->numPolicies * sizeof(CE_PolicyInformation); cdsaObj->policies = (CE_PolicyInformation *)alloc.malloc(sz); memset(cdsaObj->policies, 0, sz); snaccObj->SetCurrToFirst(); for(unsigned polDex=0; polDexnumPolicies; polDex++) { CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex]; PolicyInformation *sPolInfo = snaccObj->Curr(); CssmOid &cOid = CssmOid::overlay(cPolInfo->certPolicyId); CL_snaccOidToCssm(sPolInfo->policyIdentifier, cOid, alloc); if(sPolInfo->policyQualifiers == NULL) { continue; } cPolInfo->numPolicyQualifiers = sPolInfo->policyQualifiers->Count(); cPolInfo->policyQualifiers = (CE_PolicyQualifierInfo *) alloc.malloc(cPolInfo->numPolicyQualifiers * sizeof(CE_PolicyQualifierInfo)); sPolInfo->policyQualifiers->SetCurrToFirst(); for(unsigned qualDex=0; qualDexnumPolicyQualifiers; qualDex++) { PolicyQualifierInfo *sQualInfo = sPolInfo->policyQualifiers->Curr(); CE_PolicyQualifierInfo *cQualInfo = &cPolInfo->policyQualifiers[qualDex]; /* * leaf. * policyQualifierId == id_qt_cps : IA5String - decode and return * contents. Else return whole thing. */ CssmOid &cOid2 = CssmOid::overlay(cQualInfo->policyQualifierId); CL_snaccOidToCssm(sQualInfo->policyQualifierId, cOid2, alloc); /* convert this: */ CSM_Buffer *cbuf = sQualInfo->qualifier->value; /* to this */ CssmRemoteData outData(alloc, cQualInfo->qualifier); if(sQualInfo->policyQualifierId == id_qt_cps) { IA5String ia5; CssmAutoData berData(alloc, cbuf->Access(), cbuf->Length()); /* error is fatal, punt the whole kit'n'kaboodle and leak */ SC_decodeAsnObj(berData, ia5); char *src = ia5; outData.copy(src, ia5.Len()); } else { outData.copy(cbuf->Access(), cbuf->Length()); } outData.release(); sPolInfo->policyQualifiers->GoNext(); } snaccObj->GoNext(); } getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } void freeFieldCertPolicies ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; CE_CertPolicies *cdsaObj = (CE_CertPolicies *)cssmExt->value.parsedValue; for(unsigned polDex=0; polDexnumPolicies; polDex++) { CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex]; alloc.free(cPolInfo->certPolicyId.Data); for(unsigned qualDex=0; qualDexnumPolicyQualifiers; qualDex++) { CE_PolicyQualifierInfo *cQualInfo = &cPolInfo->policyQualifiers[qualDex]; alloc.free(cQualInfo->policyQualifierId.Data); alloc.free(cQualInfo->qualifier.Data); } alloc.free(cPolInfo->policyQualifiers); } alloc.free(cdsaObj->policies); freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } /*** *** Netscape cert type *** CDSA Format: CE_NetscapeCertType (a uint16) *** SNACC format: AsnBits *** OID: CSSMOID_NetscapeCertType ***/ void setFieldNetscapeCertType( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CE_NetscapeCertType *cdsaObj = (CE_NetscapeCertType *)cssmExt->value.parsedValue; char bits[sizeof(CE_NetscapeCertType)]; bits[0] = static_cast((*cdsaObj) >> 8); bits[1] = static_cast(*cdsaObj); memmove(bits, cdsaObj, sizeof(CE_NetscapeCertType)); AsnBits *snaccObj = new AsnBits(bits, sizeof(CE_NetscapeCertType) * 8); /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldNetscapeCertType( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; AsnBits *snaccObj; CE_NetscapeCertType *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, id_netscape_cert_type, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } unsigned toCopy = (snaccObj->BitLen() + 7) / 8; if(toCopy > 2) { /* I hope I never see this... */ errorLog0("getFieldNetscapeCertType: bitstring larger than 2 bytes!\n"); toCopy = 2; } unsigned char bits[2] = {0, 0}; memmove(bits, snaccObj->BitOcts(), toCopy); *cdsaObj = (((unsigned)bits[0]) << 8) | bits[1]; getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } /*** *** unknown extensions *** CDSA format: raw bytes in a CSSM_DATA. This data is the BER-encoding of *** some extension struct we don't know about. *** SNACC format AsnOcts *** OID CSSMOID_X509V3CertificateExtensionCStruct ***/ void setFieldUnknownExt( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); AsnOcts *snaccObj = new AsnOcts( reinterpret_cast(cssmExt->BERvalue.Data), cssmExt->BERvalue.Length); cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, true); } bool getFieldUnknownExt( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { AsnOid noOidLikeThis (1, 2); // a dummy argument DecodedExten *decodedExt = cert.findDecodedExt(noOidLikeThis, true, index, numFields); if(decodedExt == NULL) { return false; } /* failure of this dynamic_cast is fatal */ AsnOcts *snaccObj = dynamic_cast(decodedExt->snaccObj); if(snaccObj == NULL) { errorLog0("getFieldUnknownExt: dynamic_cast failure\n"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } char *octData = *snaccObj; CssmAutoData encodedBytes(fieldValue.allocator, octData, snaccObj->Len()); /* easier way to do this...? */ CssmData cData = encodedBytes.release(); getFieldExtenCommon(&cData, *decodedExt, fieldValue); return true; } void freeFieldUnknownExt ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); CssmAllocator &alloc = fieldValue.allocator; freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } #if template_pcode /*** *** Subject alternate name *** CDSA Format: CE_GeneralNames *** SNACC format: GeneralNames *** OID: CSSMOID_SubjectAltName ***/ void setFieldSomeExt( DecodedCert &cert, const CssmData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); new an approppriate AsnType snaccObj (KeyUsage, etc.); extension-specific e.g. CE_KeyUsage *cdsaObj = cssmExt->value.parsedValue; /* brute-force cdsaObj --> snaccObj */ ... /* add to mExtensions */ cert.addExtension(snaccObj, cssmExt->extnId, cssmExt->critical, false); } bool getFieldSomeExt( const DecodedCert &cert, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) { DecodedExten *decodedExt; e.g. ExtKeyUsageSyntax *snaccObj; e.g. CE_ExtendedKeyUsage *cdsaObj; bool brtn; brtn = GetFieldTop( cert, index, numFields, fieldValue.allocator, e.g. id_ce_extKeyUsage, snaccObj, cdsaObj, decodedExt); if(!brtn) { return false; } brute force snaccObj --> cdsaObj; getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue); return true; } /* only used if complex structs below cssmExt->value.parsedValue */ void freeFieldSomeExt ( CssmOwnedData &fieldValue) { CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false); CssmAllocator &alloc = fieldValue.allocator; free the stuff in cssmExt->value.parsedValue; freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue } #endif