[PATCH] Implement OCSP stapling check

110 views
Skip to first unread message

Justin Erenkrantz

unread,
Apr 10, 2014, 11:25:34 PM4/10/14
to serf...@googlegroups.com
I have no idea if we should commit it; but...it seems to work. I
haven't found any halfway decent code to support OCSP stapling in an
OpenSSL client...so, at the minimum, here's a patch that someone else
can Google later.

% test/serf_get -d https://login.live.com/

will yield something like:

2014-04-10T21:17:07.747805-06 DEBUG [l:10.10.3.118:51723
r:131.253.61.80:443] serf/buckets/ssl_buckets.c: OCSP check successful
(value 0)

I'm just not convinced that any of the online revocation protocols add
any actual value. If you haven't followed my Twitter rantings, it
seems that both Google and Mozilla are publishing their own custom CRL
sets which OpenSSL doesn't seem to support natively (see
https://github.com/agl/crlset-tools). I also don't know what they're
going to do as almost all certs are going to be rotated now.

Doing CRLDP or out-of-band OCSP would require a lot of contortions as
we'd have to stop processing once we get the certificate from the
server and then make an HTTP fetch to get that info. So, OCSP
Stapling is the least ugly solution...but, any decent MITM or hijack
simply won't do OCSP. So, I don't know if there's any value here.

And, yes, I know that there should probably be some type of #ifdef
because not all OpenSSL versions have OCSP support...but...meh. --
justin

Ask for OCSP stapling if the server supports it.

* buckets/ssl_buckets.c
(openssl/ocsp.h): Include.
(ocsp_callback): Implement.
(ssl_init_context): Set SSL_CTX and SSL params to request OCSP status.

Index: buckets/ssl_buckets.c
===================================================================
--- buckets/ssl_buckets.c (revision 2329)
+++ buckets/ssl_buckets.c (working copy)
@@ -51,6 +51,7 @@
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
+#include <openssl/ocsp.h>

#ifndef APR_ARRAY_PUSH
#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary)))
@@ -459,6 +460,111 @@ static BIO_METHOD bio_file_method = {
};

static int
+ocsp_callback(SSL *s, void *arg) {
+ serf_ssl_context_t *ctx = (serf_ssl_context_t*)arg;
+ OCSP_RESPONSE *response;
+ OCSP_BASICRESP *br;
+ OCSP_RESPDATA *rd;
+ OCSP_RESPBYTES *rb;
+ const unsigned char *p;
+ int i, len;
+ long l;
+ int failures = 0;
+ int cert_valid = 1;
+
+ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+ if (!p) {
+ /* No response sent */
+ return 1;
+ }
+ response = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if (!response) {
+ /* Error parsing OCSP response - tell the app? */
+ return 1;
+ }
+ rb = response->responseBytes;
+ l = ASN1_ENUMERATED_get(response->responseStatus);
+ switch (l) {
+ case OCSP_RESPONSE_STATUS_SUCCESSFUL:
+ break;
+ case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST:
+ case OCSP_RESPONSE_STATUS_INTERNALERROR:
+ case OCSP_RESPONSE_STATUS_TRYLATER:
+ case OCSP_RESPONSE_STATUS_SIGREQUIRED:
+ case OCSP_RESPONSE_STATUS_UNAUTHORIZED:
+ default:
+ failures |= SERF_SSL_CERT_UNKNOWN_FAILURE;
+ break;
+ }
+
+ br = OCSP_response_get1_basic(response);
+ if (!br) {
+ /* Error parsing OCSP response - tell the app? */
+ return 1;
+ }
+ rd = br->tbsResponseData;
+ for (i = 0; i < sk_OCSP_SINGLERESP_num(rd->responses); i++ ) {
+ OCSP_SINGLERESP *single;
+ OCSP_CERTID *cid;
+
+ if (!sk_OCSP_SINGLERESP_value(rd->responses, i))
+ continue;
+
+ single = sk_OCSP_SINGLERESP_value(rd->responses, i);
+ switch (single->certStatus->type) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
+ "OCSP check successful (value %d)\n",
+ single->certStatus->type);
+ break;
+ case V_OCSP_CERTSTATUS_REVOKED:
+ serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
+ "OCSP check revoked (value %d)\n",
+ single->certStatus->type);
+ failures |= SERF_SSL_CERT_REVOKED;
+ break;
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ default:
+ serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
+ "OCSP check unknown (value %d)\n",
+ single->certStatus->type);
+ failures |= SERF_SSL_CERT_UNKNOWN_FAILURE;
+ break;
+ }
+ }
+
+ OCSP_RESPONSE_free(response);
+
+ if (ctx->server_cert_callback && failures) {
+ apr_status_t status;
+ serf_ssl_certificate_t *cert;
+ apr_pool_t *subpool;
+
+ apr_pool_create(&subpool, ctx->pool);
+
+ cert = apr_palloc(subpool, sizeof(serf_ssl_certificate_t));
+ cert->ssl_cert = NULL;
+ cert->depth = 0;
+
+ /* Callback for further verification. */
+ status = ctx->server_cert_callback(ctx->server_cert_userdata,
+ failures, cert);
+ if (status == APR_SUCCESS)
+ cert_valid = 1;
+ else {
+ /* Even if openssl found the certificate valid, the application
+ told us to reject it. */
+ cert_valid = 0;
+ /* Pass the error back to the caller through the context-run. */
+ ctx->pending_err = status;
+ }
+ apr_pool_destroy(subpool);
+ }
+
+ return cert_valid;
+}
+
+static int
validate_server_certificate(int cert_valid, X509_STORE_CTX *store_ctx)
{
SSL *ssl;
@@ -1267,6 +1373,9 @@ static serf_ssl_context_t *ssl_init_context(serf_b
ssl_ctx->server_cert_callback = NULL;
ssl_ctx->server_cert_chain_callback = NULL;

+ SSL_CTX_set_tlsext_status_cb(ssl_ctx->ctx, ocsp_callback);
+ SSL_CTX_set_tlsext_status_arg(ssl_ctx->ctx, ssl_ctx);
+
SSL_CTX_set_verify(ssl_ctx->ctx, SSL_VERIFY_PEER,
validate_server_certificate);
SSL_CTX_set_options(ssl_ctx->ctx, SSL_OP_ALL);
@@ -1283,6 +1392,8 @@ static serf_ssl_context_t *ssl_init_context(serf_b

SSL_set_app_data(ssl_ctx->ssl, ssl_ctx);

+ SSL_set_tlsext_status_type(ssl_ctx->ssl, TLSEXT_STATUSTYPE_ocsp);
+
#ifdef SERF_LOGGING_ENABLED
SSL_CTX_set_info_callback(ssl_ctx->ctx, apps_ssl_info_callback);
#endif
Reply all
Reply to author
Forward
0 new messages