Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
openssl: add support for the Certificate Status Request TLS extension
Also known as "status_request" or OCSP stapling, defined in RFC6066
section 8.

Thanks-to: Joe Mason
- for the work-around for the OpenSSL bug.
  • Loading branch information
ghedo authored and bagder committed Jan 22, 2015
1 parent e888e30 commit d1cf5d5
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 2 deletions.
4 changes: 2 additions & 2 deletions docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3
Expand Up @@ -42,8 +42,8 @@ All TLS based protocols: HTTPS, FTPS, IMAPS, POP3, SMTPS etc.
.SH EXAMPLE
TODO
.SH AVAILABILITY
Added in 7.41.0. This option is currently only supported by the GnuTLS and NSS
TLS backends.
Added in 7.41.0. This option is currently only supported by the OpenSSL, GnuTLS
and NSS TLS backends.
.SH RETURN VALUE
Returns CURLE_OK if OCSP stapling is supported by the SSL backend, otherwise
returns CURLE_NOT_BUILT_IN.
Expand Down
143 changes: 143 additions & 0 deletions lib/vtls/openssl.c
Expand Up @@ -64,6 +64,7 @@
#include <openssl/md5.h>
#include <openssl/conf.h>
#include <openssl/bn.h>
#include <openssl/ocsp.h>
#else
#include <rand.h>
#include <x509v3.h>
Expand Down Expand Up @@ -1319,6 +1320,130 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)

return result;
}

static CURLcode verifystatus(struct connectdata *conn,
struct ssl_connect_data *connssl)
{
int i, ocsp_status;
const unsigned char *p;
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;

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(connssl->handle, &p);

if(!p) {
failf(data, "No OCSP response received");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if(!rsp) {
failf(data, "Invalid OCSP response");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

ocsp_status = OCSP_response_status(rsp);
if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
failf(data, "Invalid OCSP response status: %s (%d)",
OCSP_response_status_str(ocsp_status), ocsp_status);
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

br = OCSP_response_get1_basic(rsp);
if(!br) {
failf(data, "Invalid OCSP response");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

ch = SSL_get_peer_cert_chain(connssl->handle);
st = SSL_CTX_get_cert_store(connssl->ctx);

/* The authorized responder cert in the OCSP response MUST be signed by the
peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert,
no problem, but if it's an intermediate cert OpenSSL has a bug where it
expects this issuer to be present in the chain embedded in the OCSP
response. So we add it if necessary. */

/* First make sure the peer cert chain includes both a peer and an issuer,
and the OCSP response contains a responder cert. */
if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);

/* Find issuer of responder cert and add it to the OCSP response chain */
for(i = 0; i < sk_X509_num(ch); i++) {
X509 *issuer = sk_X509_value(ch, i);
if(X509_check_issued(issuer, responder) == X509_V_OK) {
if(!OCSP_basic_add1_cert(br, issuer)) {
failf(data, "Could not add issuer cert to OCSP response");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}
}
}
}

if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
failf(data, "OCSP response verification failed");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

for(i = 0; i < sk_OCSP_SINGLERESP_num(br->tbsResponseData->responses); i++) {
int cert_status, crl_reason;
OCSP_SINGLERESP *single = NULL;

ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;

if(!sk_OCSP_SINGLERESP_value(br->tbsResponseData->responses, i))
continue;

single = sk_OCSP_SINGLERESP_value(br->tbsResponseData->responses, i);

cert_status = OCSP_single_get0_status(single, &crl_reason, &rev,
&thisupd, &nextupd);

if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
failf(data, "OCSP response has expired");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}

infof(data, "SSL certificate status: %s (%d)\n",
OCSP_cert_status_str(cert_status), cert_status);

switch(cert_status) {
case V_OCSP_CERTSTATUS_GOOD:
break;

case V_OCSP_CERTSTATUS_REVOKED:
result = CURLE_SSL_INVALIDCERTSTATUS;

failf(data, "SSL certificate revocation reason: %s (%d)",
OCSP_crl_reason_str(crl_reason), crl_reason);
goto end;

case V_OCSP_CERTSTATUS_UNKNOWN:
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}
}

end:
if(br) OCSP_BASICRESP_free(br);
OCSP_RESPONSE_free(rsp);

return result;
}

#endif /* USE_SSLEAY */

/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
Expand Down Expand Up @@ -1930,6 +2055,10 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
failf(data, "SSL: couldn't create a context (handle)!");
return CURLE_OUT_OF_MEMORY;
}

if(data->set.ssl.verifystatus)
SSL_set_tlsext_status_type(connssl->handle, TLSEXT_STATUSTYPE_ocsp);

SSL_set_connect_state(connssl->handle);

connssl->server_cert = 0x0;
Expand Down Expand Up @@ -2613,6 +2742,15 @@ static CURLcode servercert(struct connectdata *conn,
infof(data, "\t SSL certificate verify ok.\n");
}

if(data->set.ssl.verifystatus) {
result = verifystatus(conn, connssl);
if(result) {
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return result;
}
}

if(!strict)
/* when not strict, we don't bother about the verify cert problems */
result = CURLE_OK;
Expand Down Expand Up @@ -3053,4 +3191,9 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
MD5_Update(&MD5pw, tmp, tmplen);
MD5_Final(md5sum, &MD5pw);
}

bool Curl_ossl_cert_status_request(void)
{
return TRUE;
}
#endif /* USE_SSLEAY */
3 changes: 3 additions & 0 deletions lib/vtls/openssl.h
Expand Up @@ -73,6 +73,8 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
unsigned char *md5sum /* output */,
size_t unused);

bool Curl_ossl_cert_status_request(void);

/* Set the API backend definition to OpenSSL */
#define CURL_SSL_BACKEND CURLSSLBACKEND_OPENSSL

Expand Down Expand Up @@ -102,6 +104,7 @@ void Curl_ossl_md5sum(unsigned char *tmp, /* input */
#define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y)
#define curlssl_random(x,y,z) Curl_ossl_random(x,y,z)
#define curlssl_md5sum(a,b,c,d) Curl_ossl_md5sum(a,b,c,d)
#define curlssl_cert_status_request() Curl_ossl_cert_status_request()

#define DEFAULT_CIPHER_SELECTION "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4"

Expand Down

0 comments on commit d1cf5d5

Please sign in to comment.