Browse Source

[mod_gnutls] support OCSP responses in sni backends

Change-Id: I7ec08bf6e414140b53019885eb906bdfe3251a2e
master
Stefan Bühler 3 years ago
parent
commit
8c68b120da
4 changed files with 71 additions and 12 deletions
  1. +2
    -2
      doc/mod_gnutls.xml
  2. +27
    -0
      src/modules/gnutls_ocsp.c
  3. +5
    -0
      src/modules/gnutls_ocsp.h
  4. +37
    -10
      src/modules/mod_gnutls.c

+ 2
- 2
doc/mod_gnutls.xml View File

@@ -185,11 +185,11 @@

<section title="OCSP stapling">
<textile>
"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).



+ 27
- 0
src/modules/gnutls_ocsp.c View File

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

+ 5
- 0
src/modules/gnutls_ocsp.h View File

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

+ 37
- 10
src/modules/mod_gnutls.c View File

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


Loading…
Cancel
Save