/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* * Certificate handling code * * $Id: certdb.c,v 1.50 2003-01-31 02:49:13 nelsonb%netscape.com Exp $ */ #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 "mcom_db.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 "nsslocks.h" #include "pk11func.h" #include "xconst.h" /* for CERT_DecodeAltNameExtension */ #ifndef NSS_3_4_CODE #define NSS_3_4_CODE #endif /* NSS_3_4_CODE */ #include "pki.h" #include "pki3hack.h" /* * 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_CertificateTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(CERTCertificate) }, { SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, /* XXX DER_DEFAULT */ offsetof(CERTCertificate,version), SEC_IntegerTemplate }, { SEC_ASN1_INTEGER, offsetof(CERTCertificate,serialNumber) }, { SEC_ASN1_INLINE, offsetof(CERTCertificate,signature), 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_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(CERTCertificate,issuerID), SEC_ObjectIDTemplate }, { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2, offsetof(CERTCertificate,subjectID), SEC_ObjectIDTemplate }, { 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, offsetof(CERTCertificate,signatureWrap.signatureAlgorithm), 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 | 0, 0, 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 | 0, 0, 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 | 0, 0, 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 | 0, 0, 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_CertificateTemplate) SEC_ASN1_CHOOSER_IMPLEMENT(SEC_SignedCertificateTemplate) SECStatus CERT_KeyFromIssuerAndSN(PRArenaPool *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; PRArenaPool *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; PRArenaPool *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; PRArenaPool *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(PRArenaPool *arena, SECItem *derCert, SECItem *key) { int rv; CERTSignedData sd; CERTCertKey certkey; PORT_Memset(&sd, 0, sizeof(CERTSignedData)); rv = SEC_ASN1DecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert); if ( rv ) { goto loser; } PORT_Memset(&certkey, 0, sizeof(CERTCertKey)); rv = SEC_ASN1DecodeItem(arena, &certkey, CERT_CertKeyTemplate, &sd.data); if ( rv ) { goto loser; } return(CERT_KeyFromIssuerAndSN(arena, &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); } /* * determine if a fortezza V1 Cert is a CA or not. */ static PRBool fortezzaIsCA( CERTCertificate *cert) { PRBool isCA = PR_FALSE; CERTSubjectPublicKeyInfo *spki = &cert->subjectPublicKeyInfo; int tag; tag = SECOID_GetAlgorithmTag(&spki->algorithm); if ((tag == SEC_OID_MISSI_KEA_DSS_OLD) || (tag == SEC_OID_MISSI_KEA_DSS) || (tag == SEC_OID_MISSI_DSS_OLD) || (tag == SEC_OID_MISSI_DSS) ) { SECItem rawkey; unsigned char *rawptr; unsigned char *end; int len; rawkey = spki->subjectPublicKey; DER_ConvertBitString(&rawkey); rawptr = rawkey.data; end = rawkey.data + rawkey.len; /* version */ rawptr += sizeof(((SECKEYPublicKey*)0)->u.fortezza.KMID)+2; /* clearance (the string up to the first byte with the hi-bit on */ while ((rawptr < end) && (*rawptr++ & 0x80)); if (rawptr >= end) { return PR_FALSE; } /* KEAPrivilege (the string up to the first byte with the hi-bit on */ while ((rawptr < end) && (*rawptr++ & 0x80)); if (rawptr >= end) { return PR_FALSE; } /* skip the key */ len = (*rawptr << 8) | rawptr[1]; rawptr += 2 + len; /* shared key */ if (rawptr >= end) { return PR_FALSE; } /* DSS Version is next */ rawptr += 2; /* DSSPrivilege (the string up to the first byte with the hi-bit on */ if (*rawptr & 0x30) isCA = PR_TRUE; } return isCA; } 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) { SECStatus rv; SECItem tmpitem; SECItem encodedExtKeyUsage; CERTOidSequence *extKeyUsage = NULL; PRBool basicConstraintPresent = PR_FALSE; CERTBasicConstraints basicConstraint; tmpitem.data = NULL; CERT_FindNSCertTypeExtension(cert, &tmpitem); 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) { cert->nsCertType = 0; } else { cert->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 ( ( cert->nsCertType & NS_CERT_TYPE_SSL_CLIENT ) && cert->emailAddr ) { cert->nsCertType |= NS_CERT_TYPE_EMAIL; } /* * for this release, we will allow SSL intermediate CAs to be * email intermediate CAs too. */ if ( cert->nsCertType & NS_CERT_TYPE_SSL_CA ) { cert->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)) { cert->nsCertType |= NS_CERT_TYPE_EMAIL_CA; } else { cert->nsCertType |= NS_CERT_TYPE_EMAIL; } } if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) == SECSuccess){ if (basicConstraintPresent == PR_TRUE && (basicConstraint.isCA)) { cert->nsCertType |= NS_CERT_TYPE_SSL_CA; } else { cert->nsCertType |= NS_CERT_TYPE_SSL_SERVER; } } if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH) == SECSuccess){ if (basicConstraintPresent == PR_TRUE && (basicConstraint.isCA)) { cert->nsCertType |= NS_CERT_TYPE_SSL_CA; } else { cert->nsCertType |= NS_CERT_TYPE_SSL_CLIENT; } } if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_EXT_KEY_USAGE_CODE_SIGN) == SECSuccess) { if (basicConstraintPresent == PR_TRUE && (basicConstraint.isCA)) { cert->nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING_CA; } else { cert->nsCertType |= NS_CERT_TYPE_OBJECT_SIGNING; } } if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_EXT_KEY_USAGE_TIME_STAMP) == SECSuccess) { cert->nsCertType |= EXT_KEY_USAGE_TIME_STAMP; } if (findOIDinOIDSeqByTagNum(extKeyUsage, SEC_OID_OCSP_RESPONDER) == SECSuccess) { cert->nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; } } else { /* if no extension, then allow any ssl or email (no ca or object * signing) */ cert->nsCertType = NS_CERT_TYPE_SSL_CLIENT | NS_CERT_TYPE_SSL_SERVER | NS_CERT_TYPE_EMAIL; /* if the basic constraint extension says the cert is a CA, then allow SSL CA and EMAIL CA and Status Responder */ if ((basicConstraintPresent == PR_TRUE) && (basicConstraint.isCA)) { cert->nsCertType |= NS_CERT_TYPE_SSL_CA; cert->nsCertType |= NS_CERT_TYPE_EMAIL_CA; cert->nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; } else if (CERT_IsCACert(cert, NULL) == PR_TRUE) { cert->nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER; } /* if the cert is a fortezza CA cert, then allow SSL CA and EMAIL CA */ if (fortezzaIsCA(cert)) { cert->nsCertType |= NS_CERT_TYPE_SSL_CA; cert->nsCertType |= NS_CERT_TYPE_EMAIL_CA; } } if (extKeyUsage != NULL) { PORT_Free(encodedExtKeyUsage.data); CERT_DestroyOidSequence(extKeyUsage); } return(SECSuccess); } /* * cert_GetKeyID() - extract or generate the subjectKeyID from a certificate */ SECStatus cert_GetKeyID(CERTCertificate *cert) { SECItem tmpitem; SECStatus rv; SECKEYPublicKey *key; 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 and the cert is * a V1 fortezza certificate, use the cert's 8 byte KMID as the * key identifier. */ key = CERT_KMIDPublicKey(cert); if (key != NULL) { if (key->keyType == fortezzaKey) { cert->subjectKeyID.data = (unsigned char *)PORT_ArenaAlloc(cert->arena, 8); if ( cert->subjectKeyID.data != NULL ) { PORT_Memcpy(cert->subjectKeyID.data, key->u.fortezza.KMID, 8); cert->subjectKeyID.len = 8; cert->keyIDGenerated = PR_FALSE; } } SECKEY_DestroyPublicKey(key); } /* 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; PRArenaPool *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) { PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION); goto loser; } /* 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; } /* initialize the certType */ rv = CERT_GetCertType(cert); if ( rv != SECSuccess ) { goto loser; } /* determine if this is a root cert */ cert->isRoot = cert_IsRootCert(cert); 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); } /* ** 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(CERTCertificate *c, PRTime *notBefore, PRTime *notAfter) { int rv; /* convert DER not-before time */ rv = DER_UTCTimeToTime(notBefore, &c->validity.notBefore); if (rv) { return(SECFailure); } /* convert DER not-after time */ rv = DER_UTCTimeToTime(notAfter, &c->validity.notAfter); if (rv) { return(SECFailure); } return(SECSuccess); } /* * Check the validity times of a certificate */ SECCertTimeValidity CERT_CheckCertValidTimes(CERTCertificate *c, PRTime t, PRBool allowOverride) { PRTime notBefore, notAfter, llPendingSlop, tmp1; SECStatus rv; /* if cert is already marked OK, then don't bother to check */ if ( allowOverride && c->timeOK ) { return(secCertTimeValid); } rv = CERT_GetCertTimes(c, ¬Before, ¬After); 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_UTCTimeToTime(notBefore, &date->lastUpdate); if (rv) { return(SECFailure); } /* convert DER not-after time */ if (date->nextUpdate.data) { rv = DER_UTCTimeToTime(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, ¬Before, ¬After); 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 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: 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; requiredCertType = NS_CERT_TYPE_EMAIL; break; case certUsageEmailRecipient: requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; requiredCertType = NS_CERT_TYPE_EMAIL; break; case certUsageObjectSigner: requiredKeyUsage = KU_DIGITAL_SIGNATURE; requiredCertType = NS_CERT_TYPE_OBJECT_SIGNING; break; case certUsageStatusResponder: requiredKeyUsage = KU_DIGITAL_SIGNATURE; 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) { SECKEYPublicKey *key; /* choose between key agreement or key encipherment based on key * type in cert */ if ( requiredUsage & KU_KEY_AGREEMENT_OR_ENCIPHERMENT ) { key = CERT_ExtractPublicKey(cert); if (!key) return SECFailure; if ( ( key->keyType == keaKey ) || ( key->keyType == fortezzaKey ) || ( key->keyType == dhKey ) ) { requiredUsage |= KU_KEY_AGREEMENT; } else { requiredUsage |= KU_KEY_ENCIPHERMENT; } /* now turn off the special bit */ requiredUsage &= (~KU_KEY_AGREEMENT_OR_ENCIPHERMENT); SECKEY_DestroyPublicKey(key); } if ( ( cert->keyUsage & requiredUsage ) != requiredUsage ) { return(SECFailure); } return(SECSuccess); } CERTCertificate * CERT_DupCertificate(CERTCertificate *c) { if (c) { #ifdef NSS_CLASSIC CERT_LockCertRefCount(c); ++c->referenceCount; CERT_UnlockCertRefCount(c); #else NSSCertificate *tmp = STAN_GetNSSCertificate(c); nssCertificate_AddRef(tmp); #endif } 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; } /* ** 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. ** ** may modify cn, so caller must pass a modifiable copy. */ static SECStatus cert_TestHostName(char * cn, const char * hn) { char * hndomain; int regvalid; if ((hndomain = PORT_Strchr(hn, '.')) == NULL) { /* No domain in URI host name */ char * cndomain; if ((cndomain = PORT_Strchr(cn, '.')) != NULL && (cndomain - cn) > 0) { /* there is a domain in the cn string, so chop it off */ *cndomain = '\0'; } } 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; } /* cn is not a regular expression */ /* compare entire hn with cert name */ if (PORT_Strcasecmp(hn, cn) == 0) { return SECSuccess; } if ( hndomain ) { /* compare just domain name with cert name */ if ( PORT_Strcasecmp(hndomain+1, cn) == 0 ) { return SECSuccess; } } PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); return SECFailure; } SECStatus cert_VerifySubjectAltName(CERTCertificate *cert, const char *hn) { PRArenaPool * arena = NULL; CERTGeneralName * nameList = NULL; CERTGeneralName * current; char * cn; int cnBufLen; unsigned int hnLen; int DNSextCount = 0; int IPextCount = 0; PRBool isIPaddr; 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 finish; } isIPaddr = (PR_SUCCESS == PR_StringToNetAddr(hn, &netAddr)); rv = SECFailure; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) goto finish; nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName); if (!current) goto finish; 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; if (cnLen + 1 > cnBufLen) { cnBufLen = cnLen + 1; cn = (char *)PORT_ArenaAlloc(arena, cnBufLen); if (!cn) goto finish; } PORT_Memcpy(cn, current->name.other.data, cnLen); cn[cnLen] = 0; 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_get_next_general_name(current); } while (current != nameList); if ((!isIPaddr && !DNSextCount) || (isIPaddr && !IPextCount)) { /* 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; } /* 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(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; /* try the cert extension first, then the common name */ cn = CERT_FindNSStringExtension(cert, SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME); if ( !cn ) { cn = CERT_GetCommonName(&cert->subject); } if ( cn ) { rv = cert_TestHostName(cn, hn); PORT_Free(cn); } else PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN); return rv; } PRBool CERT_CompareCerts(CERTCertificate *c1, 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(PRArenaPool *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; CERTCertDBHandle *handle; handle = cert->dbhandle; nickname = CERT_GetNickName(cert, handle, cert->arena); if (nickname == NULL) { 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 loser; } 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 loser; } /* look up the nickname to make sure it isn't in use already */ dummycert = CERT_FindCertByNickname(handle, nickname); if ( dummycert == NULL ) { goto done; } /* found a cert, destroy it and loop */ CERT_DestroyCertificate(dummycert); /* free the nickname */ PORT_Free(nickname); count++; } } loser: if ( nickname ) { PORT_Free(nickname); } nickname = ""; 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); } /* * 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) { CERTCertTrust *trust; SECStatus rv; unsigned int type; PRBool ret; ret = PR_FALSE; type = 0; if ( cert->trust && (cert->trust->sslFlags|cert->trust->emailFlags| cert->trust->objectSigningFlags)) { trust = cert->trust; if ( ( ( trust->sslFlags & CERTDB_VALID_CA ) == CERTDB_VALID_CA ) || ( ( trust->sslFlags & CERTDB_TRUSTED_CA ) == CERTDB_TRUSTED_CA ) ) { ret = PR_TRUE; type |= NS_CERT_TYPE_SSL_CA; } if ( ( ( trust->emailFlags & CERTDB_VALID_CA ) == CERTDB_VALID_CA ) || ( ( trust->emailFlags & CERTDB_TRUSTED_CA ) == CERTDB_TRUSTED_CA ) ) { ret = PR_TRUE; type |= NS_CERT_TYPE_EMAIL_CA; } if ( ( ( trust->objectSigningFlags & CERTDB_VALID_CA ) == CERTDB_VALID_CA ) || ( ( trust->objectSigningFlags & CERTDB_TRUSTED_CA ) == CERTDB_TRUSTED_CA ) ) { ret = PR_TRUE; type |= NS_CERT_TYPE_OBJECT_SIGNING_CA; } } else { if ( cert->nsCertType & ( NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA | NS_CERT_TYPE_OBJECT_SIGNING_CA ) ) { ret = PR_TRUE; type = (cert->nsCertType & NS_CERT_TYPE_CA); } else { CERTBasicConstraints constraints; rv = CERT_FindBasicConstraintExten(cert, &constraints); if ( rv == SECSuccess ) { if ( constraints.isCA ) { ret = PR_TRUE; type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); } } } /* finally check if it's a FORTEZZA V1 CA */ if (ret == PR_FALSE) { if (fortezzaIsCA(cert)) { ret = PR_TRUE; type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); } } } /* the isRoot flag trumps all */ if (cert->isRoot) { ret = PR_TRUE; /* set only these by default, same as above */ type = (NS_CERT_TYPE_SSL_CA | NS_CERT_TYPE_EMAIL_CA); } if ( rettype != NULL ) { *rettype = type; } 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; } /* * 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, ¬BeforeA, ¬AfterA); if ( rv != SECSuccess ) { return(PR_FALSE); } rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); 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 UTC 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(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, char *trusts) { unsigned int i; unsigned int *pflags; trust->sslFlags = 0; trust->emailFlags = 0; trust->objectSigningFlags = 0; pflags = &trust->sslFlags; for (i=0; i < PORT_Strlen(trusts); i++) { switch (trusts[i]) { case 'p': *pflags = *pflags | CERTDB_VALID_PEER; break; case 'P': *pflags = *pflags | CERTDB_TRUSTED | CERTDB_VALID_PEER; 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; #ifdef DEBUG_NSSTEAM_ONLY case 'i': *pflags = *pflags | CERTDB_INVISIBLE_CA; break; case 'g': *pflags = *pflags | CERTDB_GOVT_APPROVED_CA; break; #endif /* DEBUG_NSSTEAM_ONLY */ 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_VALID_PEER) 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); } /* in 3.4, this will only set trust */ SECStatus CERT_SaveImportedCert(CERTCertificate *cert, SECCertUsage usage, PRBool caOnly, char *nickname) { SECStatus rv; PRBool saveit; CERTCertTrust trust; PRBool isCA; unsigned int certtype; isCA = CERT_IsCACert(cert, NULL); if ( caOnly && ( !isCA ) ) { return(SECSuccess); } /* In NSS 3.4, certs are given zero trust upon import. However, this * function needs to set up default CA trust (CERTDB_VALID_CA), or * PKCS#12 imported certs will not show up correctly. In the case of a * CA cert with zero trust, continue with this function. But if the cert * does already have some trust bits, exit and do not change them. */ if (isCA && cert->trust && (cert->trust->sslFlags | cert->trust->emailFlags | cert->trust->objectSigningFlags)) { return(SECSuccess); } saveit = PR_TRUE; PORT_Memset((void *)&trust, 0, sizeof(trust)); certtype = cert->nsCertType; /* if no CA bits in cert type, then set all CA bits */ if ( isCA && ( ! ( certtype & NS_CERT_TYPE_CA ) ) ) { certtype |= NS_CERT_TYPE_CA; } /* if no app bits in cert type, then set all app bits */ if ( ( !isCA ) && ( ! ( certtype & NS_CERT_TYPE_APP ) ) ) { certtype |= NS_CERT_TYPE_APP; } switch ( usage ) { case certUsageEmailSigner: case certUsageEmailRecipient: if ( isCA ) { if ( certtype & NS_CERT_TYPE_EMAIL_CA ) { trust.emailFlags = CERTDB_VALID_CA; } } else { if ( cert->emailAddr == NULL ) { saveit = PR_FALSE; } if ( certtype & NS_CERT_TYPE_EMAIL ) { trust.emailFlags = CERTDB_VALID_PEER; if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) { /* don't save it if KeyEncipherment is not allowed */ saveit = PR_FALSE; } } } break; case certUsageUserCertImport: if ( isCA ) { if ( certtype & NS_CERT_TYPE_SSL_CA ) { trust.sslFlags = CERTDB_VALID_CA; } if ( certtype & NS_CERT_TYPE_EMAIL_CA ) { trust.emailFlags = CERTDB_VALID_CA; } if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA ) { trust.objectSigningFlags = CERTDB_VALID_CA; } } else { if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) { trust.sslFlags = CERTDB_VALID_PEER; } if ( certtype & NS_CERT_TYPE_EMAIL ) { trust.emailFlags = CERTDB_VALID_PEER; } if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) { trust.objectSigningFlags = CERTDB_VALID_PEER; } } break; case certUsageAnyCA: trust.sslFlags = CERTDB_VALID_CA; break; case certUsageSSLCA: trust.sslFlags = CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA; break; default: /* XXX added to quiet warnings; no other cases needed? */ break; } if ( saveit ) { rv = CERT_ChangeCertTrust(cert->dbhandle, cert, &trust); if ( rv != SECSuccess ) { goto loser; } } rv = SECSuccess; goto done; loser: rv = SECFailure; done: return(rv); } 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 = (CERTCertificate**)PORT_ZAlloc(sizeof(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]) fcerts++; } if ( keepCerts ) { for ( i = 0; i < fcerts; i++ ) { char* canickname = NULL; PRBool freeNickname = PR_FALSE; SECKEY_UpdateCertPQG(certs[i]); if ( CERT_IsCACert(certs[i], NULL) ) { canickname = CERT_MakeCANickname(certs[i]); if ( canickname != NULL ) { freeNickname = PR_TRUE; } } if(CERT_IsCACert(certs[i], NULL) && (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); } if (rv == SECSuccess) { CERT_SaveImportedCert(certs[i], usage, caOnly, NULL); } if (PR_TRUE == freeNickname) { PORT_Free(canickname); } /* don't care if it fails - keep going */ } } } if ( retCerts ) { *retCerts = certs; } else { if (certs) { CERT_DestroyCertArray(certs, fcerts); } } return(SECSuccess); #if 0 /* dead code here - why ?? XXX */ loser: if ( retCerts ) { *retCerts = NULL; } if ( certs ) { CERT_DestroyCertArray(certs, ncerts); } return(SECFailure); #endif } /* * a real list of certificates - need to convert CERTCertificateList * stuff and ASN 1 encoder/decoder over to using this... */ CERTCertList * CERT_NewCertList(void) { PRArenaPool *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, ¬BeforeA, ¬AfterA); if ( rv != SECSuccess ) { return(PR_FALSE); } rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); 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; PRBool bad; SECStatus rv; unsigned int certType; PRBool dummyret; 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) ) { bad = PR_FALSE; /* bad key usage */ if ( CERT_CheckKeyUsage(node->cert, requiredKeyUsage ) != SECSuccess ) { bad = PR_TRUE; } /* bad cert type */ 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. */ dummyret = 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) { if ( (cert->trust->sslFlags & CERTDB_USER ) || (cert->trust->emailFlags & CERTDB_USER ) || (cert->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) { if ( certRefCountLock == NULL ) { nss_InitLock(&certRefCountLock, nssILockRefLock); 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(CERTCertificate *cert) { if ( certTrustLock == NULL ) { nss_InitLock(&certTrustLock, nssILockCertDB); PORT_Assert(certTrustLock != NULL); } PZ_Lock(certTrustLock); return; } /* * Free the cert trust lock */ void CERT_UnlockCertTrust(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 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_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; } 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_DestroySubjectKeyIDHashTable(void) { if (gSubjKeyIDHash) { PR_Lock(gSubjKeyIDLock); PL_HashTableDestroy(gSubjKeyIDHash); gSubjKeyIDHash = NULL; PR_Unlock(gSubjKeyIDLock); PR_DestroyLock(gSubjKeyIDLock); gSubjKeyIDLock = NULL; } 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; }