/*
 * 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 "CLCertExtensions.h"
#include "CLFieldsCommon.h"
#include "clNssUtils.h"
#include "clNameUtils.h"
#include <security_utilities/utilities.h>
#include <Security/oidscert.h>
#include <Security/oidsattr.h>
#include <Security/cssmerr.h>
#include <Security/x509defs.h>
#include <Security/certextensions.h>
#include <security_utilities/globalizer.h>
#include <Security/certExtensionTemplates.h>
#include <Security/asn1Templates.h>

/***
 *** get/set/free functions called out from CertFields.cpp
 ***/

/***
 *** KeyUsage 
 *** CDSA format 	CE_KeyUsage
 *** NSS format 	CSSM_DATA, length 2
 *** OID 			CSSMOID_KeyUsage
 ***/
 
void setFieldKeyUsage(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, 
		false);
	CE_KeyUsage *cdsaObj = (CE_KeyUsage *)cssmExt->value.parsedValue;
	
	/* Alloc an NSS-style key usage in cert.coder's memory */
	SecNssCoder &coder = cert.coder();
	CSSM_DATA *nssObj = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA));
	coder.allocItem(*nssObj, 2);
	
	/* cdsaObj --> nssObj */
	nssObj->Data[0] = (*cdsaObj) >> 8;
	nssObj->Data[1] = *cdsaObj;
	
	/* Adjust length for BIT STRING encoding */
	clCssmBitStringToNss(*nssObj);
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1KeyUsageTemplate); 
}


bool getFieldKeyUsage(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	CSSM_DATA *nssObj;
	CE_KeyUsage *cdsaObj;
	bool brtn;
	
	brtn = cert.GetExtenTop<CSSM_DATA, CE_KeyUsage>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_KeyUsage,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	
	/* make a copy - can't modify length in place */
	CSSM_DATA bitString = *nssObj;
	clNssBitStringToCssm(bitString);
	unsigned toCopy = bitString.Length;
	if(toCopy > 2) {
		/* I hope I never see this... */
		clErrorLog("getFieldKeyUsage: KeyUsage larger than 2 bytes!");
		toCopy = 2;
	}
	unsigned char bits[2] = {0, 0};
	memmove(bits, bitString.Data, toCopy);
	*cdsaObj = (((unsigned)bits[0]) << 8) | bits[1];
	
	/* pass back to caller */
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

/***
 *** Basic Constraints 
 *** CDSA format: 	CE_BasicConstraints
 *** NSS format 	CE_BasicConstraints
 *** OID 			CSSMOID_BasicConstraints
 ***/
 
void setFieldBasicConstraints(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = 
		verifySetFreeExtension(fieldValue, false);
	CE_BasicConstraints *cdsaObj = 
		(CE_BasicConstraints *)cssmExt->value.parsedValue;
	
	/* Alloc an NSS-style BasicConstraints in cert.coder's memory */
	SecNssCoder &coder = cert.coder();
	NSS_BasicConstraints *nssObj = 
		(NSS_BasicConstraints *)coder.malloc(sizeof(NSS_BasicConstraints));
	memset(nssObj, 0, sizeof(*nssObj));
	
	/* cdsaObj --> nssObj */
	ArenaAllocator arenaAlloc(coder);
	clCssmBoolToNss(cdsaObj->cA, nssObj->cA, arenaAlloc);
	if(cdsaObj->pathLenConstraintPresent) {
		clIntToData(cdsaObj->pathLenConstraint, 
			nssObj->pathLenConstraint, arenaAlloc);
	}

	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1BasicConstraintsTemplate); 
}


bool getFieldBasicConstraints(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_BasicConstraints *nssObj;
	CE_BasicConstraints *cdsaObj;
	bool brtn;
	
	brtn = cert.GetExtenTop<NSS_BasicConstraints, CE_BasicConstraints>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_BasicConstraints,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}

	if(nssObj->cA.Data == NULL) {
		/* default */
		cdsaObj->cA = CSSM_FALSE;
	}
	else {
		cdsaObj->cA = clNssBoolToCssm(nssObj->cA);
	}
	if(nssObj->pathLenConstraint.Data == NULL) {
		/* optional */
		cdsaObj->pathLenConstraintPresent = CSSM_FALSE;
		cdsaObj->pathLenConstraint = 0;
	}
	else {
		cdsaObj->pathLenConstraintPresent = CSSM_TRUE;
		cdsaObj->pathLenConstraint = clDataToInt(nssObj->pathLenConstraint);
	}
	
	/* pass back to caller */
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

