Add new CERT_VerifyCertificate function - fix for 149832

This commit is contained in:
jpierre%netscape.com 2002-07-04 03:09:49 +00:00
parent 1267864ca1
commit 967d483ebe
4 changed files with 372 additions and 21 deletions

View File

@ -34,7 +34,7 @@
/*
* cert.h - public data structures and prototypes for the certificate library
*
* $Id: cert.h,v 1.16 2002/06/19 15:58:22 rangansen%netscape.com Exp $
* $Id: cert.h,v 1.17 2002/07/04 03:09:29 jpierre%netscape.com Exp $
*/
#ifndef _CERT_H_
@ -532,6 +532,27 @@ extern SECStatus CERT_VerifySignedData(CERTSignedData *sd,
void *wincx);
/*
** NEW FUNCTIONS with new bit-field-FIELD SECCertificateUsage - please use
** verify a certificate by checking validity times against a certain time,
** that we trust the issuer, and that the signature on the certificate is
** valid.
** "cert" the certificate to verify
** "checkSig" only check signatures if true
*/
extern SECStatus
CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertificateUsage requiredUsages,
int64 t, void *wincx, CERTVerifyLog *log,
SECCertificateUsage* returnedUsages);
/* same as above, but uses current time */
extern SECStatus
CERT_VerifyCertificateNow(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertificateUsage requiredUsages,
void *wincx, SECCertificateUsage* returnedUsages);
/*
** OLD OBSOLETE FUNCTIONS with enum SECCertUsage - DO NOT USE FOR NEW CODE
** verify a certificate by checking validity times against a certain time,
** that we trust the issuer, and that the signature on the certificate is
** valid.

View File

@ -33,7 +33,7 @@
/*
* certt.h - public data structures for the certificate library
*
* $Id: certt.h,v 1.18 2002/06/13 21:42:41 relyea%netscape.com Exp $
* $Id: certt.h,v 1.19 2002/07/04 03:09:29 jpierre%netscape.com Exp $
*/
#ifndef _CERTT_H_
#define _CERTT_H_
@ -473,6 +473,23 @@ typedef enum SECCertUsageEnum {
certUsageAnyCA = 11
} SECCertUsage;
typedef PRInt64 SECCertificateUsage;
#define certificateUsageSSLClient (0x0001)
#define certificateUsageSSLServer (0x0002)
#define certificateUsageSSLServerWithStepUp (0x0004)
#define certificateUsageSSLCA (0x0008)
#define certificateUsageEmailSigner (0x0010)
#define certificateUsageEmailRecipient (0x0020)
#define certificateUsageObjectSigner (0x0040)
#define certificateUsageUserCertImport (0x0080)
#define certificateUsageVerifyCA (0x0100)
#define certificateUsageProtectedObjectSigner (0x0200)
#define certificateUsageStatusResponder (0x0400)
#define certificateUsageAnyCA (0x0800)
#define highestUsage certificateUsageAnyCA
/*
* Does the cert belong to the user, a peer, or a CA.
*/

View File

