diff --git a/CHANGES b/CHANGES index 569f8ce79d00a7523f2e4f5c59110fd983dbc9e1..dedd7c328724c86c859816dd4e627ce837a5d633 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,16 @@ Changes between 0.9.6 and 0.9.7 [xx XXX 2000] + *) Add additional OCSP certificate checks. These are those specified + in RFC2560. This consists of two separate checks: the CA of the + certificate being checked must either be the OCSP signer certificate + or the issuer of the OCSP signer certificate. In the latter case the + OCSP signer certificate must contain the OCSP signing extended key + usage. This check is performed by attempting to match the OCSP + signer or the OCSP signer CA to the issuerNameHash and issuerKeyHash + in the OCSP_CERTID structures of the response. + [Steve Henson] + *) Initial OCSP certificate verification added to OCSP_basic_verify() and related routines. This uses the standard OpenSSL certificate verify routines to perform initial checks (just CA validity) and diff --git a/apps/ocsp.c b/apps/ocsp.c index 3125583ace3c777266cc2974b32f9e497665b767..bea16ebb711763f7fbe162abeebfb4663e575d2f 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -420,6 +420,8 @@ int MAIN(int argc, char **argv) BIO_printf(bio_err, "Response verify error (%d)\n", i); ERR_print_errors(bio_err); } + else + BIO_printf(bio_err, "Response verify OK\n"); ret = 0; diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h index 1cb6eadfbb53a8057e441ae4b760aa0b45d1a1d9..ef36ba991080b67b86de40f7af33bdcf408f38fa 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -561,7 +561,11 @@ void ERR_load_OCSP_strings(void); #define OCSP_F_CERT_STATUS_NEW 103 #define OCSP_F_D2I_OCSP_NONCE 109 #define OCSP_F_OCSP_BASIC_VERIFY 113 +#define OCSP_F_OCSP_CHECK_DELEGATED 117 +#define OCSP_F_OCSP_CHECK_IDS 114 +#define OCSP_F_OCSP_CHECK_ISSUER 115 #define OCSP_F_OCSP_CHECK_NONCE 112 +#define OCSP_F_OCSP_MATCH_ISSUERID 116 #define OCSP_F_OCSP_RESPONSE_GET1_BASIC 111 #define OCSP_F_OCSP_SENDREQ_BIO 110 #define OCSP_F_REQUEST_VERIFY 104 @@ -577,15 +581,18 @@ void ERR_load_OCSP_strings(void); #define OCSP_R_FAILED_TO_OPEN 109 #define OCSP_R_FAILED_TO_READ 110 #define OCSP_R_FAILED_TO_STAT 111 +#define OCSP_R_MISSING_OCSPSIGNING_USAGE 131 #define OCSP_R_MISSING_VALUE 112 #define OCSP_R_NONCE_MISSING_IN_RESPONSE 121 #define OCSP_R_NONCE_VALUE_MISMATCH 122 #define OCSP_R_NOT_BASIC_RESPONSE 120 #define OCSP_R_NO_CERTIFICATE 102 +#define OCSP_R_NO_CERTIFICATES_IN_CHAIN 128 #define OCSP_R_NO_CONTENT 115 #define OCSP_R_NO_PUBLIC_KEY 103 #define OCSP_R_NO_RESPONSE_DATA 104 #define OCSP_R_NO_SIGNATURE 105 +#define OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA 129 #define OCSP_R_REVOKED_NO_TIME 106 #define OCSP_R_ROOT_CA_NOT_TRUSTED 127 #define OCSP_R_SERVER_READ_ERROR 116 @@ -595,6 +602,7 @@ void ERR_load_OCSP_strings(void); #define OCSP_R_SIGNATURE_FAILURE 124 #define OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND 125 #define OCSP_R_UNEXPECTED_NONCE_IN_RESPONSE 123 +#define OCSP_R_UNKNOWN_MESSAGE_DIGEST 130 #define OCSP_R_UNKNOWN_NID 107 #define OCSP_R_UNSUPPORTED_OPTION 113 #define OCSP_R_VALUE_ALREADY 114 diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c index f4335d28c978fd235c10d711a8c1a9adf95a8a29..b860872cde8c80c8149ad88f3a42514034f0caf5 100644 --- a/crypto/ocsp/ocsp_err.c +++ b/crypto/ocsp/ocsp_err.c @@ -73,7 +73,11 @@ static ERR_STRING_DATA OCSP_str_functs[]= {ERR_PACK(0,OCSP_F_CERT_STATUS_NEW,0), "CERT_STATUS_NEW"}, {ERR_PACK(0,OCSP_F_D2I_OCSP_NONCE,0), "D2I_OCSP_NONCE"}, {ERR_PACK(0,OCSP_F_OCSP_BASIC_VERIFY,0), "OCSP_basic_verify"}, +{ERR_PACK(0,OCSP_F_OCSP_CHECK_DELEGATED,0), "OCSP_CHECK_DELEGATED"}, +{ERR_PACK(0,OCSP_F_OCSP_CHECK_IDS,0), "OCSP_CHECK_IDS"}, +{ERR_PACK(0,OCSP_F_OCSP_CHECK_ISSUER,0), "OCSP_CHECK_ISSUER"}, {ERR_PACK(0,OCSP_F_OCSP_CHECK_NONCE,0), "OCSP_check_nonce"}, +{ERR_PACK(0,OCSP_F_OCSP_MATCH_ISSUERID,0), "OCSP_MATCH_ISSUERID"}, {ERR_PACK(0,OCSP_F_OCSP_RESPONSE_GET1_BASIC,0), "OCSP_response_get1_basic"}, {ERR_PACK(0,OCSP_F_OCSP_SENDREQ_BIO,0), "OCSP_sendreq_bio"}, {ERR_PACK(0,OCSP_F_REQUEST_VERIFY,0), "REQUEST_VERIFY"}, @@ -92,15 +96,18 @@ static ERR_STRING_DATA OCSP_str_reasons[]= {OCSP_R_FAILED_TO_OPEN ,"failed to open"}, {OCSP_R_FAILED_TO_READ ,"failed to read"}, {OCSP_R_FAILED_TO_STAT ,"failed to stat"}, +{OCSP_R_MISSING_OCSPSIGNING_USAGE ,"missing ocspsigning usage"}, {OCSP_R_MISSING_VALUE ,"missing value"}, {OCSP_R_NONCE_MISSING_IN_RESPONSE ,"nonce missing in response"}, {OCSP_R_NONCE_VALUE_MISMATCH ,"nonce value mismatch"}, {OCSP_R_NOT_BASIC_RESPONSE ,"not basic response"}, {OCSP_R_NO_CERTIFICATE ,"no certificate"}, +{OCSP_R_NO_CERTIFICATES_IN_CHAIN ,"no certificates in chain"}, {OCSP_R_NO_CONTENT ,"no content"}, {OCSP_R_NO_PUBLIC_KEY ,"no public key"}, {OCSP_R_NO_RESPONSE_DATA ,"no response data"}, {OCSP_R_NO_SIGNATURE ,"no signature"}, +{OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA,"response contains no revocation data"}, {OCSP_R_REVOKED_NO_TIME ,"revoked no time"}, {OCSP_R_ROOT_CA_NOT_TRUSTED ,"root ca not trusted"}, {OCSP_R_SERVER_READ_ERROR ,"server read error"}, @@ -110,6 +117,7 @@ static ERR_STRING_DATA OCSP_str_reasons[]= {OCSP_R_SIGNATURE_FAILURE ,"signature failure"}, {OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND ,"signer certificate not found"}, {OCSP_R_UNEXPECTED_NONCE_IN_RESPONSE ,"unexpected nonce in response"}, +{OCSP_R_UNKNOWN_MESSAGE_DIGEST ,"unknown message digest"}, {OCSP_R_UNKNOWN_NID ,"unknown nid"}, {OCSP_R_UNSUPPORTED_OPTION ,"unsupported option"}, {OCSP_R_VALUE_ALREADY ,"value already"}, diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c index 6110825b19d85e5d5bee2ebfb8ea18d23dd88772..b656609ee8d53b473a182e5696fa3e69f94be2eb 100644 --- a/crypto/ocsp/ocsp_vfy.c +++ b/crypto/ocsp/ocsp_vfy.c @@ -62,6 +62,10 @@ static X509 *ocsp_find_signer(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, X509_STORE *st, unsigned long flags); static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id); +static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain, unsigned long flags); +static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp, OCSP_CERTID **ret); +static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid, STACK_OF(OCSP_SINGLERESP) *sresp); +static int ocsp_check_delegated(X509 *x, int flags); /* Verify a basic response message */ @@ -78,7 +82,7 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, OCSPerr(OCSP_F_OCSP_BASIC_VERIFY, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND); goto end; } - if(!(flags & OCSP_NOSIGS)) + if (!(flags & OCSP_NOSIGS)) { EVP_PKEY *skey; skey = X509_get_pubkey(signer); @@ -90,7 +94,7 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, goto end; } } - if(!(flags & OCSP_NOVERIFY)) + if (!(flags & OCSP_NOVERIFY)) { if(flags & OCSP_NOCHAIN) X509_STORE_CTX_init(&ctx, st, signer, NULL); @@ -115,15 +119,12 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, goto end; } /* At this point we have a valid certificate chain - * need to verify it against the OCSP criteria. + * need to verify it against the OCSP issuer criteria. */ -#if 0 - if(ocsp_check_issuer(bs, chain, flags)) - { - ret = 1; - goto end; - } -#endif + ret = ocsp_check_issuer(bs, chain, flags); + + /* If fatal error or valid match then finish */ + if (ret != 0) goto end; /* Easy case: explicitly trusted. Get root CA and * check for explicit trust @@ -172,16 +173,16 @@ static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id) X509 *x; /* Easy if lookup by name */ - if(id->type == V_OCSP_RESPID_NAME) + if (id->type == V_OCSP_RESPID_NAME) return X509_find_by_subject(certs, id->value.byName); /* Lookup by key hash */ /* If key hash isn't SHA1 length then forget it */ - if(id->value.byKey->length != SHA_DIGEST_LENGTH) return NULL; + if (id->value.byKey->length != SHA_DIGEST_LENGTH) return NULL; keyhash = id->value.byKey->data; /* Calculate hash of each key and compare */ - for(i = 0; i < sk_X509_num(certs); i++) + for (i = 0; i < sk_X509_num(certs); i++) { x = sk_X509_value(certs, i); key = x->cert_info->key->public_key; @@ -195,3 +196,148 @@ static X509 *ocsp_find_signer_sk(STACK_OF(X509) *certs, OCSP_RESPID *id) } +static int ocsp_check_issuer(OCSP_BASICRESP *bs, STACK_OF(X509) *chain, unsigned long flags) + { + STACK_OF(OCSP_SINGLERESP) *sresp; + X509 *signer, *sca; + OCSP_CERTID *caid = NULL; + int i; + sresp = bs->tbsResponseData->responses; + + if (sk_X509_num(chain) <= 0) + { + OCSPerr(OCSP_F_OCSP_CHECK_ISSUER, OCSP_R_NO_CERTIFICATES_IN_CHAIN); + return -1; + } + + /* See if the issuer IDs match. */ + i = ocsp_check_ids(sresp, &caid); + + /* If ID mismatch or other error then return */ + if (i <= 0) return i; + + signer = sk_X509_value(chain, 0); + /* Check to see if OCSP responder CA matches request CA */ + if (sk_X509_num(chain) > 1) + { + sca = sk_X509_value(chain, 1); + i = ocsp_match_issuerid(sca, caid, sresp); + if (i < 0) return i; + if (i) + { + /* We have a match, if extensions OK then success */ + if (ocsp_check_delegated(signer, flags)) return 1; + return 0; + } + } + + /* Otherwise check if OCSP request signed directly by request CA */ + return ocsp_match_issuerid(signer, caid, sresp); + } + + +/* Check the issuer certificate IDs for equality. If there is a mismatch with the same + * algorithm then there's no point trying to match any certificates against the issuer. + * If the issuer IDs all match then we just need to check equality against one of them. + */ + +static int ocsp_check_ids(STACK_OF(OCSP_SINGLERESP) *sresp, OCSP_CERTID **ret) + { + OCSP_CERTID *tmpid, *cid; + int i, idcount; + + idcount = sk_OCSP_SINGLERESP_num(sresp); + if (idcount <= 0) + { + OCSPerr(OCSP_F_OCSP_CHECK_IDS, OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA); + return -1; + } + + cid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId; + + *ret = NULL; + + for (i = 1; i < idcount; i++) + { + tmpid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId; + /* Check to see if IDs match */ + if (OCSP_id_issuer_cmp(cid, tmpid)) + { + /* If algoritm mismatch let caller deal with it */ + if (OBJ_cmp(tmpid->hashAlgorithm->algorithm, + cid->hashAlgorithm->algorithm)) + return 2; + /* Else mismatch */ + return 0; + } + } + + /* All IDs match: only need to check one ID */ + *ret = cid; + return 1; + } + + +static int ocsp_match_issuerid(X509 *cert, OCSP_CERTID *cid, + STACK_OF(OCSP_SINGLERESP) *sresp) + { + /* If only one ID to match then do it */ + if(cid) + { + const EVP_MD *dgst; + EVP_MD_CTX ctx; + X509_NAME *iname; + ASN1_BIT_STRING *ikey; + int mdlen; + unsigned char md[EVP_MAX_MD_SIZE]; + if (!(dgst = EVP_get_digestbyobj(cid->hashAlgorithm->algorithm))) + { + OCSPerr(OCSP_F_OCSP_MATCH_ISSUERID, OCSP_R_UNKNOWN_MESSAGE_DIGEST); + return -1; + } + + mdlen = EVP_MD_size(dgst); + if ((cid->issuerNameHash->length != mdlen) || + (cid->issuerKeyHash->length != mdlen)) + return 0; + iname = X509_get_issuer_name(cert); + if (!X509_NAME_digest(iname, dgst, md, NULL)) + return -1; + if (memcmp(md, cid->issuerNameHash->data, mdlen)) + return 0; + ikey = cert->cert_info->key->public_key; + + EVP_DigestInit(&ctx,dgst); + EVP_DigestUpdate(&ctx,ikey->data, ikey->length); + EVP_DigestFinal(&ctx,md,NULL); + if (memcmp(md, cid->issuerKeyHash->data, mdlen)) + return 0; + + return 1; + + } + else + { + /* We have to match the whole lot */ + int i, ret; + OCSP_CERTID *tmpid; + for (i = 0; i < sk_OCSP_SINGLERESP_num(sresp); i++) + { + tmpid = sk_OCSP_SINGLERESP_value(sresp, 0)->certId; + ret = ocsp_match_issuerid(cert, tmpid, NULL); + if (ret <= 0) return ret; + } + return 1; + } + + } + +static int ocsp_check_delegated(X509 *x, int flags) + { + X509_check_purpose(x, -1, 0); + if ((x->ex_flags & EXFLAG_XKUSAGE) && + (x->ex_xkusage & XKU_OCSP_SIGN)) + return 1; + OCSPerr(OCSP_F_OCSP_CHECK_DELEGATED, OCSP_R_MISSING_OCSPSIGNING_USAGE); + return 0; + }