/***
 *** Extended Key Usage
 *** CDSA format: 	CE_ExtendedKeyUsage
 *** NSS format: 	NSS_ExtKeyUsage
 *** OID 			CSSMOID_ExtendedKeyUsage
 ***/
void setFieldExtKeyUsage(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, 
		false);
	CE_ExtendedKeyUsage *cdsaObj = 
		(CE_ExtendedKeyUsage *)cssmExt->value.parsedValue;
	
	SecNssCoder &coder = cert.coder();
	NSS_ExtKeyUsage *nssObj = 
		(NSS_ExtKeyUsage *)coder.malloc(sizeof(NSS_ExtKeyUsage));
	memset(nssObj, 0, sizeof(*nssObj));
	if(cdsaObj->numPurposes != 0) {
		nssObj->purposes = 
			(CSSM_OID **)clNssNullArray(cdsaObj->numPurposes, coder);
	}
	
	/* cdsaObj --> nssObj, one 'purpose' (OID) at a time */
	for(unsigned dex=0; dex<cdsaObj->numPurposes; dex++) {
		nssObj->purposes[dex] = (CSSM_OID *)coder.malloc(sizeof(CSSM_OID));
		coder.allocCopyItem(cdsaObj->purposes[dex],
			*nssObj->purposes[dex]);
	}

	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1ExtKeyUsageTemplate); 
}

bool getFieldExtKeyUsage(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_ExtKeyUsage *nssObj;
	CE_ExtendedKeyUsage *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;
	
	brtn = cert.GetExtenTop<NSS_ExtKeyUsage, CE_ExtendedKeyUsage>(
		index,
		numFields,
		alloc,
		CSSMOID_ExtendedKeyUsage,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	
	/* nssObj --> cdsaObj, one purpose at a time */ 
	unsigned numPurposes = clNssArraySize((const void **)nssObj->purposes);
	cdsaObj->numPurposes = numPurposes;
	if(numPurposes) {
		unsigned len = numPurposes * sizeof(CSSM_OID);
		cdsaObj->purposes = (CSSM_OID_PTR)alloc.malloc(len);
		memset(cdsaObj->purposes, 0, len);
	}
	for(unsigned dex=0; dex<numPurposes; dex++) {
		clAllocCopyData(alloc, *nssObj->purposes[dex], cdsaObj->purposes[dex]);
	}

	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldExtKeyUsage(
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_ExtendedKeyUsage *cdsaObj = 
		(CE_ExtendedKeyUsage *)cssmExt->value.parsedValue;
	unsigned oidDex;
	for(oidDex=0; oidDex<cdsaObj->numPurposes; 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
 *** OID 			CSSMOID_SubjectKeyIdentifier
 ***/
 
void setFieldSubjectKeyId(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, 
		false);
	CE_SubjectKeyID *cdsaObj = (CE_SubjectKeyID *)cssmExt->value.parsedValue;
	SecNssCoder &coder = cert.coder();
	CSSM_DATA *nssObj = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA));
	coder.allocCopyItem(*cdsaObj, *nssObj);
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1SubjectKeyIdTemplate); 
}

