]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_apple_x509_cl/lib/clNameUtils.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_x509_cl / lib / clNameUtils.cpp
diff --git a/OSX/libsecurity_apple_x509_cl/lib/clNameUtils.cpp b/OSX/libsecurity_apple_x509_cl/lib/clNameUtils.cpp
new file mode 100644 (file)
index 0000000..4eb4639
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * Copyright (c) 2003,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.
+ */
+
+/*
+ * clNameUtils.cpp - support for Name, GeneralizedName, all sorts of names
+ */
+#include "clNameUtils.h"
+#include "clNssUtils.h"
+#include "cldebugging.h"
+#include <security_utilities/utilities.h>
+
+#pragma mark ----- NSS_Name <--> CSSM_X509_NAME -----
+
+/* 
+ * NSS_ATV --> CSSM_X509_TYPE_VALUE_PAIR
+ */
+static
+void CL_nssAtvToCssm(
+       const NSS_ATV                           &nssObj,
+       CSSM_X509_TYPE_VALUE_PAIR       &cssmObj,
+       Allocator                               &alloc)
+{
+       /* tag and decoded data */
+       cssmObj.valueType = nssObj.value.tag;
+       clAllocCopyData(alloc, nssObj.value.item, cssmObj.value);
+       /* the OID */
+       clAllocCopyData(alloc, nssObj.type, cssmObj.type);
+}
+
+/* NSS_RDN --> CSSM_X509_RDN */
+void CL_nssRdnToCssm(
+       const NSS_RDN   &nssObj,
+       CSSM_X509_RDN   &cssmObj,
+       Allocator       &alloc,
+       SecNssCoder             &coder)         // conversion requires further decoding
+{
+       memset(&cssmObj, 0, sizeof(cssmObj));
+       unsigned numAtvs = clNssArraySize((const void **)nssObj.atvs);
+       if(numAtvs == 0) {
+               return;
+       }
+       
+       size_t len = numAtvs * sizeof(CSSM_X509_TYPE_VALUE_PAIR);
+       cssmObj.AttributeTypeAndValue = 
+                       (CSSM_X509_TYPE_VALUE_PAIR_PTR)alloc.malloc(len);
+       cssmObj.numberOfPairs = numAtvs;
+       CSSM_X509_TYPE_VALUE_PAIR_PTR cssmAtvs = cssmObj.AttributeTypeAndValue;
+       memset(cssmAtvs, 0, len);
+       
+       for(unsigned dex=0; dex<numAtvs; dex++) {
+               CL_nssAtvToCssm(*(nssObj.atvs[dex]), cssmAtvs[dex], alloc);
+       }
+       return;
+}
+
+/* NSS_Name --> CSSM_X509_NAME */
+void CL_nssNameToCssm(
+       const NSS_Name  &nssObj,
+       CSSM_X509_NAME  &cssmObj,
+       Allocator       &alloc)
+{
+       memset(&cssmObj, 0, sizeof(cssmObj));
+       unsigned numRdns = clNssArraySize((const void **)nssObj.rdns);
+       if(numRdns == 0) {
+               /* not technically an error */
+               return;
+       }
+       
+       size_t len = numRdns * sizeof(CSSM_X509_RDN);
+       cssmObj.RelativeDistinguishedName = (CSSM_X509_RDN_PTR)alloc.malloc(len);
+       cssmObj.numberOfRDNs = numRdns;
+       CSSM_X509_RDN_PTR cssmRdns = cssmObj.RelativeDistinguishedName;
+       memset(cssmRdns, 0, len);
+       
+       SecNssCoder     coder;          // conversion requires further decoding
+       
+       for(unsigned dex=0; dex<numRdns; dex++) {
+               CL_nssRdnToCssm(*(nssObj.rdns[dex]), cssmRdns[dex], alloc, coder);
+       }
+       return;
+}
+
+/* 
+ * CSSM_X509_TYPE_VALUE_PAIR --> NSS_ATV 
+ */
+void CL_cssmAtvToNss(
+       const CSSM_X509_TYPE_VALUE_PAIR &cssmObj,
+       NSS_ATV                                                 &nssObj,
+       SecNssCoder                                             &coder)
+{
+       memset(&nssObj, 0, sizeof(nssObj));
+       
+       /* copy the OID */
+       coder.allocCopyItem(cssmObj.type, nssObj.type);
+       
+       /* tag and value */
+       nssObj.value.tag = cssmObj.valueType;
+       coder.allocCopyItem(cssmObj.value, nssObj.value.item);
+}
+
+/* CSSM_X509_RDN --> NSS_RDN */
+void CL_cssmRdnToNss(
+       const CSSM_X509_RDN     &cssmObj,
+       NSS_RDN                         &nssObj,
+       SecNssCoder                     &coder)
+{
+       memset(&nssObj, 0, sizeof(nssObj));
+       
+       /* alloc NULL-terminated array of ATV pointers */
+       unsigned numAtvs = cssmObj.numberOfPairs;
+       unsigned size = (numAtvs + 1) * sizeof(void *);
+       nssObj.atvs = (NSS_ATV **)coder.malloc(size);
+       memset(nssObj.atvs, 0, size);
+       
+       /* grind thru the elements */
+       for(unsigned atvDex=0; atvDex<numAtvs; atvDex++) {
+               nssObj.atvs[atvDex] = (NSS_ATV *)coder.malloc(sizeof(NSS_ATV));
+               CL_cssmAtvToNss(cssmObj.AttributeTypeAndValue[atvDex],
+                       *nssObj.atvs[atvDex], coder);
+       }
+}
+
+/* CSSM_X509_NAME --> NSS_Name */
+void CL_cssmNameToNss(
+       const CSSM_X509_NAME    &cssmObj,
+       NSS_Name                                &nssObj,
+       SecNssCoder                             &coder)
+{
+       memset(&nssObj, 0, sizeof(nssObj));
+       
+       /* alloc NULL-terminated array of RDN pointers */
+       unsigned numRdns = cssmObj.numberOfRDNs;
+       nssObj.rdns = (NSS_RDN **)clNssNullArray(numRdns, coder);
+       
+       /* grind thru the elements */
+       for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
+               nssObj.rdns[rdnDex] = (NSS_RDN *)coder.malloc(sizeof(NSS_RDN));
+               CL_cssmRdnToNss(cssmObj.RelativeDistinguishedName[rdnDex],
+                       *nssObj.rdns[rdnDex], coder);
+       }
+}
+
+#pragma mark ----- Name Normalization -----
+
+void CL_normalizeString(
+       char *strPtr,
+       int &strLen)                                    // IN/OUT
+{
+       char *pCh = strPtr;                             // working ptr
+       char *pD = pCh;                                 // start of good string chars
+       char *pEos = pCh + strLen - 1;
+       
+       if(strLen == 0) {
+               return;
+       }
+
+       /* adjust if Length included NULL terminator */
+       while(*pEos == 0) {
+               pEos--;
+       }
+       
+       /* Remove trailing spaces */
+       while(isspace(*pEos)) {
+               pEos--;
+       }
+       
+       /* Point to one past last non-space character */
+       pEos++;
+
+       /* upper case */
+       while(pCh < pEos) {
+               *pCh = toupper(*pCh);
+               pCh++;
+       }
+       
+       /* clean out whitespace */
+       /* 
+        * 1. skip all leading whitespace 
+        */
+       pCh = pD;
+       while(isspace(*pCh) && (pCh < pEos)) {
+               pCh++;
+       }
+       
+       /*
+        * 2. eliminate multiple whitespace.
+        *    pCh points to first non-white char.
+        *        pD still points to start of string
+        */
+       char ch;
+       while(pCh < pEos) {
+               ch = *pCh++;
+               *pD++ = ch;             // normal case
+               if( isspace(ch) ){
+                       /* skip 'til next nonwhite */
+                       while(isspace(*pCh) && (pCh < pEos)) {
+                               pCh++;
+                       }
+               }
+       };
+
+       strLen = (int)(pD - strPtr);
+}
+
+/* 
+ * 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 CL_normalizeX509NameNSS(
+       NSS_Name &nssName,
+       SecNssCoder &coder)
+{
+       unsigned numRdns = clNssArraySize((const void **)nssName.rdns);
+       if(numRdns == 0) {
+               /* not technically an error */
+               return;
+       }
+       
+       for(unsigned rdnDex=0; rdnDex<numRdns; rdnDex++) {
+               NSS_RDN *rdn = nssName.rdns[rdnDex];
+               assert(rdn != NULL);
+               unsigned numAttrs = clNssArraySize((const void **)rdn->atvs);
+               if(numAttrs == 0) {
+                       clFieldLog("clNormalizeX509Name: zero numAttrs at index %d", rdnDex);
+                       continue;
+               }
+               
+               /* descend into array of attribute/values */
+               for(unsigned attrDex=0; attrDex<numAttrs; attrDex++) {
+                       NSS_ATV *attr = rdn->atvs[attrDex];
+                       assert(attr != NULL);
+                       
+                       /* 
+                        * 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) {
+                               /* skip it */
+                               continue;
+                       }
+
+                       /* normalize */
+                       char *strPtr = (char *)attrVal.item.Data;
+                       int newLen = (int)attrVal.item.Length;
+                       CL_normalizeString(strPtr, newLen);
+                       
+                       /* possible length adjustment */
+                       attrVal.item.Length = newLen;
+               }       /* for each attribute/value */
+       }               /* for each RDN */
+}
+
+#pragma mark ----- CE_GeneralNames <--> NSS_GeneralNames -----
+
+void CL_nssGeneralNameToCssm(
+       NSS_GeneralName &nssObj,
+       CE_GeneralName &cdsaObj,
+       SecNssCoder &coder,                             // for temp decoding
+       Allocator &alloc)                       // destination 
+{
+       memset(&cdsaObj, 0, sizeof(cdsaObj));
+       PRErrorCode prtn;
+
+       /* for caller's CE_GeneralName */
+       CSSM_BOOL berEncoded = CSSM_FALSE;
+       CE_GeneralNameType cdsaTag;
+       
+       /*
+        * At this point, depending on the decoded object's tag, we either
+        * have the final bytes to copy out, or we need to decode further.
+        * After this switch, if doCopy is true, give the caller a copy
+        * of nssObj.item.
+        */
+       bool doCopy = true;
+       switch(nssObj.tag) {
+               case NGT_OtherName:             // ASN_ANY -> CE_OtherName
+               {
+                       cdsaTag = GNT_OtherName;
+                       
+                       /* decode to coder memory */
+                       CE_OtherName *nssOther = 
+                               (CE_OtherName *)coder.malloc(sizeof(CE_OtherName));
+                       memset(nssOther, 0, sizeof(CE_OtherName));
+                       prtn = coder.decodeItem(nssObj.item, 
+                               kSecAsn1GenNameOtherNameTemplate, 
+                               nssOther);
+                       if(prtn) {
+                               clErrorLog("CL_nssGeneralNameToCssm: error decoding "
+                                               "OtherName\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }
+                       
+                       /* copy out to caller */
+                       clAllocData(alloc, cdsaObj.name, sizeof(CE_OtherName));
+                       clCopyOtherName(*nssOther, *((CE_OtherName *)cdsaObj.name.Data), 
+                               alloc);
+                       doCopy = false;
+                       break;
+               }
+               case NGT_RFC822Name:    // IA5String, done
+                       cdsaTag = GNT_RFC822Name;
+                       break;
+               case NGT_DNSName:               // IA5String
+                       cdsaTag = GNT_DNSName;
+                       break;
+               case NGT_X400Address:   // ASY_ANY, leave alone
+                       cdsaTag = GNT_X400Address;
+                       berEncoded = CSSM_TRUE;
+                       break;
+               case NGT_DirectoryName: // ASN_ANY --> NSS_Name
+               {
+                       cdsaTag = GNT_DirectoryName;
+                       
+                       /* Decode to coder memory */
+                       NSS_Name *nssName = (NSS_Name *)coder.malloc(sizeof(NSS_Name));
+                       memset(nssName, 0, sizeof(NSS_Name));
+                       prtn = coder.decodeItem(nssObj.item, kSecAsn1NameTemplate, nssName);
+                       if(prtn) {
+                               clErrorLog("CL_nssGeneralNameToCssm: error decoding "
+                                               "NSS_Name\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }
+                       
+                       /* convert & copy out to caller */
+                       clAllocData(alloc, cdsaObj.name, sizeof(CSSM_X509_NAME));
+                       CL_nssNameToCssm(*nssName, 
+                               *((CSSM_X509_NAME *)cdsaObj.name.Data), alloc);
+                       doCopy = false;
+                       break;
+               }
+               case NGT_EdiPartyName:  // ASN_ANY, leave alone
+                       cdsaTag = GNT_EdiPartyName;
+                       berEncoded = CSSM_TRUE;
+                       break;
+               case NGT_URI:                   // IA5String
+                       cdsaTag = GNT_URI;
+                       break;
+               case NGT_IPAddress:             // OCTET_STRING
+                       cdsaTag = GNT_IPAddress;
+                       break;
+               case NGT_RegisteredID:  // OID
+                       cdsaTag = GNT_RegisteredID;
+                       break;
+               default:
+                       clErrorLog("CL_nssGeneralNameToCssm: bad name tag\n");
+                       CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+       }       
+       
+       cdsaObj.nameType = cdsaTag;
+       cdsaObj.berEncoded = berEncoded;
+       if(doCopy) {
+               clAllocCopyData(alloc, nssObj.item, cdsaObj.name);
+       }
+}
+
+void CL_nssGeneralNamesToCssm(
+       const NSS_GeneralNames &nssObj,
+       CE_GeneralNames &cdsaObj,
+       SecNssCoder &coder,                             // for temp decoding
+       Allocator &alloc)                       // destination 
+{
+       memset(&cdsaObj, 0, sizeof(cdsaObj));
+       unsigned numNames = clNssArraySize((const void **)nssObj.names);
+       if(numNames == 0) {
+               return;
+       }
+       
+       /*
+        * Decode each name element, currently a raw ASN_ANY blob.
+        * Then convert each result into CDSA form.
+        * This array of (NSS_GeneralName)s is temporary, it doesn't
+        * persist outside of this routine other than the fact that it's
+        * mallocd by the coder arena pool. 
+        */
+       NSS_GeneralName *names = 
+               (NSS_GeneralName *)coder.malloc(sizeof(NSS_GeneralName) * numNames);
+       memset(names, 0, sizeof(NSS_GeneralName) * numNames);
+       cdsaObj.generalName = (CE_GeneralName *)alloc.malloc(
+               sizeof(CE_GeneralName) * numNames);
+       cdsaObj.numNames = numNames;
+       
+       for(unsigned dex=0; dex<numNames; dex++) {
+               if(coder.decodeItem(*nssObj.names[dex], kSecAsn1GeneralNameTemplate,
+                               &names[dex])) {
+                       clErrorLog("***CL_nssGeneralNamesToCssm: Error decoding "
+                               "General.name\n");
+                       CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+               }
+               
+               CL_nssGeneralNameToCssm(names[dex],
+                       cdsaObj.generalName[dex],
+                       coder, alloc);
+       }
+}
+
+void CL_cssmGeneralNameToNss(
+       CE_GeneralName &cdsaObj,
+       NSS_GeneralName &nssObj,                // actually an NSSTaggedItem
+       SecNssCoder &coder)                             // for temp decoding
+{
+       memset(&nssObj, 0, sizeof(nssObj));
+       
+       /*
+        * The default here is just to use the app-supplied data as is...
+        */
+       nssObj.item = cdsaObj.name;
+       unsigned char itemTag;                  // for nssObj.tag
+       bool doCopy = false;                    // unless we have to modify tag byte
+       unsigned char overrideTag;              // to force context-specific tag for
+                                                                       //   an ASN_ANY
+       PRErrorCode prtn;
+                                                                       
+       switch(cdsaObj.nameType) {
+               case GNT_OtherName:     
+                       /*
+                        * Caller supplies an CE_OtherName. Encode it.
+                        */
+                       if((cdsaObj.name.Length != sizeof(CE_OtherName)) ||
+                          (cdsaObj.name.Data == NULL)) {
+                               clErrorLog("CL_cssmGeneralNameToNss: OtherName.Length"
+                                       " error\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }
+                       prtn = coder.encodeItem(cdsaObj.name.Data,
+                               kSecAsn1OtherNameTemplate, nssObj.item);
+                       if(prtn) {
+                               clErrorLog("CL_cssmGeneralNameToNss: OtherName encode"
+                                       " error\n");
+                               CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
+                       }
+                       itemTag = NGT_OtherName;
+                       break;
+               case GNT_RFC822Name:            // IA5String
+                       itemTag = NGT_RFC822Name;
+                       break;
+               case GNT_DNSName:                       // IA5String
+                       itemTag = NGT_DNSName;
+                       break;
+               case GNT_X400Address:           // caller's resposibility
+                       /*
+                        * Encoded as ASN_ANY, the only thing we do is to 
+                        * force the correct context-specific tag
+                        */
+                       itemTag = GNT_X400Address;
+                       if(!cdsaObj.berEncoded) {
+                               clErrorLog("CL_cssmGeneralNameToNss: X400Address must"
+                                       " be BER-encoded\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }                       
+                       overrideTag = SEC_ASN1_CONTEXT_SPECIFIC | 
+                               SEC_ASN1_CONSTRUCTED | NGT_X400Address;
+                       doCopy = true;
+                       break;
+               case GNT_DirectoryName: 
+               {
+                       /*
+                        * Caller supplies an CSSM_X509_NAME. Convert to NSS
+                        * format and encode it.
+                        */
+                       if((cdsaObj.name.Length != sizeof(CSSM_X509_NAME)) || 
+                          (cdsaObj.name.Data == NULL)) {
+                               clErrorLog("CL_cssmGeneralNameToNss: DirectoryName.Length"
+                                       " error\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }
+                       NSS_Name nssName;
+                       CSSM_X509_NAME_PTR cdsaName = 
+                               (CSSM_X509_NAME_PTR)cdsaObj.name.Data;
+                       CL_cssmNameToNss(*cdsaName, nssName, coder);
+                       prtn = coder.encodeItem(&nssName,
+                               kSecAsn1NameTemplate, nssObj.item);
+                       if(prtn) {
+                               clErrorLog("CL_cssmGeneralNameToNss: X509Name encode"
+                                       " error\n");
+                               CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
+                       }
+                       itemTag = GNT_DirectoryName;
+                       
+                       /*
+                        * AND, munge the tag to make it a context-specific
+                        * sequence
+                        * no, not needed, this is wrapped in an explicit...
+                        */
+                       //nssObj.item.Data[0] = SEC_ASN1_CONTEXT_SPECIFIC | 
+                       //      SEC_ASN1_CONSTRUCTED | GNT_DirectoryName;
+
+                       break;
+               }
+               case GNT_EdiPartyName:          // caller's resposibility
+                       /*
+                        * Encoded as ASN_ANY, the only thing we do is to 
+                        * force the correct context-specific tag
+                        */
+                       itemTag = GNT_EdiPartyName;
+                       if(!cdsaObj.berEncoded) {
+                               clErrorLog("CL_cssmGeneralNameToNss: EdiPartyName must"
+                                       " be BER-encoded\n");
+                               CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
+                       }
+                       overrideTag = SEC_ASN1_CONTEXT_SPECIFIC |  NGT_X400Address;
+                       doCopy = true;
+                       break;
+               case GNT_URI:                           // IA5String
+                       itemTag = GNT_URI;
+                       break;
+               case GNT_IPAddress:                     // OCTET_STRING
+                       itemTag = NGT_IPAddress;
+                       break;
+               case GNT_RegisteredID:          // OID
+                       itemTag = NGT_RegisteredID;
+                       break;
+               default:
+                       clErrorLog("CL_cssmGeneralNameToNss: bad name tag\n");
+                       CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG);
+       }
+       if(doCopy) {
+               coder.allocCopyItem(cdsaObj.name, nssObj.item);
+               nssObj.item.Data[0] = overrideTag;
+       }
+       nssObj.tag = itemTag;
+}
+
+void CL_cssmGeneralNamesToNss(
+       const CE_GeneralNames &cdsaObj, 
+       NSS_GeneralNames &nssObj,
+       SecNssCoder &coder)
+{
+       uint32 numNames = cdsaObj.numNames;
+       nssObj.names = (CSSM_DATA **)clNssNullArray(numNames, coder);
+       
+       /* 
+        * Convert each element in cdsaObj to NSS form, encode, drop into
+        * the ASN_ANY array.
+        *
+        * This array of (NSS_GeneralName)s is temporary, it doesn't
+        * persist outside of this routine other than the fact that it's
+        * mallocd by the coder arena pool. 
+        */
+       NSS_GeneralName *names = 
+               (NSS_GeneralName *)coder.malloc(sizeof(NSS_GeneralName) * numNames);
+       memset(names, 0, sizeof(NSS_GeneralName) * numNames);
+       for(unsigned dex=0; dex<cdsaObj.numNames; dex++) {
+               nssObj.names[dex] = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA));
+               memset(nssObj.names[dex], 0, sizeof(CSSM_DATA));
+               CL_cssmGeneralNameToNss(cdsaObj.generalName[dex],
+                       names[dex], coder);
+               if(coder.encodeItem(&names[dex], kSecAsn1GeneralNameTemplate,
+                               *nssObj.names[dex])) {
+                       clErrorLog("***Error encoding General.name\n");
+                       CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
+               }
+       }
+}
+
+/* Copy a CE_OtherName */
+void clCopyOtherName(
+       const CE_OtherName &src,
+       CE_OtherName &dst,
+       Allocator &alloc)
+{
+       clAllocCopyData(alloc, src.typeId, dst.typeId);
+       clAllocCopyData(alloc, src.value, dst.value);
+}
+
+#pragma mark ----- Name-related Free -----
+
+void CL_freeAuthorityKeyId(
+       CE_AuthorityKeyID                               &cdsaObj,
+       Allocator                                       &alloc)
+{
+       alloc.free(cdsaObj.keyIdentifier.Data);
+       CL_freeCssmGeneralNames(cdsaObj.generalNames, alloc);
+       alloc.free(cdsaObj.generalNames);
+       alloc.free(cdsaObj.serialNumber.Data);
+       memset(&cdsaObj, 0, sizeof(CE_AuthorityKeyID));
+}
+
+void CL_freeCssmGeneralName(
+       CE_GeneralName          &genName,
+       Allocator                       &alloc)
+{
+       switch(genName.nameType) {
+               /* 
+                * Two special cases here.
+                */
+               case GNT_DirectoryName:
+                       if((!genName.berEncoded) &&                                     // we're flexible
+                                       (genName.name.Length == 
+                                               sizeof(CSSM_X509_NAME))) {              // paranoia
+                               CL_freeX509Name((CSSM_X509_NAME_PTR)genName.name.Data, alloc);
+                       }
+                       break;
+                       
+               case GNT_OtherName:
+                       if((!genName.berEncoded) &&                                     // we're flexible
+                                       (genName.name.Length == 
+                                               sizeof(CE_OtherName))) {                // paranoia
+                               CE_OtherName *con = (CE_OtherName *)genName.name.Data;
+                               CL_freeOtherName(con, alloc);
+                       }
+                       break;
+               default:
+                       break;
+       }
+       /* and always free this */
+       alloc.free(genName.name.Data);
+}
+
+void CL_freeCssmGeneralNames(
+       CE_GeneralNames                 *cdsaObj,
+       Allocator                               &alloc)
+{
+       if(cdsaObj == NULL) {
+               return;
+       }
+       for(unsigned i=0; i<cdsaObj->numNames; i++) {
+               CL_freeCssmGeneralName(cdsaObj->generalName[i], alloc);
+       }
+       if(cdsaObj->numNames) {
+               memset(cdsaObj->generalName, 0, cdsaObj->numNames * sizeof(CE_GeneralName));
+               alloc.free(cdsaObj->generalName);
+       }
+       memset(cdsaObj, 0, sizeof(CE_GeneralNames));
+}
+
+void CL_freeCssmDistPointName(
+       CE_DistributionPointName        *cssmDpn,
+       Allocator                               &alloc)
+{
+       if(cssmDpn == NULL) {
+               return;
+       }
+       switch(cssmDpn->nameType) {
+               case CE_CDNT_FullName:
+                       CL_freeCssmGeneralNames(cssmDpn->dpn.fullName, alloc);
+                       alloc.free(cssmDpn->dpn.fullName);
+                       break;
+               case CE_CDNT_NameRelativeToCrlIssuer:
+                       CL_freeX509Rdn(cssmDpn->dpn.rdn, alloc);
+                       alloc.free(cssmDpn->dpn.rdn);
+                       break;
+       }
+       memset(cssmDpn, 0, sizeof(*cssmDpn));
+}
+
+void CL_freeCssmDistPoints(
+       CE_CRLDistPointsSyntax  *cssmDps,
+       Allocator                       &alloc)
+{
+       if(cssmDps == NULL) {
+               return;
+       }
+       for(unsigned dex=0; dex<cssmDps->numDistPoints; dex++) {
+               CE_CRLDistributionPoint *cssmDp = &cssmDps->distPoints[dex];
+               if(cssmDp->distPointName) {
+                       CL_freeCssmDistPointName(cssmDp->distPointName, alloc);
+                       alloc.free(cssmDp->distPointName);
+               }
+               if(cssmDp->crlIssuer) {
+                       CL_freeCssmGeneralNames(cssmDp->crlIssuer, alloc);
+                       alloc.free(cssmDp->crlIssuer);
+               }
+       }
+       memset(cssmDps->distPoints, 0, 
+               cssmDps->numDistPoints * sizeof(CE_CRLDistributionPoint));
+       alloc.free(cssmDps->distPoints);
+       memset(cssmDps, 0, sizeof(*cssmDps));
+}
+
+/* free contents of an CSSM_X509_NAME */
+void CL_freeX509Name(
+       CSSM_X509_NAME_PTR      x509Name,
+       Allocator               &alloc)
+{
+       if(x509Name == NULL) {
+               return;
+       }
+       for(unsigned rdnDex=0; rdnDex<x509Name->numberOfRDNs; rdnDex++) {
+               CSSM_X509_RDN_PTR rdn = &x509Name->RelativeDistinguishedName[rdnDex];
+               CL_freeX509Rdn(rdn, alloc);
+       }
+       alloc.free(x509Name->RelativeDistinguishedName);
+       memset(x509Name, 0, sizeof(CSSM_X509_NAME));
+}
+
+void CL_freeX509Rdn(
+       CSSM_X509_RDN_PTR       rdn,
+       Allocator               &alloc)
+{
+       if(rdn == NULL) {
+               return;
+       }
+       for(unsigned atvDex=0; atvDex<rdn->numberOfPairs; atvDex++) {
+               CSSM_X509_TYPE_VALUE_PAIR_PTR atv = 
+                       &rdn->AttributeTypeAndValue[atvDex];
+               alloc.free(atv->type.Data);
+               alloc.free(atv->value.Data);
+               memset(atv, 0, sizeof(CSSM_X509_TYPE_VALUE_PAIR));
+       }
+       alloc.free(rdn->AttributeTypeAndValue);
+       memset(rdn, 0, sizeof(CSSM_X509_RDN));
+}
+
+void CL_freeOtherName(
+       CE_OtherName            *cssmOther,
+       Allocator               &alloc)
+{
+       if(cssmOther == NULL) {
+               return;
+       }
+       alloc.free(cssmOther->typeId.Data);
+       alloc.free(cssmOther->value.Data);
+       memset(cssmOther, 0, sizeof(*cssmOther));
+}
+
+void CL_freeCssmIssuingDistPoint(
+       CE_IssuingDistributionPoint     *cssmIdp,
+       Allocator                               &alloc)
+{
+       CL_freeCssmDistPointName(cssmIdp->distPointName, alloc);
+}
+