X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_apple_x509_cl/lib/DecodedExtensions.cpp?ds=inline diff --git a/OSX/libsecurity_apple_x509_cl/lib/DecodedExtensions.cpp b/OSX/libsecurity_apple_x509_cl/lib/DecodedExtensions.cpp new file mode 100644 index 00000000..ace807be --- /dev/null +++ b/OSX/libsecurity_apple_x509_cl/lib/DecodedExtensions.cpp @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2002,2011-2012,2014 Apple 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. + */ + + +/* + * DecodedItem.cpp - class representing the common portions of + * NSS-format decoded certs and CRLs, with extensions parsed and + * decoded (still in NSS format). + */ + +#include "DecodedItem.h" +#include "cldebugging.h" +#include "AppleX509CLSession.h" +#include "CSPAttacher.h" +#include "CLFieldsCommon.h" +#include "clNssUtils.h" +#include "clNameUtils.h" +#include +#include +#include + +#define MIN_EXTENSIONS 4 // initial size of *mExtensions + +DecodedExten::DecodedExten( + const CSSM_OID &extnId, // copied + bool critical, + void *nssObj, // NSS_KeyUsage, NSS_BasicConstraints, + // etc. NOT COPIED, exists in same + // memory space as coder + bool berEncoded, // indicates unknown extension which we + // do not BER-decode when parsing a cert + const SecAsn1Template *templ, // to decode/encode if !berEncoded + SecNssCoder &coder, // all local allocs from here + const CSSM_DATA *rawExtn) // NSS_CertExtension.value, copied to + // mRawExtn + : mCritical(critical), + mNssObj(nssObj), + mBerEncoded(berEncoded), + mTempl(templ), + mCoder(coder), + mRawExtn(NULL) +{ + coder.allocCopyItem(extnId, mExtnId); + if(rawExtn) { + mRawExtn = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA)); + coder.allocCopyItem(*rawExtn, *mRawExtn); + } +} + +DecodedExten::~DecodedExten() +{ + /* the only stuff we allocated was in the coder pool and will be freed + * when coder is freed */ +} + +/* + * Given a fully encoded BER object, create a CSSM_X509EXT_TAGandVALUE + * representing it. We malloc the CSSM_X509EXT_TAGandVALUE itself using + * specified allocator, but the referent (in CSSM_X509EXT_TAGandVALUE.value) + * merely points to data inside the berValue. + */ +CSSM_X509EXT_TAGandVALUE *DecodedExten::createTagAndValue( + const CSSM_DATA &berValue, + Allocator &alloc) const +{ + if((berValue.Data == NULL) || (berValue.Length == 0)) { + return NULL; + } + CSSM_X509EXT_TAGandVALUE *tv = (CSSM_X509EXT_TAGandVALUE *) + alloc.malloc(sizeof(CSSM_X509EXT_TAGandVALUE)); + + // Important: by the time we get called, the extension data + // has already been deconstructed, and the raw value we are + // handed in berValue does not include ASN.1 type or length. + // Since createTagAndValue is only called in the case where + // the contents are unknown (and thus opaque), always treat + // as an octet string. + + tv->type = SEC_ASN1_OCTET_STRING; + tv->value.Length = berValue.Length; + tv->value.Data = berValue.Data; + return tv; + +#if 0 + tv->type = berValue.Data[0]; + + /* + * length of length is variable; ASN spec says it can actually be up to + * 127 bytes, but we only have room for 32 bits! + */ + const uint8 *lp = berValue.Data + 1; + uint8 len1 = *lp; + if((len1 & 0x80) == 0) { + /* short form */ + tv->value.Length = len1; + tv->value.Data = const_cast(lp + 1); + return tv; + } + + unsigned numLenBytes = len1 & 0x7f; + if(numLenBytes > 4) { + clErrorLog("createTagAndValue: impossible length of length (%u)\n", numLenBytes); + alloc.free(tv); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + uint32 len = 0; + lp++; // points to first length byte + for(uint32 dex=0; dexvalue.Length = len; + tv->value.Data = const_cast(lp); + return tv; +#endif +} + +/* + * Convert this extension to a CSSM_X509_EXTENSION, in the specified + * (app-level) alloc space, after its contents have + * been converted to a native CDSA object (CE_KeyUsage, etc.). + */ +void DecodedExten::convertToCdsa( + void *cdsaObj, // e.g. CE_KeyUsage + // CSSM_DATA_PTR for berEncoded + CSSM_X509_EXTENSION_PTR cssmExt, // contents RETURNED + Allocator &alloc) const +{ + clAllocCopyData(alloc, mExtnId, cssmExt->extnId); + cssmExt->critical = mCritical ? CSSM_TRUE : CSSM_FALSE; + + /* + * in either case copy the raw extension data if we have it (we may not + * have it if this was created via setField). + */ + if(mRawExtn) { + clAllocCopyData(alloc, *mRawExtn, cssmExt->BERvalue); + } + else { + cssmExt->BERvalue.Data = NULL; + cssmExt->BERvalue.Length = 0; + } + if(mBerEncoded) { + /* an extension we never parsed or understood */ + assert(cdsaObj == NULL); + cssmExt->format = CSSM_X509_DATAFORMAT_ENCODED; + cssmExt->value.tagAndValue = createTagAndValue(cssmExt->BERvalue, alloc); + } + else { + /* caller sees parsed version plus raw BER-encoded bytes */ + assert(cdsaObj != NULL); + cssmExt->format = CSSM_X509_DATAFORMAT_PARSED; + /* in app alloc's space, mallocd by getField*() */ + cssmExt->value.parsedValue = cdsaObj; + } +} + +/* + * Convert a DecodedExten to a CSSM_X509_EXTENSION. This includes + * the mapping of the extnId to a known CDSA type and type and doing the + * actual NSS-to-CDSA conversion. At the time this function is + * called, the DecodedExten either has a valid mNssObj, or it's an + * unknown extension type in which case mNssObj is an AsnOcts containing + * the opaquely DER-encoded extension value. + * + * Currently only used when decoding a CRL and converting it en masse + * to CDSA. + */ +template +void nssToCssm( + const DecodedExten &decodedExt, + NssType *&nssObj, // RETURNED + CdsaType *&cdsaObj, // mallocd and RETURNED + Allocator &alloc) +{ + nssObj = (NssType *)(decodedExt.nssObj()); + assert(nssObj != NULL); + cdsaObj = (CdsaType *)alloc.malloc(sizeof(CdsaType)); + memset(cdsaObj, 0, sizeof(CdsaType)); +} + +void DecodedExten::parse( + CSSM_X509_EXTENSION_PTR cssmExt, // mallocd by caller, RETURNED + Allocator &alloc) const +{ + void *vCdsaObj = NULL; + if(mBerEncoded) { + /* non-understood extension */ + convertToCdsa(NULL, cssmExt, alloc); + return; + } + if(clCompareCssmData(&mExtnId, &CSSMOID_AuthorityKeyIdentifier)) { + CE_AuthorityKeyID *cdsaObj; + NSS_AuthorityKeyId *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + CL_nssAuthorityKeyIdToCssm(*nssObj, *cdsaObj, mCoder, alloc); + vCdsaObj = cdsaObj; + } + /* same encoding (uint32) for all of these: */ + else if(clCompareCssmData(&mExtnId, &CSSMOID_CrlNumber) || + clCompareCssmData(&mExtnId, &CSSMOID_DeltaCrlIndicator) || + clCompareCssmData(&mExtnId, &CSSMOID_CrlReason)) { + CE_CrlNumber *cdsaObj; + CSSM_DATA *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + CSSM_RETURN toThrow; + if(clCompareCssmData(&mExtnId, &CSSMOID_CrlReason)) { + toThrow = CSSMERR_CL_INVALID_CRL_POINTER; + } + else { + /* tolerate crlNumber > 4 bytes */ + toThrow = CSSM_OK; + } + *cdsaObj = clDataToInt(*nssObj, toThrow); + vCdsaObj = cdsaObj; + } + /* same encoding (GeneralNames) for all of these: */ + else if(clCompareCssmData(&mExtnId, &CSSMOID_IssuerAltName) || + clCompareCssmData(&mExtnId, &CSSMOID_SubjectAltName) || + clCompareCssmData(&mExtnId, &CSSMOID_CertIssuer)) { + CE_GeneralNames *cdsaObj; + NSS_GeneralNames *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + CL_nssGeneralNamesToCssm(*nssObj, *cdsaObj, mCoder, alloc); + vCdsaObj = cdsaObj; + } + else if(clCompareCssmData(&mExtnId, &CSSMOID_IssuingDistributionPoint)) { + CE_IssuingDistributionPoint *cdsaObj; + NSS_IssuingDistributionPoint *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + CL_nssIssuingDistPointToCssm(nssObj, cdsaObj, mCoder, alloc); + vCdsaObj = cdsaObj; + } + /* + CrashTracer: [USER] 48 crashes in WebProcess at ... + This code should not normally be executed, since it seems from RFC5280 that + CSSMOID_IssuingDistributionPoint is the correct extension to use. + */ + else if(clCompareCssmData(&mExtnId, &CSSMOID_CrlDistributionPoints)) { + CE_CRLDistPointsSyntax *cdsaObj; + NSS_CRLDistributionPoints *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + CL_nssDistPointsToCssm((const NSS_CRLDistributionPoints&)*nssObj, *cdsaObj, mCoder, alloc); + vCdsaObj = cdsaObj; + } + /* + * cert entry extensions + */ + else if(clCompareCssmData(&mExtnId, &CSSMOID_HoldInstructionCode)) { + /* value is just an OID */ + CSSM_OID *cdsaObj; + CSSM_DATA *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + clAllocCopyData(alloc, *nssObj, *cdsaObj); + vCdsaObj = cdsaObj; + } + else if(clCompareCssmData(&mExtnId, &CSSMOID_InvalidityDate)) { + /* GeneralizedTime */ + CSSM_DATA *cdsaObj; + CSSM_DATA *nssObj; + nssToCssm( + *this, + nssObj, + cdsaObj, + alloc); + clAllocCopyData(alloc, *nssObj, *cdsaObj); + vCdsaObj = cdsaObj; + } + else { + /* if we get here, this routine is not keeping up with + * clOidToNssInfo() */ + // assert(0); + CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); + } + convertToCdsa(vCdsaObj, cssmExt, alloc); +} + + +#pragma mark ------ DecodedExtensions ------ + +/* + * A variable-size array of DecodedExtens. + * Used for storing cert and CRL extensions as well as per-CRL-entry + * extensions. + */ +DecodedExtensions::DecodedExtensions( + SecNssCoder &coder, + Allocator &alloc) + : mCoder(coder), + mAlloc(alloc), + mExtensions(NULL), + mNumExtensions(0), + mSizeofExtensions(0) +{ + +} + +DecodedExtensions::~DecodedExtensions() +{ + for(unsigned i=0; iextnId, cook up an appropriate + * NSS-specific type (NSS_KeyUsage, etc.); + */ + CSSM_DATA &rawExtn = nssExten->value; + bool berEncoded = false; + bool found; // we understand this OID + unsigned nssObjLen; // size of associated NSS object + const SecAsn1Template *templ = NULL; // template for decoding + void *nssObj = NULL; // decode destination + found = clOidToNssInfo(nssExten->extnId, nssObjLen, templ); + if(!found) { + /* + * We don't know how to deal with this. + */ + berEncoded = true; + } + else { + /* + * Create NSS-style object specific to this extension, just + * by knowing its length and ASN template. + * Decode the extensions's extnValue into that object. We don't + * have to know what kind of object it is anymore. + */ + assert(templ != NULL); + nssObj = mCoder.malloc(nssObjLen); + memset(nssObj, 0, nssObjLen); + PRErrorCode prtn; + prtn = mCoder.decodeItem(rawExtn, templ, nssObj); + if(prtn) { + /* + * FIXME - what do we do here? For now flag it + * as an non-understood extension... + */ + clErrorLog("decodeExtensions: extension decode error\n"); + nssObj = NULL; + berEncoded = true; + } + } + if((nssObj != NULL) || berEncoded) { + /* append if the decode was successful */ + addExtension(nssExten->extnId, + clNssBoolToCssm(nssExten->critical), + nssObj, + berEncoded, + templ, + &rawExtn); + } + } +} + +/* + * Encode into a NSS-style Extensions. + * + * Each extension object, currently stored as some AsnType subclass, + * is BER-encoded and the result is stored as an octet string + * (AsnOcts) in a new Extension object in the TBS. + * + * Called from {Crl,Cert}CreateTemplate via encode{Tbs,Cts}(). + */ +void DecodedExtensions::encodeToNss( + NSS_CertExtension **&extensions) +{ + assert(extensions == NULL); + + if(mNumExtensions == 0) { + /* no extensions, no error */ + return; + } + + /* malloc a NULL_terminated array of NSS_CertExtension pointers */ + unsigned len = (mNumExtensions + 1) * sizeof(NSS_CertExtension *); + extensions = (NSS_CertExtension **)mCoder.malloc(len); + memset(extensions, 0, len); + + /* grind thru our DecodedExtens, creating an NSS_CertExtension for + * each one */ + for(unsigned extenDex=0; extenDexberEncoded()) { + /* unknown extension type, it's already encoded */ + const CSSM_DATA *srcBer = (const CSSM_DATA *)decodedExt->rawExtn(); + assert(srcBer != NULL); + mCoder.allocCopyItem(*srcBer, thisNssExten->value); + } + else { + PRErrorCode prtn; + prtn = mCoder.encodeItem(decodedExt->nssObj(), + decodedExt->templ(), thisNssExten->value); + if(prtn) { + clErrorLog("encodeToNss: extension encode error"); + CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); + } + } + ArenaAllocator arenaAlloc(mCoder); + if(decodedExt->critical()) { + /* optional, default false */ + clCssmBoolToNss(CSSM_TRUE, thisNssExten->critical, arenaAlloc); + } + mCoder.allocCopyItem(decodedExt->extnId(), thisNssExten->extnId); + } +} + +/* add/retrieve entries */ +void DecodedExtensions::addExtension( + const CSSM_OID &extnId, // copied + bool critical, + void *nssObj, // NSS_KeyUsage, NSS_BasicConstraints, + // etc. NOT COPIED, exists in same + // memory space as coder + bool berEncoded, // indicates unknown extension which we + // do not BER-decode when parsing a cert + const SecAsn1Template *templ, // required if !berEncoded + const CSSM_DATA *rawExtn) // NSS_CertExtension.value, copied, + // optional (not present during a + // SetField op) +{ + if(mNumExtensions == mSizeofExtensions) { + /* expand by doubling, or initial malloc */ + mSizeofExtensions = mNumExtensions ? + (2 * mNumExtensions) : MIN_EXTENSIONS; + mExtensions = (DecodedExten **)mAlloc.realloc( + mExtensions, mSizeofExtensions * sizeof(DecodedExten)); + } + mExtensions[mNumExtensions++] = new DecodedExten(extnId, + critical, nssObj, berEncoded, templ, mCoder, rawExtn); +} + +const DecodedExten *DecodedExtensions::getExtension( + unsigned extenDex) const +{ + assert(extenDex < mNumExtensions); + return mExtensions[extenDex]; +} + +/* Convert to CSSM_X509_EXTENSIONS */ +/* Currently only used when decoding a CRL and converting it en masse + * to CDSA */ +void DecodedExtensions::convertToCdsa( + CSSM_X509_EXTENSIONS &cssmExtens, + Allocator &alloc) const +{ + memset(&cssmExtens, 0, sizeof(cssmExtens)); + if(mNumExtensions == 0) { + return; + } + cssmExtens.extensions = (CSSM_X509_EXTENSION_PTR)alloc.malloc( + sizeof(CSSM_X509_EXTENSION) * mNumExtensions); + memset(cssmExtens.extensions, 0, + sizeof(CSSM_X509_EXTENSION) * mNumExtensions); + cssmExtens.numberOfExtensions = mNumExtensions; + for(unsigned dex=0; dexparse(&cssmExtens.extensions[dex], alloc); + } + catch(...) { + /* FIXME - what now? */ + clFieldLog("DecodedExtensions:convertToCdsa: extension " + "decode error"); + } + } +} +