bool getFieldSubjectKeyId(
	DecodedItem	 	&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	CSSM_DATA *nssObj;
	CE_SubjectKeyID *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;
	
	brtn = cert.GetExtenTop<CSSM_DATA, CE_SubjectKeyID>(
		index,
		numFields,
		alloc,
		CSSMOID_SubjectKeyIdentifier,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	
	/* if this fails, we're out of sync with nssExtenInfo[] in 
	 * CLFieldsCommon.cpp */
	assert(nssObj != NULL);	
	clAllocCopyData(alloc, *nssObj, *cdsaObj);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldSubjectKeyId (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &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
 *** NSS format: 	NSS_AuthorityKeyId
 *** OID 			CSSMOID_AuthorityKeyIdentifier
 ***/
 
void setFieldAuthorityKeyId(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, 
		false);
	CE_AuthorityKeyID *cdsaObj = 
		(CE_AuthorityKeyID *)cssmExt->value.parsedValue;
	
	/* Alloc an NSS-style AuthorityKeyId in cert.coder's memory */
	SecNssCoder &coder = cert.coder();
	NSS_AuthorityKeyId *nssObj = 
		(NSS_AuthorityKeyId *)coder.malloc(sizeof(NSS_AuthorityKeyId));
	memset(nssObj, 0, sizeof(*nssObj));
	
	/* convert caller's CDSA-style CE_AuthorityKeyID to NSS */
	CL_cssmAuthorityKeyIdToNss(*cdsaObj, *nssObj, coder);
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1AuthorityKeyIdTemplate); 
}

bool getFieldAuthorityKeyId(
	DecodedItem		 	&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_AuthorityKeyId *nssObj;
	CE_AuthorityKeyID *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;
	
	brtn = cert.GetExtenTop<NSS_AuthorityKeyId, CE_AuthorityKeyID>(
		index,
		numFields,
		alloc,
		CSSMOID_AuthorityKeyIdentifier,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	assert(nssObj != NULL);
	
	/* nssObj --> cdsaObj */
	CL_nssAuthorityKeyIdToCssm(*nssObj, *cdsaObj, cert.coder(), alloc);
	
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldAuthorityKeyId (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_AuthorityKeyID *cdsaObj = (CE_AuthorityKeyID *)cssmExt->value.parsedValue;
	CL_freeAuthorityKeyId(*cdsaObj, alloc);
	freeFieldExtenCommon(cssmExt, alloc);		// frees extnId, parsedValue, BERvalue
}

/***
 *** Subject/Issuer alternate name
 *** CDSA Format:	CE_GeneralNames
 *** NSS format:	NSS_GeneralNames
 *** OID: 			CSSMOID_SubjectAltName, CSSMOID_IssuerAltName
 ***/
void setFieldSubjIssuerAltName(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = 
		verifySetFreeExtension(fieldValue, false);
	CE_GeneralNames *cdsaObj = (CE_GeneralNames *)cssmExt->value.parsedValue;

	/* Alloc an NSS-style GeneralNames in cert.coder's memory */
	SecNssCoder &coder = cert.coder();
	NSS_GeneralNames *nssObj = 
		(NSS_GeneralNames *)coder.malloc(sizeof(NSS_GeneralNames));
	memset(nssObj, 0, sizeof(*nssObj));
	
	/* cdsaObj --> nssObj */
	CL_cssmGeneralNamesToNss(*cdsaObj, *nssObj, coder);
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1GeneralNamesTemplate); 
}

bool getFieldSubjAltName(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_GeneralNames *nssObj;
	CE_GeneralNames *cdsaObj; 
	bool brtn;
	
	brtn = cert.GetExtenTop<NSS_GeneralNames, CE_GeneralNames>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_SubjectAltName,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	CL_nssGeneralNamesToCssm(*nssObj, *cdsaObj,	
		cert.coder(), fieldValue.allocator);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

bool getFieldIssuerAltName(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_GeneralNames *nssObj;
	CE_GeneralNames *cdsaObj; 
	bool brtn;
	
	brtn = cert.GetExtenTop<NSS_GeneralNames, CE_GeneralNames>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_IssuerAltName,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	CL_nssGeneralNamesToCssm(*nssObj, *cdsaObj,	
		cert.coder(), fieldValue.allocator);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldSubjIssuerAltName (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_GeneralNames *cdsaObj = (CE_GeneralNames *)cssmExt->value.parsedValue;
	CL_freeCssmGeneralNames(cdsaObj, alloc);
	freeFieldExtenCommon(cssmExt, alloc);		// frees extnId, parsedValue, BERvalue
}

/***
 *** Certificate Policies
 *** CDSA Format:	CE_CertPolicies
 *** NSS format :	NSS_CertPolicies
 *** OID: 			CSSMOID_CertificatePolicies
 ***/
 
#define MAX_IA5_NAME_SIZE	1024

void setFieldCertPolicies(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = 
		verifySetFreeExtension(fieldValue, false);
	SecNssCoder &coder = cert.coder();
	NSS_CertPolicies *nssObj = 
		(NSS_CertPolicies *)coder.malloc(sizeof(NSS_CertPolicies));
	memset(nssObj, 0, sizeof(NSS_CertPolicies));
	CE_CertPolicies *cdsaObj = 
		(CE_CertPolicies *)cssmExt->value.parsedValue;
	
	if(cdsaObj->numPolicies) {
		nssObj->policies = 
			(NSS_PolicyInformation **)clNssNullArray(
				cdsaObj->numPolicies, coder);
	}
	for(unsigned polDex=0; polDex<cdsaObj->numPolicies; polDex++) {
		CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex];
		NSS_PolicyInformation *nPolInfo = (NSS_PolicyInformation *)
			coder.malloc(sizeof(NSS_PolicyInformation));
		memset(nPolInfo, 0, sizeof(*nPolInfo));
		nssObj->policies[polDex] = nPolInfo;
		
		coder.allocCopyItem(cPolInfo->certPolicyId, nPolInfo->certPolicyId);

		unsigned numQual = cPolInfo->numPolicyQualifiers;
		if(numQual != 0) {
			nPolInfo->policyQualifiers = 
				(NSS_PolicyQualifierInfo **)clNssNullArray(numQual,
					coder);
		}
		for(unsigned qualDex=0; qualDex<numQual; qualDex++) {
			CE_PolicyQualifierInfo *cQualInfo = 
				&cPolInfo->policyQualifiers[qualDex];
			NSS_PolicyQualifierInfo *nQualInfo = 
				(NSS_PolicyQualifierInfo *)coder.malloc(
					sizeof(NSS_PolicyQualifierInfo));
			memset(nQualInfo, 0, sizeof(NSS_PolicyQualifierInfo));
			nPolInfo->policyQualifiers[qualDex] = nQualInfo;
			
			/* 
			 * 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.
			 */
			coder.allocCopyItem(cQualInfo->policyQualifierId,
				nQualInfo->policyQualifierId);
				
			if(clCompareCssmData(&cQualInfo->policyQualifierId, 
					&CSSMOID_QT_CPS)) {
				if(coder.encodeItem(&cQualInfo->qualifier,
						kSecAsn1IA5StringTemplate,
						nQualInfo->qualifier)) {
					clErrorLog("setFieldCertPOlicies: IA5 encode error\n");
					CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR);
				}
			}
			else {
				/* uninterpreted, copy over directly */
				coder.allocCopyItem(cQualInfo->qualifier,
					nQualInfo->qualifier);
			}
		}	/* for each qualifier */
	}	/* for each policy */
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1CertPoliciesTemplate); 
}

bool getFieldCertPolicies( 
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_CertPolicies *nssObj;
	CE_CertPolicies *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;
	brtn = cert.GetExtenTop<NSS_CertPolicies, CE_CertPolicies>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_CertificatePolicies,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	assert(nssObj != NULL);
	
	memset(cdsaObj, 0, sizeof(*cdsaObj));
	cdsaObj->numPolicies = 
		clNssArraySize((const void **)nssObj->policies);
	unsigned sz = cdsaObj->numPolicies * sizeof(CE_PolicyInformation);
	if(sz) {
		cdsaObj->policies = (CE_PolicyInformation *)alloc.malloc(sz);
		memset(cdsaObj->policies, 0, sz);
	}

	for(unsigned polDex=0; polDex<cdsaObj->numPolicies; polDex++) {
		CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex];
		NSS_PolicyInformation *nPolInfo = nssObj->policies[polDex];
		clAllocCopyData(alloc, nPolInfo->certPolicyId,
			cPolInfo->certPolicyId);
		if(nPolInfo->policyQualifiers == NULL) {
			continue;
		}
		
		cPolInfo->numPolicyQualifiers = 
			clNssArraySize((const void **)nPolInfo->policyQualifiers);
		sz = cPolInfo->numPolicyQualifiers * 
			 sizeof(CE_PolicyQualifierInfo);
		cPolInfo->policyQualifiers = (CE_PolicyQualifierInfo *)
			alloc.malloc(sz);
		memset(cPolInfo->policyQualifiers, 0, sz);
		
		for(unsigned qualDex=0; qualDex<cPolInfo->numPolicyQualifiers; 
				qualDex++) {
			NSS_PolicyQualifierInfo *nQualInfo = 
				nPolInfo->policyQualifiers[qualDex];
			CE_PolicyQualifierInfo *cQualInfo = 
				&cPolInfo->policyQualifiers[qualDex];
			
			/* 
			 * leaf. 
			 * policyQualifierId == CSSMOID_QT_CPS : 
			 *  		IA5String - decode and return contents.
			 * Else return whole thing. 
			 */
			clAllocCopyData(alloc, nQualInfo->policyQualifierId,
				cQualInfo->policyQualifierId);
			CSSM_DATA toCopy = nQualInfo->qualifier;
			if(clCompareCssmData(&nQualInfo->policyQualifierId, 
						&CSSMOID_QT_CPS)) {
				/* decode as IA5String to temp memory */
				toCopy.Data = NULL;
				toCopy.Length = 0;
				if(cert.coder().decodeItem(nQualInfo->qualifier,
						kSecAsn1IA5StringTemplate,
						&toCopy)) {
					clErrorLog("***getCertPolicies: bad IA5String!\n");
					CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT);
				}
			}
			/* else copy out nQualInfo->qualifier */
			clAllocCopyData(alloc, toCopy, cQualInfo->qualifier);
		}	/* for each qualifier */
	}		/* for each policy info */
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldCertPolicies (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_CertPolicies *cdsaObj = (CE_CertPolicies *)cssmExt->value.parsedValue;
	for(unsigned polDex=0; polDex<cdsaObj->numPolicies; polDex++) {
		CE_PolicyInformation *cPolInfo = &cdsaObj->policies[polDex];
		alloc.free(cPolInfo->certPolicyId.Data);
		for(unsigned qualDex=0; 
		             qualDex<cPolInfo->numPolicyQualifiers; 
					 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)
 *** NSS format 	CSSM_DATA, length 2
 *** OID: 			CSSMOID_NetscapeCertType
 ***/
void setFieldNetscapeCertType(		
	DecodedItem	&cert, 
	const CssmData &fieldValue) 
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, 
		false);
	CE_NetscapeCertType *cdsaObj = 
		(CE_NetscapeCertType *)cssmExt->value.parsedValue;
	
	/* Alloc an NSS-style key usage in cert.coder's memory */
	SecNssCoder &coder = cert.coder();
	CSSM_DATA *nssObj = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA));
	coder.allocItem(*nssObj, 2);
	
	/* cdsaObj --> nssObj */
	nssObj->Data[0] = (*cdsaObj) >> 8;
	nssObj->Data[1] = *cdsaObj;
	
	/* Adjust length for BIT STRING encoding */
	clCssmBitStringToNss(*nssObj);
	
	/* add to mExtensions */
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1NetscapeCertTypeTemplate); 
}

