diff nss/lib/certdb/certdb.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/certdb/certdb.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,3275 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Certificate handling code
+ */
+
+#include "nssilock.h"
+#include "prmon.h"
+#include "prtime.h"
+#include "cert.h"
+#include "certi.h"
+#include "secder.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "genname.h"
+#include "keyhi.h"
+#include "secitem.h"
+#include "certdb.h"
+#include "prprf.h"
+#include "sechash.h"
+#include "prlong.h"
+#include "certxutl.h"
+#include "portreg.h"
+#include "secerr.h"
+#include "sslerr.h"
+#include "pk11func.h"
+#include "xconst.h"   /* for  CERT_DecodeAltNameExtension */
+
+#include "pki.h"
+#include "pki3hack.h"
+
+SEC_ASN1_MKSUB(CERT_TimeChoiceTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_SkipTemplate)
+
+/*
+ * Certificate database handling code
+ */
+
+
+const SEC_ASN1Template CERT_CertExtensionTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(CERTCertExtension) },
+    { SEC_ASN1_OBJECT_ID,
+	  offsetof(CERTCertExtension,id) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN,		/* XXX DER_DEFAULT */
+	  offsetof(CERTCertExtension,critical) },
+    { SEC_ASN1_OCTET_STRING,
+	  offsetof(CERTCertExtension,value) },
+    { 0, }
+};
+
+const SEC_ASN1Template CERT_SequenceOfCertExtensionTemplate[] = {
+    { SEC_ASN1_SEQUENCE_OF, 0, CERT_CertExtensionTemplate }
+};
+
+const SEC_ASN1Template CERT_TimeChoiceTemplate[] = {
+  { SEC_ASN1_CHOICE, offsetof(SECItem, type), 0, sizeof(SECItem) },
+  { SEC_ASN1_UTC_TIME, 0, 0, siUTCTime },
+  { SEC_ASN1_GENERALIZED_TIME, 0, 0, siGeneralizedTime },
+  { 0 }
+};
+
+const SEC_ASN1Template CERT_ValidityTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(CERTValidity) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+          offsetof(CERTValidity,notBefore),
+          SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+          offsetof(CERTValidity,notAfter),
+          SEC_ASN1_SUB(CERT_TimeChoiceTemplate), 0 },
+    { 0 }
+};
+
+const SEC_ASN1Template CERT_CertificateTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+      0, NULL, sizeof(CERTCertificate) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0, /* XXX DER_DEFAULT */ 
+	  offsetof(CERTCertificate,version),
+	  SEC_ASN1_SUB(SEC_IntegerTemplate) },
+    { SEC_ASN1_INTEGER,
+	  offsetof(CERTCertificate,serialNumber) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(CERTCertificate,signature),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_SAVE, 
+	  offsetof(CERTCertificate,derIssuer) },
+    { SEC_ASN1_INLINE,
+	  offsetof(CERTCertificate,issuer),
+	  CERT_NameTemplate },
+    { SEC_ASN1_INLINE,
+	  offsetof(CERTCertificate,validity),
+	  CERT_ValidityTemplate },
+    { SEC_ASN1_SAVE,
+	  offsetof(CERTCertificate,derSubject) },
+    { SEC_ASN1_INLINE,
+	  offsetof(CERTCertificate,subject),
+	  CERT_NameTemplate },
+    { SEC_ASN1_SAVE,
+	  offsetof(CERTCertificate,derPublicKey) },
+    { SEC_ASN1_INLINE,
+	  offsetof(CERTCertificate,subjectPublicKeyInfo),
+	  CERT_SubjectPublicKeyInfoTemplate },
+    { SEC_ASN1_OPTIONAL |  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+	  offsetof(CERTCertificate,issuerID),
+	  SEC_ASN1_SUB(SEC_BitStringTemplate) },
+    { SEC_ASN1_OPTIONAL |  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+	  offsetof(CERTCertificate,subjectID),
+	  SEC_ASN1_SUB(SEC_BitStringTemplate) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | 3,
+	  offsetof(CERTCertificate,extensions),
+	  CERT_SequenceOfCertExtensionTemplate },
+    { 0 }
+};
+
+const SEC_ASN1Template SEC_SignedCertificateTemplate[] =
+{
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(CERTCertificate) },
+    { SEC_ASN1_SAVE, 
+	  offsetof(CERTCertificate,signatureWrap.data) },
+    { SEC_ASN1_INLINE, 
+	  0, CERT_CertificateTemplate },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	  offsetof(CERTCertificate,signatureWrap.signatureAlgorithm),
+	  SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_BIT_STRING,
+	  offsetof(CERTCertificate,signatureWrap.signature) },
+    { 0 }
+};
+
+/*
+ * Find the subjectName in a DER encoded certificate
+ */
+const SEC_ASN1Template SEC_CertSubjectTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SECItem) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	  0, SEC_ASN1_SUB(SEC_SkipTemplate) },	/* version */
+    { SEC_ASN1_SKIP },		/* serial number */
+    { SEC_ASN1_SKIP },		/* signature algorithm */
+    { SEC_ASN1_SKIP },		/* issuer */
+    { SEC_ASN1_SKIP },		/* validity */
+    { SEC_ASN1_ANY, 0, NULL },		/* subject */
+    { SEC_ASN1_SKIP_REST },
+    { 0 }
+};
+
+/*
+ * Find the issuerName in a DER encoded certificate
+ */
+const SEC_ASN1Template SEC_CertIssuerTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SECItem) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	  0, SEC_ASN1_SUB(SEC_SkipTemplate) },	/* version */
+    { SEC_ASN1_SKIP },		/* serial number */
+    { SEC_ASN1_SKIP },		/* signature algorithm */
+    { SEC_ASN1_ANY, 0, NULL },		/* issuer */
+    { SEC_ASN1_SKIP_REST },
+    { 0 }
+};
+/*
+ * Find the subjectName in a DER encoded certificate
+ */
+const SEC_ASN1Template SEC_CertSerialNumberTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(SECItem) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	  0, SEC_ASN1_SUB(SEC_SkipTemplate) },	/* version */
+    { SEC_ASN1_ANY, 0, NULL }, /* serial number */
+    { SEC_ASN1_SKIP_REST },
+    { 0 }
+};
+
+/*
+ * Find the issuer and serialNumber in a DER encoded certificate.
+ * This data is used as the database lookup key since its the unique
+ * identifier of a certificate.
+ */
+const SEC_ASN1Template CERT_CertKeyTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	  0, NULL, sizeof(CERTCertKey) },
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | 
+	  SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	  0, SEC_ASN1_SUB(SEC_SkipTemplate) },	/* version */ 
+    { SEC_ASN1_INTEGER,
+	  offsetof(CERTCertKey,serialNumber) },
+    { SEC_ASN1_SKIP },		/* signature algorithm */
+    { SEC_ASN1_ANY,
+	  offsetof(CERTCertKey,derIssuer) },
+    { SEC_ASN1_SKIP_REST },
+    { 0 }
+};
+
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_TimeChoiceTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate)
+SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SequenceOfCertExtensionTemplate)
+
+SECStatus
+CERT_KeyFromIssuerAndSN(PLArenaPool *arena, SECItem *issuer, SECItem *sn,
+			SECItem *key)
+{
+    key->len = sn->len + issuer->len;
+
+    if ((sn->data == NULL) || (issuer->data == NULL)) {
+	goto loser;
+    }
+    
+    key->data = (unsigned char*)PORT_ArenaAlloc(arena, key->len);
+    if ( !key->data ) {
+	goto loser;
+    }
+
+    /* copy the serialNumber */
+    PORT_Memcpy(key->data, sn->data, sn->len);
+
+    /* copy the issuer */
+    PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
+
+    return(SECSuccess);
+
+loser:
+    return(SECFailure);
+}
+
+
+/*
+ * Extract the subject name from a DER certificate
+ */
+SECStatus
+CERT_NameFromDERCert(SECItem *derCert, SECItem *derName)
+{
+    int rv;
+    PLArenaPool *arena;
+    CERTSignedData sd;
+    void *tmpptr;
+    
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    
+    if ( ! arena ) {
+	return(SECFailure);
+    }
+   
+    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
+    
+    if ( rv ) {
+	goto loser;
+    }
+    
+    PORT_Memset(derName, 0, sizeof(SECItem));
+    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSubjectTemplate, &sd.data);
+
+    if ( rv ) {
+	goto loser;
+    }
+
+    tmpptr = derName->data;
+    derName->data = (unsigned char*)PORT_Alloc(derName->len);
+    if ( derName->data == NULL ) {
+	goto loser;
+    }
+    
+    PORT_Memcpy(derName->data, tmpptr, derName->len);
+    
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECSuccess);
+
+loser:
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECFailure);
+}
+
+SECStatus
+CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName)
+{
+    int rv;
+    PLArenaPool *arena;
+    CERTSignedData sd;
+    void *tmpptr;
+    
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    
+    if ( ! arena ) {
+	return(SECFailure);
+    }
+   
+    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
+    
+    if ( rv ) {
+	goto loser;
+    }
+    
+    PORT_Memset(derName, 0, sizeof(SECItem));
+    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertIssuerTemplate, &sd.data);
+
+    if ( rv ) {
+	goto loser;
+    }
+
+    tmpptr = derName->data;
+    derName->data = (unsigned char*)PORT_Alloc(derName->len);
+    if ( derName->data == NULL ) {
+	goto loser;
+    }
+    
+    PORT_Memcpy(derName->data, tmpptr, derName->len);
+    
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECSuccess);
+
+loser:
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECFailure);
+}
+
+SECStatus
+CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName)
+{
+    int rv;
+    PLArenaPool *arena;
+    CERTSignedData sd;
+    void *tmpptr;
+    
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    
+    if ( ! arena ) {
+	return(SECFailure);
+    }
+   
+    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
+    
+    if ( rv ) {
+	goto loser;
+    }
+    
+    PORT_Memset(derName, 0, sizeof(SECItem));
+    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSerialNumberTemplate, &sd.data);
+
+    if ( rv ) {
+	goto loser;
+    }
+
+    tmpptr = derName->data;
+    derName->data = (unsigned char*)PORT_Alloc(derName->len);
+    if ( derName->data == NULL ) {
+	goto loser;
+    }
+    
+    PORT_Memcpy(derName->data, tmpptr, derName->len);
+    
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECSuccess);
+
+loser:
+    PORT_FreeArena(arena, PR_FALSE);
+    return(SECFailure);
+}
+
+/*
+ * Generate a database key, based on serial number and issuer, from a
+ * DER certificate.
+ */
+SECStatus
+CERT_KeyFromDERCert(PLArenaPool *reqArena, SECItem *derCert, SECItem *key)
+{
+    int rv;
+    CERTSignedData sd;
+    CERTCertKey certkey;
+
+    if (!reqArena) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    PORT_Memset(&sd, 0, sizeof(CERTSignedData));
+    rv = SEC_QuickDERDecodeItem(reqArena, &sd, CERT_SignedDataTemplate,
+                                derCert);
+    
+    if ( rv ) {
+	goto loser;
+    }
+    
+    PORT_Memset(&certkey, 0, sizeof(CERTCertKey));
+    rv = SEC_QuickDERDecodeItem(reqArena, &certkey, CERT_CertKeyTemplate,
+                                &sd.data);
+
+    if ( rv ) {
+	goto loser;
+    }
+
+    return(CERT_KeyFromIssuerAndSN(reqArena, &certkey.derIssuer,
+				   &certkey.serialNumber, key));
+loser:
+    return(SECFailure);
+}
+
+/*
+ * fill in keyUsage field of the cert based on the cert extension
+ * if the extension is not critical, then we allow all uses
+ */
+static SECStatus
+GetKeyUsage(CERTCertificate *cert)
+{
+    SECStatus rv;
+    SECItem tmpitem;
+    
+    rv = CERT_FindKeyUsageExtension(cert, &tmpitem);
+    if ( rv == SECSuccess ) {
+	/* remember the actual value of the extension */
+	cert->rawKeyUsage = tmpitem.data[0];
+	cert->keyUsagePresent = PR_TRUE;
+	cert->keyUsage = tmpitem.data[0];
+
+	PORT_Free(tmpitem.data);
+	tmpitem.data = NULL;
+	
+    } else {
+	/* if the extension is not present, then we allow all uses */
+	cert->keyUsage = KU_ALL;
+	cert->rawKeyUsage = KU_ALL;
+	cert->keyUsagePresent = PR_FALSE;
+    }
+
+    if ( CERT_GovtApprovedBitSet(cert) ) {
+	cert->keyUsage |= KU_NS_GOVT_APPROVED;
+	cert->rawKeyUsage |= KU_NS_GOVT_APPROVED;
+    }
+    
+    return(SECSuccess);
+}
+
+
+static SECStatus
+findOIDinOIDSeqByTagNum(CERTOidSequence *seq, SECOidTag tagnum)
+{
+    SECItem **oids;
+    SECItem *oid;
+    SECStatus rv = SECFailure;
+    
+    if (seq != NULL) {
+	oids = seq->oids;
+	while (oids != NULL && *oids != NULL) {
+	    oid = *oids;
+	    if (SECOID_FindOIDTag(oid) == tagnum) {
+		rv = SECSuccess;
+		break;
+	    }
+	    oids++;
+	}
+    }
+    return rv;
+}
+
+/*
+ * fill in nsCertType field of the cert based on the cert extension
+ */
+SECStatus
+cert_GetCertType(CERTCertificate *cert)
+{
+    PRUint32 nsCertType;
+
+    if (cert->nsCertType) {
+        /* once set, no need to recalculate */
+        return SECSuccess;
+    }
+    nsCertType = cert_ComputeCertType(cert);
+
+    /* Assert that it is safe to cast &cert->nsCertType to "PRInt32 *" */
+    PORT_Assert(sizeof(cert->nsCertType) == sizeof(PRInt32));
+    PR_ATOMIC_SET((PRInt32 *)&cert->nsCertType, nsCertType);
+    return SECSuccess;
+}
+
+PRUint32
+cert_ComputeCertType(CERTCertificate *cert)
+{
+    SECStatus rv;
+    SECItem tmpitem;
+    SECItem encodedExtKeyUsage;
+    CERTOidSequence *extKeyUsage = NULL;
+    PRBool basicConstraintPresent = PR_FALSE;
+    CERTBasicConstraints basicConstraint;
+    PRUint32 nsCertType = 0;
+
+    tmpitem.data = NULL;
+    CERT_FindNSCertTypeExtension(cert, &tmpitem);
+    encodedExtKeyUsage.data = NULL;
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, 
+				&encodedExtKeyUsage);
+    if (rv == SECSuccess) {
+	extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage);
+    }
+    rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
+    if (rv == SECSuccess) {
+	basicConstraintPresent = PR_TRUE;
+    }
+    if (tmpitem.data != NULL || extKeyUsage != NULL) {
+	if (tmpitem.data == NULL) {
+	    nsCertType = 0;
+	} else {
+	    nsCertType = tmpitem.data[0];
+	}
+
+	/* free tmpitem data pointer to avoid memory leak */
+	PORT_Free(tmpitem.data);
+	tmpitem.data = NULL;
+	
+	/*
+	 * for this release, we will allow SSL certs with an email address
+	 * to be used for email
+	 */
+	if ( ( nsCertType & NS_CERT_TYPE_SSL_CLIENT ) &&
+	    cert->emailAddr && cert->emailAddr[0]) {
+	    nsCertType |= NS_CERT_TYPE_EMAIL;
+	}
+	/*
+	 * for this release, we will allow SSL intermediate CAs to be
+	 * email intermediate CAs too.
+	 */
+	if ( nsCertType & NS_CERT_TYPE_SSL_CA ) {
+	    nsCertType |= NS_CERT_TYPE_EMAIL_CA;
+	}
+	/*
+	 * allow a cert with the extended key usage of EMail Protect
+	 * to be used for email or as an email CA, if basic constraints
+	 * indicates that it is a CA.
+	 */
+	if (findOIDinOIDSeqByTagNum(extKeyUsage, 
+				    SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT) ==
+	    SECSuccess) {
+	    if (basicConstraintPresent == PR_TRUE &&
+		(basicConstraint.isCA)) {
+		nsCertType |= NS_CERT_TYPE_EMAIL_CA;
+	    } else {
+		nsCertType |= NS_CERT_TYPE_EMAIL;
+	    }
+	}
+	if (findOIDinOIDSeqByTagNum(extKeyUsage, 
+				    SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) ==
+	    SECSuccess){
+	    if (basicConstraintPresent == PR_TRUE &&
+		(basicConstraint.isCA)) {
+		nsCertType |= NS_CERT_TYPE_SSL_CA;
+	    } else {
+		nsCertType |= NS_CERT_TYPE_SSL_SERVER;
+	    }
+	}
+	/*
+	 * Treat certs with step-up OID as also having SSL server type.
+ 	 * COMODO needs this behaviour until June 2020.  See Bug 737802.
+	 */
+	if (findOIDinOIDSeqByTagNum(extKeyUsage, 
+				    SEC_OID_NS_KEY_USAGE_GOVT_APPROVED) ==
+	    SECSuccess){
+	    if (basicConstraintPresent == PR_TRUE &&
+		(basicConstraint.isCA)) {
+		nsCertType |= NS_CERT_TYPE_SSL_CA;
+	    } else {
+		nsCertType |= NS_CERT_TYPE_SSL_SERVER;
+	    }
+	}
+	if (findOIDinOIDSeqByTagNum(extKeyUsage,
+				    SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) ==
+	    SECSuccess){
+	    if (basicConstraintPresent == PR_TRUE &&
+		(basicConstraint.isCA)) {
+		nsCertType |= NS_CERT_TYPE_SSL_CA;
+	    } else {
+		nsCertType |= NS_CERT_TYPE_SSL_CLIENT;
+	    }
+	}
+	if (findOIDinOIDSeqByTagNum(extKeyUsage,
+				    SEC_OID_EXT_KEY_USAGE_CODE_SIGN) ==
+	    SECSuccess) {
+	    if (basicConstraintPresent == PR_TRUE &&
+		(basicConstraint.isCA)) {
+		nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
+	    } else {
+		nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING;
+	    }
+	}
+	if (findOIDinOIDSeqByTagNum(extKeyUsage,
+				    SEC_OID_EXT_KEY_USAGE_TIME_STAMP) ==
+	    SECSuccess) {
+	    nsCertType |= EXT_KEY_USAGE_TIME_STAMP;
+	}
+	if (findOIDinOIDSeqByTagNum(extKeyUsage,
+				    SEC_OID_OCSP_RESPONDER) == 
+	    SECSuccess) {
+	    nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
+	}
+    } else {
+	/* If no NS Cert Type extension and no EKU extension, then */
+	nsCertType = 0;
+	if (CERT_IsCACert(cert, &nsCertType))
+	    nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
+	/* if the basic constraint extension says the cert is a CA, then
+	   allow SSL CA and EMAIL CA and Status Responder */
+	if (basicConstraintPresent && basicConstraint.isCA ) {
+	    nsCertType |= (NS_CERT_TYPE_SSL_CA   |
+		           NS_CERT_TYPE_EMAIL_CA |
+		           EXT_KEY_USAGE_STATUS_RESPONDER);
+	}
+	/* allow any ssl or email (no ca or object signing. */
+	nsCertType |= NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER |
+	              NS_CERT_TYPE_EMAIL;
+    }
+
+    if (encodedExtKeyUsage.data != NULL) {
+	PORT_Free(encodedExtKeyUsage.data);
+    }
+    if (extKeyUsage != NULL) {
+	CERT_DestroyOidSequence(extKeyUsage);
+    }
+    return nsCertType;
+}
+
+/*
+ * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate
+ */
+SECStatus
+cert_GetKeyID(CERTCertificate *cert)
+{
+    SECItem tmpitem;
+    SECStatus rv;
+    
+    cert->subjectKeyID.len = 0;
+
+    /* see of the cert has a key identifier extension */
+    rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
+    if ( rv == SECSuccess ) {
+	cert->subjectKeyID.data = (unsigned char*) PORT_ArenaAlloc(cert->arena, tmpitem.len);
+	if ( cert->subjectKeyID.data != NULL ) {
+	    PORT_Memcpy(cert->subjectKeyID.data, tmpitem.data, tmpitem.len);
+	    cert->subjectKeyID.len = tmpitem.len;
+	    cert->keyIDGenerated = PR_FALSE;
+	}
+	
+	PORT_Free(tmpitem.data);
+    }
+    
+    /* if the cert doesn't have a key identifier extension, then generate one*/
+    if ( cert->subjectKeyID.len == 0 ) {
+	/*
+	 * pkix says that if the subjectKeyID is not present, then we should
+	 * use the SHA-1 hash of the DER-encoded publicKeyInfo from the cert
+	 */
+	cert->subjectKeyID.data = (unsigned char *)PORT_ArenaAlloc(cert->arena, SHA1_LENGTH);
+	if ( cert->subjectKeyID.data != NULL ) {
+	    rv = PK11_HashBuf(SEC_OID_SHA1,cert->subjectKeyID.data,
+			      cert->derPublicKey.data,
+			      cert->derPublicKey.len);
+	    if ( rv == SECSuccess ) {
+		cert->subjectKeyID.len = SHA1_LENGTH;
+	    }
+	}
+    }
+
+    if ( cert->subjectKeyID.len == 0 ) {
+	return(SECFailure);
+    }
+    return(SECSuccess);
+
+}
+
+static PRBool
+cert_IsRootCert(CERTCertificate *cert)
+{
+    SECStatus rv;
+    SECItem tmpitem;
+
+    /* cache the authKeyID extension, if present */
+    cert->authKeyID = CERT_FindAuthKeyIDExten(cert->arena, cert);
+
+    /* it MUST be self-issued to be a root */
+    if (cert->derIssuer.len == 0 ||
+        !SECITEM_ItemsAreEqual(&cert->derIssuer, &cert->derSubject))
+    {
+	return PR_FALSE;
+    }
+
+    /* check the authKeyID extension */
+    if (cert->authKeyID) {
+	/* authority key identifier is present */
+	if (cert->authKeyID->keyID.len > 0) {
+	    /* the keyIdentifier field is set, look for subjectKeyID */
+	    rv = CERT_FindSubjectKeyIDExtension(cert, &tmpitem);
+	    if (rv == SECSuccess) {
+		PRBool match;
+		/* also present, they MUST match for it to be a root */
+		match = SECITEM_ItemsAreEqual(&cert->authKeyID->keyID,
+		                              &tmpitem);
+		PORT_Free(tmpitem.data);
+		if (!match) return PR_FALSE; /* else fall through */
+	    } else {
+		/* the subject key ID is required when AKI is present */
+		return PR_FALSE;
+	    }
+	}
+	if (cert->authKeyID->authCertIssuer) {
+	    SECItem *caName;
+	    caName = (SECItem *)CERT_GetGeneralNameByType(
+	                                  cert->authKeyID->authCertIssuer,
+	                                  certDirectoryName, PR_TRUE);
+	    if (caName) {
+		if (!SECITEM_ItemsAreEqual(&cert->derIssuer, caName)) {
+		    return PR_FALSE;
+		} /* else fall through */
+	    } /* else ??? could not get general name as directory name? */
+	}
+	if (cert->authKeyID->authCertSerialNumber.len > 0) {
+	    if (!SECITEM_ItemsAreEqual(&cert->serialNumber,
+	                         &cert->authKeyID->authCertSerialNumber)) {
+		return PR_FALSE;
+	    } /* else fall through */
+	}
+	/* all of the AKI fields that were present passed the test */
+	return PR_TRUE;
+    }
+    /* else the AKI was not present, so this is a root */
+    return PR_TRUE;
+}
+
+/*
+ * take a DER certificate and decode it into a certificate structure
+ */
+CERTCertificate *
+CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
+			 char *nickname)
+{
+    CERTCertificate *cert;
+    PLArenaPool *arena;
+    void *data;
+    int rv;
+    int len;
+    char *tmpname;
+    
+    /* make a new arena */
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    
+    if ( !arena ) {
+	return 0;
+    }
+
+    /* allocate the certificate structure */
+    cert = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate));
+    
+    if ( !cert ) {
+	goto loser;
+    }
+    
+    cert->arena = arena;
+    
+    if ( copyDER ) {
+	/* copy the DER data for the cert into this arena */
+	data = (void *)PORT_ArenaAlloc(arena, derSignedCert->len);
+	if ( !data ) {
+	    goto loser;
+	}
+	cert->derCert.data = (unsigned char *)data;
+	cert->derCert.len = derSignedCert->len;
+	PORT_Memcpy(data, derSignedCert->data, derSignedCert->len);
+    } else {
+	/* point to passed in DER data */
+	cert->derCert = *derSignedCert;
+    }
+
+    /* decode the certificate info */
+    rv = SEC_QuickDERDecodeItem(arena, cert, SEC_SignedCertificateTemplate,
+		    &cert->derCert);
+
+    if ( rv ) {
+	goto loser;
+    }
+
+    if (cert_HasUnknownCriticalExten (cert->extensions) == PR_TRUE) {
+        cert->options.bits.hasUnsupportedCriticalExt = PR_TRUE;
+    }
+
+    /* generate and save the database key for the cert */
+    rv = CERT_KeyFromIssuerAndSN(arena, &cert->derIssuer, &cert->serialNumber,
+			&cert->certKey);
+    if ( rv ) {
+	goto loser;
+    }
+
+    /* set the nickname */
+    if ( nickname == NULL ) {
+	cert->nickname = NULL;
+    } else {
+	/* copy and install the nickname */
+	len = PORT_Strlen(nickname) + 1;
+	cert->nickname = (char*)PORT_ArenaAlloc(arena, len);
+	if ( cert->nickname == NULL ) {
+	    goto loser;
+	}
+
+	PORT_Memcpy(cert->nickname, nickname, len);
+    }
+
+    /* set the email address */
+    cert->emailAddr = cert_GetCertificateEmailAddresses(cert);
+    
+    /* initialize the subjectKeyID */
+    rv = cert_GetKeyID(cert);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    /* initialize keyUsage */
+    rv = GetKeyUsage(cert);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    /* determine if this is a root cert */
+    cert->isRoot = cert_IsRootCert(cert);
+
+    /* initialize the certType */
+    rv = cert_GetCertType(cert);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    tmpname = CERT_NameToAscii(&cert->subject);
+    if ( tmpname != NULL ) {
+	cert->subjectName = PORT_ArenaStrdup(cert->arena, tmpname);
+	PORT_Free(tmpname);
+    }
+    
+    tmpname = CERT_NameToAscii(&cert->issuer);
+    if ( tmpname != NULL ) {
+	cert->issuerName = PORT_ArenaStrdup(cert->arena, tmpname);
+	PORT_Free(tmpname);
+    }
+    
+    cert->referenceCount = 1;
+    cert->slot = NULL;
+    cert->pkcs11ID = CK_INVALID_HANDLE;
+    cert->dbnickname = NULL;
+    
+    return(cert);
+    
+loser:
+
+    if ( arena ) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+    
+    return(0);
+}
+
+CERTCertificate *
+__CERT_DecodeDERCertificate(SECItem *derSignedCert, PRBool copyDER,
+			 char *nickname)
+{
+    return CERT_DecodeDERCertificate(derSignedCert, copyDER, nickname);
+}
+
+
+CERTValidity *
+CERT_CreateValidity(PRTime notBefore, PRTime notAfter)
+{
+    CERTValidity *v;
+    int rv;
+    PLArenaPool *arena;
+
+    if (notBefore > notAfter) {
+       PORT_SetError(SEC_ERROR_INVALID_ARGS);
+       return NULL;
+    }
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    
+    if ( !arena ) {
+	return(0);
+    }
+    
+    v = (CERTValidity*) PORT_ArenaZAlloc(arena, sizeof(CERTValidity));
+    if (v) {
+	v->arena = arena;
+	rv = DER_EncodeTimeChoice(arena, &v->notBefore, notBefore);
+	if (rv) goto loser;
+	rv = DER_EncodeTimeChoice(arena, &v->notAfter, notAfter);
+	if (rv) goto loser;
+    }
+    return v;
+
+  loser:
+    CERT_DestroyValidity(v);
+    return 0;
+}
+
+SECStatus
+CERT_CopyValidity(PLArenaPool *arena, CERTValidity *to, CERTValidity *from)
+{
+    SECStatus rv;
+
+    CERT_DestroyValidity(to);
+    to->arena = arena;
+    
+    rv = SECITEM_CopyItem(arena, &to->notBefore, &from->notBefore);
+    if (rv) return rv;
+    rv = SECITEM_CopyItem(arena, &to->notAfter, &from->notAfter);
+    return rv;
+}
+
+void
+CERT_DestroyValidity(CERTValidity *v)
+{
+    if (v && v->arena) {
+	PORT_FreeArena(v->arena, PR_FALSE);
+    }
+    return;
+}
+
+/*
+** Amount of time that a certifiate is allowed good before it is actually
+** good. This is used for pending certificates, ones that are about to be
+** valid. The slop is designed to allow for some variance in the clocks
+** of the machine checking the certificate.
+*/
+#define PENDING_SLOP (24L*60L*60L)		/* seconds per day */
+static PRInt32 pendingSlop = PENDING_SLOP;	/* seconds */
+
+PRInt32
+CERT_GetSlopTime(void)
+{
+    return pendingSlop;			/* seconds */
+}
+
+SECStatus
+CERT_SetSlopTime(PRInt32 slop)		/* seconds */
+{
+    if (slop < 0)
+	return SECFailure;
+    pendingSlop = slop;
+    return SECSuccess;
+}
+
+SECStatus
+CERT_GetCertTimes(const CERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
+{
+    SECStatus rv;
+
+    if (!c || !notBefore || !notAfter) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    
+    /* convert DER not-before time */
+    rv = DER_DecodeTimeChoice(notBefore, &c->validity.notBefore);
+    if (rv) {
+	return(SECFailure);
+    }
+    
+    /* convert DER not-after time */
+    rv = DER_DecodeTimeChoice(notAfter, &c->validity.notAfter);
+    if (rv) {
+	return(SECFailure);
+    }
+
+    return(SECSuccess);
+}
+
+/*
+ * Check the validity times of a certificate
+ */
+SECCertTimeValidity
+CERT_CheckCertValidTimes(const CERTCertificate *c, PRTime t,
+                         PRBool allowOverride)
+{
+    PRTime notBefore, notAfter, llPendingSlop, tmp1;
+    SECStatus rv;
+
+    if (!c) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return(secCertTimeUndetermined);
+    }
+    /* if cert is already marked OK, then don't bother to check */
+    if ( allowOverride && c->timeOK ) {
+	return(secCertTimeValid);
+    }
+
+    rv = CERT_GetCertTimes(c, &notBefore, &notAfter);
+    
+    if (rv) {
+	return(secCertTimeExpired); /*XXX is this the right thing to do here?*/
+    }
+    
+    LL_I2L(llPendingSlop, pendingSlop);
+    /* convert to micro seconds */
+    LL_UI2L(tmp1, PR_USEC_PER_SEC);
+    LL_MUL(llPendingSlop, llPendingSlop, tmp1);
+    LL_SUB(notBefore, notBefore, llPendingSlop);
+    if ( LL_CMP( t, <, notBefore ) ) {
+	PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
+	return(secCertTimeNotValidYet);
+    }
+    if ( LL_CMP( t, >, notAfter) ) {
+	PORT_SetError(SEC_ERROR_EXPIRED_CERTIFICATE);
+	return(secCertTimeExpired);
+    }
+
+    return(secCertTimeValid);
+}
+
+SECStatus
+SEC_GetCrlTimes(CERTCrl *date, PRTime *notBefore, PRTime *notAfter)
+{
+    int rv;
+    
+    /* convert DER not-before time */
+    rv = DER_DecodeTimeChoice(notBefore, &date->lastUpdate);
+    if (rv) {
+	return(SECFailure);
+    }
+    
+    /* convert DER not-after time */
+    if (date->nextUpdate.data) {
+	rv = DER_DecodeTimeChoice(notAfter, &date->nextUpdate);
+	if (rv) {
+	    return(SECFailure);
+	}
+    }
+    else {
+	LL_I2L(*notAfter, 0L);
+    }
+    return(SECSuccess);
+}
+
+/* These routines should probably be combined with the cert
+ * routines using an common extraction routine.
+ */
+SECCertTimeValidity
+SEC_CheckCrlTimes(CERTCrl *crl, PRTime t) {
+    PRTime notBefore, notAfter, llPendingSlop, tmp1;
+    SECStatus rv;
+
+    rv = SEC_GetCrlTimes(crl, &notBefore, &notAfter);
+    
+    if (rv) {
+	return(secCertTimeExpired); 
+    }
+
+    LL_I2L(llPendingSlop, pendingSlop);
+    /* convert to micro seconds */
+    LL_I2L(tmp1, PR_USEC_PER_SEC);
+    LL_MUL(llPendingSlop, llPendingSlop, tmp1);
+    LL_SUB(notBefore, notBefore, llPendingSlop);
+    if ( LL_CMP( t, <, notBefore ) ) {
+	return(secCertTimeNotValidYet);
+    }
+
+    /* If next update is omitted and the test for notBefore passes, then
+       we assume that the crl is up to date.
+     */
+    if ( LL_IS_ZERO(notAfter) ) {
+	return(secCertTimeValid);
+    }
+
+    if ( LL_CMP( t, >, notAfter) ) {
+	return(secCertTimeExpired);
+    }
+
+    return(secCertTimeValid);
+}
+
+PRBool
+SEC_CrlIsNewer(CERTCrl *inNew, CERTCrl *old) {
+    PRTime newNotBefore, newNotAfter;
+    PRTime oldNotBefore, oldNotAfter;
+    SECStatus rv;
+
+    /* problems with the new CRL? reject it */
+    rv = SEC_GetCrlTimes(inNew, &newNotBefore, &newNotAfter);
+    if (rv) return PR_FALSE;
+
+    /* problems with the old CRL? replace it */
+    rv = SEC_GetCrlTimes(old, &oldNotBefore, &oldNotAfter);
+    if (rv) return PR_TRUE;
+
+    /* Question: what about the notAfter's? */
+    return ((PRBool)LL_CMP(oldNotBefore, <, newNotBefore));
+}
+   
+/*
+ * return required key usage and cert type based on cert usage 
+ */
+SECStatus
+CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage,
+				 PRBool ca,
+				 unsigned int *retKeyUsage,
+				 unsigned int *retCertType)
+{
+    unsigned int requiredKeyUsage = 0;
+    unsigned int requiredCertType = 0;
+    
+    if ( ca ) {
+	switch ( usage ) {
+	  case certUsageSSLServerWithStepUp:
+	    requiredKeyUsage = KU_NS_GOVT_APPROVED | KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_SSL_CA;
+	    break;
+	  case certUsageSSLClient:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_SSL_CA;
+	    break;
+	  case certUsageSSLServer:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_SSL_CA;
+	    break;
+	  case certUsageSSLCA:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_SSL_CA;
+	    break;
+	  case certUsageEmailSigner:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_EMAIL_CA;
+	    break;
+	  case certUsageEmailRecipient:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_EMAIL_CA;
+	    break;
+	  case certUsageObjectSigner:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA;
+	    break;
+	  case certUsageAnyCA:
+	  case certUsageVerifyCA:
+	  case certUsageStatusResponder:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING_CA |
+		NS_CERT_TYPE_EMAIL_CA |
+		    NS_CERT_TYPE_SSL_CA;
+	    break;
+	  default:
+	    PORT_Assert(0);
+	    goto loser;
+	}
+    } else {
+	switch ( usage ) {
+	  case certUsageSSLClient:
+	    /* 
+	     * RFC 5280 lists digitalSignature and keyAgreement for
+	     * id-kp-clientAuth.  NSS does not support the *_fixed_dh and
+	     * *_fixed_ecdh client certificate types.
+	     */
+	    requiredKeyUsage = KU_DIGITAL_SIGNATURE;
+	    requiredCertType = NS_CERT_TYPE_SSL_CLIENT;
+	    break;
+	  case certUsageSSLServer:
+	    requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
+	    requiredCertType = NS_CERT_TYPE_SSL_SERVER;
+	    break;
+	  case certUsageSSLServerWithStepUp:
+	    requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT |
+		KU_NS_GOVT_APPROVED;
+	    requiredCertType = NS_CERT_TYPE_SSL_SERVER;
+	    break;
+	  case certUsageSSLCA:
+	    requiredKeyUsage = KU_KEY_CERT_SIGN;
+	    requiredCertType = NS_CERT_TYPE_SSL_CA;
+	    break;
+	  case certUsageEmailSigner:
+	    requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
+	    requiredCertType = NS_CERT_TYPE_EMAIL;
+	    break;
+	  case certUsageEmailRecipient:
+	    requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT;
+	    requiredCertType = NS_CERT_TYPE_EMAIL;
+	    break;
+	  case certUsageObjectSigner:
+	    /* RFC 5280 lists only digitalSignature for id-kp-codeSigning. */
+	    requiredKeyUsage = KU_DIGITAL_SIGNATURE;
+	    requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING;
+	    break;
+	  case certUsageStatusResponder:
+	    requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION;
+	    requiredCertType = EXT_KEY_USAGE_STATUS_RESPONDER;
+	    break;
+	  default:
+	    PORT_Assert(0);
+	    goto loser;
+	}
+    }
+
+    if ( retKeyUsage != NULL ) {
+	*retKeyUsage = requiredKeyUsage;
+    }
+    if ( retCertType != NULL ) {
+	*retCertType = requiredCertType;
+    }
+
+    return(SECSuccess);
+loser:
+    return(SECFailure);
+}
+
+/*
+ * check the key usage of a cert against a set of required values
+ */
+SECStatus
+CERT_CheckKeyUsage(CERTCertificate *cert, unsigned int requiredUsage)
+{
+    if (!cert) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+    /* choose between key agreement or key encipherment based on key
+     * type in cert
+     */
+    if ( requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT ) {
+	KeyType keyType = CERT_GetCertKeyType(&cert->subjectPublicKeyInfo);
+	/* turn off the special bit */
+	requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT);
+
+	switch (keyType) {
+	case rsaKey:
+	    requiredUsage |= KU_KEY_ENCIPHERMENT;
+	    break;
+	case dsaKey:
+	    requiredUsage |= KU_DIGITAL_SIGNATURE;
+	    break;
+	case dhKey:
+	    requiredUsage |= KU_KEY_AGREEMENT;
+	    break;
+	case ecKey:
+	    /* Accept either signature or agreement. */
+	    if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_KEY_AGREEMENT)))
+		 goto loser;
+	    break;
+	default:
+	    goto loser;
+	}
+    }
+
+    /* Allow either digital signature or non-repudiation */
+    if ( requiredUsage & KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION ) {
+	/* turn off the special bit */
+	requiredUsage &= (~KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION);
+
+        if (!(cert->keyUsage & (KU_DIGITAL_SIGNATURE | KU_NON_REPUDIATION)))
+             goto loser;
+     }
+    
+    if ( (cert->keyUsage & requiredUsage) == requiredUsage ) 
+    	return SECSuccess;
+
+loser:
+    PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
+    return SECFailure;
+}
+
+
+CERTCertificate *
+CERT_DupCertificate(CERTCertificate *c)
+{
+    if (c) {
+	NSSCertificate *tmp = STAN_GetNSSCertificate(c);
+	nssCertificate_AddRef(tmp);
+    }
+    return c;
+}
+
+/*
+ * Allow use of default cert database, so that apps(such as mozilla) don't
+ * have to pass the handle all over the place.
+ */
+static CERTCertDBHandle *default_cert_db_handle = 0;
+
+void
+CERT_SetDefaultCertDB(CERTCertDBHandle *handle)
+{
+    default_cert_db_handle = handle;
+    
+    return;
+}
+
+CERTCertDBHandle *
+CERT_GetDefaultCertDB(void)
+{
+    return(default_cert_db_handle);
+}
+
+/* XXX this would probably be okay/better as an xp routine? */
+static void
+sec_lower_string(char *s)
+{
+    if ( s == NULL ) {
+	return;
+    }
+    
+    while ( *s ) {
+	*s = PORT_Tolower(*s);
+	s++;
+    }
+    
+    return;
+}
+
+static PRBool
+cert_IsIPAddr(const char *hn)
+{
+    PRBool            isIPaddr       = PR_FALSE;
+    PRNetAddr         netAddr;
+    isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
+    return isIPaddr;
+}
+
+/*
+** Add a domain name to the list of names that the user has explicitly
+** allowed (despite cert name mismatches) for use with a server cert.
+*/
+SECStatus
+CERT_AddOKDomainName(CERTCertificate *cert, const char *hn)
+{
+    CERTOKDomainName *domainOK;
+    int	       newNameLen;
+
+    if (!hn || !(newNameLen = strlen(hn))) {
+    	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+    domainOK = (CERTOKDomainName *)PORT_ArenaZAlloc(cert->arena, 
+				  (sizeof *domainOK) + newNameLen);
+    if (!domainOK) 
+    	return SECFailure;	/* error code is already set. */
+
+    PORT_Strcpy(domainOK->name, hn);
+    sec_lower_string(domainOK->name);
+
+    /* put at head of list. */
+    domainOK->next = cert->domainOK;
+    cert->domainOK = domainOK;
+    return SECSuccess;
+}
+
+/* returns SECSuccess if hn matches pattern cn,
+** returns SECFailure with SSL_ERROR_BAD_CERT_DOMAIN if no match,
+** returns SECFailure with some other error code if another error occurs.
+**
+** This function may modify string cn, so caller must pass a modifiable copy.
+*/
+static SECStatus
+cert_TestHostName(char * cn, const char * hn)
+{
+    static int useShellExp = -1;
+
+    if (useShellExp < 0) {
+        useShellExp = (NULL != PR_GetEnv("NSS_USE_SHEXP_IN_CERT_NAME"));
+    }
+    if (useShellExp) {
+    	/* Backward compatible code, uses Shell Expressions (SHEXP). */
+	int regvalid = PORT_RegExpValid(cn);
+	if (regvalid != NON_SXP) {
+	    SECStatus rv;
+	    /* cn is a regular expression, try to match the shexp */
+	    int match = PORT_RegExpCaseSearch(hn, cn);
+
+	    if ( match == 0 ) {
+		rv = SECSuccess;
+	    } else {
+		PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+		rv = SECFailure;
+	    }
+	    return rv;
+	}
+    } else {
+	/* New approach conforms to RFC 6125. */
+	char *wildcard    = PORT_Strchr(cn, '*');
+	char *firstcndot  = PORT_Strchr(cn, '.');
+	char *secondcndot = firstcndot ? PORT_Strchr(firstcndot+1, '.') : NULL;
+	char *firsthndot  = PORT_Strchr(hn, '.');
+
+	/* For a cn pattern to be considered valid, the wildcard character...
+	 * - may occur only in a DNS name with at least 3 components, and
+	 * - may occur only as last character in the first component, and
+	 * - may be preceded by additional characters, and
+	 * - must not be preceded by an IDNA ACE prefix (xn--)
+	 */
+	if (wildcard && secondcndot && secondcndot[1] && firsthndot 
+	    && firstcndot  - wildcard  == 1 /* wildcard is last char in first component */
+	    && secondcndot - firstcndot > 1 /* second component is non-empty */
+	    && PORT_Strrchr(cn, '*') == wildcard /* only one wildcard in cn */
+	    && !PORT_Strncasecmp(cn, hn, wildcard - cn)
+	    && !PORT_Strcasecmp(firstcndot, firsthndot)
+	       /* If hn starts with xn--, then cn must start with wildcard */
+	    && (PORT_Strncasecmp(hn, "xn--", 4) || wildcard == cn)) {
+	    /* valid wildcard pattern match */
+	    return SECSuccess;
+	}
+    }
+    /* String cn has no wildcard or shell expression.  
+     * Compare entire string hn with cert name. 
+     */
+    if (PORT_Strcasecmp(hn, cn) == 0) {
+	return SECSuccess;
+    }
+
+    PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+    return SECFailure;
+}
+
+
+SECStatus
+cert_VerifySubjectAltName(const CERTCertificate *cert, const char *hn)
+{
+    PLArenaPool *     arena          = NULL;
+    CERTGeneralName * nameList       = NULL;
+    CERTGeneralName * current;
+    char *            cn;
+    int               cnBufLen;
+    unsigned int      hnLen;
+    int               DNSextCount    = 0;
+    int               IPextCount     = 0;
+    PRBool            isIPaddr       = PR_FALSE;
+    SECStatus         rv             = SECFailure;
+    SECItem           subAltName;
+    PRNetAddr         netAddr;
+    char              cnbuf[128];
+
+    subAltName.data = NULL;
+    hnLen    = strlen(hn);
+    cn       = cnbuf;
+    cnBufLen = sizeof cnbuf;
+
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, 
+				&subAltName);
+    if (rv != SECSuccess) {
+	goto fail;
+    }
+    isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr));
+    rv = SECFailure;
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena) 
+	goto fail;
+
+    nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
+    if (!current)
+    	goto fail;
+
+    do {
+	switch (current->type) {
+	case certDNSName:
+	    if (!isIPaddr) {
+		/* DNS name current->name.other.data is not null terminated.
+		** so must copy it.  
+		*/
+		int cnLen = current->name.other.len;
+		rv = CERT_RFC1485_EscapeAndQuote(cn, cnBufLen, 
+					    (char *)current->name.other.data,
+					    cnLen);
+		if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_OUTPUT_LEN) {
+		    cnBufLen = cnLen * 3 + 3; /* big enough for worst case */
+		    cn = (char *)PORT_ArenaAlloc(arena, cnBufLen);
+		    if (!cn)
+			goto fail;
+		    rv = CERT_RFC1485_EscapeAndQuote(cn, cnBufLen, 
+					    (char *)current->name.other.data,
+					    cnLen);
+		}
+		if (rv == SECSuccess)
+		    rv = cert_TestHostName(cn ,hn);
+		if (rv == SECSuccess)
+		    goto finish;
+	    }
+	    DNSextCount++;
+	    break;
+	case certIPAddress:
+	    if (isIPaddr) {
+		int match = 0;
+		PRIPv6Addr v6Addr;
+		if (current->name.other.len == 4 &&         /* IP v4 address */
+		    netAddr.inet.family == PR_AF_INET) {
+		    match = !memcmp(&netAddr.inet.ip, 
+		                    current->name.other.data, 4);
+		} else if (current->name.other.len == 16 && /* IP v6 address */
+		    netAddr.ipv6.family == PR_AF_INET6) {
+		    match = !memcmp(&netAddr.ipv6.ip,
+		                     current->name.other.data, 16);
+		} else if (current->name.other.len == 16 && /* IP v6 address */
+		    netAddr.inet.family == PR_AF_INET) {
+		    /* convert netAddr to ipv6, then compare. */
+		    /* ipv4 must be in Network Byte Order on input. */
+		    PR_ConvertIPv4AddrToIPv6(netAddr.inet.ip, &v6Addr);
+		    match = !memcmp(&v6Addr, current->name.other.data, 16);
+		} else if (current->name.other.len == 4 &&  /* IP v4 address */
+		    netAddr.inet.family == PR_AF_INET6) {
+		    /* convert netAddr to ipv6, then compare. */
+		    PRUint32 ipv4 = (current->name.other.data[0] << 24) |
+		                    (current->name.other.data[1] << 16) |
+				    (current->name.other.data[2] <<  8) |
+				     current->name.other.data[3];
+		    /* ipv4 must be in Network Byte Order on input. */
+		    PR_ConvertIPv4AddrToIPv6(PR_htonl(ipv4), &v6Addr);
+		    match = !memcmp(&netAddr.ipv6.ip, &v6Addr, 16);
+		} 
+		if (match) {
+		    rv = SECSuccess;
+		    goto finish;
+		}
+	    }
+	    IPextCount++;
+	    break;
+	default:
+	    break;
+	}
+	current = CERT_GetNextGeneralName(current);
+    } while (current != nameList);
+
+fail:
+
+    if (!(isIPaddr ? IPextCount : DNSextCount)) {
+	/* no relevant value in the extension was found. */
+	PORT_SetError(SEC_ERROR_EXTENSION_NOT_FOUND);
+    } else {
+	PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+    }
+    rv = SECFailure;
+
+finish:
+
+    /* Don't free nameList, it's part of the arena. */
+    if (arena) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+
+    if (subAltName.data) {
+	SECITEM_FreeItem(&subAltName, PR_FALSE);
+    }
+
+    return rv;
+}
+
+/*
+ * If found:
+ *   - subAltName contains the extension (caller must free)
+ *   - return value is the decoded namelist (allocated off arena)
+ * if not found, or if failure to decode:
+ *   - return value is NULL
+ */
+CERTGeneralName *
+cert_GetSubjectAltNameList(const CERTCertificate *cert, PLArenaPool *arena)
+{
+    CERTGeneralName * nameList       = NULL;
+    SECStatus         rv             = SECFailure;
+    SECItem           subAltName;
+
+    if (!cert || !arena)
+      return NULL;
+
+    subAltName.data = NULL;
+
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, 
+                                &subAltName);
+    if (rv != SECSuccess)
+      return NULL;
+
+    nameList = CERT_DecodeAltNameExtension(arena, &subAltName);
+    SECITEM_FreeItem(&subAltName, PR_FALSE);
+    return nameList;
+}
+
+PRUint32
+cert_CountDNSPatterns(CERTGeneralName *firstName)
+{
+    CERTGeneralName * current;
+    PRUint32 count = 0;
+
+    if (!firstName)
+      return 0;
+
+    current = firstName;
+    do {
+        switch (current->type) {
+        case certDNSName:
+        case certIPAddress:
+            ++count;
+            break;
+        default:
+            break;
+        }
+        current = CERT_GetNextGeneralName(current);
+    } while (current != firstName);
+
+    return count;
+}
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/* will fill nickNames, 
+ * will allocate all data from nickNames->arena,
+ * numberOfGeneralNames should have been obtained from cert_CountDNSPatterns,
+ * will ensure the numberOfGeneralNames matches the number of output entries.
+ */
+SECStatus
+cert_GetDNSPatternsFromGeneralNames(CERTGeneralName *firstName,
+                                    PRUint32 numberOfGeneralNames, 
+                                    CERTCertNicknames *nickNames)
+{
+    CERTGeneralName *currentInput;
+    char **currentOutput;
+
+    if (!firstName || !nickNames || !numberOfGeneralNames)
+      return SECFailure;
+
+    nickNames->numnicknames = numberOfGeneralNames;
+    nickNames->nicknames = PORT_ArenaAlloc(nickNames->arena,
+                                       sizeof(char *) * numberOfGeneralNames);
+    if (!nickNames->nicknames)
+      return SECFailure;
+
+    currentInput = firstName;
+    currentOutput = nickNames->nicknames;
+    do {
+        char *cn = NULL;
+        char ipbuf[INET6_ADDRSTRLEN];
+        PRNetAddr addr;
+
+        if (numberOfGeneralNames < 1) {
+          /* internal consistency error */
+          return SECFailure;
+        }
+
+        switch (currentInput->type) {
+        case certDNSName:
+            /* DNS name currentInput->name.other.data is not null terminated.
+            ** so must copy it.  
+            */
+            cn = (char *)PORT_ArenaAlloc(nickNames->arena, 
+                                         currentInput->name.other.len + 1);
+            if (!cn)
+              return SECFailure;
+            PORT_Memcpy(cn, currentInput->name.other.data, 
+                            currentInput->name.other.len);
+            cn[currentInput->name.other.len] = 0;
+            break;
+        case certIPAddress:
+            if (currentInput->name.other.len == 4) {
+              addr.inet.family = PR_AF_INET;
+              memcpy(&addr.inet.ip, currentInput->name.other.data, 
+                                    currentInput->name.other.len);
+            } else if (currentInput->name.other.len == 16) {
+              addr.ipv6.family = PR_AF_INET6;
+              memcpy(&addr.ipv6.ip, currentInput->name.other.data, 
+                                    currentInput->name.other.len);
+            }
+            if (PR_NetAddrToString(&addr, ipbuf, sizeof(ipbuf)) == PR_FAILURE)
+              return SECFailure;
+            cn = PORT_ArenaStrdup(nickNames->arena, ipbuf);
+            if (!cn)
+              return SECFailure;
+            break;
+        default:
+            break;
+        }
+        if (cn) {
+          *currentOutput = cn;
+          nickNames->totallen += PORT_Strlen(cn);
+          ++currentOutput;
+          --numberOfGeneralNames;
+        }
+        currentInput = CERT_GetNextGeneralName(currentInput);
+    } while (currentInput != firstName);
+
+    return (numberOfGeneralNames == 0) ? SECSuccess : SECFailure;
+}
+
+/*
+ * Collect all valid DNS names from the given cert.
+ * The output arena will reference some temporaray data,
+ * but this saves us from dealing with two arenas.
+ * The caller may free all data by freeing CERTCertNicknames->arena.
+ */
+CERTCertNicknames *
+CERT_GetValidDNSPatternsFromCert(CERTCertificate *cert)
+{
+    CERTGeneralName *generalNames;
+    CERTCertNicknames *nickNames;
+    PLArenaPool *arena;
+    char *singleName;
+    
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena) {
+        return NULL;
+    }
+    
+    nickNames = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
+    if (!nickNames) {
+      PORT_FreeArena(arena, PR_FALSE);
+      return NULL;
+    }
+
+    /* init the structure */
+    nickNames->arena = arena;
+    nickNames->head = NULL;
+    nickNames->numnicknames = 0;
+    nickNames->nicknames = NULL;
+    nickNames->totallen = 0;
+
+    generalNames = cert_GetSubjectAltNameList(cert, arena);
+    if (generalNames) {
+      SECStatus rv_getnames = SECFailure; 
+      PRUint32 numNames = cert_CountDNSPatterns(generalNames);
+
+      if (numNames) {
+        rv_getnames = cert_GetDNSPatternsFromGeneralNames(generalNames, 
+                                                          numNames, nickNames);
+      }
+
+      /* if there were names, we'll exit now, either with success or failure */
+      if (numNames) {
+        if (rv_getnames == SECSuccess) {
+          return nickNames;
+        }
+
+        /* failure to produce output */
+        PORT_FreeArena(arena, PR_FALSE);
+        return NULL;
+      }
+    }
+
+    /* no SAN extension or no names found in extension */
+    singleName = CERT_GetCommonName(&cert->subject);
+    if (singleName) {
+      nickNames->numnicknames = 1;
+      nickNames->nicknames = PORT_ArenaAlloc(arena, sizeof(char *));
+      if (nickNames->nicknames) {
+        *nickNames->nicknames = PORT_ArenaStrdup(arena, singleName);
+      }
+      PORT_Free(singleName);
+
+      /* Did we allocate both the buffer of pointers and the string? */
+      if (nickNames->nicknames && *nickNames->nicknames) {
+        return nickNames;
+      }
+    }
+
+    PORT_FreeArena(arena, PR_FALSE);
+    return NULL;
+}
+
+/* Make sure that the name of the host we are connecting to matches the
+ * name that is incoded in the common-name component of the certificate
+ * that they are using.
+ */
+SECStatus
+CERT_VerifyCertName(const CERTCertificate *cert, const char *hn)
+{
+    char *    cn;
+    SECStatus rv;
+    CERTOKDomainName *domainOK;
+
+    if (!hn || !strlen(hn)) {
+    	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    /* if the name is one that the user has already approved, it's OK. */
+    for (domainOK = cert->domainOK; domainOK; domainOK = domainOK->next) {
+	if (0 == PORT_Strcasecmp(hn, domainOK->name)) {
+	    return SECSuccess;
+    	}
+    }
+
+    /* Per RFC 2818, if the SubjectAltName extension is present, it must
+    ** be used as the cert's identity.
+    */
+    rv = cert_VerifySubjectAltName(cert, hn);
+    if (rv == SECSuccess || PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND)
+    	return rv;
+
+    cn = CERT_GetCommonName(&cert->subject);
+    if ( cn ) {
+        PRBool isIPaddr = cert_IsIPAddr(hn);
+        if (isIPaddr) {
+            if (PORT_Strcasecmp(hn, cn) == 0) {
+                rv =  SECSuccess;
+            } else {
+                PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+                rv = SECFailure;
+            }
+        } else {
+            rv = cert_TestHostName(cn, hn);
+        }
+	PORT_Free(cn);
+    } else 
+	PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
+    return rv;
+}
+
+PRBool
+CERT_CompareCerts(const CERTCertificate *c1, const CERTCertificate *c2)
+{
+    SECComparison comp;
+    
+    comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
+    if ( comp == SECEqual ) { /* certs are the same */
+	return(PR_TRUE);
+    } else {
+	return(PR_FALSE);
+    }
+}
+
+static SECStatus
+StringsEqual(char *s1, char *s2) {
+    if ( ( s1 == NULL ) || ( s2 == NULL ) ) {
+	if ( s1 != s2 ) { /* only one is null */
+	    return(SECFailure);
+	}
+	return(SECSuccess); /* both are null */
+    }
+	
+    if ( PORT_Strcmp( s1, s2 ) != 0 ) {
+	return(SECFailure); /* not equal */
+    }
+
+    return(SECSuccess); /* strings are equal */
+}
+
+
+PRBool
+CERT_CompareCertsForRedirection(CERTCertificate *c1, CERTCertificate *c2)
+{
+    SECComparison comp;
+    char *c1str, *c2str;
+    SECStatus eq;
+    
+    comp = SECITEM_CompareItem(&c1->derCert, &c2->derCert);
+    if ( comp == SECEqual ) { /* certs are the same */
+	return(PR_TRUE);
+    }
+	
+    /* check if they are issued by the same CA */
+    comp = SECITEM_CompareItem(&c1->derIssuer, &c2->derIssuer);
+    if ( comp != SECEqual ) { /* different issuer */
+	return(PR_FALSE);
+    }
+
+    /* check country name */
+    c1str = CERT_GetCountryName(&c1->subject);
+    c2str = CERT_GetCountryName(&c2->subject);
+    eq = StringsEqual(c1str, c2str);
+    PORT_Free(c1str);
+    PORT_Free(c2str);
+    if ( eq != SECSuccess ) {
+	return(PR_FALSE);
+    }
+
+    /* check locality name */
+    c1str = CERT_GetLocalityName(&c1->subject);
+    c2str = CERT_GetLocalityName(&c2->subject);
+    eq = StringsEqual(c1str, c2str);
+    PORT_Free(c1str);
+    PORT_Free(c2str);
+    if ( eq != SECSuccess ) {
+	return(PR_FALSE);
+    }
+	
+    /* check state name */
+    c1str = CERT_GetStateName(&c1->subject);
+    c2str = CERT_GetStateName(&c2->subject);
+    eq = StringsEqual(c1str, c2str);
+    PORT_Free(c1str);
+    PORT_Free(c2str);
+    if ( eq != SECSuccess ) {
+	return(PR_FALSE);
+    }
+
+    /* check org name */
+    c1str = CERT_GetOrgName(&c1->subject);
+    c2str = CERT_GetOrgName(&c2->subject);
+    eq = StringsEqual(c1str, c2str);
+    PORT_Free(c1str);
+    PORT_Free(c2str);
+    if ( eq != SECSuccess ) {
+	return(PR_FALSE);
+    }
+
+#ifdef NOTDEF	
+    /* check orgUnit name */
+    /*
+     * We need to revisit this and decide which fields should be allowed to be
+     * different
+     */
+    c1str = CERT_GetOrgUnitName(&c1->subject);
+    c2str = CERT_GetOrgUnitName(&c2->subject);
+    eq = StringsEqual(c1str, c2str);
+    PORT_Free(c1str);
+    PORT_Free(c2str);
+    if ( eq != SECSuccess ) {
+	return(PR_FALSE);
+    }
+#endif
+
+    return(PR_TRUE); /* all fields but common name are the same */
+}
+
+
+/* CERT_CertChainFromCert and CERT_DestroyCertificateList moved
+   to certhigh.c */
+
+
+CERTIssuerAndSN *
+CERT_GetCertIssuerAndSN(PLArenaPool *arena, CERTCertificate *cert)
+{
+    CERTIssuerAndSN *result;
+    SECStatus rv;
+
+    if ( arena == NULL ) {
+	arena = cert->arena;
+    }
+    
+    result = (CERTIssuerAndSN*)PORT_ArenaZAlloc(arena, sizeof(*result));
+    if (result == NULL) {
+	PORT_SetError (SEC_ERROR_NO_MEMORY);
+	return NULL;
+    }
+
+    rv = SECITEM_CopyItem(arena, &result->derIssuer, &cert->derIssuer);
+    if (rv != SECSuccess)
+	return NULL;
+
+    rv = CERT_CopyName(arena, &result->issuer, &cert->issuer);
+    if (rv != SECSuccess)
+	return NULL;
+
+    rv = SECITEM_CopyItem(arena, &result->serialNumber, &cert->serialNumber);
+    if (rv != SECSuccess)
+	return NULL;
+
+    return result;
+}
+
+char *
+CERT_MakeCANickname(CERTCertificate *cert)
+{
+    char *firstname = NULL;
+    char *org = NULL;
+    char *nickname = NULL;
+    int count;
+    CERTCertificate *dummycert;
+    
+    firstname = CERT_GetCommonName(&cert->subject);
+    if ( firstname == NULL ) {
+	firstname = CERT_GetOrgUnitName(&cert->subject);
+    }
+
+    org = CERT_GetOrgName(&cert->issuer);
+    if (org == NULL) {
+	org = CERT_GetDomainComponentName(&cert->issuer);
+	if (org == NULL) {
+	    if (firstname) {
+		org = firstname;
+		firstname = NULL;
+	    } else {
+		org = PORT_Strdup("Unknown CA");
+	    }
+	}
+    }
+
+    /* can only fail if PORT_Strdup fails, in which case
+     * we're having memory problems. */
+    if (org == NULL) {
+	goto done;
+    }
+
+    
+    count = 1;
+    while ( 1 ) {
+
+	if ( firstname ) {
+	    if ( count == 1 ) {
+		nickname = PR_smprintf("%s - %s", firstname, org);
+	    } else {
+		nickname = PR_smprintf("%s - %s #%d", firstname, org, count);
+	    }
+	} else {
+	    if ( count == 1 ) {
+		nickname = PR_smprintf("%s", org);
+	    } else {
+		nickname = PR_smprintf("%s #%d", org, count);
+	    }
+	}
+	if ( nickname == NULL ) {
+	    goto done;
+	}
+
+	/* look up the nickname to make sure it isn't in use already */
+	dummycert = CERT_FindCertByNickname(cert->dbhandle, nickname);
+
+	if ( dummycert == NULL ) {
+	    goto done;
+	}
+	
+	/* found a cert, destroy it and loop */
+	CERT_DestroyCertificate(dummycert);
+
+	/* free the nickname */
+	PORT_Free(nickname);
+
+	count++;
+    }
+
+done:
+    if ( firstname ) {
+	PORT_Free(firstname);
+    }
+    if ( org ) {
+	PORT_Free(org);
+    }
+    
+    return(nickname);
+}
+
+/* CERT_Import_CAChain moved to certhigh.c */
+
+void
+CERT_DestroyCrl (CERTSignedCrl *crl)
+{
+    SEC_DestroyCrl (crl);
+}
+
+static int
+cert_Version(CERTCertificate *cert)
+{
+    int version = 0;
+    if (cert && cert->version.data && cert->version.len) {
+	version = DER_GetInteger(&cert->version);
+	if (version < 0)
+	    version = 0;
+    }
+    return version;
+}
+
+static unsigned int
+cert_ComputeTrustOverrides(CERTCertificate *cert, unsigned int cType)
+{
+    CERTCertTrust trust;
+    SECStatus rv = SECFailure;
+
+    rv = CERT_GetCertTrust(cert, &trust);
+
+    if (rv == SECSuccess && (trust.sslFlags |
+		  trust.emailFlags |
+		  trust.objectSigningFlags)) {
+
+	if (trust.sslFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED)) 
+	    cType |= NS_CERT_TYPE_SSL_SERVER|NS_CERT_TYPE_SSL_CLIENT;
+	if (trust.sslFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA)) 
+	    cType |= NS_CERT_TYPE_SSL_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+	if (trust.sslFlags & CERTDB_NOT_TRUSTED) 
+	    cType &= ~(NS_CERT_TYPE_SSL_SERVER|NS_CERT_TYPE_SSL_CLIENT|
+	               NS_CERT_TYPE_SSL_CA);
+#endif
+	if (trust.emailFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED)) 
+	    cType |= NS_CERT_TYPE_EMAIL;
+	if (trust.emailFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA)) 
+	    cType |= NS_CERT_TYPE_EMAIL_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+	if (trust.emailFlags & CERTDB_NOT_TRUSTED) 
+	    cType &= ~(NS_CERT_TYPE_EMAIL|NS_CERT_TYPE_EMAIL_CA);
+#endif
+	if (trust.objectSigningFlags & (CERTDB_TERMINAL_RECORD|CERTDB_TRUSTED)) 
+	    cType |= NS_CERT_TYPE_OBJECT_SIGNING;
+	if (trust.objectSigningFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA)) 
+	    cType |= NS_CERT_TYPE_OBJECT_SIGNING_CA;
+#if defined(CERTDB_NOT_TRUSTED)
+	if (trust.objectSigningFlags & CERTDB_NOT_TRUSTED) 
+	    cType &= ~(NS_CERT_TYPE_OBJECT_SIGNING|
+	               NS_CERT_TYPE_OBJECT_SIGNING_CA);
+#endif
+    }
+    return cType;
+}
+
+/*
+ * Does a cert belong to a CA?  We decide based on perm database trust
+ * flags, Netscape Cert Type Extension, and KeyUsage Extension.
+ */
+PRBool
+CERT_IsCACert(CERTCertificate *cert, unsigned int *rettype)
+{
+    unsigned int cType = cert->nsCertType;
+    PRBool ret = PR_FALSE;
+
+    if (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | 
+                NS_CERT_TYPE_OBJECT_SIGNING_CA)) {
+        ret = PR_TRUE;
+    } else {
+	SECStatus rv;
+	CERTBasicConstraints constraints;
+
+	rv = CERT_FindBasicConstraintExten(cert, &constraints);
+	if (rv == SECSuccess && constraints.isCA) {
+	    ret = PR_TRUE;
+	    cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
+	} 
+    }
+
+    /* finally check if it's an X.509 v1 root CA */
+    if (!ret && 
+        (cert->isRoot && cert_Version(cert) < SEC_CERTIFICATE_VERSION_3)) {
+	ret = PR_TRUE;
+	cType |= (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA);
+    }
+    /* Now apply trust overrides, if any */
+    cType = cert_ComputeTrustOverrides(cert, cType);
+    ret = (cType & (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA |
+                    NS_CERT_TYPE_OBJECT_SIGNING_CA)) ? PR_TRUE : PR_FALSE;
+
+    if (rettype != NULL) {
+	*rettype = cType;
+    }
+    return ret;
+}
+
+PRBool
+CERT_IsCADERCert(SECItem *derCert, unsigned int *type) {
+    CERTCertificate *cert;
+    PRBool isCA;
+
+    /* This is okay -- only looks at extensions */
+    cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
+    if (cert == NULL) return PR_FALSE;
+
+    isCA = CERT_IsCACert(cert,type);
+    CERT_DestroyCertificate (cert);
+    return isCA;
+}
+
+PRBool
+CERT_IsRootDERCert(SECItem *derCert)
+{
+    CERTCertificate *cert;
+    PRBool isRoot;
+
+    /* This is okay -- only looks at extensions */
+    cert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
+    if (cert == NULL) return PR_FALSE;
+
+    isRoot = cert->isRoot;
+    CERT_DestroyCertificate (cert);
+    return isRoot;
+}
+
+CERTCompareValidityStatus
+CERT_CompareValidityTimes(CERTValidity* val_a, CERTValidity* val_b)
+{
+    PRTime notBeforeA, notBeforeB, notAfterA, notAfterB;
+
+    if (!val_a || !val_b)
+    {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return certValidityUndetermined;
+    }
+
+    if ( SECSuccess != DER_DecodeTimeChoice(&notBeforeA, &val_a->notBefore) ||
+         SECSuccess != DER_DecodeTimeChoice(&notBeforeB, &val_b->notBefore) ||
+         SECSuccess != DER_DecodeTimeChoice(&notAfterA, &val_a->notAfter) ||
+         SECSuccess != DER_DecodeTimeChoice(&notAfterB, &val_b->notAfter) ) {
+        return certValidityUndetermined;
+    }
+
+    /* sanity check */
+    if (LL_CMP(notBeforeA,>,notAfterA) || LL_CMP(notBeforeB,>,notAfterB)) {
+        PORT_SetError(SEC_ERROR_INVALID_TIME);
+        return certValidityUndetermined;
+    }
+
+    if (LL_CMP(notAfterA,!=,notAfterB)) {
+        /* one cert validity goes farther into the future, select it */
+        return LL_CMP(notAfterA,<,notAfterB) ?
+            certValidityChooseB : certValidityChooseA;
+    }
+    /* the two certs have the same expiration date */
+    PORT_Assert(LL_CMP(notAfterA, == , notAfterB));
+    /* do they also have the same start date ? */
+    if (LL_CMP(notBeforeA,==,notBeforeB)) {
+	return certValidityEqual;
+    }
+    /* choose cert with the later start date */
+    return LL_CMP(notBeforeA,<,notBeforeB) ?
+        certValidityChooseB : certValidityChooseA;
+}
+
+/*
+ * is certa newer than certb?  If one is expired, pick the other one.
+ */
+PRBool
+CERT_IsNewer(CERTCertificate *certa, CERTCertificate *certb)
+{
+    PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
+    SECStatus rv;
+    PRBool newerbefore, newerafter;
+    
+    rv = CERT_GetCertTimes(certa, &notBeforeA, &notAfterA);
+    if ( rv != SECSuccess ) {
+	return(PR_FALSE);
+    }
+    
+    rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
+    if ( rv != SECSuccess ) {
+	return(PR_TRUE);
+    }
+
+    newerbefore = PR_FALSE;
+    if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
+	newerbefore = PR_TRUE;
+    }
+
+    newerafter = PR_FALSE;
+    if ( LL_CMP(notAfterA, >, notAfterB) ) {
+	newerafter = PR_TRUE;
+    }
+    
+    if ( newerbefore && newerafter ) {
+	return(PR_TRUE);
+    }
+    
+    if ( ( !newerbefore ) && ( !newerafter ) ) {
+	return(PR_FALSE);
+    }
+
+    /* get current time */
+    now = PR_Now();
+
+    if ( newerbefore ) {
+	/* cert A was issued after cert B, but expires sooner */
+	/* if A is expired, then pick B */
+	if ( LL_CMP(notAfterA, <, now ) ) {
+	    return(PR_FALSE);
+	}
+	return(PR_TRUE);
+    } else {
+	/* cert B was issued after cert A, but expires sooner */
+	/* if B is expired, then pick A */
+	if ( LL_CMP(notAfterB, <, now ) ) {
+	    return(PR_TRUE);
+	}
+	return(PR_FALSE);
+    }
+}
+
+void
+CERT_DestroyCertArray(CERTCertificate **certs, unsigned int ncerts)
+{
+    unsigned int i;
+    
+    if ( certs ) {
+	for ( i = 0; i < ncerts; i++ ) {
+	    if ( certs[i] ) {
+		CERT_DestroyCertificate(certs[i]);
+	    }
+	}
+
+	PORT_Free(certs);
+    }
+    
+    return;
+}
+
+char *
+CERT_FixupEmailAddr(const char *emailAddr)
+{
+    char *retaddr;
+    char *str;
+
+    if ( emailAddr == NULL ) {
+	return(NULL);
+    }
+    
+    /* copy the string */
+    str = retaddr = PORT_Strdup(emailAddr);
+    if ( str == NULL ) {
+	return(NULL);
+    }
+    
+    /* make it lower case */
+    while ( *str ) {
+	*str = tolower( *str );
+	str++;
+    }
+    
+    return(retaddr);
+}
+
+/*
+ * NOTE - don't allow encode of govt-approved or invisible bits
+ */
+SECStatus
+CERT_DecodeTrustString(CERTCertTrust *trust, const char *trusts)
+{
+    unsigned int i;
+    unsigned int *pflags;
+    
+    if (!trust) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+    trust->sslFlags = 0;
+    trust->emailFlags = 0;
+    trust->objectSigningFlags = 0;
+    if (!trusts) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    pflags = &trust->sslFlags;
+    
+    for (i=0; i < PORT_Strlen(trusts); i++) {
+	switch (trusts[i]) {
+	  case 'p':
+	      *pflags = *pflags | CERTDB_TERMINAL_RECORD;
+	      break;
+
+	  case 'P':
+	      *pflags = *pflags | CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD;
+	      break;
+
+	  case 'w':
+	      *pflags = *pflags | CERTDB_SEND_WARN;
+	      break;
+
+	  case 'c':
+	      *pflags = *pflags | CERTDB_VALID_CA;
+	      break;
+
+	  case 'T':
+	      *pflags = *pflags | CERTDB_TRUSTED_CLIENT_CA | CERTDB_VALID_CA;
+	      break;
+
+	  case 'C' :
+	      *pflags = *pflags | CERTDB_TRUSTED_CA | CERTDB_VALID_CA;
+	      break;
+
+	  case 'u':
+	      *pflags = *pflags | CERTDB_USER;
+	      break;
+
+	  case 'i':
+	      *pflags = *pflags | CERTDB_INVISIBLE_CA;
+	      break;
+	  case 'g':
+	      *pflags = *pflags | CERTDB_GOVT_APPROVED_CA;
+	      break;
+
+	  case ',':
+	      if ( pflags == &trust->sslFlags ) {
+		  pflags = &trust->emailFlags;
+	      } else {
+		  pflags = &trust->objectSigningFlags;
+	      }
+	      break;
+	  default:
+	      return SECFailure;
+	}
+    }
+
+    return SECSuccess;
+}
+
+static void
+EncodeFlags(char *trusts, unsigned int flags)
+{
+    if (flags & CERTDB_VALID_CA)
+	if (!(flags & CERTDB_TRUSTED_CA) &&
+	    !(flags & CERTDB_TRUSTED_CLIENT_CA))
+	    PORT_Strcat(trusts, "c");
+    if (flags & CERTDB_TERMINAL_RECORD)
+	if (!(flags & CERTDB_TRUSTED))
+	    PORT_Strcat(trusts, "p");
+    if (flags & CERTDB_TRUSTED_CA)
+	PORT_Strcat(trusts, "C");
+    if (flags & CERTDB_TRUSTED_CLIENT_CA)
+	PORT_Strcat(trusts, "T");
+    if (flags & CERTDB_TRUSTED)
+	PORT_Strcat(trusts, "P");
+    if (flags & CERTDB_USER)
+	PORT_Strcat(trusts, "u");
+    if (flags & CERTDB_SEND_WARN)
+	PORT_Strcat(trusts, "w");
+    if (flags & CERTDB_INVISIBLE_CA)
+	PORT_Strcat(trusts, "I");
+    if (flags & CERTDB_GOVT_APPROVED_CA)
+	PORT_Strcat(trusts, "G");
+    return;
+}
+
+char *
+CERT_EncodeTrustString(CERTCertTrust *trust)
+{
+    char tmpTrustSSL[32];
+    char tmpTrustEmail[32];
+    char tmpTrustSigning[32];
+    char *retstr = NULL;
+
+    if ( trust ) {
+	tmpTrustSSL[0] = '\0';
+	tmpTrustEmail[0] = '\0';
+	tmpTrustSigning[0] = '\0';
+    
+	EncodeFlags(tmpTrustSSL, trust->sslFlags);
+	EncodeFlags(tmpTrustEmail, trust->emailFlags);
+	EncodeFlags(tmpTrustSigning, trust->objectSigningFlags);
+    
+	retstr = PR_smprintf("%s,%s,%s", tmpTrustSSL, tmpTrustEmail,
+			     tmpTrustSigning);
+    }
+    
+    return(retstr);
+}
+
+SECStatus
+CERT_ImportCerts(CERTCertDBHandle *certdb, SECCertUsage usage,
+		 unsigned int ncerts, SECItem **derCerts,
+		 CERTCertificate ***retCerts, PRBool keepCerts,
+		 PRBool caOnly, char *nickname)
+{
+    unsigned int i;
+    CERTCertificate **certs = NULL;
+    SECStatus rv;
+    unsigned int fcerts = 0;
+
+    if ( ncerts ) {
+	certs = PORT_ZNewArray(CERTCertificate*, ncerts);
+	if ( certs == NULL ) {
+	    return(SECFailure);
+	}
+    
+	/* decode all of the certs into the temporary DB */
+	for ( i = 0, fcerts= 0; i < ncerts; i++) {
+	    certs[fcerts] = CERT_NewTempCertificate(certdb,
+	                                            derCerts[i],
+	                                            NULL,
+	                                            PR_FALSE,
+	                                            PR_TRUE);
+	    if (certs[fcerts]) {
+		SECItem subjKeyID = {siBuffer, NULL, 0};
+		if (CERT_FindSubjectKeyIDExtension(certs[fcerts],
+		                                   &subjKeyID) == SECSuccess) {
+		    if (subjKeyID.data) {
+			cert_AddSubjectKeyIDMapping(&subjKeyID, certs[fcerts]);
+		    }
+		    SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+		}
+		fcerts++;
+	    }
+	}
+
+	if ( keepCerts ) {
+	    for ( i = 0; i < fcerts; i++ ) {
+                char* canickname = NULL;
+                PRBool isCA;
+
+		SECKEY_UpdateCertPQG(certs[i]);
+                
+                isCA = CERT_IsCACert(certs[i], NULL);
+                if ( isCA ) {
+                    canickname = CERT_MakeCANickname(certs[i]);
+                }
+
+		if(isCA && (fcerts > 1)) {
+		    /* if we are importing only a single cert and specifying
+		     * a nickname, we want to use that nickname if it a CA,
+		     * otherwise if there are more than one cert, we don't
+		     * know which cert it belongs to. But we still may try
+                     * the individual canickname from the cert itself.
+		     */
+		    rv = CERT_AddTempCertToPerm(certs[i], canickname, NULL);
+		} else {
+		    rv = CERT_AddTempCertToPerm(certs[i],
+                                                nickname?nickname:canickname, NULL);
+		}
+
+                PORT_Free(canickname);
+		/* don't care if it fails - keep going */
+	    }
+	}
+    }
+
+    if ( retCerts ) {
+	*retCerts = certs;
+    } else {
+	if (certs) {
+	    CERT_DestroyCertArray(certs, fcerts);
+	}
+    }
+
+    return ((fcerts || !ncerts) ? SECSuccess : SECFailure);
+}
+
+/*
+ * a real list of certificates - need to convert CERTCertificateList
+ * stuff and ASN 1 encoder/decoder over to using this...
+ */
+CERTCertList *
+CERT_NewCertList(void)
+{
+    PLArenaPool *arena = NULL;
+    CERTCertList *ret = NULL;
+    
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if ( arena == NULL ) {
+	goto loser;
+    }
+    
+    ret = (CERTCertList *)PORT_ArenaZAlloc(arena, sizeof(CERTCertList));
+    if ( ret == NULL ) {
+	goto loser;
+    }
+    
+    ret->arena = arena;
+    
+    PR_INIT_CLIST(&ret->list);
+    
+    return(ret);
+
+loser:
+    if ( arena != NULL ) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+    
+    return(NULL);
+}
+
+void
+CERT_DestroyCertList(CERTCertList *certs)
+{
+    PRCList *node;
+
+    while( !PR_CLIST_IS_EMPTY(&certs->list) ) {
+	node = PR_LIST_HEAD(&certs->list);
+	CERT_DestroyCertificate(((CERTCertListNode *)node)->cert);
+	PR_REMOVE_LINK(node);
+    }
+    
+    PORT_FreeArena(certs->arena, PR_FALSE);
+    
+    return;
+}
+
+void
+CERT_RemoveCertListNode(CERTCertListNode *node)
+{
+    CERT_DestroyCertificate(node->cert);
+    PR_REMOVE_LINK(&node->links);
+    return;
+}
+
+
+SECStatus
+CERT_AddCertToListTailWithData(CERTCertList *certs, 
+				CERTCertificate *cert, void *appData)
+{
+    CERTCertListNode *node;
+    
+    node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
+						sizeof(CERTCertListNode));
+    if ( node == NULL ) {
+	goto loser;
+    }
+    
+    PR_INSERT_BEFORE(&node->links, &certs->list);
+    /* certs->count++; */
+    node->cert = cert;
+    node->appData = appData;
+    return(SECSuccess);
+    
+loser:
+    return(SECFailure);
+}
+
+SECStatus
+CERT_AddCertToListTail(CERTCertList *certs, CERTCertificate *cert)
+{
+    return CERT_AddCertToListTailWithData(certs, cert, NULL);
+}
+
+SECStatus
+CERT_AddCertToListHeadWithData(CERTCertList *certs, 
+					CERTCertificate *cert, void *appData)
+{
+    CERTCertListNode *node;
+    CERTCertListNode *head;
+    
+    head = CERT_LIST_HEAD(certs);
+
+    if (head == NULL) return CERT_AddCertToListTail(certs,cert);
+
+    node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
+						sizeof(CERTCertListNode));
+    if ( node == NULL ) {
+	goto loser;
+    }
+    
+    PR_INSERT_BEFORE(&node->links, &head->links);
+    /* certs->count++; */
+    node->cert = cert;
+    node->appData = appData;
+    return(SECSuccess);
+    
+loser:
+    return(SECFailure);
+}
+
+SECStatus
+CERT_AddCertToListHead(CERTCertList *certs, CERTCertificate *cert)
+{
+    return CERT_AddCertToListHeadWithData(certs, cert, NULL);
+}
+
+/*
+ * Sort callback function to determine if cert a is newer than cert b.
+ * Not valid certs are considered older than valid certs.
+ */
+PRBool
+CERT_SortCBValidity(CERTCertificate *certa,
+		    CERTCertificate *certb,
+		    void *arg)
+{
+    PRTime sorttime;
+    PRTime notBeforeA, notAfterA, notBeforeB, notAfterB;
+    SECStatus rv;
+    PRBool newerbefore, newerafter;
+    PRBool aNotValid = PR_FALSE, bNotValid = PR_FALSE;
+
+    sorttime = *(PRTime *)arg;
+    
+    rv = CERT_GetCertTimes(certa, &notBeforeA, &notAfterA);
+    if ( rv != SECSuccess ) {
+	return(PR_FALSE);
+    }
+    
+    rv = CERT_GetCertTimes(certb, &notBeforeB, &notAfterB);
+    if ( rv != SECSuccess ) {
+	return(PR_TRUE);
+    }
+    newerbefore = PR_FALSE;
+    if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
+	newerbefore = PR_TRUE;
+    }
+    newerafter = PR_FALSE;
+    if ( LL_CMP(notAfterA, >, notAfterB) ) {
+	newerafter = PR_TRUE;
+    }
+
+    /* check if A is valid at sorttime */
+    if ( CERT_CheckCertValidTimes(certa, sorttime, PR_FALSE)
+	!= secCertTimeValid ) {
+	aNotValid = PR_TRUE;
+    }
+
+    /* check if B is valid at sorttime */
+    if ( CERT_CheckCertValidTimes(certb, sorttime, PR_FALSE)
+	!= secCertTimeValid ) {
+	bNotValid = PR_TRUE;
+    }
+
+    /* a is valid, b is not */
+    if ( bNotValid && ( ! aNotValid ) ) {
+	return(PR_TRUE);
+    }
+
+    /* b is valid, a is not */
+    if ( aNotValid && ( ! bNotValid ) ) {
+	return(PR_FALSE);
+    }
+    
+    /* a and b are either valid or not valid */
+    if ( newerbefore && newerafter ) {
+	return(PR_TRUE);
+    }
+    
+    if ( ( !newerbefore ) && ( !newerafter ) ) {
+	return(PR_FALSE);
+    }
+
+    if ( newerbefore ) {
+	/* cert A was issued after cert B, but expires sooner */
+	return(PR_TRUE);
+    } else {
+	/* cert B was issued after cert A, but expires sooner */
+	return(PR_FALSE);
+    }
+}
+
+
+SECStatus
+CERT_AddCertToListSorted(CERTCertList *certs,
+			 CERTCertificate *cert,
+			 CERTSortCallback f,
+			 void *arg)
+{
+    CERTCertListNode *node;
+    CERTCertListNode *head;
+    PRBool ret;
+    
+    node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
+						sizeof(CERTCertListNode));
+    if ( node == NULL ) {
+	goto loser;
+    }
+    
+    head = CERT_LIST_HEAD(certs);
+    
+    while ( !CERT_LIST_END(head, certs) ) {
+
+	/* if cert is already in the list, then don't add it again */
+	if ( cert == head->cert ) {
+	    /*XXX*/
+	    /* don't keep a reference */
+	    CERT_DestroyCertificate(cert);
+	    goto done;
+	}
+	
+	ret = (* f)(cert, head->cert, arg);
+	/* if sort function succeeds, then insert before current node */
+	if ( ret ) {
+	    PR_INSERT_BEFORE(&node->links, &head->links);
+	    goto done;
+	}
+
+	head = CERT_LIST_NEXT(head);
+    }
+    /* if we get to the end, then just insert it at the tail */
+    PR_INSERT_BEFORE(&node->links, &certs->list);
+
+done:    
+    /* certs->count++; */
+    node->cert = cert;
+    return(SECSuccess);
+    
+loser:
+    return(SECFailure);
+}
+
+/* This routine is here because pcertdb.c still has a call to it.
+ * The SMIME profile code in pcertdb.c should be split into high (find
+ * the email cert) and low (store the profile) code.  At that point, we
+ * can move this to certhigh.c where it belongs.
+ *
+ * remove certs from a list that don't have keyUsage and certType
+ * that match the given usage.
+ */
+SECStatus
+CERT_FilterCertListByUsage(CERTCertList *certList, SECCertUsage usage,
+			   PRBool ca)
+{
+    unsigned int requiredKeyUsage;
+    unsigned int requiredCertType;
+    CERTCertListNode *node, *savenode;
+    SECStatus rv;
+    
+    if (certList == NULL) goto loser;
+
+    rv = CERT_KeyUsageAndTypeForCertUsage(usage, ca, &requiredKeyUsage,
+					  &requiredCertType);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    node = CERT_LIST_HEAD(certList);
+	
+    while ( !CERT_LIST_END(node, certList) ) {
+
+	PRBool bad = (PRBool)(!node->cert);
+
+	/* bad key usage ? */
+	if ( !bad && 
+	     CERT_CheckKeyUsage(node->cert, requiredKeyUsage) != SECSuccess ) {
+	    bad = PR_TRUE;
+	}
+	/* bad cert type ? */
+	if ( !bad ) {
+	    unsigned int certType = 0;
+	    if ( ca ) {
+		/* This function returns a more comprehensive cert type that
+		 * takes trust flags into consideration.  Should probably
+		 * fix the cert decoding code to do this.
+		 */
+		(void)CERT_IsCACert(node->cert, &certType);
+	    } else {
+		certType = node->cert->nsCertType;
+	    }
+	    if ( !( certType & requiredCertType ) ) {
+		bad = PR_TRUE;
+	    }
+	}
+
+	if ( bad ) {
+	    /* remove the node if it is bad */
+	    savenode = CERT_LIST_NEXT(node);
+	    CERT_RemoveCertListNode(node);
+	    node = savenode;
+	} else {
+	    node = CERT_LIST_NEXT(node);
+	}
+    }
+    return(SECSuccess);
+    
+loser:
+    return(SECFailure);
+}
+
+PRBool CERT_IsUserCert(CERTCertificate* cert)
+{
+    CERTCertTrust trust;
+    SECStatus rv = SECFailure;
+
+    rv = CERT_GetCertTrust(cert, &trust);
+    if (rv == SECSuccess &&
+        ((trust.sslFlags & CERTDB_USER ) ||
+         (trust.emailFlags & CERTDB_USER ) ||
+         (trust.objectSigningFlags & CERTDB_USER )) ) {
+        return PR_TRUE;
+    } else {
+        return PR_FALSE;
+    }
+}
+
+SECStatus
+CERT_FilterCertListForUserCerts(CERTCertList *certList)
+{
+    CERTCertListNode *node, *freenode;
+    CERTCertificate *cert;
+
+    if (!certList) {
+        return SECFailure;
+    }
+
+    node = CERT_LIST_HEAD(certList);
+    
+    while ( ! CERT_LIST_END(node, certList) ) {
+	cert = node->cert;
+	if ( PR_TRUE != CERT_IsUserCert(cert) ) {
+	    /* Not a User Cert, so remove this cert from the list */
+	    freenode = node;
+	    node = CERT_LIST_NEXT(node);
+	    CERT_RemoveCertListNode(freenode);
+	} else {
+	    /* Is a User cert, so leave it in the list */
+	    node = CERT_LIST_NEXT(node);
+	}
+    }
+
+    return(SECSuccess);
+}
+
+static PZLock *certRefCountLock = NULL;
+
+/*
+ * Acquire the cert reference count lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+void
+CERT_LockCertRefCount(CERTCertificate *cert)
+{
+    PORT_Assert(certRefCountLock != NULL);
+    PZ_Lock(certRefCountLock);
+    return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+void
+CERT_UnlockCertRefCount(CERTCertificate *cert)
+{
+    PRStatus prstat;
+
+    PORT_Assert(certRefCountLock != NULL);
+    
+    prstat = PZ_Unlock(certRefCountLock);
+    
+    PORT_Assert(prstat == PR_SUCCESS);
+
+    return;
+}
+
+static PZLock *certTrustLock = NULL;
+
+/*
+ * Acquire the cert trust lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+void
+CERT_LockCertTrust(const CERTCertificate *cert)
+{
+    PORT_Assert(certTrustLock != NULL);
+    PZ_Lock(certTrustLock);
+    return;
+}
+
+SECStatus
+cert_InitLocks(void)
+{
+    if ( certRefCountLock == NULL ) {
+        certRefCountLock = PZ_NewLock(nssILockRefLock);
+        PORT_Assert(certRefCountLock != NULL);
+        if (!certRefCountLock) {
+            return SECFailure;
+        }
+    }
+
+    if ( certTrustLock == NULL ) {
+        certTrustLock = PZ_NewLock(nssILockCertDB);
+        PORT_Assert(certTrustLock != NULL);
+        if (!certTrustLock) {
+            PZ_DestroyLock(certRefCountLock);
+            certRefCountLock = NULL;
+            return SECFailure;
+        }
+    }    
+
+    return SECSuccess;
+}
+
+SECStatus
+cert_DestroyLocks(void)
+{
+    SECStatus rv = SECSuccess;
+
+    PORT_Assert(certRefCountLock != NULL);
+    if (certRefCountLock) {
+        PZ_DestroyLock(certRefCountLock);
+        certRefCountLock = NULL;
+    } else {
+        rv = SECFailure;
+    }
+
+    PORT_Assert(certTrustLock != NULL);
+    if (certTrustLock) {
+        PZ_DestroyLock(certTrustLock);
+        certTrustLock = NULL;
+    } else {
+        rv = SECFailure;
+    }
+    return rv;
+}
+
+/*
+ * Free the cert trust lock
+ */
+void
+CERT_UnlockCertTrust(const CERTCertificate *cert)
+{
+    PRStatus prstat;
+
+    PORT_Assert(certTrustLock != NULL);
+    
+    prstat = PZ_Unlock(certTrustLock);
+    
+    PORT_Assert(prstat == PR_SUCCESS);
+
+    return;
+}
+
+
+/*
+ * Get the StatusConfig data for this handle
+ */
+CERTStatusConfig *
+CERT_GetStatusConfig(CERTCertDBHandle *handle)
+{
+  return handle->statusConfig;
+}
+
+/*
+ * Set the StatusConfig data for this handle.  There
+ * should not be another configuration set.
+ */
+void
+CERT_SetStatusConfig(CERTCertDBHandle *handle, CERTStatusConfig *statusConfig)
+{
+  PORT_Assert(handle->statusConfig == NULL);
+  handle->statusConfig = statusConfig;
+}
+
+/*
+ * Code for dealing with subjKeyID to cert mappings.
+ */
+
+static PLHashTable *gSubjKeyIDHash = NULL;
+static PRLock      *gSubjKeyIDLock = NULL;
+static PLHashTable *gSubjKeyIDSlotCheckHash = NULL;
+static PRLock      *gSubjKeyIDSlotCheckLock = NULL;
+
+static void *cert_AllocTable(void *pool, PRSize size)
+{
+    return PORT_Alloc(size);
+}
+
+static void cert_FreeTable(void *pool, void *item)
+{
+    PORT_Free(item);
+}
+
+static PLHashEntry* cert_AllocEntry(void *pool, const void *key)
+{
+    return PORT_New(PLHashEntry);
+}
+
+static void cert_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
+{
+    SECITEM_FreeItem((SECItem*)(he->value), PR_TRUE);
+    if (flag == HT_FREE_ENTRY) {
+        SECITEM_FreeItem((SECItem*)(he->key), PR_TRUE);
+        PORT_Free(he);
+    }
+}
+
+static PLHashAllocOps cert_AllocOps = {
+    cert_AllocTable, cert_FreeTable, cert_AllocEntry, cert_FreeEntry
+};
+
+SECStatus
+cert_CreateSubjectKeyIDSlotCheckHash(void)
+{
+    /*
+     * This hash is used to remember the series of a slot
+     * when we last checked for user certs
+     */
+    gSubjKeyIDSlotCheckHash = PL_NewHashTable(0, SECITEM_Hash,
+                                             SECITEM_HashCompare,
+                                             SECITEM_HashCompare,
+                                             &cert_AllocOps, NULL);
+    if (!gSubjKeyIDSlotCheckHash) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return SECFailure;
+    }
+    gSubjKeyIDSlotCheckLock = PR_NewLock();
+    if (!gSubjKeyIDSlotCheckLock) {
+        PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
+        gSubjKeyIDSlotCheckHash = NULL;
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return SECFailure;
+    }
+    return SECSuccess;
+}
+
+SECStatus
+cert_CreateSubjectKeyIDHashTable(void)
+{
+    gSubjKeyIDHash = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
+                                    SECITEM_HashCompare,
+                                    &cert_AllocOps, NULL);
+    if (!gSubjKeyIDHash) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return SECFailure;
+    }
+    gSubjKeyIDLock = PR_NewLock();
+    if (!gSubjKeyIDLock) {
+        PL_HashTableDestroy(gSubjKeyIDHash);
+        gSubjKeyIDHash = NULL;
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        return SECFailure;
+    }
+    /* initialize the companion hash (for remembering slot series) */
+    if (cert_CreateSubjectKeyIDSlotCheckHash() != SECSuccess) {
+	cert_DestroySubjectKeyIDHashTable();
+	return SECFailure;
+    }
+    return SECSuccess;
+}
+
+SECStatus
+cert_AddSubjectKeyIDMapping(SECItem *subjKeyID, CERTCertificate *cert)
+{
+    SECItem *newKeyID, *oldVal, *newVal;
+    SECStatus rv = SECFailure;
+
+    if (!gSubjKeyIDLock) {
+	/* If one is created, then both are there.  So only check for one. */
+	return SECFailure;
+    }
+
+    newVal = SECITEM_DupItem(&cert->derCert);
+    if (!newVal) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto done;
+    }
+    newKeyID = SECITEM_DupItem(subjKeyID);
+    if (!newKeyID) {
+        SECITEM_FreeItem(newVal, PR_TRUE);
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto done;
+    }
+
+    PR_Lock(gSubjKeyIDLock);
+    /* The hash table implementation does not free up the memory 
+     * associated with the key of an already existing entry if we add a 
+     * duplicate, so we would wind up leaking the previously allocated 
+     * key if we don't remove before adding.
+     */
+    oldVal = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID);
+    if (oldVal) {
+        PL_HashTableRemove(gSubjKeyIDHash, subjKeyID);
+    }
+
+    rv = (PL_HashTableAdd(gSubjKeyIDHash, newKeyID, newVal)) ? SECSuccess :
+                                                               SECFailure;
+    PR_Unlock(gSubjKeyIDLock);
+done:
+    return rv;
+}
+
+SECStatus
+cert_RemoveSubjectKeyIDMapping(SECItem *subjKeyID)
+{
+    SECStatus rv;
+    if (!gSubjKeyIDLock)
+        return SECFailure;
+
+    PR_Lock(gSubjKeyIDLock);
+    rv = (PL_HashTableRemove(gSubjKeyIDHash, subjKeyID)) ? SECSuccess :
+                                                           SECFailure;
+    PR_Unlock(gSubjKeyIDLock);
+    return rv;
+}
+
+SECStatus
+cert_UpdateSubjectKeyIDSlotCheck(SECItem *slotid, int series)
+{
+    SECItem *oldSeries, *newSlotid, *newSeries;
+    SECStatus rv = SECFailure;
+
+    if (!gSubjKeyIDSlotCheckLock) {
+	return rv;
+    }
+
+    newSlotid = SECITEM_DupItem(slotid);
+    newSeries = SECITEM_AllocItem(NULL, NULL, sizeof(int));
+    if (!newSlotid || !newSeries ) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto loser;
+    }
+    PORT_Memcpy(newSeries->data, &series, sizeof(int));
+
+    PR_Lock(gSubjKeyIDSlotCheckLock);
+    oldSeries = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
+    if (oldSeries) {
+	/* 
+	 * make sure we don't leak the key of an existing entry
+	 * (similar to cert_AddSubjectKeyIDMapping, see comment there)
+	 */
+        PL_HashTableRemove(gSubjKeyIDSlotCheckHash, slotid);
+    }
+    rv = (PL_HashTableAdd(gSubjKeyIDSlotCheckHash, newSlotid, newSeries)) ?
+         SECSuccess : SECFailure;
+    PR_Unlock(gSubjKeyIDSlotCheckLock);
+    if (rv == SECSuccess) {
+	return rv;
+    }
+
+loser:
+    if (newSlotid) {
+        SECITEM_FreeItem(newSlotid, PR_TRUE);
+    }
+    if (newSeries) {
+        SECITEM_FreeItem(newSeries, PR_TRUE);
+    }
+    return rv;
+}
+
+int
+cert_SubjectKeyIDSlotCheckSeries(SECItem *slotid)
+{
+    SECItem *seriesItem = NULL;
+    int series;
+
+    if (!gSubjKeyIDSlotCheckLock) {
+	PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+	return -1;
+    }
+
+    PR_Lock(gSubjKeyIDSlotCheckLock);
+    seriesItem = (SECItem *)PL_HashTableLookup(gSubjKeyIDSlotCheckHash, slotid);
+    PR_Unlock(gSubjKeyIDSlotCheckLock);
+     /* getting a null series just means we haven't registered one yet, 
+      * just return 0 */
+    if (seriesItem == NULL) {
+	return 0;
+    }
+    /* if we got a series back, assert if it's not the proper length. */
+    PORT_Assert(seriesItem->len == sizeof(int));
+    if (seriesItem->len != sizeof(int)) {
+	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+	return -1;
+    }
+    PORT_Memcpy(&series, seriesItem->data, sizeof(int));
+    return series;
+}
+
+SECStatus
+cert_DestroySubjectKeyIDSlotCheckHash(void)
+{
+    if (gSubjKeyIDSlotCheckHash) {
+        PR_Lock(gSubjKeyIDSlotCheckLock);
+        PL_HashTableDestroy(gSubjKeyIDSlotCheckHash);
+        gSubjKeyIDSlotCheckHash = NULL;
+        PR_Unlock(gSubjKeyIDSlotCheckLock);
+        PR_DestroyLock(gSubjKeyIDSlotCheckLock);
+        gSubjKeyIDSlotCheckLock = NULL;
+    }
+    return SECSuccess;
+}
+
+SECStatus
+cert_DestroySubjectKeyIDHashTable(void)
+{
+    if (gSubjKeyIDHash) {
+        PR_Lock(gSubjKeyIDLock);
+        PL_HashTableDestroy(gSubjKeyIDHash);
+        gSubjKeyIDHash = NULL;
+        PR_Unlock(gSubjKeyIDLock);
+        PR_DestroyLock(gSubjKeyIDLock);
+        gSubjKeyIDLock = NULL;
+    }
+    cert_DestroySubjectKeyIDSlotCheckHash();
+    return SECSuccess;
+}
+
+SECItem*
+cert_FindDERCertBySubjectKeyID(SECItem *subjKeyID)
+{
+    SECItem   *val;
+ 
+    if (!gSubjKeyIDLock)
+        return NULL;
+
+    PR_Lock(gSubjKeyIDLock);
+    val = (SECItem*)PL_HashTableLookup(gSubjKeyIDHash, subjKeyID);
+    if (val) {
+        val = SECITEM_DupItem(val);
+    }
+    PR_Unlock(gSubjKeyIDLock);
+    return val;
+}
+
+CERTCertificate*
+CERT_FindCertBySubjectKeyID(CERTCertDBHandle *handle, SECItem *subjKeyID)
+{
+    CERTCertificate *cert = NULL;
+    SECItem *derCert;
+
+    derCert = cert_FindDERCertBySubjectKeyID(subjKeyID);
+    if (derCert) {
+        cert = CERT_FindCertByDERCert(handle, derCert);
+        SECITEM_FreeItem(derCert, PR_TRUE);
+    }
+    return cert;
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)