/* * 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. */ /* * CrlFields.cpp - convert between NSS-based NSS_Crl components and CDSA-style * fields. A major component of DecodedCrl. * * Created 8/29/2002 by Doug Mitchell. * Copyright (c) 2002 by Apple Computer. */ #include "DecodedCrl.h" #include #include "cldebugging.h" #include "CLCrlExtensions.h" #include "CLCertExtensions.h" #include "CLFieldsCommon.h" #include "clNssUtils.h" #include "clNameUtils.h" #include #include #include #include #include static void CL_freeCssmExtensions( CSSM_X509_EXTENSIONS &extens, CssmAllocator &alloc); /*** *** Version *** Format = DER-encoded int (max of four bytes in this case) ***/ static bool getField_Version ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { const DecodedCrl &crl = dynamic_cast(item); const CSSM_DATA &vers = crl.mCrl.tbs.version; if(!tbsGetCheck(vers.Data, index)) { /* not present, optional */ return false; } fieldValue.copy(vers.Data, vers.Length); numFields = 1; return true; } static void setField_Version ( DecodedItem &item, const CssmData &fieldValue) { DecodedCrl &crl = dynamic_cast(item); CSSM_DATA &vers = crl.mCrl.tbs.version; tbsSetCheck(vers.Data, fieldValue, 0, "version"); crl.coder().allocCopyItem(fieldValue, vers); } /*** issuer *** Format = CSSM_X509_NAME *** class Name from sm_x501if ***/ static bool getField_Issuer ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(index != 0) { return false; } bool brtn; const DecodedCrl &crl = dynamic_cast(item); try { brtn = getField_RDN_NSS(crl.mCrl.tbs.issuer, fieldValue); if(brtn) { numFields = 1; } } catch (...) { freeField_RDN(fieldValue); throw; } return brtn; } static void setField_Issuer ( DecodedItem &item, const CssmData &fieldValue) { DecodedCrl &crl = dynamic_cast(item); const CSSM_X509_NAME *cssmName = (const CSSM_X509_NAME *)fieldValue.Data; NSS_Name &nssName = crl.mCrl.tbs.issuer; tbsSetCheck(nssName.rdns, fieldValue, sizeof(CSSM_X509_NAME), "IssuerName"); CL_cssmNameToNss(*cssmName, nssName, crl.coder()); } /*** *** This/Next update *** Format: CSSM_X509_TIME ***/ static bool getField_ThisUpdate ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { const DecodedCrl &crl = dynamic_cast(item); const NSS_Time &srcTime = crl.mCrl.tbs.thisUpdate; return getField_TimeNSS(srcTime, index, numFields, fieldValue); } static void setField_ThisUpdate ( DecodedItem &item, const CssmData &fieldValue) { DecodedCrl &crl = dynamic_cast(item); NSS_Time &dstTime = crl.mCrl.tbs.thisUpdate; tbsSetCheck(dstTime.item.Data, fieldValue, sizeof(CSSM_X509_TIME), "NotBefore"); setField_TimeNSS(fieldValue, dstTime, crl.coder()); } static bool getField_NextUpdate ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { const DecodedCrl &crl = dynamic_cast(item); const NSS_Time &srcTime = crl.mCrl.tbs.nextUpdate; return getField_TimeNSS(srcTime, index, numFields, fieldValue); } static void setField_NextUpdate ( DecodedItem &item, const CssmData &fieldValue) { DecodedCrl &crl = dynamic_cast(item); NSS_Time &dstTime = crl.mCrl.tbs.nextUpdate; tbsSetCheck(dstTime.item.Data, fieldValue, sizeof(CSSM_X509_TIME), "NotBefore"); setField_TimeNSS(fieldValue, dstTime, crl.coder()); } /*** *** Issuer Name (normalized and encoded version) *** Format = CSSM_DATA containing the DER encoding of the normalized name ***/ static bool getFieldIssuerNorm( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { if(index != 0) { return false; } const DecodedCrl &crl = dynamic_cast(item); return getField_normRDN_NSS(crl.mCrl.tbs.derIssuer, numFields, fieldValue); } /*** *** TBS AlgId *** Format = CSSM_X509_ALGORITHM_IDENTIFIER ***/ static bool getField_CrlTbsAlgId ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { const DecodedCrl &crl = dynamic_cast(item); const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId = crl.mCrl.signatureAlgorithm; if(!tbsGetCheck(srcAlgId.algorithm.Data, index)) { return false; } getField_AlgIdNSS(srcAlgId, fieldValue); numFields = 1; return true; } /* * Support for entries in revocation list */ static void nssRevokedEntryToCssm( NSS_RevokedCert &nssEntry, CSSM_X509_REVOKED_CERT_ENTRY &cssmEntry, CssmAllocator &alloc) { clAllocCopyData(alloc, nssEntry.userCertificate, cssmEntry.certificateSerialNumber); CL_nssTimeToCssm(nssEntry.revocationDate, cssmEntry.revocationDate, alloc); /* CSSM_X509_EXTENSIONS extensions */ NSS_CertExtension **nssExtens = nssEntry.extensions; if(nssExtens == NULL) { /* done */ return; } /* * First we have to decode the NSS-style Extensions into a * DecodedExtensions object. For cert- and CRL-wide extensions, this * is done at the construction of Decoded{Cert,Crl}. However for * per-CRL-entry entensions, this is (currently) the only place * this decoding is done. */ SecNssCoder coder; DecodedExtensions decodedExtens(coder, alloc); decodedExtens.decodeFromNss(nssExtens); /* convert to CDSA style */ decodedExtens.convertToCdsa(cssmEntry.extensions, alloc); } static void freeCssmEntry( CSSM_X509_REVOKED_CERT_ENTRY_PTR cssmEntry, CssmAllocator &alloc) { if(cssmEntry == NULL) { return; } if(cssmEntry->certificateSerialNumber.Data) { alloc.free(cssmEntry->certificateSerialNumber.Data); cssmEntry->certificateSerialNumber.Data = NULL; cssmEntry->certificateSerialNumber.Length = 0; } CL_freeCssmTime(&cssmEntry->revocationDate, alloc); /* CSSM_X509_EXTENSIONS extensions */ CL_freeCssmExtensions(cssmEntry->extensions, alloc); memset(cssmEntry, 0, sizeof(CSSM_X509_REVOKED_CERT_ENTRY)); } static void nssRevokedListToCssm( NSS_RevokedCert **nssList, // may be NULL CSSM_X509_REVOKED_CERT_LIST_PTR cssmList, CssmAllocator &alloc) { unsigned numEntries = clNssArraySize((const void **)nssList); cssmList->numberOfRevokedCertEntries = numEntries; if(numEntries == 0) { cssmList->revokedCertEntry = NULL; return; } cssmList->revokedCertEntry = (CSSM_X509_REVOKED_CERT_ENTRY_PTR)alloc.malloc( sizeof(CSSM_X509_REVOKED_CERT_ENTRY) * numEntries); memset(cssmList->revokedCertEntry, 0, sizeof(CSSM_X509_REVOKED_CERT_ENTRY) * numEntries); for(unsigned dex=0; dexrevokedCertEntry[dex]; nssRevokedEntryToCssm(*nssEntry, *cssmEntry, alloc); } } static void freeCssmRevokedList( CSSM_X509_REVOKED_CERT_LIST_PTR cssmList, CssmAllocator &alloc) { if(cssmList == NULL) { return; } for(unsigned dex=0; dexnumberOfRevokedCertEntries; dex++) { CSSM_X509_REVOKED_CERT_ENTRY_PTR cssmEntry = &cssmList->revokedCertEntry[dex]; freeCssmEntry(cssmEntry, alloc); } if(cssmList->revokedCertEntry) { alloc.free(cssmList->revokedCertEntry); } memset(cssmList, 0, sizeof(CSSM_X509_REVOKED_CERT_LIST)); } /*** *** SignedCRL *** Format: CSSM_X509_SIGNED_CRL (the whole enchilada, parsed) ***/ static bool getField_SignedCrl ( DecodedItem &item, unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { CssmAllocator &alloc = fieldValue.allocator; const DecodedCrl &nssCrl = dynamic_cast(item); const NSS_TBSCrl &nssTbs = nssCrl.mCrl.tbs; fieldValue.malloc(sizeof(CSSM_X509_SIGNED_CRL)); CSSM_X509_SIGNED_CRL &cssmCrl = *((CSSM_X509_SIGNED_CRL *)fieldValue.data()); memset(&cssmCrl, 0, sizeof(CSSM_X509_SIGNED_CRL)); CSSM_X509_TBS_CERTLIST &cssmTbs = cssmCrl.tbsCertList; /* version */ clAllocCopyData(alloc, nssTbs.version, cssmTbs.version); /* CSSM_X509_ALGORITHM_IDENTIFIER signature - in TBS and CRL */ CL_copyAlgId(nssTbs.signature, cssmTbs.signature, alloc); CL_copyAlgId(nssCrl.mCrl.signatureAlgorithm, cssmCrl.signature.algorithmIdentifier, alloc); /* CSSM_X509_NAME issuer */ CL_nssNameToCssm(nssTbs.issuer, cssmTbs.issuer, alloc); /* CSSM_X509_TIME thisUpdate, nextUpdate */ CL_nssTimeToCssm(nssTbs.thisUpdate, cssmTbs.thisUpdate, alloc); CL_nssTimeToCssm(nssTbs.nextUpdate, cssmTbs.nextUpdate, alloc); /* CSSM_X509_REVOKED_CERT_LIST_PTR revokedCertificates */ if(nssTbs.revokedCerts != NULL) { cssmTbs.revokedCertificates = (CSSM_X509_REVOKED_CERT_LIST_PTR) alloc.malloc(sizeof(CSSM_X509_REVOKED_CERT_LIST)); memset(cssmTbs.revokedCertificates, 0, sizeof(CSSM_X509_REVOKED_CERT_LIST)); nssRevokedListToCssm(nssTbs.revokedCerts, cssmTbs.revokedCertificates, alloc); } /* CSSM_X509_EXTENSIONS extensions */ const DecodedExtensions &decodedExtens = nssCrl.decodedExtens(); decodedExtens.convertToCdsa(cssmTbs.extensions, alloc); /* raw signature - stored in bits - note signature.algId set above */ CSSM_DATA nssSig = nssCrl.mCrl.signature; nssSig.Length = (nssSig.Length + 7) / 8; clAllocCopyData(alloc, nssSig, cssmCrl.signature.encrypted); numFields = 1; return true; } static void setField_SignedCrl ( DecodedItem &item, const CssmData &fieldValue) { /* TBD - writing CRLs not supported now */ CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); } void freeField_SignedCrl ( CssmOwnedData &fieldValue) { CSSM_X509_SIGNED_CRL *cssmCrl = (CSSM_X509_SIGNED_CRL *)fieldValue.data(); if(cssmCrl == NULL) { return; } if(fieldValue.length() != sizeof(CSSM_X509_SIGNED_CRL)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } CssmAllocator &alloc = fieldValue.allocator; CSSM_X509_TBS_CERTLIST_PTR cssmTbs = &cssmCrl->tbsCertList; if(cssmTbs == NULL) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } /* run down the fields */ if(cssmTbs->version.Data) { alloc.free(cssmTbs->version.Data); } /* CSSM_X509_ALGORITHM_IDENTIFIER signature - in TBS and CRL */ CL_freeCssmAlgId(&cssmTbs->signature, alloc); CL_freeCssmAlgId(&cssmCrl->signature.algorithmIdentifier, alloc); /* issuer, thisUpdate, nextUpdate */ CL_freeX509Name(&cssmTbs->issuer, alloc); CL_freeCssmTime(&cssmTbs->thisUpdate, alloc); CL_freeCssmTime(&cssmTbs->nextUpdate, alloc); /* CSSM_X509_REVOKED_CERT_LIST_PTR revokedCertificates */ freeCssmRevokedList(cssmTbs->revokedCertificates, alloc); alloc.free(cssmTbs->revokedCertificates); /* CSSM_X509_EXTENSIONS extensions */ CL_freeCssmExtensions(cssmTbs->extensions, alloc); /* raw signature - note signature.algId freed above */ alloc.free(cssmCrl->signature.encrypted.Data); memset(cssmCrl, 0, sizeof(CSSM_X509_SIGNED_CRL)); } /* * Table to map OID to {get,set,free}field */ typedef struct { const CSSM_OID *fieldId; getItemFieldFcn *getFcn; setItemFieldFcn *setFcn; freeFieldFcn *freeFcn; // OPTIONAL - NULL means just free the // top-level data } oidToFieldFuncs; static const oidToFieldFuncs crlFieldTable[] = { /* this first one, which returns everything in a parsed format, * is intended to be normally the only field used */ { &CSSMOID_X509V2CRLSignedCrlCStruct, &getField_SignedCrl, &setField_SignedCrl, &freeField_SignedCrl }, { &CSSMOID_X509V2CRLVersion, &getField_Version, &setField_Version, NULL }, { &CSSMOID_X509V1CRLIssuerNameCStruct, &getField_Issuer, &setField_Issuer, &freeField_RDN }, { &CSSMOID_X509V1CRLThisUpdate, &getField_ThisUpdate, &setField_ThisUpdate, &freeField_Time }, { &CSSMOID_X509V1CRLNextUpdate, &getField_NextUpdate, &setField_NextUpdate, &freeField_Time }, { &CSSMOID_X509V1IssuerName, getFieldIssuerNorm, &setField_ReadOnly, NULL }, { &CSSMOID_X509V1SignatureAlgorithmTBS, &getField_CrlTbsAlgId, &setField_ReadOnly, &freeField_AlgId }, // ...etc.. /* * Extensions, implemented in CrlExtensions.cpp * When adding new ones, also add to: * -- clOidToNssInfo() in CLFieldsCommon.cpp * -- get/set/free functions in CrlExtensions.{cpp,h} * -- DecodedExten::parse in DecodedExtensions.cpp */ { &CSSMOID_CrlNumber, &getFieldCrlNumber, &setFieldCrlNumber, freeFieldSimpleExtension }, { &CSSMOID_DeltaCrlIndicator, &getFieldDeltaCrl, &setFieldCrlNumber, freeFieldSimpleExtension }, { &CSSMOID_CertIssuer, // get/set not implemented &getField_Unimplemented, &setField_ReadOnly, &freeFieldSubjIssuerAltName}, { &CSSMOID_CrlReason, // get/set not implemented &getField_Unimplemented, &setField_ReadOnly, freeFieldSimpleExtension}, { &CSSMOID_IssuingDistributionPoint, // get/set not implemented &getField_Unimplemented, &setField_ReadOnly, &freeFieldIssuingDistPoint}, { &CSSMOID_HoldInstructionCode, // get/set not implemented &getField_Unimplemented, &setField_ReadOnly, &freeFieldOidOrData}, { &CSSMOID_InvalidityDate, // get/set not implemented &getField_Unimplemented, &setField_ReadOnly, &freeFieldOidOrData}, /* in common with CertExtensions */ { &CSSMOID_AuthorityKeyIdentifier, &getFieldAuthorityKeyId, &setFieldAuthorityKeyId, &freeFieldAuthorityKeyId } , { &CSSMOID_X509V3CertificateExtensionCStruct, &getFieldUnknownExt, &setFieldUnknownExt, &freeFieldUnknownExt }, { &CSSMOID_SubjectAltName, &getFieldSubjAltName, &setFieldSubjIssuerAltName, &freeFieldSubjIssuerAltName } , { &CSSMOID_IssuerAltName, &getFieldIssuerAltName, &setFieldSubjIssuerAltName, &freeFieldSubjIssuerAltName } , // etc.. }; #define NUM_KNOWN_FIELDS (sizeof(crlFieldTable) / sizeof(oidToFieldFuncs)) #define NUM_STD_CRL_FIELDS 2 /* TBD not including extensions */ /* map an OID to an oidToFieldFuncs */ static const oidToFieldFuncs *oidToFields( const CssmOid &fieldId) { const oidToFieldFuncs *fieldTable = crlFieldTable; for(unsigned i=0; ifieldId)) { return fieldTable; } fieldTable++; } CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); } /* * Common routine to free OID-specific field data. Used in the * public DecodedCrl::freeCrlFieldData and when freeing * extensions in a CSSM_X509_TBS_CERTLIST. */ static void CL_freeCrlFieldData( const CssmOid &fieldId, CssmOwnedData &fieldValue, bool reset = true) { if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) { CssmError::throwMe(CSSM_ERRCODE_INVALID_FIELD_POINTER); } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); if(fieldFuncs->freeFcn != NULL) { /* optional - simple cases handled below */ fieldFuncs->freeFcn(fieldValue); } if(reset) { fieldValue.reset(); fieldValue.release(); } } /* * Common routime to free a CSSM_X509_EXTENSIONS. Used to free * CSSM_X509_TBS_CERTLIST.extensions and * CSSM_X509_REVOKED_CERT_ENTRY.extensions. * We just cook up a CssmOid and a CssmOwnedData for each extension * and pass to CL_freeCrlFieldData(). */ static void CL_freeCssmExtensions( CSSM_X509_EXTENSIONS &extens, CssmAllocator &alloc) { for(uint32 dex=0; dexformat) { case CSSM_X509_DATAFORMAT_ENCODED: fieldOid = &CSSMOID_X509V3CertificateExtensionCStruct; break; case CSSM_X509_DATAFORMAT_PARSED: case CSSM_X509_DATAFORMAT_PAIR: fieldOid = &exten->extnId; break; default: clErrorLog("CL_freeCssmExtensions: bad exten->format (%d)", (int)exten->format); CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } const CssmOid &fieldId = CssmOid::overlay(*fieldOid); CssmData cData((uint8 *)exten, sizeof(CSSM_X509_EXTENSION)); CssmRemoteData fieldValue(alloc, cData); CL_freeCrlFieldData(fieldId, fieldValue, false); fieldValue.release(); // but no free (via reset() */ } alloc.free(extens.extensions); memset(&extens, 0, sizeof(CSSM_X509_EXTENSIONS)); } /*** *** Public functions ***/ /* * Obtain the index'th occurrence of field specified by fieldId in specified cert. * Format of the returned field depends on fieldId. * Returns total number of fieldId fields in the cert if index is 0. * FieldValue assumed to be empty on entry. * Returns true if specified field was found, else returns false. */ bool DecodedCrl::getCrlFieldData( const CssmOid &fieldId, // which field unsigned index, // which occurrence (0 = first) uint32 &numFields, // RETURNED CssmOwnedData &fieldValue) // RETURNED { switch(mState) { case IS_Empty: case IS_Building: clErrorLog("DecodedCrl::getCrlField: can't parse undecoded CRL!"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); case IS_DecodedAll: case IS_DecodedTBS: break; } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); return fieldFuncs->getFcn(*this, index, numFields, fieldValue); } /* * Set the field specified by fieldId in the specified Cert. * Note no index - individual field routines either append (for extensions) * or if field already set ::throwMe(for all others) */ void DecodedCrl::setCrlField( const CssmOid &fieldId, // which field const CssmData &fieldValue) { switch(mState) { case IS_Empty: // first time thru mState = IS_Building; break; case IS_Building: // subsequent passes break; case IS_DecodedAll: case IS_DecodedTBS: clErrorLog("DecodedCrl::setCrlField: can't build on a decoded CRL!"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } if((fieldValue.data() == NULL) || (fieldValue.length() == 0)) { CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); } const oidToFieldFuncs *fieldFuncs = oidToFields(fieldId); const CssmData &value = CssmData::overlay(fieldValue); fieldFuncs->setFcn(*this, value); } /* * Free the fieldId-specific data referred to by fieldValue->Data. * No state from DecodedCrl needed; use the routine shared with * CL_freeCssmExtensions(). */ void DecodedCrl::freeCrlFieldData( const CssmOid &fieldId, CssmOwnedData &fieldValue) { CL_freeCrlFieldData(fieldId, fieldValue); } /* * Common means to get all fields from a decoded CRL. Used in * CrlGetAllTemplateFields and CrlGetAllFields. */ void DecodedCrl::getAllParsedCrlFields( uint32 &NumberOfFields, // RETURNED CSSM_FIELD_PTR &CrlFields) // RETURNED { /* this is the max - some might be missing */ uint32 maxFields = NUM_STD_CRL_FIELDS + mDecodedExtensions.numExtensions(); CSSM_FIELD_PTR outFields = (CSSM_FIELD_PTR)mAlloc.malloc( maxFields * sizeof(CSSM_FIELD)); /* * We'll be copying oids and values for fields we find into * outFields; current number of valid fields found in numOutFields. */ memset(outFields, 0, maxFields * sizeof(CSSM_FIELD)); uint32 numOutFields = 0; CSSM_FIELD_PTR currOutField; uint32 currOidDex; const CSSM_OID *currOid; CssmAutoData aData(mAlloc); // for malloc/copy of outgoing data /* query for each OID we know about */ for(currOidDex=0; currOidDexfieldId; uint32 numFields; // for THIS oid /* * Return false if field not there, which is not an error here. * Actual exceptions are fatal. */ if(!fieldFuncs->getFcn(*this, 0, // index - looking for first one numFields, aData)) { continue; } /* got some data for this oid - copy it and oid to outgoing CrlFields */ assert(numOutFields < maxFields); currOutField = &outFields[numOutFields]; currOutField->FieldValue = aData.release(); aData.copy(*currOid); currOutField->FieldOid = aData.release(); numOutFields++; /* if more fields are available for this OID, snag them too */ for(uint32 fieldDex=1; fieldDexgetFcn(*this, fieldDex, numFields, // shouldn't change aData); if(!brtn) { clErrorLog("getAllParsedCrlFields: index screwup"); CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); } assert(numOutFields < maxFields); currOutField = &outFields[numOutFields]; currOutField->FieldValue = aData.release(); aData.copy(*currOid); currOutField->FieldOid = aData.release(); numOutFields++; } /* multiple fields for currOid */ } /* for each known OID */ NumberOfFields = numOutFields; CrlFields = outFields; } void DecodedCrl::describeFormat( CssmAllocator &alloc, uint32 &NumberOfFields, CSSM_OID_PTR &OidList) { /* malloc in app's space, do deep copy (including ->Data) */ CSSM_OID_PTR oidList = (CSSM_OID_PTR)alloc.malloc( NUM_KNOWN_FIELDS * sizeof(CSSM_OID)); memset(oidList, 0, NUM_KNOWN_FIELDS * sizeof(CSSM_OID)); for(unsigned i=0; i