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