Commit e8af92fc authored by Dr. Stephen Henson's avatar Dr. Stephen Henson
Browse files

Implement remaining OCSP verify checks in

accordance with RFC2560.
parent 361ef5f4
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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;

+8 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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"},
+159 −13
Original line number Diff line number Diff line
@@ -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 */

@@ -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
@@ -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;
	}