Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nspr.h"
#include "secerr.h"
#include "secport.h"
#include "seccomon.h"
#include "secoid.h"
#include "genname.h"
#include "keyhi.h"
#include "cert.h"
#include "certdb.h"
#include "certi.h"
#include "cryptohi.h"
#ifndef NSS_DISABLE_LIBPKIX
#include "pkix.h"
#include "pkix_pl_cert.h"
#else
#include "nss.h"
#endif /* NSS_DISABLE_LIBPKIX */
#include "nsspki.h"
#include "pkitm.h"
#include "pkim.h"
#include "pki3hack.h"
#include "base.h"
#include "keyi.h"
/*
* Check the validity times of a certificate
*/
SECStatus
CERT_CertTimesValid(CERTCertificate *c)
{
SECCertTimeValidity valid = CERT_CheckCertValidTimes(c, PR_Now(), PR_TRUE);
return (valid == secCertTimeValid) ? SECSuccess : SECFailure;
}
static SECStatus
checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key)
{
SECStatus rv;
SECOidTag sigAlg;
SECOidTag curve;
PRUint32 policyFlags = 0;
PRInt32 minLen, len;
sigAlg = SECOID_GetAlgorithmTag(sigAlgorithm);
switch (sigAlg) {
case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
if (key->keyType != ecKey) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
curve = SECKEY_GetECCOid(&key->u.ec.DEREncodedParams);
if (curve != 0) {
if (NSS_GetAlgorithmPolicy(curve, &policyFlags) == SECFailure ||
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
return SECSuccess;
}
PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
return SECFailure;
case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: {
PORTCheapArenaPool tmpArena;
SECOidTag hashAlg;
SECOidTag maskHashAlg;
PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
rv = sec_DecodeRSAPSSParams(&tmpArena.arena,
&sigAlgorithm->parameters,
&hashAlg, &maskHashAlg, NULL);
PORT_DestroyCheapArena(&tmpArena);
if (rv != SECSuccess) {
return SECFailure;
}
if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess &&
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess &&
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
}
/* fall through to RSA key checking */
case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
if (key->keyType != rsaKey && key->keyType != rsaPssKey) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
len = 8 * key->u.rsa.modulus.len;
rv = NSS_OptionGet(NSS_RSA_MIN_KEY_SIZE, &minLen);
if (rv != SECSuccess) {
return SECFailure;
}
if (len < minLen) {
return SECFailure;
}
return SECSuccess;
case SEC_OID_ANSIX9_DSA_SIGNATURE:
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
case SEC_OID_SDN702_DSA_SIGNATURE:
case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST:
if (key->keyType != dsaKey) {
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
return SECFailure;
}
len = 8 * key->u.dsa.params.prime.len;
rv = NSS_OptionGet(NSS_DSA_MIN_KEY_SIZE, &minLen);
if (rv != SECSuccess) {
return SECFailure;
}
if (len < minLen) {
return SECFailure;
}
return SECSuccess;
default:
return SECSuccess;
}
}
/*
* verify the signature of a signed data object with the given DER publickey
*/
SECStatus
CERT_VerifySignedDataWithPublicKey(const CERTSignedData *sd,
SECKEYPublicKey *pubKey,
void *wincx)
{
SECStatus rv;
SECItem sig;
SECOidTag sigAlg;
SECOidTag encAlg;
SECOidTag hashAlg;
PRUint32 policyFlags;
if (!pubKey || !sd) {
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
return SECFailure;
}
/* Can we use this algorithm for signature verification? */
sigAlg = SECOID_GetAlgorithmTag(&sd->signatureAlgorithm);
rv = sec_DecodeSigAlg(pubKey, sigAlg,
&sd->signatureAlgorithm.parameters,
&encAlg, &hashAlg);
if (rv != SECSuccess) {
return SECFailure; /* error is set */
}
rv = NSS_GetAlgorithmPolicy(encAlg, &policyFlags);
if (rv == SECSuccess &&
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
if (rv == SECSuccess &&
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
rv = checkKeyParams(&sd->signatureAlgorithm, pubKey);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
/* check the signature */
sig = sd->signature;
/* convert sig->len from bit counts to byte count. */
DER_ConvertBitString(&sig);
rv = VFY_VerifyDataWithAlgorithmID(sd->data.data, sd->data.len, pubKey,
&sig, &sd->signatureAlgorithm,
&hashAlg, wincx);
if (rv != SECSuccess) {
return SECFailure; /* error is set */
}
/* for some algorithms, hash algorithm is only known after verification */
rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
if (rv == SECSuccess &&
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
return SECFailure;
}
return SECSuccess;
}
/*
* verify the signature of a signed data object with the given DER publickey
*/
SECStatus
CERT_VerifySignedDataWithPublicKeyInfo(CERTSignedData *sd,
CERTSubjectPublicKeyInfo *pubKeyInfo,
void *wincx)
{
SECKEYPublicKey *pubKey;
SECStatus rv = SECFailure;
/* get cert's public key */
pubKey = SECKEY_ExtractPublicKey(pubKeyInfo);
if (pubKey) {
rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
SECKEY_DestroyPublicKey(pubKey);
}
return rv;
}
/*
* verify the signature of a signed data object with the given certificate
*/
SECStatus
CERT_VerifySignedData(CERTSignedData *sd, CERTCertificate *cert,
PRTime t, void *wincx)
{
SECKEYPublicKey *pubKey = 0;
SECStatus rv = SECFailure;
SECCertTimeValidity validity;
/* check the certificate's validity */
validity = CERT_CheckCertValidTimes(cert, t, PR_FALSE);
if (validity != secCertTimeValid) {
return rv;
}
/* get cert's public key */
pubKey = CERT_ExtractPublicKey(cert);
if (pubKey) {
rv = CERT_VerifySignedDataWithPublicKey(sd, pubKey, wincx);
SECKEY_DestroyPublicKey(pubKey);
}
return rv;
}
SECStatus
SEC_CheckCRL(CERTCertDBHandle *handle, CERTCertificate *cert,
CERTCertificate *caCert, PRTime t, void *wincx)
{
return CERT_CheckCRL(cert, caCert, NULL, t, wincx);
}
/*
* Find the issuer of a cert. Use the authorityKeyID if it exists.
*/
CERTCertificate *
CERT_FindCertIssuer(CERTCertificate *cert, PRTime validTime, SECCertUsage usage)
{
NSSCertificate *me;
NSSTime *nssTime;
NSSTrustDomain *td;
NSSCryptoContext *cc;
NSSCertificate *chain[3];
NSSUsage nssUsage;
PRStatus status;
me = STAN_GetNSSCertificate(cert);
if (!me) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
nssTime = NSSTime_SetPRTime(NULL, validTime);
nssUsage.anyUsage = PR_FALSE;
nssUsage.nss3usage = usage;
nssUsage.nss3lookingForCA = PR_TRUE;
memset(chain, 0, 3 * sizeof(NSSCertificate *));
td = STAN_GetDefaultTrustDomain();
cc = STAN_GetDefaultCryptoContext();
(void)NSSCertificate_BuildChain(me, nssTime, &nssUsage, NULL,
chain, 2, NULL, &status, td, cc);
nss_ZFreeIf(nssTime);
if (status == PR_SUCCESS) {
PORT_Assert(me == chain[0]);
/* if it's a root, the chain will only have one cert */
if (!chain[1]) {
/* already has a reference from the call to BuildChain */
return cert;
}
NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */
return STAN_GetCERTCertificate(chain[1]); /* return the 2nd */
}
if (chain[0]) {
PORT_Assert(me == chain[0]);
NSSCertificate_Destroy(chain[0]); /* the first cert in the chain */
}
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
return NULL;
}
/*
* return required trust flags for various cert usages for CAs
*/
SECStatus
CERT_TrustFlagsForCACertUsage(SECCertUsage usage,
unsigned int *retFlags,
SECTrustType *retTrustType)
{
unsigned int requiredFlags;
SECTrustType trustType;
switch (usage) {
case certUsageSSLClient:
requiredFlags = CERTDB_TRUSTED_CLIENT_CA;
trustType = trustSSL;
break;
case certUsageSSLServer:
case certUsageSSLCA:
requiredFlags = CERTDB_TRUSTED_CA;
trustType = trustSSL;
break;
case certUsageIPsec:
requiredFlags = CERTDB_TRUSTED_CA;
trustType = trustSSL;
break;
case certUsageSSLServerWithStepUp:
requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA;
trustType = trustSSL;
break;
case certUsageEmailSigner:
case certUsageEmailRecipient:
requiredFlags = CERTDB_TRUSTED_CA;
trustType = trustEmail;
break;
case certUsageObjectSigner:
requiredFlags = CERTDB_TRUSTED_CA;
trustType = trustObjectSigning;
break;
case certUsageVerifyCA:
case certUsageAnyCA:
case certUsageStatusResponder:
requiredFlags = CERTDB_TRUSTED_CA;
trustType = trustTypeNone;
break;
default:
PORT_Assert(0);
goto loser;
}
if (retFlags != NULL) {
*retFlags = requiredFlags;
}
if (retTrustType != NULL) {
*retTrustType = trustType;
}
return (SECSuccess);
loser:
return (SECFailure);
}
void
cert_AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, long error,
unsigned int depth, void *arg)
{
CERTVerifyLogNode *node, *tnode;
PORT_Assert(log != NULL);
node = (CERTVerifyLogNode *)PORT_ArenaAlloc(log->arena,
sizeof(CERTVerifyLogNode));
if (node != NULL) {
node->cert = CERT_DupCertificate(cert);
node->error = error;
node->depth = depth;
node->arg = arg;
if (log->tail == NULL) {
/* empty list */
log->head = log->tail = node;
node->prev = NULL;
node->next = NULL;
} else if (depth >= log->tail->depth) {
/* add to tail */
node->prev = log->tail;
log->tail->next = node;
log->tail = node;
node->next = NULL;
} else if (depth < log->head->depth) {
/* add at head */
node->prev = NULL;
node->next = log->head;
log->head->prev = node;
log->head = node;
} else {
/* add in middle */
tnode = log->tail;
while (tnode != NULL) {
if (depth >= tnode->depth) {
/* insert after tnode */
node->prev = tnode;
node->next = tnode->next;
tnode->next->prev = node;
tnode->next = node;
break;
}
tnode = tnode->prev;
}
}
log->count++;
}
return;
}
#define EXIT_IF_NOT_LOGGING(log) \
if (log == NULL) { \
goto loser; \
}
#define LOG_ERROR_OR_EXIT(log, cert, depth, arg) \
if (log != NULL) { \
cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
(void *)(PRWord)arg); \
} else { \
goto loser; \
}
#define LOG_ERROR(log, cert, depth, arg) \
if (log != NULL) { \
cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, \
(void *)(PRWord)arg); \
}
/* /C=CN/O=WoSign CA Limited/CN=CA \xE6\xB2\x83\xE9\x80\x9A\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6
* Using a consistent naming convention, this would actually be called
* 'CA沃通根证书DN', but since GCC 6.2.1 apparently can't handle UTF-8
* identifiers, this will have to do.
*/
static const unsigned char CAWoSignRootDN[72] = {
0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
0x0C, 0x12, 0x43, 0x41, 0x20, 0xE6, 0xB2, 0x83, 0xE9, 0x80, 0x9A, 0xE6, 0xA0,
0xB9, 0xE8, 0xAF, 0x81, 0xE4, 0xB9, 0xA6
};
/* /C=CN/O=WoSign CA Limited/CN=CA WoSign ECC Root */
static const unsigned char CAWoSignECCRootDN[72] = {
0x30, 0x46, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
0x69, 0x74, 0x65, 0x64, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x12, 0x43, 0x41, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x45,
0x43, 0x43, 0x20, 0x52, 0x6F, 0x6F, 0x74
};
/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign */
static const unsigned char CertificationAuthorityofWoSignDN[87] = {
0x30, 0x55, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
0x69, 0x74, 0x65, 0x64, 0x31, 0x2A, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x21, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E
};
/* /C=CN/O=WoSign CA Limited/CN=Certification Authority of WoSign G2 */
static const unsigned char CertificationAuthorityofWoSignG2DN[90] = {
0x30, 0x58, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x43, 0x4E, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11,
0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x43, 0x41, 0x20, 0x4C, 0x69, 0x6D,
0x69, 0x74, 0x65, 0x64, 0x31, 0x2D, 0x30, 0x2B, 0x06, 0x03, 0x55, 0x04, 0x03,
0x13, 0x24, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79, 0x20,
0x6F, 0x66, 0x20, 0x57, 0x6F, 0x53, 0x69, 0x67, 0x6E, 0x20, 0x47, 0x32
};
/* /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority */
static const unsigned char StartComCertificationAuthorityDN[127] = {
0x30, 0x7D, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
0x31, 0x2B, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x22, 0x53, 0x65,
0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20,
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53,
0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20,
0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E,
0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79
};
/* /C=IL/O=StartCom Ltd./CN=StartCom Certification Authority G2 */
static const unsigned char StartComCertificationAuthorityG2DN[85] = {
0x30, 0x53, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x49, 0x4C, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x0D,
0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x4C, 0x74, 0x64, 0x2E,
0x31, 0x2C, 0x30, 0x2A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74,
0x61, 0x72, 0x74, 0x43, 0x6F, 0x6D, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F,
0x72, 0x69, 0x74, 0x79, 0x20, 0x47, 0x32
};
struct DataAndLength {
const unsigned char *data;
PRUint32 len;
};
static const struct DataAndLength StartComAndWoSignDNs[] = {
{ CAWoSignRootDN,
sizeof(CAWoSignRootDN) },
{ CAWoSignECCRootDN,
sizeof(CAWoSignECCRootDN) },
{ CertificationAuthorityofWoSignDN,
sizeof(CertificationAuthorityofWoSignDN) },
{ CertificationAuthorityofWoSignG2DN,
sizeof(CertificationAuthorityofWoSignG2DN) },
{ StartComCertificationAuthorityDN,
sizeof(StartComCertificationAuthorityDN) },
{ StartComCertificationAuthorityG2DN,
sizeof(StartComCertificationAuthorityG2DN) },
};
static PRBool
CertIsStartComOrWoSign(const CERTCertificate *cert)
{
int i;
const struct DataAndLength *dn = StartComAndWoSignDNs;
for (i = 0; i < sizeof(StartComAndWoSignDNs) / sizeof(struct DataAndLength); ++i, dn++) {
if (cert->derSubject.len == dn->len &&
memcmp(cert->derSubject.data, dn->data, dn->len) == 0) {
return PR_TRUE;
}
}
return PR_FALSE;
}
SECStatus
isIssuerCertAllowedAtCertIssuanceTime(CERTCertificate *issuerCert,
CERTCertificate *referenceCert)
{
if (!issuerCert || !referenceCert) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
if (CertIsStartComOrWoSign(issuerCert)) {
/* PRTime is microseconds since the epoch, whereas JS time is milliseconds.
* (new Date("2016-10-21T00:00:00Z")).getTime() * 1000
*/
static const PRTime OCTOBER_21_2016 = 1477008000000000;
PRTime notBefore, notAfter;
SECStatus rv;
rv = CERT_GetCertTimes(referenceCert, &notBefore, &notAfter);
if (rv != SECSuccess)
return rv;
if (notBefore > OCTOBER_21_2016) {
return SECFailure;
}
}
return SECSuccess;
}
static SECStatus
cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, PRBool *sigerror,
SECCertUsage certUsage, PRTime t, void *wincx,
CERTVerifyLog *log, PRBool *revoked)
{
SECTrustType trustType;
CERTBasicConstraints basicConstraint;
CERTCertificate *issuerCert = NULL;
CERTCertificate *subjectCert = NULL;
CERTCertificate *badCert = NULL;
PRBool isca;
SECStatus rv;
SECStatus rvFinal = SECSuccess;
int count;
int currentPathLen = 0;
int pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
unsigned int caCertType;
unsigned int requiredCAKeyUsage;
unsigned int requiredFlags;
PLArenaPool *arena = NULL;
CERTGeneralName *namesList = NULL;
CERTCertificate **certsList = NULL;
int certsListLen = 16;
int namesCount = 0;
PRBool subjectCertIsSelfIssued;
CERTCertTrust issuerTrust;
if (revoked) {
*revoked = PR_FALSE;
}
if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
&requiredCAKeyUsage,
&caCertType) !=
SECSuccess) {
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredCAKeyUsage = 0;
caCertType = 0;
}
switch (certUsage) {
case certUsageSSLClient:
case certUsageSSLServer:
case certUsageIPsec:
case certUsageSSLCA:
case certUsageSSLServerWithStepUp:
case certUsageEmailSigner:
case certUsageEmailRecipient:
case certUsageObjectSigner:
case certUsageVerifyCA:
case certUsageAnyCA:
case certUsageStatusResponder:
if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
&trustType) != SECSuccess) {
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
/* XXX continuing with requiredFlags = 0 seems wrong. It'll
* cause the following test to be true incorrectly:
* flags = SEC_GET_TRUST_FLAGS(issuerCert->trust, trustType);
* if (( flags & requiredFlags ) == requiredFlags) {
* rv = rvFinal;
* goto done;
* }
* There are three other instances of this problem.
*/
requiredFlags = 0;
trustType = trustSSL;
}
break;
default:
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredFlags = 0;
trustType = trustSSL; /* This used to be 0, but we need something
* that matches the enumeration type.
*/
caCertType = 0;
}
subjectCert = CERT_DupCertificate(cert);
if (subjectCert == NULL) {
goto loser;
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
certsList = PORT_ZNewArray(CERTCertificate *, certsListLen);
if (certsList == NULL)
goto loser;
/* RFC 3280 says that the name constraints will apply to the names
** in the leaf (EE) cert, whether it is self issued or not, so
** we pretend that it is not.
*/
subjectCertIsSelfIssued = PR_FALSE;
for (count = 0; count < CERT_MAX_CERT_CHAIN; count++) {
PRBool validCAOverride = PR_FALSE;
/* Construct a list of names for the current and all previous
* certifcates (except leaf (EE) certs, root CAs, and self-issued
* intermediate CAs) to be verified against the name constraints
* extension of the issuer certificate.
*/
if (subjectCertIsSelfIssued == PR_FALSE) {
CERTGeneralName *subjectNameList;
int subjectNameListLen;
int i;
PRBool getSubjectCN = (!count &&
(certUsage == certUsageSSLServer || certUsage == certUsageIPsec));
subjectNameList =
CERT_GetConstrainedCertificateNames(subjectCert, arena,
getSubjectCN);
if (!subjectNameList)
goto loser;
subjectNameListLen = CERT_GetNamesLength(subjectNameList);
if (!subjectNameListLen)
goto loser;
if (certsListLen <= namesCount + subjectNameListLen) {
CERTCertificate **tmpCertsList;
certsListLen = (namesCount + subjectNameListLen) * 2;
tmpCertsList =
(CERTCertificate **)PORT_Realloc(certsList,
certsListLen *
sizeof(CERTCertificate *));
if (tmpCertsList == NULL) {
goto loser;
}
certsList = tmpCertsList;
}
for (i = 0; i < subjectNameListLen; i++) {
certsList[namesCount + i] = subjectCert;
}
namesCount += subjectNameListLen;
namesList = cert_CombineNamesLists(namesList, subjectNameList);
}
/* check if the cert has an unsupported critical extension */
if (subjectCert->options.bits.hasUnsupportedCriticalExt) {
PORT_SetError(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
}
/* check that the signatureAlgorithm field of the certificate
* matches the signature field of the tbsCertificate */
if (SECOID_CompareAlgorithmID(
&subjectCert->signatureWrap.signatureAlgorithm,
&subjectCert->signature)) {
PORT_SetError(SEC_ERROR_ALGORITHM_MISMATCH);
LOG_ERROR(log, subjectCert, count, 0);
goto loser;
}
/* find the certificate of the issuer */
issuerCert = CERT_FindCertIssuer(subjectCert, t, certUsage);
if (!issuerCert) {
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
LOG_ERROR(log, subjectCert, count, 0);
goto loser;
}
/* verify the signature on the cert */
if (checkSig) {
rv = CERT_VerifySignedData(&subjectCert->signatureWrap,
issuerCert, t, wincx);
if (rv != SECSuccess) {
if (sigerror) {
*sigerror = PR_TRUE;
}
if (PORT_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE) {
PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
} else {
if (PORT_GetError() !=
SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
}
LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
}
}
}
/* If the basicConstraint extension is included in an immediate CA
* certificate, make sure that the isCA flag is on. If the
* pathLenConstraint component exists, it must be greater than the
* number of CA certificates we have seen so far. If the extension
* is omitted, we will assume that this is a CA certificate with
* an unlimited pathLenConstraint (since it already passes the
* netscape-cert-type extension checking).
*/
rv = CERT_FindBasicConstraintExten(issuerCert, &basicConstraint);
if (rv != SECSuccess) {
if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
}
pathLengthLimit = CERT_UNLIMITED_PATH_CONSTRAINT;
/* no basic constraints found, we aren't (yet) a CA. */
isca = PR_FALSE;
} else {
if (basicConstraint.isCA == PR_FALSE) {
PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
}
pathLengthLimit = basicConstraint.pathLenConstraint;
isca = PR_TRUE;
}
/* make sure that the path len constraint is properly set.*/
if (pathLengthLimit >= 0 && currentPathLen > pathLengthLimit) {
PORT_SetError(SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, pathLengthLimit);
}
/* make sure that the entire chain is within the name space of the
* current issuer certificate.
*/
rv = CERT_CompareNameSpace(issuerCert, namesList, certsList,
arena, &badCert);
if (rv != SECSuccess || badCert != NULL) {
PORT_SetError(SEC_ERROR_CERT_NOT_IN_NAME_SPACE);
LOG_ERROR_OR_EXIT(log, badCert, count + 1, 0);
goto loser;
}
rv = isIssuerCertAllowedAtCertIssuanceTime(issuerCert, cert);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
LOG_ERROR(log, issuerCert, count + 1, 0);
goto loser;
}
/* XXX - the error logging may need to go down into CRL stuff at some
* point
*/
/* check revoked list (issuer) */
rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx);
if (rv == SECFailure) {
if (revoked) {
*revoked = PR_TRUE;
}
LOG_ERROR_OR_EXIT(log, subjectCert, count, 0);
} else if (rv == SECWouldBlock) {
/* We found something fishy, so we intend to issue an
* error to the user, but the user may wish to continue
* processing, in which case we better make sure nothing
* worse has happened... so keep cranking the loop */
rvFinal = SECFailure;
if (revoked) {
*revoked = PR_TRUE;
}
LOG_ERROR(log, subjectCert, count, 0);
}
if (CERT_GetCertTrust(issuerCert, &issuerTrust) == SECSuccess) {
/* we have some trust info, but this does NOT imply that this
* cert is actually trusted for any purpose. The cert may be
* explicitly UNtrusted. We won't know until we examine the
* trust bits.
*/
unsigned int flags;
if (certUsage != certUsageAnyCA &&
certUsage != certUsageStatusResponder) {
/*
* XXX This choice of trustType seems arbitrary.
*/
if (certUsage == certUsageVerifyCA) {
if (subjectCert->nsCertType & NS_CERT_TYPE_EMAIL_CA) {
trustType = trustEmail;
} else if (subjectCert->nsCertType & NS_CERT_TYPE_SSL_CA) {
trustType = trustSSL;
} else {
trustType = trustObjectSigning;
}
}
flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
if ((flags & requiredFlags) == requiredFlags) {
/* we found a trusted one, so return */
rv = rvFinal;
goto done;
}
if (flags & CERTDB_VALID_CA) {
validCAOverride = PR_TRUE;
}
/* is it explicitly distrusted? */
if ((flags & CERTDB_TERMINAL_RECORD) &&
((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
/* untrusted -- the cert is explicitly untrusted, not
* just that it doesn't chain to a trusted cert */
PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
}
} else {
/* Check if we have any valid trust when cheching for
* certUsageAnyCA or certUsageStatusResponder. */
for (trustType = trustSSL; trustType < trustTypeNone;
trustType++) {
flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
if ((flags & requiredFlags) == requiredFlags) {
rv = rvFinal;
goto done;
}
if (flags & CERTDB_VALID_CA)
validCAOverride = PR_TRUE;
}
/* We have 2 separate loops because we want any single trust
* bit to allow this usage to return trusted. Only if none of
* the trust bits are on do we check to see if the cert is
* untrusted */
for (trustType = trustSSL; trustType < trustTypeNone;
trustType++) {
flags = SEC_GET_TRUST_FLAGS(&issuerTrust, trustType);
/* is it explicitly distrusted? */
if ((flags & CERTDB_TERMINAL_RECORD) &&
((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
/* untrusted -- the cert is explicitly untrusted, not
* just that it doesn't chain to a trusted cert */
PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, flags);
}
}
}
}
if (!validCAOverride) {
/*
* Make sure that if this is an intermediate CA in the chain that
* it was given permission by its signer to be a CA.
*/
/*
* if basicConstraints says it is a ca, then we check the
* nsCertType. If the nsCertType has any CA bits set, then
* it must have the right one.
*/
if (!isca || (issuerCert->nsCertType & NS_CERT_TYPE_CA)) {
isca = (issuerCert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
}
if (!isca) {
PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, 0);
}
/* make sure key usage allows cert signing */
if (CERT_CheckKeyUsage(issuerCert, requiredCAKeyUsage) != SECSuccess) {
PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
LOG_ERROR_OR_EXIT(log, issuerCert, count + 1, requiredCAKeyUsage);
}
}
/* make sure that the issuer is not self signed. If it is, then
* stop here to prevent looping.
*/
if (issuerCert->isRoot) {
PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
LOG_ERROR(log, issuerCert, count + 1, 0);
goto loser;
}
/* The issuer cert will be the subject cert in the next loop.
* A cert is self-issued if its subject and issuer are equal and
* both are of non-zero length.
*/
subjectCertIsSelfIssued = (PRBool)
SECITEM_ItemsAreEqual(&issuerCert->derIssuer,
&issuerCert->derSubject) &&
issuerCert->derSubject.len >
0;
if (subjectCertIsSelfIssued == PR_FALSE) {
/* RFC 3280 says only non-self-issued intermediate CA certs
* count in path length.
*/
++currentPathLen;
}
CERT_DestroyCertificate(subjectCert);
subjectCert = issuerCert;
issuerCert = NULL;
}
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
LOG_ERROR(log, subjectCert, count, 0);
loser:
rv = SECFailure;
done:
if (certsList != NULL) {
PORT_Free(certsList);
}
if (issuerCert) {
CERT_DestroyCertificate(issuerCert);
}
if (subjectCert) {
CERT_DestroyCertificate(subjectCert);
}
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return rv;
}
SECStatus
cert_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, PRBool *sigerror,
SECCertUsage certUsage, PRTime t, void *wincx,
CERTVerifyLog *log, PRBool *revoked)
{
if (CERT_GetUsePKIXForValidation()) {
return cert_VerifyCertChainPkix(cert, checkSig, certUsage, t,
wincx, log, sigerror, revoked);
}
return cert_VerifyCertChainOld(handle, cert, checkSig, sigerror,
certUsage, t, wincx, log, revoked);
}
SECStatus
CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, PRTime t,
void *wincx, CERTVerifyLog *log)
{
return cert_VerifyCertChain(handle, cert, checkSig, NULL, certUsage, t,
wincx, log, NULL);
}
/*
* verify that a CA can sign a certificate with the requested usage.
*/
SECStatus
CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, PRTime t,
void *wincx, CERTVerifyLog *log)
{
SECTrustType trustType;
CERTBasicConstraints basicConstraint;
PRBool isca;
PRBool validCAOverride = PR_FALSE;
SECStatus rv;
SECStatus rvFinal = SECSuccess;
unsigned int flags;
unsigned int caCertType;
unsigned int requiredCAKeyUsage;
unsigned int requiredFlags;
CERTCertificate *issuerCert;
CERTCertTrust certTrust;
if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
&requiredCAKeyUsage,
&caCertType) != SECSuccess) {
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredCAKeyUsage = 0;
caCertType = 0;
}
switch (certUsage) {
case certUsageSSLClient:
case certUsageSSLServer:
case certUsageIPsec:
case certUsageSSLCA:
case certUsageSSLServerWithStepUp:
case certUsageEmailSigner:
case certUsageEmailRecipient:
case certUsageObjectSigner:
case certUsageVerifyCA:
case certUsageStatusResponder:
if (CERT_TrustFlagsForCACertUsage(certUsage, &requiredFlags,
&trustType) != SECSuccess) {
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredFlags = 0;
trustType = trustSSL;
}
break;
default:
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredFlags = 0;
trustType = trustSSL; /* This used to be 0, but we need something
* that matches the enumeration type.
*/
caCertType = 0;
}
/* If the basicConstraint extension is included in an intermmediate CA
* certificate, make sure that the isCA flag is on. If the
* pathLenConstraint component exists, it must be greater than the
* number of CA certificates we have seen so far. If the extension
* is omitted, we will assume that this is a CA certificate with
* an unlimited pathLenConstraint (since it already passes the
* netscape-cert-type extension checking).
*/
rv = CERT_FindBasicConstraintExten(cert, &basicConstraint);
if (rv != SECSuccess) {
if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND) {
LOG_ERROR_OR_EXIT(log, cert, 0, 0);
}
/* no basic constraints found, we aren't (yet) a CA. */
isca = PR_FALSE;
} else {
if (basicConstraint.isCA == PR_FALSE) {
PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
LOG_ERROR_OR_EXIT(log, cert, 0, 0);
}
/* can't check path length if we don't know the previous path */
isca = PR_TRUE;
}
if (CERT_GetCertTrust(cert, &certTrust) == SECSuccess) {
/* we have some trust info, but this does NOT imply that this
* cert is actually trusted for any purpose. The cert may be
* explicitly UNtrusted. We won't know until we examine the
* trust bits.
*/
if (certUsage == certUsageStatusResponder) {
/* Check the special case of certUsageStatusResponder */
issuerCert = CERT_FindCertIssuer(cert, t, certUsage);
if (issuerCert) {
if (SEC_CheckCRL(handle, cert, issuerCert, t, wincx) !=
SECSuccess) {
PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
CERT_DestroyCertificate(issuerCert);
goto loser;
}
CERT_DestroyCertificate(issuerCert);
}
/* XXX We have NOT determined that this cert is trusted.
* For years, NSS has treated this as trusted,
* but it seems incorrect.
*/
rv = rvFinal;
goto done;
}
/*
* check the trust params of the issuer
*/
flags = SEC_GET_TRUST_FLAGS(&certTrust, trustType);
if ((flags & requiredFlags) == requiredFlags) {
/* we found a trusted one, so return */
rv = rvFinal;
goto done;
}
if (flags & CERTDB_VALID_CA) {
validCAOverride = PR_TRUE;
}
/* is it explicitly distrusted? */
if ((flags & CERTDB_TERMINAL_RECORD) &&
((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0)) {
/* untrusted -- the cert is explicitly untrusted, not
* just that it doesn't chain to a trusted cert */
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
LOG_ERROR_OR_EXIT(log, cert, 0, flags);
}
}
if (!validCAOverride) {
/*
* Make sure that if this is an intermediate CA in the chain that
* it was given permission by its signer to be a CA.
*/
/*
* if basicConstraints says it is a ca, then we check the
* nsCertType. If the nsCertType has any CA bits set, then
* it must have the right one.
*/
if (!isca || (cert->nsCertType & NS_CERT_TYPE_CA)) {
isca = (cert->nsCertType & caCertType) ? PR_TRUE : PR_FALSE;
}
if (!isca) {
PORT_SetError(SEC_ERROR_CA_CERT_INVALID);
LOG_ERROR_OR_EXIT(log, cert, 0, 0);
}
/* make sure key usage allows cert signing */
if (CERT_CheckKeyUsage(cert, requiredCAKeyUsage) != SECSuccess) {
PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
LOG_ERROR_OR_EXIT(log, cert, 0, requiredCAKeyUsage);
}
}
/* make sure that the issuer is not self signed. If it is, then
* stop here to prevent looping.
*/
if (cert->isRoot) {
PORT_SetError(SEC_ERROR_UNTRUSTED_ISSUER);
LOG_ERROR(log, cert, 0, 0);
goto loser;
}
return CERT_VerifyCertChain(handle, cert, checkSig, certUsage, t,
wincx, log);
loser:
rv = SECFailure;
done:
return rv;
}
#define NEXT_USAGE() \
{ \
i *= 2; \
certUsage++; \
continue; \
}
#define VALID_USAGE() \
{ \
NEXT_USAGE(); \
}
#define INVALID_USAGE() \
{ \
if (returnedUsages) { \
*returnedUsages &= (~i); \
} \
if (PR_TRUE == requiredUsage) { \
valid = SECFailure; \
} \
NEXT_USAGE(); \
}
/*
* check the leaf cert against trust and usage.
* returns success if the cert is not distrusted. If the cert is
* trusted, then the trusted bool will be true.
* returns failure if the cert is distrusted. If failure, flags
* will return the flag bits that indicated distrust.
*/
SECStatus
cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage,
unsigned int *failedFlags, PRBool *trusted)
{
unsigned int flags;
CERTCertTrust trust;
*failedFlags = 0;
*trusted = PR_FALSE;
/* check trust flags to see if this cert is directly trusted */
if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
switch (certUsage) {
case certUsageSSLClient:
case certUsageSSLServer:
case certUsageIPsec:
flags = trust.sslFlags;
/* is the cert directly trusted or not trusted ? */
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if (flags & CERTDB_TRUSTED) { /* trust this cert */
*trusted = PR_TRUE;
return SECSuccess;
} else { /* don't trust this cert */
*failedFlags = flags;
return SECFailure;
}
}
break;
case certUsageSSLServerWithStepUp:
/* XXX - step up certs can't be directly trusted, only distrust */
flags = trust.sslFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if ((flags & CERTDB_TRUSTED) == 0) {
/* don't trust this cert */
*failedFlags = flags;
return SECFailure;
}
}
break;
case certUsageSSLCA:
flags = trust.sslFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
/* don't trust this cert */
*failedFlags = flags;
return SECFailure;
}
}
break;
case certUsageEmailSigner:
case certUsageEmailRecipient:
flags = trust.emailFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if (flags & CERTDB_TRUSTED) { /* trust this cert */
*trusted = PR_TRUE;
return SECSuccess;
} else { /* don't trust this cert */
*failedFlags = flags;
return SECFailure;
}
}
break;
case certUsageObjectSigner:
flags = trust.objectSigningFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if (flags & CERTDB_TRUSTED) { /* trust this cert */
*trusted = PR_TRUE;
return SECSuccess;
} else { /* don't trust this cert */
*failedFlags = flags;
return SECFailure;
}
}
break;
case certUsageVerifyCA:
case certUsageStatusResponder:
flags = trust.sslFlags;
/* is the cert directly trusted or not trusted ? */
if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
(CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
*trusted = PR_TRUE;
return SECSuccess;
}
flags = trust.emailFlags;
/* is the cert directly trusted or not trusted ? */
if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
(CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
*trusted = PR_TRUE;
return SECSuccess;
}
flags = trust.objectSigningFlags;
/* is the cert directly trusted or not trusted ? */
if ((flags & (CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) ==
(CERTDB_VALID_CA | CERTDB_TRUSTED_CA)) {
*trusted = PR_TRUE;
return SECSuccess;
}
/* fall through to test distrust */
case certUsageAnyCA:
case certUsageUserCertImport:
/* do we distrust these certs explicitly */
flags = trust.sslFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
*failedFlags = flags;
return SECFailure;
}
}
flags = trust.emailFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
*failedFlags = flags;
return SECFailure;
}
}
/* fall through */
case certUsageProtectedObjectSigner:
flags = trust.objectSigningFlags;
if (flags & CERTDB_TERMINAL_RECORD) { /* the trust record is
* authoritative */
if ((flags & (CERTDB_TRUSTED | CERTDB_TRUSTED_CA)) == 0) {
*failedFlags = flags;
return SECFailure;
}
}
break;
}
}
return SECSuccess;
}
/*
* verify a certificate by checking if it's valid and that we
* trust the issuer.
*
* certificateUsage contains a bitfield of all cert usages that are
* required for verification to succeed
*
* a bitfield of cert usages is returned in *returnedUsages
* if requiredUsages is non-zero, the returned bitmap is only
* for those required usages, otherwise it is for all usages
*
*/
SECStatus
CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertificateUsage requiredUsages, PRTime t,
void *wincx, CERTVerifyLog *log, SECCertificateUsage *returnedUsages)
{
SECStatus rv;
SECStatus valid;
unsigned int requiredKeyUsage;
unsigned int requiredCertType;
unsigned int flags;
unsigned int certType;
PRBool allowOverride;
SECCertTimeValidity validity;
CERTStatusConfig *statusConfig;
PRInt32 i;
SECCertUsage certUsage = 0;
PRBool checkedOCSP = PR_FALSE;
PRBool checkAllUsages = PR_FALSE;
PRBool revoked = PR_FALSE;
PRBool sigerror = PR_FALSE;
PRBool trusted = PR_FALSE;
if (!requiredUsages) {
/* there are no required usages, so the user probably wants to
get status for all usages */
checkAllUsages = PR_TRUE;
}
if (returnedUsages) {
*returnedUsages = 0;
} else {
/* we don't have a place to return status for all usages,
so we can skip checks for usages that aren't required */
checkAllUsages = PR_FALSE;
}
valid = SECSuccess; /* start off assuming cert is valid */
/* make sure that the cert is valid at time t */
allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) ||
(requiredUsages & certificateUsageSSLServerWithStepUp) ||
(requiredUsages & certificateUsageIPsec));
validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
if (validity != secCertTimeValid) {
valid = SECFailure;
LOG_ERROR_OR_EXIT(log, cert, 0, validity);
}
/* check key usage and netscape cert type */
cert_GetCertType(cert);
certType = cert->nsCertType;
for (i = 1; i <= certificateUsageHighest &&
(SECSuccess == valid || returnedUsages || log);) {
PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE;
if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) {
NEXT_USAGE();
}
if (returnedUsages) {
*returnedUsages |= i; /* start off assuming this usage is valid */
}
switch (certUsage) {
case certUsageSSLClient:
case certUsageSSLServer:
case certUsageSSLServerWithStepUp:
case certUsageSSLCA:
case certUsageEmailSigner:
case certUsageEmailRecipient:
case certUsageObjectSigner:
case certUsageStatusResponder:
case certUsageIPsec:
rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
&requiredKeyUsage,
&requiredCertType);
if (rv != SECSuccess) {
PORT_Assert(0);
/* EXIT_IF_NOT_LOGGING(log); XXX ??? */
requiredKeyUsage = 0;
requiredCertType = 0;
INVALID_USAGE();
}
break;
case certUsageAnyCA:
case certUsageProtectedObjectSigner:
case certUsageUserCertImport:
case certUsageVerifyCA:
/* these usages cannot be verified */
NEXT_USAGE();
default:
PORT_Assert(0);
requiredKeyUsage = 0;
requiredCertType = 0;
INVALID_USAGE();
}
if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
if (PR_TRUE == requiredUsage) {
PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
}
LOG_ERROR(log, cert, 0, requiredKeyUsage);
INVALID_USAGE();
}
if (!(certType & requiredCertType)) {
if (PR_TRUE == requiredUsage) {
PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
}
LOG_ERROR(log, cert, 0, requiredCertType);
INVALID_USAGE();
}
rv = cert_CheckLeafTrust(cert, certUsage, &flags, &trusted);
if (rv == SECFailure) {
if (PR_TRUE == requiredUsage) {
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
}
LOG_ERROR(log, cert, 0, flags);
INVALID_USAGE();
} else if (trusted) {
VALID_USAGE();
}
if (PR_TRUE == revoked || PR_TRUE == sigerror) {
INVALID_USAGE();
}
rv = cert_VerifyCertChain(handle, cert,
checkSig, &sigerror,
certUsage, t, wincx, log,
&revoked);
if (rv != SECSuccess) {
/* EXIT_IF_NOT_LOGGING(log); XXX ???? */
INVALID_USAGE();
}
/*
* Check OCSP revocation status, but only if the cert we are checking
* is not a status responder itself. We only do this in the case
* where we checked the cert chain (above); explicit trust "wins"
* (avoids status checking, just as it avoids CRL checking) by
* bypassing this code.
*/
if (PR_FALSE == checkedOCSP) {
checkedOCSP = PR_TRUE; /* only check OCSP once */
statusConfig = CERT_GetStatusConfig(handle);
if (requiredUsages != certificateUsageStatusResponder &&
statusConfig != NULL) {
if (statusConfig->statusChecker != NULL) {
rv = (*statusConfig->statusChecker)(handle, cert,
t, wincx);
if (rv != SECSuccess) {
LOG_ERROR(log, cert, 0, 0);
revoked = PR_TRUE;
INVALID_USAGE();
}
}
}
}
NEXT_USAGE();
}
loser:
return (valid);
}
SECStatus
CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, PRTime t,
void *wincx, CERTVerifyLog *log)
{
return cert_VerifyCertWithFlags(handle, cert, checkSig, certUsage, t,
CERT_VERIFYCERT_USE_DEFAULTS, wincx, log);
}
SECStatus
cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, PRTime t,
PRUint32 flags, void *wincx, CERTVerifyLog *log)
{
SECStatus rv;
unsigned int requiredKeyUsage;
unsigned int requiredCertType;
unsigned int failedFlags;
unsigned int certType;
PRBool trusted;
PRBool allowOverride;
SECCertTimeValidity validity;
CERTStatusConfig *statusConfig;
#ifdef notdef
/* check if this cert is in the Evil list */
rv = CERT_CheckForEvilCert(cert);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
LOG_ERROR_OR_EXIT(log, cert, 0, 0);
}
#endif
/* make sure that the cert is valid at time t */
allowOverride = (PRBool)((certUsage == certUsageSSLServer) ||
(certUsage == certUsageSSLServerWithStepUp) ||
(certUsage == certUsageIPsec));
validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
if (validity != secCertTimeValid) {
LOG_ERROR_OR_EXIT(log, cert, 0, validity);
}
/* check key usage and netscape cert type */
cert_GetCertType(cert);
certType = cert->nsCertType;
switch (certUsage) {
case certUsageSSLClient:
case certUsageSSLServer:
case certUsageSSLServerWithStepUp:
case certUsageIPsec:
case certUsageSSLCA:
case certUsageEmailSigner:
case certUsageEmailRecipient:
case certUsageObjectSigner:
case certUsageStatusResponder:
rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE,
&requiredKeyUsage,
&requiredCertType);
if (rv != SECSuccess) {
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredKeyUsage = 0;
requiredCertType = 0;
}
break;
case certUsageVerifyCA:
case certUsageAnyCA:
requiredKeyUsage = KU_KEY_CERT_SIGN;
requiredCertType = NS_CERT_TYPE_CA;
if (!(certType & NS_CERT_TYPE_CA)) {
certType |= NS_CERT_TYPE_CA;
}
break;
default:
PORT_Assert(0);
EXIT_IF_NOT_LOGGING(log);
requiredKeyUsage = 0;
requiredCertType = 0;
}
if (CERT_CheckKeyUsage(cert, requiredKeyUsage) != SECSuccess) {
PORT_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE);
LOG_ERROR_OR_EXIT(log, cert, 0, requiredKeyUsage);
}
if (!(certType & requiredCertType)) {
PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE);
LOG_ERROR_OR_EXIT(log, cert, 0, requiredCertType);
}
rv = cert_CheckLeafTrust(cert, certUsage, &failedFlags, &trusted);
if (rv == SECFailure) {
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
LOG_ERROR_OR_EXIT(log, cert, 0, failedFlags);
} else if (trusted) {
goto done;
}
rv = CERT_VerifyCertChain(handle, cert, checkSig, certUsage,
t, wincx, log);
if (rv != SECSuccess) {
EXIT_IF_NOT_LOGGING(log);
}
/*
* Check revocation status, but only if the cert we are checking is not a
* status responder itself and the caller did not ask us to skip the check.
* We only do this in the case where we checked the cert chain (above);
* explicit trust "wins" (avoids status checking, just as it avoids CRL
* checking, which is all done inside VerifyCertChain) by bypassing this
* code.
*/
if (!(flags & CERT_VERIFYCERT_SKIP_OCSP) &&
certUsage != certUsageStatusResponder) {
statusConfig = CERT_GetStatusConfig(handle);
if (statusConfig && statusConfig->statusChecker) {
rv = (*statusConfig->statusChecker)(handle, cert,
t, wincx);
if (rv != SECSuccess) {
LOG_ERROR_OR_EXIT(log, cert, 0, 0);
}
}
}
done:
if (log && log->head) {
return SECFailure;
}
return (SECSuccess);
loser:
rv = SECFailure;
return (rv);
}
/*
* verify a certificate by checking if its valid and that we
* trust the issuer. Verify time against now.
*/
SECStatus
CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertificateUsage requiredUsages,
void *wincx, SECCertificateUsage *returnedUsages)
{
return (CERT_VerifyCertificate(handle, cert, checkSig,
requiredUsages, PR_Now(), wincx, NULL, returnedUsages));
}
/* obsolete, do not use for new code */
SECStatus
CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, void *wincx)
{
return (CERT_VerifyCert(handle, cert, checkSig,
certUsage, PR_Now(), wincx, NULL));
}
/* [ FROM pcertdb.c ] */
/*
* Supported usage values and types:
* certUsageSSLClient
* certUsageSSLServer
* certUsageSSLServerWithStepUp
* certUsageIPsec
* certUsageEmailSigner
* certUsageEmailRecipient
* certUsageObjectSigner
*/
CERTCertificate *
CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName,
CERTCertOwner owner, SECCertUsage usage,
PRBool preferTrusted, PRTime validTime, PRBool validOnly)
{
CERTCertList *certList = NULL;
CERTCertificate *cert = NULL;
CERTCertTrust certTrust;
unsigned int requiredTrustFlags;
SECTrustType requiredTrustType;
unsigned int flags;
PRBool lookingForCA = PR_FALSE;
SECStatus rv;
CERTCertListNode *node;
CERTCertificate *saveUntrustedCA = NULL;
/* if preferTrusted is set, must be a CA cert */
PORT_Assert(!(preferTrusted && (owner != certOwnerCA)));
if (owner == certOwnerCA) {
lookingForCA = PR_TRUE;
if (preferTrusted) {
rv = CERT_TrustFlagsForCACertUsage(usage, &requiredTrustFlags,
&requiredTrustType);
if (rv != SECSuccess) {
goto loser;
}
requiredTrustFlags |= CERTDB_VALID_CA;
}
}
certList = CERT_CreateSubjectCertList(NULL, handle, derName, validTime,
validOnly);
if (certList != NULL) {
rv = CERT_FilterCertListByUsage(certList, usage, lookingForCA);
if (rv != SECSuccess) {
goto loser;
}
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
cert = node->cert;
/* looking for a trusted CA cert */
if ((owner == certOwnerCA) && preferTrusted &&
(requiredTrustType != trustTypeNone)) {
if (CERT_GetCertTrust(cert, &certTrust) != SECSuccess) {
flags = 0;
} else {
flags = SEC_GET_TRUST_FLAGS(&certTrust, requiredTrustType);
}
if ((flags & requiredTrustFlags) != requiredTrustFlags) {
/* cert is not trusted */
/* if this is the first cert to get this far, then save
* it, so we can use it if we can't find a trusted one
*/
if (saveUntrustedCA == NULL) {
saveUntrustedCA = cert;
}
goto endloop;
}
}
/* if we got this far, then this cert meets all criteria */
break;
endloop:
node = CERT_LIST_NEXT(node);
cert = NULL;
}
/* use the saved one if we have it */
if (cert == NULL) {
cert = saveUntrustedCA;
}
/* if we found one then bump the ref count before freeing the list */
if (cert != NULL) {
/* bump the ref count */
cert = CERT_DupCertificate(cert);
}
CERT_DestroyCertList(certList);
}
return (cert);
loser:
if (certList != NULL) {
CERT_DestroyCertList(certList);
}
return (NULL);
}
/* [ From certdb.c ] */
/*
* Filter a list of certificates, removing those certs that do not have
* one of the named CA certs somewhere in their cert chain.
*
* "certList" - the list of certificates to filter
* "nCANames" - number of CA names
* "caNames" - array of CA names in string(rfc 1485) form
* "usage" - what use the certs are for, this is used when
* selecting CA certs
*/
SECStatus
CERT_FilterCertListByCANames(CERTCertList *certList, int nCANames,
char **caNames, SECCertUsage usage)
{
CERTCertificate *issuerCert = NULL;
CERTCertificate *subjectCert;
CERTCertListNode *node, *freenode;
CERTCertificate *cert;
int n;
char **names;
PRBool found;
PRTime time;
if (nCANames <= 0) {
return (SECSuccess);
}
time = PR_Now();
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
cert = node->cert;
subjectCert = CERT_DupCertificate(cert);
/* traverse the CA certs for this cert */
found = PR_FALSE;
while (subjectCert != NULL) {
n = nCANames;
names = caNames;
if (subjectCert->issuerName != NULL) {
while (n > 0) {
if (PORT_Strcmp(*names, subjectCert->issuerName) == 0) {
found = PR_TRUE;
break;
}
n--;
names++;
}
}
if (found) {
break;
}
issuerCert = CERT_FindCertIssuer(subjectCert, time, usage);
if (issuerCert == subjectCert) {
CERT_DestroyCertificate(issuerCert);
issuerCert = NULL;
break;
}
CERT_DestroyCertificate(subjectCert);
subjectCert = issuerCert;
}
CERT_DestroyCertificate(subjectCert);
if (!found) {
/* CA was not found, so remove this cert from the list */
freenode = node;
node = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(freenode);
} else {
/* CA was found, so leave it in the list */
node = CERT_LIST_NEXT(node);
}
}
return (SECSuccess);
}
/*
* Given a certificate, return a string containing the nickname, and possibly
* one of the validity strings, based on the current validity state of the
* certificate.
*
* "arena" - arena to allocate returned string from. If NULL, then heap
* is used.
* "cert" - the cert to get nickname from
* "expiredString" - the string to append to the nickname if the cert is
* expired.
* "notYetGoodString" - the string to append to the nickname if the cert is
* not yet good.
*/
char *
CERT_GetCertNicknameWithValidity(PLArenaPool *arena, CERTCertificate *cert,
char *expiredString, char *notYetGoodString)
{
SECCertTimeValidity validity;
char *nickname = NULL, *tmpstr = NULL;
const char *srcNickname = cert->nickname;
if (!srcNickname) {
srcNickname = "{???}";
}
validity = CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE);
/* if the cert is good, then just use the nickname directly */
if (validity == secCertTimeValid) {
if (arena == NULL) {
nickname = PORT_Strdup(srcNickname);
} else {
nickname = PORT_ArenaStrdup(arena, srcNickname);
}
if (nickname == NULL) {
goto loser;
}
} else {
/* if the cert is not valid, then tack one of the strings on the
* end
*/
if (validity == secCertTimeExpired) {
tmpstr = PR_smprintf("%s%s", srcNickname,
expiredString);
} else if (validity == secCertTimeNotValidYet) {
/* not yet valid */
tmpstr = PR_smprintf("%s%s", srcNickname,
notYetGoodString);
} else {
/* undetermined */
tmpstr = PR_smprintf("%s",
"(NULL) (Validity Unknown)");
}
if (tmpstr == NULL) {
goto loser;
}
if (arena) {
/* copy the string into the arena and free the malloc'd one */
nickname = PORT_ArenaStrdup(arena, tmpstr);
PORT_Free(tmpstr);
} else {
nickname = tmpstr;
}
if (nickname == NULL) {
goto loser;
}
}
return (nickname);
loser:
return (NULL);
}
/*
* Collect the nicknames from all certs in a CertList. If the cert is not
* valid, append a string to that nickname.
*
* "certList" - the list of certificates
* "expiredString" - the string to append to the nickname of any expired cert
* "notYetGoodString" - the string to append to the nickname of any cert
* that is not yet valid
*/
CERTCertNicknames *
CERT_NicknameStringsFromCertList(CERTCertList *certList, char *expiredString,
char *notYetGoodString)
{
CERTCertNicknames *names;
PLArenaPool *arena;
CERTCertListNode *node;
char **nn;
/* allocate an arena */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
return (NULL);
}
/* allocate the structure */
names = PORT_ArenaAlloc(arena, sizeof(CERTCertNicknames));
if (names == NULL) {
goto loser;
}
/* init the structure */
names->arena = arena;
names->head = NULL;
names->numnicknames = 0;
names->nicknames = NULL;
names->totallen = 0;
/* count the certs in the list */
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
names->numnicknames++;
node = CERT_LIST_NEXT(node);
}
/* allocate nicknames array */
names->nicknames = PORT_ArenaAlloc(arena,
sizeof(char *) * names->numnicknames);
if (names->nicknames == NULL) {
goto loser;
}
/* just in case printf can't deal with null strings */
if (expiredString == NULL) {
expiredString = "";
}
if (notYetGoodString == NULL) {
notYetGoodString = "";
}
/* traverse the list of certs and collect the nicknames */
nn = names->nicknames;
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
*nn = CERT_GetCertNicknameWithValidity(arena, node->cert,
expiredString,
notYetGoodString);
if (*nn == NULL) {
goto loser;
}
names->totallen += PORT_Strlen(*nn);
nn++;
node = CERT_LIST_NEXT(node);
}
return (names);
loser:
PORT_FreeArena(arena, PR_FALSE);
return (NULL);
}
/*
* Extract the nickname from a nickmake string that may have either
* expiredString or notYetGoodString appended.
*
* Args:
* "namestring" - the string containing the nickname, and possibly
* one of the validity label strings
* "expiredString" - the expired validity label string
* "notYetGoodString" - the not yet good validity label string
*
* Returns the raw nickname
*/
char *
CERT_ExtractNicknameString(char *namestring, char *expiredString,
char *notYetGoodString)
{
int explen, nyglen, namelen;
int retlen;
char *retstr;
namelen = PORT_Strlen(namestring);
explen = PORT_Strlen(expiredString);
nyglen = PORT_Strlen(notYetGoodString);
if (namelen > explen) {
if (PORT_Strcmp(expiredString, &namestring[namelen - explen]) == 0) {
retlen = namelen - explen;
retstr = (char *)PORT_Alloc(retlen + 1);
if (retstr == NULL) {
goto loser;
}
PORT_Memcpy(retstr, namestring, retlen);
retstr[retlen] = '\0';
goto done;
}
}
if (namelen > nyglen) {
if (PORT_Strcmp(notYetGoodString, &namestring[namelen - nyglen]) == 0) {
retlen = namelen - nyglen;
retstr = (char *)PORT_Alloc(retlen + 1);
if (retstr == NULL) {
goto loser;
}
PORT_Memcpy(retstr, namestring, retlen);
retstr[retlen] = '\0';
goto done;
}
}
/* if name string is shorter than either invalid string, then it must
* be a raw nickname
*/
retstr = PORT_Strdup(namestring);
done:
return (retstr);
loser:
return (NULL);
}
CERTCertList *
CERT_GetCertChainFromCert(CERTCertificate *cert, PRTime time, SECCertUsage usage)
{
CERTCertList *chain = NULL;
int count = 0;
if (NULL == cert) {
return NULL;
}
cert = CERT_DupCertificate(cert);
if (NULL == cert) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
chain = CERT_NewCertList();
if (NULL == chain) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
while (cert != NULL && ++count <= CERT_MAX_CERT_CHAIN) {
if (SECSuccess != CERT_AddCertToListTail(chain, cert)) {
/* return partial chain */
PORT_SetError(SEC_ERROR_NO_MEMORY);
return chain;
}
if (cert->isRoot) {
/* return complete chain */
return chain;
}
cert = CERT_FindCertIssuer(cert, time, usage);
}
/* return partial chain */
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
return chain;
}