зеркало из https://github.com/mozilla/pjs.git
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.
This commit is contained in:
Родитель
7cb1945111
Коммит
0cd1ab2a75
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче