mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
1997 lines
58 KiB
C
1997 lines
58 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is the Netscape security libraries.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
#include "nspr.h"
|
||
|
#include "secerr.h"
|
||
|
#include "secport.h"
|
||
|
#include "seccomon.h"
|
||
|
#include "secoid.h"
|
||
|
#include "sslerr.h"
|
||
|
#include "genname.h"
|
||
|
#include "keyhi.h"
|
||
|
#include "cert.h"
|
||
|
#include "certdb.h"
|
||
|
#include "certi.h"
|
||
|
#include "cryptohi.h"
|
||
|
#include "pkix.h"
|
||
|
/*#include "pkix_sample_modules.h" */
|
||
|
#include "pkix_pl_cert.h"
|
||
|
|
||
|
|
||
|
#include "nsspki.h"
|
||
|
#include "pkitm.h"
|
||
|
#include "pkim.h"
|
||
|
#include "pki3hack.h"
|
||
|
#include "base.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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* verify the signature of a signed data object with the given DER publickey
|
||
|
*/
|
||
|
SECStatus
|
||
|
CERT_VerifySignedDataWithPublicKey(CERTSignedData *sd,
|
||
|
SECKEYPublicKey *pubKey,
|
||
|
void *wincx)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
SECItem sig;
|
||
|
SECOidTag hashAlg = SEC_OID_UNKNOWN;
|
||
|
|
||
|
if ( !pubKey || !sd ) {
|
||
|
PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
|
||
|
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) {
|
||
|
/* Are we honoring signatures for this algorithm? */
|
||
|
PRUint32 policyFlags = 0;
|
||
|
rv = NSS_GetAlgorithmPolicy(hashAlg, &policyFlags);
|
||
|
if (rv == SECSuccess &&
|
||
|
!(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
||
|
rv = SECFailure;
|
||
|
}
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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,
|
||
|
int64 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Software FORTEZZA installation hack. The software fortezza installer does
|
||
|
* not have access to the krl and cert.db file. Accept FORTEZZA Certs without
|
||
|
* KRL's in this case.
|
||
|
*/
|
||
|
static int dont_use_krl = 0;
|
||
|
/* not a public exposed function... */
|
||
|
void sec_SetCheckKRLState(int value) { dont_use_krl = value; }
|
||
|
|
||
|
SECStatus
|
||
|
SEC_CheckKRL(CERTCertDBHandle *handle,SECKEYPublicKey *key,
|
||
|
CERTCertificate *rootCert, int64 t, void * wincx)
|
||
|
{
|
||
|
CERTSignedCrl *crl = NULL;
|
||
|
SECStatus rv = SECFailure;
|
||
|
SECStatus rv2;
|
||
|
CERTCrlEntry **crlEntry;
|
||
|
SECCertTimeValidity validity;
|
||
|
CERTCertificate *issuerCert = NULL;
|
||
|
|
||
|
if (dont_use_krl) return SECSuccess;
|
||
|
|
||
|
/* first look up the KRL */
|
||
|
crl = SEC_FindCrlByName(handle,&rootCert->derSubject, SEC_KRL_TYPE);
|
||
|
if (crl == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_NO_KRL);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* get the issuing certificate */
|
||
|
issuerCert = CERT_FindCertByName(handle, &crl->crl.derName);
|
||
|
if (issuerCert == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* now verify the KRL signature */
|
||
|
rv2 = CERT_VerifySignedData(&crl->signatureWrap, issuerCert, t, wincx);
|
||
|
if (rv2 != SECSuccess) {
|
||
|
PORT_SetError(SEC_ERROR_KRL_BAD_SIGNATURE);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* Verify the date validity of the KRL */
|
||
|
validity = SEC_CheckCrlTimes(&crl->crl, t);
|
||
|
if (validity == secCertTimeExpired) {
|
||
|
PORT_SetError(SEC_ERROR_KRL_EXPIRED);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* now make sure the key in this cert is still valid */
|
||
|
if (key->keyType != fortezzaKey) {
|
||
|
PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
|
||
|
goto done; /* This should be an assert? */
|
||
|
}
|
||
|
|
||
|
/* now make sure the key is not on the revocation list */
|
||
|
for (crlEntry = crl->crl.entries; crlEntry && *crlEntry; crlEntry++) {
|
||
|
if (PORT_Memcmp((*crlEntry)->serialNumber.data,
|
||
|
key->u.fortezza.KMID,
|
||
|
(*crlEntry)->serialNumber.len) == 0) {
|
||
|
PORT_SetError(SEC_ERROR_REVOKED_KEY);
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
rv = SECSuccess;
|
||
|
|
||
|
done:
|
||
|
if (issuerCert) CERT_DestroyCertificate(issuerCert);
|
||
|
if (crl) SEC_DestroyCrl(crl);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
SECStatus
|
||
|
SEC_CheckCRL(CERTCertDBHandle *handle,CERTCertificate *cert,
|
||
|
CERTCertificate *caCert, int64 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, int64 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 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, unsigned 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 *)arg); \
|
||
|
} else { \
|
||
|
goto loser; \
|
||
|
}
|
||
|
|
||
|
#define LOG_ERROR(log,cert,depth,arg) \
|
||
|
if ( log != NULL ) { \
|
||
|
cert_AddToVerifyLog(log, cert, PORT_GetError(), depth, (void *)arg); \
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef enum { cbd_None, cbd_User, cbd_CA } cbd_FortezzaType;
|
||
|
|
||
|
static SECStatus
|
||
|
cert_VerifyFortezzaV1Cert(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||
|
cbd_FortezzaType *next_type, cbd_FortezzaType last_type,
|
||
|
int64 t, void *wincx)
|
||
|
{
|
||
|
unsigned char priv = 0;
|
||
|
SECKEYPublicKey *key;
|
||
|
SECStatus rv;
|
||
|
|
||
|
*next_type = cbd_CA;
|
||
|
|
||
|
/* read the key */
|
||
|
key = CERT_ExtractPublicKey(cert);
|
||
|
|
||
|
/* Cant' get Key? fail. */
|
||
|
if (key == NULL) {
|
||
|
PORT_SetError(SEC_ERROR_BAD_KEY);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* if the issuer is not an old fortezza cert, we bail */
|
||
|
if (key->keyType != fortezzaKey) {
|
||
|
SECKEY_DestroyPublicKey(key);
|
||
|
/* CA Cert not fortezza */
|
||
|
PORT_SetError(SEC_ERROR_NOT_FORTEZZA_ISSUER);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* get the privilege mask */
|
||
|
if (key->u.fortezza.DSSpriviledge.len > 0) {
|
||
|
priv = key->u.fortezza.DSSpriviledge.data[0];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* make sure the CA's keys are OK
|
||
|
*/
|
||
|
|
||
|
rv = SEC_CheckKRL(handle, key, NULL, t, wincx);
|
||
|
SECKEY_DestroyPublicKey(key);
|
||
|
if (rv != SECSuccess) {
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
switch (last_type) {
|
||
|
case cbd_User:
|
||
|
/* first check for subordination */
|
||
|
/*rv = FortezzaSubordinateCheck(cert,issuerCert);*/
|
||
|
rv = SECSuccess;
|
||
|
|
||
|
/* now check for issuer privilege */
|
||
|
if ((rv != SECSuccess) || ((priv & 0x10) == 0)) {
|
||
|
/* bail */
|
||
|
PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
case cbd_CA:
|
||
|
if ((priv & 0x20) == 0) {
|
||
|
/* bail */
|
||
|
PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
break;
|
||
|
case cbd_None:
|
||
|
*next_type = (priv & 0x30) ? cbd_CA : cbd_User;
|
||
|
break;
|
||
|
default:
|
||
|
/* bail */ /* shouldn't ever happen */
|
||
|
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
static SECStatus
|
||
|
cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||
|
PRBool checkSig, PRBool* sigerror,
|
||
|
SECCertUsage certUsage, int64 t, void *wincx,
|
||
|
CERTVerifyLog *log, PRBool* revoked)
|
||
|
{
|
||
|
SECTrustType trustType;
|
||
|
CERTBasicConstraints basicConstraint;
|
||
|
CERTCertificate *issuerCert = NULL;
|
||
|
CERTCertificate *subjectCert = NULL;
|
||
|
CERTCertificate *badCert = NULL;
|
||
|
PRBool isca;
|
||
|
PRBool isFortezzaV1 = PR_FALSE;
|
||
|
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;
|
||
|
PRArenaPool *arena = NULL;
|
||
|
CERTGeneralName *namesList = NULL;
|
||
|
CERTCertificate **certsList = NULL;
|
||
|
int certsListLen = 16;
|
||
|
int namesCount = 0;
|
||
|
PRBool subjectCertIsSelfIssued;
|
||
|
|
||
|
cbd_FortezzaType last_type = cbd_None;
|
||
|
|
||
|
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 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);
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/* determine if the cert is fortezza.
|
||
|
*/
|
||
|
isFortezzaV1 = (PRBool)
|
||
|
(CERT_GetCertKeyType(&subjectCert->subjectPublicKeyInfo)
|
||
|
== fortezzaKey);
|
||
|
|
||
|
if (isFortezzaV1) {
|
||
|
rv = cert_VerifyFortezzaV1Cert(handle, subjectCert, &last_type,
|
||
|
cbd_None, t, wincx);
|
||
|
if (rv == SECFailure) {
|
||
|
/**** PORT_SetError is already set by cert_VerifyFortezzaV1Cert **/
|
||
|
LOG_ERROR_OR_EXIT(log,subjectCert,0,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
subjectNameList = CERT_GetCertificateNames(subjectCert, arena);
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* 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 {
|
||
|
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
|
||
|
LOG_ERROR_OR_EXIT(log,subjectCert,count,0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* XXX - fortezza may need error logging stuff added
|
||
|
*/
|
||
|
if (isFortezzaV1) {
|
||
|
rv = cert_VerifyFortezzaV1Cert(handle, issuerCert, &last_type,
|
||
|
last_type, t, wincx);
|
||
|
if (rv == SECFailure) {
|
||
|
/**** PORT_SetError is already set by *
|
||
|
* cert_VerifyFortezzaV1Cert **/
|
||
|
LOG_ERROR_OR_EXIT(log,subjectCert,0,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).
|
||
|
*
|
||
|
* In the fortezza (V1) case, we've already checked the CA bits
|
||
|
* in the key, so we're presumed to be a CA; however we really don't
|
||
|
* want to bypass Basic constraint or netscape extension parsing.
|
||
|
*
|
||
|
* In Fortezza V2, basicConstraint will be set for every CA,PCA,PAA
|
||
|
*/
|
||
|
|
||
|
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, if we're fortezza, CA bit is already
|
||
|
* verified (isca = PR_TRUE). otherwise, we aren't (yet) a ca
|
||
|
* isca = PR_FALSE */
|
||
|
isca = isFortezzaV1;
|
||
|
} 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);
|
||
|
}
|
||
|
|
||
|
/* 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 ( issuerCert->trust ) {
|
||
|
/* 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(issuerCert->trust, trustType);
|
||
|
if (( flags & requiredFlags ) == requiredFlags) {
|
||
|
/* we found a trusted one, so return */
|
||
|
rv = rvFinal;
|
||
|
goto done;
|
||
|
}
|
||
|
if (flags & CERTDB_VALID_CA) {
|
||
|
validCAOverride = PR_TRUE;
|
||
|
}
|
||
|
} 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(issuerCert->trust, trustType);
|
||
|
if ((flags & requiredFlags) == requiredFlags) {
|
||
|
rv = rvFinal;
|
||
|
goto done;
|
||
|
}
|
||
|
if (flags & CERTDB_VALID_CA)
|
||
|
validCAOverride = PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 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;
|
||
|
}
|
||
|
/* 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, int64 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, int64 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, int64 t,
|
||
|
void *wincx, CERTVerifyLog *log)
|
||
|
{
|
||
|
SECTrustType trustType;
|
||
|
CERTBasicConstraints basicConstraint;
|
||
|
PRBool isca;
|
||
|
PRBool validCAOverride = PR_FALSE;
|
||
|
SECStatus rv;
|
||
|
SECStatus rvFinal = SECSuccess;
|
||
|
int flags;
|
||
|
unsigned int caCertType;
|
||
|
unsigned int requiredCAKeyUsage;
|
||
|
unsigned int requiredFlags;
|
||
|
CERTCertificate *issuerCert;
|
||
|
|
||
|
|
||
|
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 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).
|
||
|
*
|
||
|
* In the fortezza (V1) case, we've already checked the CA bits
|
||
|
* in the key, so we're presumed to be a CA; however we really don't
|
||
|
* want to bypass Basic constraint or netscape extension parsing.
|
||
|
*
|
||
|
* In Fortezza V2, basicConstraint will be set for every CA,PCA,PAA
|
||
|
*/
|
||
|
|
||
|
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, if we're fortezza, CA bit is already
|
||
|
* verified (isca = PR_TRUE). otherwise, we aren't (yet) a ca
|
||
|
* isca = PR_FALSE */
|
||
|
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->trust ) {
|
||
|
/* 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 parms of the issuer
|
||
|
*/
|
||
|
flags = SEC_GET_TRUST_FLAGS(cert->trust, trustType);
|
||
|
if ( ( flags & requiredFlags ) == requiredFlags) {
|
||
|
/* we found a trusted one, so return */
|
||
|
rv = rvFinal;
|
||
|
goto done;
|
||
|
}
|
||
|
if (flags & CERTDB_VALID_CA) {
|
||
|
validCAOverride = PR_TRUE;
|
||
|
}
|
||
|
}
|
||
|
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(); \
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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, int64 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;
|
||
|
|
||
|
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));
|
||
|
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:
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
/* check trust flags to see if this cert is directly trusted */
|
||
|
if ( cert->trust ) { /* the cert is in the DB */
|
||
|
switch ( certUsage ) {
|
||
|
case certUsageSSLClient:
|
||
|
case certUsageSSLServer:
|
||
|
flags = cert->trust->sslFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
|
||
|
if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
|
||
|
VALID_USAGE();
|
||
|
} else { /* don't trust this cert */
|
||
|
if (PR_TRUE == requiredUsage) {
|
||
|
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
|
||
|
}
|
||
|
LOG_ERROR(log,cert,0,flags);
|
||
|
INVALID_USAGE();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case certUsageSSLServerWithStepUp:
|
||
|
/* XXX - step up certs can't be directly trusted */
|
||
|
break;
|
||
|
case certUsageSSLCA:
|
||
|
break;
|
||
|
case certUsageEmailSigner:
|
||
|
case certUsageEmailRecipient:
|
||
|
flags = cert->trust->emailFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) ==
|
||
|
( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) {
|
||
|
VALID_USAGE();
|
||
|
}
|
||
|
break;
|
||
|
case certUsageObjectSigner:
|
||
|
flags = cert->trust->objectSigningFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
|
||
|
if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
|
||
|
VALID_USAGE();
|
||
|
} else { /* don't trust this cert */
|
||
|
if (PR_TRUE == requiredUsage) {
|
||
|
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
|
||
|
}
|
||
|
LOG_ERROR(log,cert,0,flags);
|
||
|
INVALID_USAGE();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case certUsageVerifyCA:
|
||
|
case certUsageStatusResponder:
|
||
|
flags = cert->trust->sslFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
VALID_USAGE();
|
||
|
}
|
||
|
flags = cert->trust->emailFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
VALID_USAGE();
|
||
|
}
|
||
|
flags = cert->trust->objectSigningFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
VALID_USAGE();
|
||
|
}
|
||
|
break;
|
||
|
case certUsageAnyCA:
|
||
|
case certUsageProtectedObjectSigner:
|
||
|
case certUsageUserCertImport:
|
||
|
/* XXX to make the compiler happy. Should these be
|
||
|
* explicitly handled?
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 reponder 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, int64 t,
|
||
|
void *wincx, CERTVerifyLog *log)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
unsigned int requiredKeyUsage;
|
||
|
unsigned int requiredCertType;
|
||
|
unsigned int flags;
|
||
|
unsigned int certType;
|
||
|
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));
|
||
|
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 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);
|
||
|
}
|
||
|
|
||
|
/* check trust flags to see if this cert is directly trusted */
|
||
|
if ( cert->trust ) { /* the cert is in the DB */
|
||
|
switch ( certUsage ) {
|
||
|
case certUsageSSLClient:
|
||
|
case certUsageSSLServer:
|
||
|
flags = cert->trust->sslFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
|
||
|
if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
|
||
|
goto winner;
|
||
|
} else { /* don't trust this cert */
|
||
|
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
|
||
|
LOG_ERROR_OR_EXIT(log,cert,0,flags);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case certUsageSSLServerWithStepUp:
|
||
|
/* XXX - step up certs can't be directly trusted */
|
||
|
break;
|
||
|
case certUsageSSLCA:
|
||
|
break;
|
||
|
case certUsageEmailSigner:
|
||
|
case certUsageEmailRecipient:
|
||
|
flags = cert->trust->emailFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) ==
|
||
|
( CERTDB_VALID_PEER | CERTDB_TRUSTED ) ) {
|
||
|
goto winner;
|
||
|
}
|
||
|
break;
|
||
|
case certUsageObjectSigner:
|
||
|
flags = cert->trust->objectSigningFlags;
|
||
|
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( flags & CERTDB_VALID_PEER ) {/*the trust record is valid*/
|
||
|
if ( flags & CERTDB_TRUSTED ) { /* trust this cert */
|
||
|
goto winner;
|
||
|
} else { /* don't trust this cert */
|
||
|
PORT_SetError(SEC_ERROR_UNTRUSTED_CERT);
|
||
|
LOG_ERROR_OR_EXIT(log,cert,0,flags);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case certUsageVerifyCA:
|
||
|
case certUsageStatusResponder:
|
||
|
flags = cert->trust->sslFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
goto winner;
|
||
|
}
|
||
|
flags = cert->trust->emailFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
goto winner;
|
||
|
}
|
||
|
flags = cert->trust->objectSigningFlags;
|
||
|
/* is the cert directly trusted or not trusted ? */
|
||
|
if ( ( flags & ( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) ==
|
||
|
( CERTDB_VALID_CA | CERTDB_TRUSTED_CA ) ) {
|
||
|
goto winner;
|
||
|
}
|
||
|
break;
|
||
|
case certUsageAnyCA:
|
||
|
case certUsageProtectedObjectSigner:
|
||
|
case certUsageUserCertImport:
|
||
|
/* XXX to make the compiler happy. Should these be
|
||
|
* explicitly handled?
|
||
|
*/
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 reponder 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, which
|
||
|
* is all done inside VerifyCertChain) by bypassing this code.
|
||
|
*/
|
||
|
statusConfig = CERT_GetStatusConfig(handle);
|
||
|
if (certUsage != certUsageStatusResponder && statusConfig != NULL) {
|
||
|
if (statusConfig->statusChecker != NULL) {
|
||
|
rv = (* statusConfig->statusChecker)(handle, cert,
|
||
|
t, wincx);
|
||
|
if (rv != SECSuccess) {
|
||
|
LOG_ERROR_OR_EXIT(log,cert,0,0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
winner:
|
||
|
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
|
||
|
* certUsageEmailSigner
|
||
|
* certUsageEmailRecipient
|
||
|
* certUsageObjectSigner
|
||
|
*/
|
||
|
|
||
|
CERTCertificate *
|
||
|
CERT_FindMatchingCert(CERTCertDBHandle *handle, SECItem *derName,
|
||
|
CERTCertOwner owner, SECCertUsage usage,
|
||
|
PRBool preferTrusted, int64 validTime, PRBool validOnly)
|
||
|
{
|
||
|
CERTCertList *certList = NULL;
|
||
|
CERTCertificate *cert = NULL;
|
||
|
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->trust == NULL ) {
|
||
|
flags = 0;
|
||
|
} else {
|
||
|
flags = SEC_GET_TRUST_FLAGS(cert->trust, 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;
|
||
|
int64 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(PRArenaPool *arena, CERTCertificate *cert,
|
||
|
char *expiredString, char *notYetGoodString)
|
||
|
{
|
||
|
SECCertTimeValidity validity;
|
||
|
char *nickname = NULL, *tmpstr = NULL;
|
||
|
|
||
|
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(cert->nickname);
|
||
|
} else {
|
||
|
nickname = PORT_ArenaStrdup(arena, cert->nickname);
|
||
|
}
|
||
|
|
||
|
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", cert->nickname,
|
||
|
expiredString);
|
||
|
} else if ( validity == secCertTimeNotValidYet ) {
|
||
|
/* not yet valid */
|
||
|
tmpstr = PR_smprintf("%s%s", cert->nickname,
|
||
|
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;
|
||
|
PRArenaPool *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, int64 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;
|
||
|
}
|