@ -556,13 +556,10 @@ AddToVerifyLog(CERTVerifyLog *log, CERTCertificate *cert, unsigned long error,
AddToVerifyLog(log, cert, PORT_GetError(), depth, (void *)arg); \
}
SECStatus
CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
__CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, int64 t,
void *wincx, CERTVerifyLog *log)
void *wincx, CERTVerifyLog *log, PRBool doCRL, PRBool* revoked)
{
SECTrustType trustType;
CERTBasicConstraints basicConstraint;
@ -590,6 +587,10 @@ CERT_VerifyCertChain(CERTCertDBHandle *handle, CERTCertificate *cert,
enum { cbd_None, cbd_User, cbd_CA } last_type = cbd_None;
SECKEYPublicKey *key;
if (revoked) {
*revoked = PR_FALSE;
}
if (CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_TRUE,
&requiredCAKeyUsage,
&caCertType)
@ -855,17 +856,25 @@ fortezzaDone:
* point
*/
/* check revoked list (issuer) */
rv = SEC_CheckCRL(handle, subjectCert, issuerCert, t, wincx);
if (rv == SECFailure) {
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;
LOG_ERROR(log,subjectCert,count,0);
}
if (PR_TRUE == doCRL) {
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 ) {
@ -977,13 +986,304 @@ done:
}
return rv;
}
#define NEXT_ITERATION() { \
i*=2; \
certUsage++; \
continue; \
}
#define VALID_USAGE() { \
NEXT_ITERATION(); \
}
#define INVALID_USAGE() { \
if (returnedUsages) { \
*returnedUsages &= !i; \
} \
if (PR_TRUE == requiredUsage) { \
valid = SECFailure; \
} \
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, certUsage, t, wincx, log,
PR_TRUE, NULL);
}
/*
* verify a certificate by checking if its valid and that we
* verify a certificate by checking if it's valid and that we
* trust the issuer.
* Note that this routine does not verify the signature of the certificate.
*
* 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;
PRBool checkedChain = PR_FALSE;
PRInt32 i;
SECCertUsage certUsage = 0;
PRBool doOCSP = PR_FALSE;
PRBool checkedCRL = PR_FALSE;
PRBool checkedOCSP = PR_FALSE;
PRBool checkAllUsages = PR_FALSE;
PRBool revoked = 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 */
#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(log,cert,0,0);
return SECFailure;
}
#endif
/* make sure that the cert is valid at time t */
allowOverride = (PRBool)((requiredUsages & certUsageSSLServer) ||
(requiredUsages & certificateUsageSSLServerWithStepUp));
validity = CERT_CheckCertValidTimes(cert, t, allowOverride);
if ( validity != secCertTimeValid ) {
LOG_ERROR(log,cert,0,validity);
return SECFailure;
}
/* check key usage and netscape cert type */
CERT_GetCertType(cert);
certType = cert->nsCertType;
for (i=1;i<=highestUsage && !(SECFailure == valid && !returnedUsages) ;) {
PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE;
if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) {
NEXT_ITERATION();
}
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 certUsageVerifyCA:
requiredKeyUsage = KU_KEY_CERT_SIGN;
requiredCertType = NS_CERT_TYPE_CA;
if ( ! ( certType & NS_CERT_TYPE_CA ) ) {
certType |= NS_CERT_TYPE_CA;
}
break;
case certUsageAnyCA:
case certUsageProtectedObjectSigner:
case certUsageUserCertImport:
/* these usages cannot be verified */
NEXT_ITERATION();
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) {
INVALID_USAGE();
}
/* only optionally check signature the first time */
/* don't check CRL as part of cert chain, we are doing that separately */
/* rv = __CERT_VerifyCertChain(handle, cert, (PR_TRUE == checkedChain) ? PR_FALSE : checkSig,
certUsage, t, wincx, log, issuerCert, PR_TRUE); */
rv = __CERT_VerifyCertChain(handle, cert, (PR_TRUE == checkedChain) ? PR_FALSE : checkSig,
certUsage, t, wincx, log, checkedChain?PR_FALSE:PR_TRUE, &revoked);
checkedChain = PR_TRUE;
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_ITERATION();
}
return(valid);
}
/* obsolete, do not use for new code */
SECStatus
CERT_VerifyCert(CERTCertDBHandle *handle, CERTCertificate *cert,
PRBool checkSig, SECCertUsage certUsage, int64 t,
void *wincx, CERTVerifyLog *log)
@ -1172,6 +1472,16 @@ loser:
* 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)
{
@ -1179,6 +1489,7 @@ CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert,
certUsage, PR_Now(), wincx, NULL));
}
/* [ FROM pcertdb.c ] */
/*
* Supported usage values and types:

View File

@ -695,6 +695,8 @@ CERT_VerifyOCSPResponseSignature;
CERT_GetOCSPResponseStatus;
CERT_DestroyOCSPCertID;
CERT_CreateOCSPCertID;
CERT_VerifyCertificate;
CERT_VerifyCertificateNow;
;+ local:
;+ *;
;+};