bool getFieldNetscapeCertType(
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	CSSM_DATA *nssObj;
	CE_NetscapeCertType *cdsaObj;
	bool brtn;
	
	brtn = cert.GetExtenTop<CSSM_DATA, CE_NetscapeCertType>(
		index,
		numFields,
		fieldValue.allocator,
		CSSMOID_NetscapeCertType,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	
	/* make a copy - can't modify length in place */
	CSSM_DATA bitString = *nssObj;
	clNssBitStringToCssm(bitString);
	unsigned toCopy = bitString.Length;
	if(toCopy > 2) {
		/* I hope I never see this... */
		clErrorLog("getFieldKeyUsage: CertType larger than 2 bytes!");
		toCopy = 2;
	}
	unsigned char bits[2] = {0, 0};
	memmove(bits, bitString.Data, toCopy);
	*cdsaObj = (((unsigned)bits[0]) << 8) | bits[1];
	
	/* pass back to caller */
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

/***
 *** CRL Distribution points
 *** CDSA Format:	CE_CRLDistPointsSyntax
 *** NSS format:	NSS_CRLDistributionPoints
 *** OID: 			CSSMOID_CrlDistributionPoints
 ***/
void setFieldCrlDistPoints(		
	DecodedItem	&cert, 
	const CssmData &fieldValue)  
{
	CSSM_X509_EXTENSION_PTR cssmExt = 
		verifySetFreeExtension(fieldValue, false);
	CE_CRLDistPointsSyntax *cdsaObj = 
		(CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
	SecNssCoder &coder = cert.coder();
	NSS_CRLDistributionPoints *nssObj = 
		(NSS_CRLDistributionPoints *)coder.malloc(
				sizeof(NSS_CRLDistributionPoints));
		
	CL_cssmDistPointsToNss(*cdsaObj, *nssObj, coder);
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1CRLDistributionPointsTemplate); 
}

bool getFieldCrlDistPoints( 
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_CRLDistributionPoints *nssObj;
	CE_CRLDistPointsSyntax *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;

	brtn = cert.GetExtenTop<NSS_CRLDistributionPoints, 
			CE_CRLDistPointsSyntax>(
		index,
		numFields,
		alloc,
		CSSMOID_CrlDistributionPoints,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	assert(nssObj != NULL);
	CL_nssDistPointsToCssm(*nssObj, *cdsaObj, cert.coder(), alloc);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldCrlDistPoints (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_CRLDistPointsSyntax *cdsaObj = 
		(CE_CRLDistPointsSyntax *)cssmExt->value.parsedValue;
	CL_freeCssmDistPoints(cdsaObj, alloc);
	freeFieldExtenCommon(cssmExt, alloc);		// frees extnId, parsedValue, BERvalue
}

/***
 *** {Subject,Authority}InfoAccess
 ***
 *** CDSA Format:	CE_AuthorityInfoAccess
 *** NSS format:	NSS_AuthorityInfoAccess
 *** OID: 			CSSMOID_AuthorityInfoAccess, CSSMOID_SubjectInfoAccess
 ***/
void setFieldAuthInfoAccess(		
	DecodedItem	&cert, 
	const CssmData &fieldValue)  
{
	CSSM_X509_EXTENSION_PTR cssmExt = 
		verifySetFreeExtension(fieldValue, false);
	CE_AuthorityInfoAccess *cdsaObj = 
		(CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
	SecNssCoder &coder = cert.coder();
	NSS_AuthorityInfoAccess *nssObj = 
		(NSS_AuthorityInfoAccess *)coder.malloc(
				sizeof(NSS_AuthorityInfoAccess));
		
	CL_cssmInfoAccessToNss(*cdsaObj, *nssObj, coder);
	cert.addExtension(nssObj, cssmExt->extnId, cssmExt->critical, false,
		kSecAsn1AuthorityInfoAccessTemplate); 
}

bool getFieldAuthInfoAccess( 
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_AuthorityInfoAccess *nssObj;
	CE_AuthorityInfoAccess *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;

	brtn = cert.GetExtenTop<NSS_AuthorityInfoAccess, 
			CE_AuthorityInfoAccess>(
		index,
		numFields,
		alloc,
		CSSMOID_AuthorityInfoAccess,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	assert(nssObj != NULL);
	CL_infoAccessToCssm(*nssObj, *cdsaObj, cert.coder(), alloc);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

bool getFieldSubjInfoAccess( 
	DecodedItem 		&cert,
	unsigned			index,			// which occurrence (0 = first)
	uint32				&numFields,		// RETURNED
	CssmOwnedData		&fieldValue) 
{
	const DecodedExten *decodedExt;
	NSS_AuthorityInfoAccess *nssObj;
	CE_AuthorityInfoAccess *cdsaObj;
	bool brtn;
	Allocator &alloc = fieldValue.allocator;

	brtn = cert.GetExtenTop<NSS_AuthorityInfoAccess, 
			CE_AuthorityInfoAccess>(
		index,
		numFields,
		alloc,
		CSSMOID_SubjectInfoAccess,
		nssObj,
		cdsaObj,
		decodedExt);
	if(!brtn) {
		return false;
	}
	assert(nssObj != NULL);
	CL_infoAccessToCssm(*nssObj, *cdsaObj, cert.coder(), alloc);
	getFieldExtenCommon(cdsaObj, *decodedExt, fieldValue);
	return true;
}

void freeFieldInfoAccess (
	CssmOwnedData		&fieldValue)
{
	CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, false);
	Allocator &alloc = fieldValue.allocator;
	CE_AuthorityInfoAccess *cdsaObj = 
		(CE_AuthorityInfoAccess *)cssmExt->value.parsedValue;
	CL_freeInfoAccess(*cdsaObj, alloc);
	freeFieldExtenCommon(cssmExt, alloc);		// frees extnId, parsedValue, BERvalue

}