From 8c68b120da6d69b819692d81d610c8a30d18b949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Wed, 26 Jul 2017 09:46:13 +0200 Subject: [PATCH] [mod_gnutls] support OCSP responses in sni backends Change-Id: I7ec08bf6e414140b53019885eb906bdfe3251a2e --- doc/mod_gnutls.xml | 4 ++-- src/modules/gnutls_ocsp.c | 27 ++++++++++++++++++++++ src/modules/gnutls_ocsp.h | 5 +++++ src/modules/mod_gnutls.c | 47 ++++++++++++++++++++++++++++++--------- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/doc/mod_gnutls.xml b/doc/mod_gnutls.xml index 0c8b342..511a8df 100644 --- a/doc/mod_gnutls.xml +++ b/doc/mod_gnutls.xml @@ -185,11 +185,11 @@
- "OCSP stapling":https://en.wikipedia.org/wiki/OCSP_stapling is used to assure a client the certificate is still valid (i.e. not revoked); you can put an OCSP response into the certificate file in an "OCSP RESPNSE"-PEM block (there is probably no standard for this, juse base64-encode the DER-response you have), or specify the (DER or PEM formatted) OCSP response as separate file using "ocsp" in a "pemfile" block. + "OCSP stapling":https://en.wikipedia.org/wiki/OCSP_stapling is used to assure a client the certificate is still valid (i.e. not revoked); you can put an OCSP response into the certificate file in an "OCSP RESPONSE"-PEM block (there is probably no standard for this, juse base64-encode the DER-response you have), or specify the (DER or PEM formatted) OCSP response as separate file using "ocsp" in a "pemfile" block. Server Name Indication (SNI) should work fine, as an OCSP response is only used if it matches the certificate in use for the connection. - The fetch backends do NOT support OCSP stapling yet (in the future they should support the "OCSP RESPNSE"-PEM block). + The fetch backends do support OCSP stapling if the OCSP response is appended as PEM block. Lighttpd does NOT automatically reload OCSP responses; you have to restart to load new OCSP responses (a cron job is probably the right way to do it). diff --git a/src/modules/gnutls_ocsp.c b/src/modules/gnutls_ocsp.c index cffc27f..795af27 100644 --- a/src/modules/gnutls_ocsp.c +++ b/src/modules/gnutls_ocsp.c @@ -286,3 +286,30 @@ error: gnutls_free(decoded.data); return result; } + +gboolean li_gnutls_ocsp_search_datum(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t const* file) { + int r; + gnutls_datum_t decoded = { NULL, 0 }; + gboolean result = FALSE; + + r = gnutls_pem_base64_decode_alloc("OCSP RESPONSE", file, &decoded); + + if (GNUTLS_E_SUCCESS <= r) { + result = add_response(srv, ocsp, &decoded); + if (!result) { + ERROR(srv, "%s", "Failed loading OCSP response from PEM block"); + goto error; + } + } else if (GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR == r) { + /* ignore GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR */ + } else { + ERROR(srv, "gnutls_pem_base64_decode_alloc failed to decode OCSP RESPONSE from PEM block (%s): %s", + gnutls_strerror_name(r), gnutls_strerror(r)); + /* continue anyway */ + } + result = TRUE; + +error: + gnutls_free(decoded.data); + return result; +} diff --git a/src/modules/gnutls_ocsp.h b/src/modules/gnutls_ocsp.h index 7920ac5..fb4c7b9 100644 --- a/src/modules/gnutls_ocsp.h +++ b/src/modules/gnutls_ocsp.h @@ -22,4 +22,9 @@ LI_API gboolean li_gnutls_ocsp_add(liServer *srv, liGnuTLSOCSP *ocsp, const char */ LI_API gboolean li_gnutls_ocsp_search(liServer *srv, liGnuTLSOCSP *ocsp, const char* filename); +/* search in PEM datum for a OCSP RESPONSE block and add it if there is one; + * returns only FALSE if a block was found which COULDN'T be loaded + */ +LI_API gboolean li_gnutls_ocsp_search_datum(liServer *srv, liGnuTLSOCSP *ocsp, gnutls_datum_t const* file); + #endif diff --git a/src/modules/mod_gnutls.c b/src/modules/mod_gnutls.c index 95c63fa..ad8cad7 100644 --- a/src/modules/mod_gnutls.c +++ b/src/modules/mod_gnutls.c @@ -61,6 +61,8 @@ struct mod_connection_ctx { struct mod_context { gint refcount; + liServer *srv; + liSSLSessionDB *session_db; gnutls_certificate_credentials_t server_cert; @@ -89,6 +91,12 @@ struct fetch_cert_backend_lookup { liFetchEntry *entry; mod_context *ctx; }; + +typedef struct sni_cert_data sni_cert_data; +struct sni_cert_data { + gnutls_certificate_credentials_t creds; + liGnuTLSOCSP* ocsp; +}; #endif static void mod_gnutls_context_release(mod_context *ctx); @@ -116,8 +124,10 @@ static int pin_callback(void *user, int attempt, const char *token_url, const ch #endif /* defined(HAVE_PIN) */ #ifdef USE_SNI -static gnutls_certificate_credentials_t creds_from_gstring(mod_context *ctx, GString *str) { +static sni_cert_data* creds_from_gstring(mod_context *ctx, GString *str) { + sni_cert_data* data = NULL; gnutls_certificate_credentials_t creds = NULL; + liGnuTLSOCSP* ocsp = NULL; gnutls_datum_t pemfile; int r; @@ -133,15 +143,26 @@ static gnutls_certificate_credentials_t creds_from_gstring(mod_context *ctx, GSt #endif if (GNUTLS_E_SUCCESS > (r = gnutls_certificate_set_x509_key_mem(creds, &pemfile, &pemfile, GNUTLS_X509_FMT_PEM))) { - goto error_free_creds; + goto error; } gnutls_certificate_set_dh_params(creds, ctx->dh_params); - return creds; + ocsp = li_gnutls_ocsp_new(); + if (!li_gnutls_ocsp_search_datum(ctx->srv, ocsp, &pemfile)) { + goto error; + } + li_gnutls_ocsp_use(ocsp, creds); + + data = g_slice_new0(sni_cert_data); + data->creds = creds; + data->ocsp = ocsp; -error_free_creds: - gnutls_certificate_free_credentials(creds); + return data; + +error: + if (NULL != creds) gnutls_certificate_free_credentials(creds); + if (NULL != ocsp) li_gnutls_ocsp_free(ocsp); return NULL; } @@ -179,10 +200,14 @@ static void fetch_cert_refresh(liFetchDatabase* db, gpointer data, liFetchEntry li_fetch_entry_refresh_skip(new_entry); } static void fetch_cert_free_entry(gpointer data, liFetchEntry *entry) { - gnutls_certificate_credentials_t creds = entry->data; + sni_cert_data* sni_data = entry->data; UNUSED(data); - if (NULL != creds) gnutls_certificate_free_credentials(creds); + if (NULL != sni_data) { + if (NULL != sni_data->creds) gnutls_certificate_free_credentials(sni_data->creds); + if (NULL != sni_data->ocsp) li_gnutls_ocsp_free(sni_data->ocsp); + g_slice_free(sni_cert_data, sni_data); + } li_fetch_entry_release((liFetchEntry*) entry->backend_data); } static void fetch_cert_free_db(gpointer data) { @@ -288,6 +313,8 @@ static mod_context *mod_gnutls_context_new(liServer *srv) { } #endif + ctx->srv = srv; + ctx->ocsp = li_gnutls_ocsp_new(); ctx->refcount = 1; @@ -490,9 +517,9 @@ static void sni_job_cb(liJob *job) { conctx->sni_entry = li_fetch_get(conctx->ctx->sni_db, conctx->sni_server_name, conctx->sni_jobref, &conctx->sni_db_wait); if (conctx->sni_entry != NULL) { - gnutls_certificate_credentials_t creds = conctx->sni_entry->data; - if (NULL != creds) { - gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, creds); + sni_cert_data* data = conctx->sni_entry->data; + if (NULL != data) { + gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, data->creds); } else if (NULL != conctx->ctx->sni_fallback_cert) { gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, conctx->ctx->sni_fallback_cert); }