]> git.saurik.com Git - apple/security.git/blobdiff - AppleX509CL/DecodedExtensions.cpp
Security-163.tar.gz
[apple/security.git] / AppleX509CL / DecodedExtensions.cpp
diff --git a/AppleX509CL/DecodedExtensions.cpp b/AppleX509CL/DecodedExtensions.cpp
new file mode 100644 (file)
index 0000000..b2709b1
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2002 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.
+ */
+
+
+/*
+ * 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 <Security/cssmapple.h>
+#include <Security/oidscert.h>
+
+#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 SEC_ASN1Template *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 */
+}
+
+/*
+ * 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
+       CssmAllocator                   &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.parsedValue = NULL;
+       }
+       else {
+               /* caller sees parsed version plus raw BER-encoded bytes */
+               assert(cdsaObj != NULL);
+               if(mRawExtn) {
+                       /* normal decode & parse case */
+                       cssmExt->format = CSSM_X509_DATAFORMAT_PAIR;
+               }
+               else {
+                       /* setField */
+                       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<class NssType, class CdsaType>
+void nssToCssm(
+       const DecodedExten      &decodedExt,
+       NssType                         *&nssObj,               // RETURNED
+       CdsaType                        *&cdsaObj,              // mallocd and RETURNED
+       CssmAllocator           &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
+       CssmAllocator                   &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<NSS_AuthorityKeyId, CE_AuthorityKeyID>(
+                       *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<CSSM_DATA, CE_CrlNumber>(
+                       *this,
+                       nssObj,
+                       cdsaObj,
+                       alloc);
+               *cdsaObj = clDataToInt(*nssObj);
+               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<NSS_GeneralNames, CE_GeneralNames>(
+                       *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<NSS_IssuingDistributionPoint, CE_IssuingDistributionPoint>(
+                       *this,
+                       nssObj,
+                       cdsaObj,
+                       alloc);
+               CL_nssIssuingDistPointToCssm(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<CSSM_DATA, CSSM_OID>(
+                       *this,
+                       nssObj,
+                       cdsaObj,
+                       alloc);
+               clAllocCopyData(alloc, *nssObj, *cdsaObj);
+               vCdsaObj = cdsaObj;
+       }
+       else if(clCompareCssmData(&mExtnId, &CSSMOID_InvalidityDate)) {
+               /* GeneralizedTime */
+               CSSM_DATA *cdsaObj;
+               CSSM_DATA *nssObj;
+               nssToCssm<CSSM_DATA, CSSM_DATA>(
+                       *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,
+       CssmAllocator   &alloc)
+       :       mCoder(coder),
+               mAlloc(alloc),
+               mExtensions(NULL),
+               mNumExtensions(0),
+               mSizeofExtensions(0)
+{
+
+}
+       
+DecodedExtensions::~DecodedExtensions()
+{
+       for(unsigned i=0; i<mNumExtensions; i++) {
+               assert(mExtensions[i] != NULL);
+               delete mExtensions[i];
+       }
+       mAlloc.free(mExtensions);
+       mExtensions = NULL;
+       mNumExtensions = 0;
+       mSizeofExtensions = 0;
+}
+
+
+/*
+ * Initialize by decoding a NSS-style NSS_CertExtension array.
+ * This involves figuring out what kind of object is represented in the 
+ * octet string in the  extension, decoding it, and appending the resulting 
+ * NSS object to mExtensions in the form of a DecodedExten.
+ *
+ * Called when decoding either a cert or a CRL (for caching it or 
+ * getting its fields) or a cert template (only via 
+ * CertGetAllTemplateFields()).
+ */
+void DecodedExtensions::decodeFromNss(
+       NSS_CertExtension       **extensions)   
+{
+       if(extensions == NULL) {
+               /* OK, no extensions present */
+               return;
+       }
+       unsigned numExtens = clNssArraySize((const void **)extensions);
+
+       /* traverse extension list */
+       for(unsigned dex=0; dex<numExtens; dex++) {
+               NSS_CertExtension *nssExten = extensions[dex];
+               
+               /*
+                * For this extension->extnId, cook up an approppriate 
+                * 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 SEC_ASN1Template *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; extenDex<mNumExtensions; extenDex++) {
+               NSS_CertExtension *thisNssExten = 
+                       (NSS_CertExtension *)mCoder.malloc(sizeof(NSS_CertExtension));
+               memset(thisNssExten, 0, sizeof(NSS_CertExtension));
+               extensions[extenDex] = thisNssExten;
+               
+               const DecodedExten *decodedExt = getExtension(extenDex);
+               
+               /* BER-encode the extension object if appropriate */
+               if(decodedExt->berEncoded()) {
+                       /* 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 SEC_ASN1Template *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,
+       CssmAllocator                           &alloc) const
+{
+       memset(&cssmExtens, 0, sizeof(cssmExtens));
+       if(mNumExtensions == NULL) {
+               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; dex<mNumExtensions; dex++) {
+               try {
+                       getExtension(dex)->parse(&cssmExtens.extensions[dex], alloc);
+               }
+               catch(...) {
+                       /* FIXME - what now? */
+                       clFieldLog("DecodedExtensions:convertToCdsa: extension "
+                               "decode error");
+               }
+       }
+}
+