Currently the OCSP_basic_verify() function fails with many apparently valid OCSP
responses (e.g. all those sent by Cloudflare servers). Other libraries (GnuTLS,
NSS) have no problem with them.

Essentially, in crypto/ocsp/ocsp_vfy.c in the OCSP_basic_verify() function, the
X509_STORE_CTX_init() function is called like this:

  init_res = X509_STORE_CTX_init(&ctx, st, signer, bs->certs);

where ctx is the X509_STORE_CTX to be initialized, st is the trust store passed
by the user, signer is the signer of the OCSP response (which is what needs to
be validated), and bs is the decoded OCSP basic response.

The problem is the last argument. OpenSSL uses the cert list embedded in the
OCSP response to build the trust chain, but it seems that in some cases this
list is somewhat broken. Other libraries (e.g. GnuTLS), do the verification
differently, without including those bs->certs that OpenSSL uses.

I attached the patch and a simple test case. You can compile it with:

  $ cc ocsp_test.c -lcrypto -lssl

To test the problem run:

  $ ./a.out digitalocean.com 443
  OCSP response verification failed

after the patch:

  $ ./a.out digitalocean.com 443
  OK

Cheers

>From 38ce9b993e9ffc76416ccdc26dee53b24b2cd33c Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <alessan...@ghedini.me>
Date: Tue, 20 Jan 2015 12:27:00 +0100
Subject: [PATCH] Don't use the cert list embedded in the OCSP response to
 build the trust chain

Instead use the certificate stack passed by the user.

This is required in some cases where the embedded chain is somewhat broken and
causes the OCSP verification to fail, e.g. all DigiCert/Cloudflare sites.
---
 crypto/ocsp/ocsp_vfy.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crypto/ocsp/ocsp_vfy.c b/crypto/ocsp/ocsp_vfy.c
index fc0d4cc..846971e 100644
--- a/crypto/ocsp/ocsp_vfy.c
+++ b/crypto/ocsp/ocsp_vfy.c
@@ -108,7 +108,7 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs,
 		if(flags & OCSP_NOCHAIN)
 			init_res = X509_STORE_CTX_init(&ctx, st, signer, NULL);
 		else
-			init_res = X509_STORE_CTX_init(&ctx, st, signer, bs->certs);
+			init_res = X509_STORE_CTX_init(&ctx, st, signer, certs);
 		if(!init_res)
 			{
 			ret = -1;
-- 
2.1.4

#include <stdio.h>
#include <netdb.h>

#include <openssl/ssl.h>
#include <openssl/ocsp.h>

int main(int argc, char *argv[]) {
	int sd;

	SSL *ssl;
	BIO *sbio;
	SSL_CTX *ctx;

	SSL_library_init();
	SSL_load_error_strings();

	ctx = SSL_CTX_new(SSLv23_client_method());

	SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs");

	sd = tcp_connect(argv[1], argv[2]);

	ssl = SSL_new(ctx);

	SSL_set_fd(ssl, (int) sd);
	SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp);

	if (SSL_connect(ssl) <= 0) {
		puts("SSL connect error");
		exit(-1);
	}

	if (SSL_get_verify_result(ssl) != X509_V_OK) {
		puts("Certificate doesn't verify");
		exit(-1);
	}

	/* ==== VERIFY OCSP RESPONSE ==== */

	int i, ocsp_status;
	const unsigned char *p;

	OCSP_RESPONSE *rsp = NULL;
	OCSP_BASICRESP *br = NULL;
	X509_STORE     *st = NULL;
	STACK_OF(X509) *ch = NULL;

	long len = SSL_get_tlsext_status_ocsp_resp(ssl, &p);

	if (!p) {
		puts("No OCSP response received");
		exit(-1);
	}

	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
	if (!rsp) {
		puts("Invalid OCSP response");
		exit(-1);
	}

	ocsp_status = OCSP_response_status(rsp);
	if (ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
		printf("Invalid OCSP response status: %s (%d)",
		       OCSP_response_status_str(ocsp_status), ocsp_status);
		exit(-1);
	}

	br = OCSP_response_get1_basic(rsp);
	if (!br) {
		puts("Invalid OCSP response");
		exit(-1);
	}

	ch = SSL_get_peer_cert_chain(ssl);
	st = SSL_CTX_get_cert_store(ctx);

	if (OCSP_basic_verify(br, ch, st, 0) <= 0) {
		puts("OCSP response verification failed");
		exit(-1);
	}

	puts("OK");

	return 0;
}

int tcp_connect(char *host, char *port) {
	int err, sd;
	struct addrinfo hints, *res, *r;

	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;

	err = getaddrinfo(host, port, &hints, &res);
	if (err != 0) {
		perror("getaddrinfo()");
		exit(-1);
	}

	for (r = res; r != NULL; r = r->ai_next) {
		sd = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
		if (sd == -1)
			continue;

		if (connect(sd, r->ai_addr, r->ai_addrlen) == 0)
			break;

		close(sd);
	}

	freeaddrinfo(res);

	return sd;
}
_______________________________________________
openssl-dev mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev

Reply via email to