1) factor out fortezzav1 from the chain processing code to make the code easier
to read. 2) only extract keys if we are using fortezzav1 cert (should speed up cert verify a bit). 3) Add function to verify a specific CA cert to verify a userCert Usage. git-svn-id: svn://10.0.0.236/trunk@126239 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
fdde256a14
commit
d3854a3ac9
@ -34,7 +34,7 @@
|
||||
/*
|
||||
* cert.h - public data structures and prototypes for the certificate library
|
||||
*
|
||||
* $Id: cert.h,v 1.18 2002-07-19 00:59:20 jpierre%netscape.com Exp $
|
||||
* $Id: cert.h,v 1.19 2002-08-02 17:51:17 relyea%netscape.com Exp $
|
||||
*/
|
||||
|
||||
#ifndef _CERT_H_
|
||||
@ -571,6 +571,16 @@ CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
PRBool checkSig, SECCertificateUsage requiredUsages,
|
||||
void *wincx, SECCertificateUsage* returnedUsages);
|
||||
|
||||
/*
|
||||
** Verify that a CA cert can certify some (unspecified) leaf cert for a given
|
||||
** purpose. This is used by UI code to help identify where a chain may be
|
||||
** broken and why. This takes identical parameters to CERT_VerifyCert
|
||||
*/
|
||||
extern SECStatus
|
||||
CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
PRBool checkSig, SECCertUsage certUsage, int64 t,
|
||||
void *wincx, CERTVerifyLog *log);
|
||||
|
||||
/*
|
||||
** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE
|
||||
** verify a certificate by checking validity times against a certain time,
|
||||
|
||||
@ -556,8 +556,87 @@ AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, unsigned long error,
|
||||
AddToVerifyLog(log, cert, PORT_GetError(), depth, (void *)arg); \
|
||||
}
|
||||
|
||||
SECStatus
|
||||
__CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
|
||||
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_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
PRBool checkSig, PRBool* sigerror,
|
||||
SECCertUsage certUsage, int64 t, void *wincx,
|
||||
CERTVerifyLog *log, PRBool doCRL, PRBool* revoked)
|
||||
@ -585,8 +664,7 @@ __CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
int namesIndexLen = 10;
|
||||
int namesCount = 0;
|
||||
|
||||
enum { cbd_None, cbd_User, cbd_CA } last_type = cbd_None;
|
||||
SECKEYPublicKey *key;
|
||||
cbd_FortezzaType last_type = cbd_None;
|
||||
|
||||
if (revoked) {
|
||||
*revoked = PR_FALSE;
|
||||
@ -635,36 +713,19 @@ __CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
goto loser;
|
||||
}
|
||||
|
||||
/* determine if the cert is fortezza. Getting the key is an easy
|
||||
* way to determine it, especially since we need to get the privillege
|
||||
* from the key anyway.
|
||||
/* determine if the cert is fortezza.
|
||||
*/
|
||||
key = CERT_ExtractPublicKey(cert);
|
||||
isFortezzaV1 = (PRBool)
|
||||
(CERT_GetCertKeyType(&subjectCert->subjectPublicKeyInfo)
|
||||
== fortezzaKey);
|
||||
|
||||
if (key != NULL) {
|
||||
isFortezzaV1 = (PRBool)(key->keyType == fortezzaKey);
|
||||
|
||||
/* find out what type of cert we are starting with */
|
||||
if (isFortezzaV1) {
|
||||
unsigned char priv = 0;;
|
||||
|
||||
rv = SEC_CheckKRL(handle, key, NULL, t, wincx);
|
||||
if (rv == SECFailure) {
|
||||
/**** PORT_SetError is already set by SEC_CheckKRL **/
|
||||
SECKEY_DestroyPublicKey(key);
|
||||
/**** Bob - should we log and continue when logging? **/
|
||||
LOG_ERROR(log,subjectCert,0,0);
|
||||
goto loser;
|
||||
}
|
||||
|
||||
if (key->u.fortezza.DSSpriviledge.len > 0) {
|
||||
priv = key->u.fortezza.DSSpriviledge.data[0];
|
||||
}
|
||||
|
||||
last_type = (priv & 0x30) ? cbd_CA : cbd_User;
|
||||
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);
|
||||
}
|
||||
|
||||
SECKEY_DestroyPublicKey(key);
|
||||
}
|
||||
|
||||
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||||
@ -731,76 +792,13 @@ __CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
* XXX - fortezza may need error logging stuff added
|
||||
*/
|
||||
if (isFortezzaV1) {
|
||||
unsigned char priv = 0;
|
||||
|
||||
/* read the key */
|
||||
key = CERT_ExtractPublicKey(issuerCert);
|
||||
|
||||
/* Cant' get Key? fail. */
|
||||
if (key == NULL) {
|
||||
PORT_SetError(SEC_ERROR_BAD_KEY);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
goto fortezzaDone;
|
||||
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 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);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
goto fortezzaDone;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (rv != SECSuccess) {
|
||||
SECKEY_DestroyPublicKey(key);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
goto fortezzaDone;
|
||||
/** fall through looking for more stuff **/
|
||||
} else {
|
||||
SECKEY_DestroyPublicKey(key);
|
||||
}
|
||||
|
||||
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);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
}
|
||||
break;
|
||||
case cbd_CA:
|
||||
case cbd_None:
|
||||
if ((priv & 0x20) == 0) {
|
||||
/* bail */
|
||||
PORT_SetError (SEC_ERROR_CA_CERT_INVALID);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* bail */ /* shouldn't ever happen */
|
||||
PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
|
||||
LOG_ERROR_OR_EXIT(log,issuerCert,count+1,0);
|
||||
}
|
||||
|
||||
fortezzaDone:
|
||||
last_type = cbd_CA;
|
||||
}
|
||||
|
||||
/* If the basicConstraint extension is included in an immediate CA
|
||||
@ -991,6 +989,177 @@ done:
|
||||
return rv;
|
||||
}
|
||||
|
||||
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, PR_TRUE, 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;
|
||||
SECStatus rv;
|
||||
SECComparison rvCompare;
|
||||
SECStatus rvFinal = SECSuccess;
|
||||
int flags;
|
||||
unsigned int caCertType;
|
||||
unsigned int requiredCAKeyUsage;
|
||||
unsigned int requiredFlags;
|
||||
|
||||
|
||||
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 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(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 ) {
|
||||
/*
|
||||
* check the trust parms of the issuer
|
||||
*/
|
||||
flags = SEC_GET_TRUST_FLAGS(cert->trust, trustType);
|
||||
|
||||
if ( (flags & CERTDB_VALID_CA) ||
|
||||
(certUsage == certUsageStatusResponder)) {
|
||||
if ( ( flags & requiredFlags ) == requiredFlags ||
|
||||
certUsage == certUsageStatusResponder ) {
|
||||
/* we found a trusted one, so return */
|
||||
rv = rvFinal;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 ( isca ) {
|
||||
/*
|
||||
* 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 ( cert->nsCertType & NS_CERT_TYPE_CA ) {
|
||||
if ( cert->nsCertType & caCertType ) {
|
||||
isca = PR_TRUE;
|
||||
} else {
|
||||
isca = PR_FALSE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( cert->nsCertType & caCertType ) {
|
||||
isca = PR_TRUE;
|
||||
} else {
|
||||
isca = 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.
|
||||
*/
|
||||
rvCompare = SECITEM_CompareItem(&cert->derSubject,
|
||||
&cert->derIssuer);
|
||||
if (rvCompare == SECEqual) {
|
||||
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_ITERATION() { \
|
||||
i*=2; \
|
||||
certUsage++; \
|
||||
@ -1011,15 +1180,6 @@ done:
|
||||
NEXT_ITERATION(); \
|
||||
}
|
||||
|
||||
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,
|
||||
PR_TRUE, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* verify a certificate by checking if it's valid and that we
|
||||
* trust the issuer.
|
||||
@ -1238,7 +1398,7 @@ CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert,
|
||||
}
|
||||
|
||||
/* only check CRL and signature for the first usage check */
|
||||
rv = __CERT_VerifyCertChain(handle, cert,
|
||||
rv = cert_VerifyCertChain(handle, cert,
|
||||
(PR_TRUE == checkedChain) ? PR_FALSE : checkSig, &sigerror,
|
||||
certUsage, t, wincx, log,
|
||||
(PR_TRUE == checkedChain) ? PR_FALSE : PR_TRUE, &revoked);
|
||||
|
||||
@ -692,7 +692,8 @@ CERT_DestroyOCSPRequest;
|
||||
CERT_EncodeOCSPRequest;
|
||||
CERT_GetOCSPResponseStatus;
|
||||
CERT_GetOCSPStatusForCertID;
|
||||
CERT_VerifyCertChain;
|
||||
CERT_VerifyCACertForUsage;
|
||||
CERT_VerifyCertChain; /* no longer needed remove before 3.6 RTM!! */
|
||||
CERT_VerifyCertificate;
|
||||
CERT_VerifyCertificateNow;
|
||||
CERT_VerifyOCSPResponseSignature;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user