diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-12-08 13:45:24 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2018-02-19 15:29:37 +0100 |
commit | 5623c86b5678ef93e9670a6f7bc412e2c8dda62a (patch) | |
tree | 35fe40a30e4da89c3410dcc477fce78282ae1983 | |
parent | 4a4f5b48a8db833adaaef9c0bbf02a9eb7b20700 (diff) | |
download | gnutls-5623c86b5678ef93e9670a6f7bc412e2c8dda62a.tar.gz |
ocsp: enhanced the OCSP response loading APIs
Introduced gnutls_certificate_set_ocsp_status_request_file2() and
gnutls_certificate_set_ocsp_status_request_mem(). These functions
behave as the equivalent certificate loading functions and pre-load
the OCSP response provided as a file, either in DER or in PEM form.
In addition, ensure that if the server is provided a problematic OCSP
response, or the OCSP response is not renewed before it is invalid, we
will not provide it to the clients.
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r-- | lib/auth/cert.c | 16 | ||||
-rw-r--r-- | lib/auth/cert.h | 5 | ||||
-rw-r--r-- | lib/cert.c | 10 | ||||
-rw-r--r-- | lib/ext/status_request.c | 19 | ||||
-rw-r--r-- | lib/gnutls_int.h | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/abstract.h | 2 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 19 | ||||
-rw-r--r-- | lib/libgnutls.map | 4 | ||||
-rw-r--r-- | lib/ocsp-api.c | 283 | ||||
-rw-r--r-- | lib/tls13/certificate.c | 36 | ||||
-rw-r--r-- | lib/x509.c | 2 | ||||
-rw-r--r-- | lib/x509/Makefile.am | 2 | ||||
-rw-r--r-- | lib/x509/ocsp.c | 52 | ||||
-rw-r--r-- | lib/x509/ocsp.h | 30 |
14 files changed, 405 insertions, 77 deletions
diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 89d0aa248e..465bcf4888 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -50,7 +50,7 @@ static void selected_certs_set(gnutls_session_t session, gnutls_pcert_st * certs, int ncerts, - gnutls_datum_t *ocsp, int nocsp, + gnutls_ocsp_data_st *ocsp, unsigned nocsp, gnutls_privkey_t key, int need_free, gnutls_status_request_ocsp_func ocsp_func, void *ocsp_func_ptr); @@ -352,7 +352,7 @@ call_get_cert_callback(gnutls_session_t session, gnutls_certificate_type_t type = gnutls_certificate_type_get(session); gnutls_certificate_credentials_t cred; gnutls_pcert_st *pcert = NULL; - gnutls_datum_t *ocsp = NULL; + gnutls_ocsp_data_st *ocsp = NULL; unsigned int ocsp_length = 0; unsigned int pcert_length = 0; @@ -491,8 +491,8 @@ _gnutls_select_client_cert(gnutls_session_t session, cert_list[0], cred->certs[indx]. cert_list_length, - cred->certs[indx].ocsp_responses, - cred->certs[indx].ocsp_responses_length, + cred->certs[indx].ocsp_data, + cred->certs[indx].ocsp_data_length, cred->certs[indx].pkey, 0, NULL, 0); } else { @@ -1155,7 +1155,7 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session) for (i = 0; i < session->internals.selected_ocsp_length; i++) { _gnutls_free_datum(&session->internals. - selected_ocsp[i]); + selected_ocsp[i].response); } gnutls_free(session->internals.selected_ocsp); @@ -1174,7 +1174,7 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session) static void selected_certs_set(gnutls_session_t session, gnutls_pcert_st * certs, int ncerts, - gnutls_datum_t *ocsp, int nocsp, + gnutls_ocsp_data_st *ocsp, unsigned nocsp, gnutls_privkey_t key, int need_free, gnutls_status_request_ocsp_func ocsp_func, void *ocsp_func_ptr) @@ -1439,8 +1439,8 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e selected_certs_set(session, &cred->certs[idx].cert_list[0], cred->certs[idx].cert_list_length, - &cred->certs[idx].ocsp_responses[0], - cred->certs[idx].ocsp_responses_length, + &cred->certs[idx].ocsp_data[0], + cred->certs[idx].ocsp_data_length, cred->certs[idx].pkey, 0, NULL, NULL); } diff --git a/lib/auth/cert.h b/lib/auth/cert.h index 9a3ecb6434..16dec78fd8 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -39,8 +39,9 @@ typedef struct { gnutls_status_request_ocsp_func ocsp_func; void *ocsp_func_ptr; /* corresponding OCSP response function + ptr */ - gnutls_datum_t ocsp_responses[MAX_OCSP_RESPONSES]; /* corresponding OCSP response file */ - unsigned int ocsp_responses_length; + + gnutls_ocsp_data_st ocsp_data[MAX_OCSP_RESPONSES]; + unsigned int ocsp_data_length; /* the private key corresponding to certificate */ gnutls_privkey_t pkey; diff --git a/lib/cert.c b/lib/cert.c index 1b514e9367..e3cedad0c3 100644 --- a/lib/cert.c +++ b/lib/cert.c @@ -61,9 +61,9 @@ void gnutls_certificate_free_keys(gnutls_certificate_credentials_t sc) } gnutls_free(sc->certs[i].cert_list); - for (j = 0; j < sc->certs[i].ocsp_responses_length; j++) { - gnutls_free(sc->certs[i].ocsp_responses[j].data); - sc->certs[i].ocsp_responses[j].data = NULL; + for (j = 0; j < sc->certs[i].ocsp_data_length; j++) { + gnutls_free(sc->certs[i].ocsp_data[j].response.data); + sc->certs[i].ocsp_data[j].response.data = NULL; } _gnutls_str_array_clear(&sc->certs[i].names); gnutls_privkey_deinit(sc->certs[i].pkey); @@ -366,7 +366,7 @@ static int call_legacy_cert_cb1(gnutls_session_t session, const struct gnutls_cert_retr_st *info, gnutls_pcert_st **certs, unsigned int *pcert_length, - gnutls_datum_t **ocsp, + gnutls_ocsp_data_st **ocsp, unsigned int *ocsp_length, gnutls_privkey_t *privkey, unsigned int *flags) @@ -510,7 +510,7 @@ static int call_legacy_cert_cb2(gnutls_session_t session, const struct gnutls_cert_retr_st *info, gnutls_pcert_st **certs, unsigned int *pcert_length, - gnutls_datum_t **ocsp, + gnutls_ocsp_data_st **ocsp, unsigned int *ocsp_length, gnutls_privkey_t *privkey, unsigned int *flags) diff --git a/lib/ext/status_request.c b/lib/ext/status_request.c index d99aab6a1a..a16a092e22 100644 --- a/lib/ext/status_request.c +++ b/lib/ext/status_request.c @@ -172,7 +172,24 @@ server_send(gnutls_session_t session, if (cred == NULL) /* no certificate authentication */ return gnutls_assert_val(0); - if (session->internals.selected_ocsp_func) { + if (session->internals.selected_ocsp_length > 0) { + if (session->internals.selected_ocsp[0].response.data) { + if (session->internals.selected_ocsp[0].exptime != 0 && + (gnutls_time(0) >= session->internals.selected_ocsp[0].exptime)) { + gnutls_assert(); + return 0; + } + + ret = _gnutls_set_datum(&priv->sresp, + session->internals.selected_ocsp[0].response.data, + session->internals.selected_ocsp[0].response.size); + if (ret < 0) + return gnutls_assert_val(ret); + return GNUTLS_E_INT_RET_0; + } else { + return 0; + } + } else if (session->internals.selected_ocsp_func) { func = session->internals.selected_ocsp_func; func_ptr = session->internals.selected_ocsp_func_ptr; } else if (cred->glob_ocsp_func) { diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 439b2e8890..baa9c14589 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1091,7 +1091,7 @@ typedef struct { * functions, set the ocsp_func. The former takes precedence when * set. */ - gnutls_datum_t *selected_ocsp; + gnutls_ocsp_data_st *selected_ocsp; uint16_t selected_ocsp_length; gnutls_status_request_ocsp_func selected_ocsp_func; void *selected_ocsp_func_ptr; diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index bc97086262..4c638fda42 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -678,7 +678,7 @@ typedef int gnutls_certificate_retrieve_function3( const struct gnutls_cert_retr_st *info, gnutls_pcert_st **certs, unsigned int *pcert_length, - gnutls_datum_t **ocsp, + gnutls_ocsp_data_st **ocsp, unsigned int *ocsp_length, gnutls_privkey_t *privkey, unsigned int *flags); diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 120ee33454..17f52424df 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1883,7 +1883,7 @@ int gnutls_certificate_get_x509_crt(gnutls_certificate_credentials_t res, /* OCSP status request extension, RFC 6066 */ typedef int (*gnutls_status_request_ocsp_func) - (gnutls_session_t session, void *ptr, gnutls_datum_t * ocsp_response); + (gnutls_session_t session, void *ptr, gnutls_datum_t *ocsp_response); void gnutls_certificate_set_ocsp_status_request_function @@ -1900,6 +1900,23 @@ gnutls_certificate_set_ocsp_status_request_file (gnutls_certificate_credentials_t res, const char *response_file, unsigned idx); +int +gnutls_certificate_set_ocsp_status_request_file2 +(gnutls_certificate_credentials_t res, const char *response_file, + unsigned idx, gnutls_x509_crt_fmt_t fmt); + +int +gnutls_certificate_set_ocsp_status_request_mem +(gnutls_certificate_credentials_t res, const gnutls_datum_t *resp, + unsigned idx, gnutls_x509_crt_fmt_t fmt); + +typedef struct gnutls_ocsp_data_st { + unsigned int version; /* must be zero */ + gnutls_datum_t response; + time_t exptime; + unsigned char padding[32]; +} gnutls_ocsp_data_st; + int gnutls_ocsp_status_request_enable_client(gnutls_session_t session, gnutls_datum_t * responder_id, size_t responder_id_size, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index e861aeaf7d..3ff7958b05 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1214,7 +1214,9 @@ GNUTLS_3_6_xx gnutls_ocsp_resp_import2; gnutls_ocsp_resp_export2; gnutls_ocsp_resp_list_import2; - gnutls_certificate_retrieve_function3; + gnutls_certificate_set_retrieve_function3; + gnutls_certificate_set_ocsp_status_request_file2; + gnutls_certificate_set_ocsp_status_request_mem; } GNUTLS_3_6_2; GNUTLS_FIPS140_3_4 { diff --git a/lib/ocsp-api.c b/lib/ocsp-api.c index e5c002f0fb..eb87afa71b 100644 --- a/lib/ocsp-api.c +++ b/lib/ocsp-api.c @@ -35,6 +35,7 @@ #ifdef ENABLE_OCSP #include <gnutls/ocsp.h> +#include "x509/ocsp.h" /** * gnutls_ocsp_status_request_get: @@ -222,15 +223,10 @@ unsigned resp_matches_pcert(gnutls_ocsp_resp_t resp, const gnutls_pcert_st *cert * @response_file: a filename of the OCSP response * @idx: is a certificate index as returned by gnutls_certificate_set_key() and friends * - * This function sets the filename of an OCSP response, that will be + * This function loads the provided OCSP response. It will be * sent to the client if requests an OCSP certificate status for * the certificate chain specified by @idx. * - * This is a convenience function which may be inefficient on busy servers since - * the file is opened on every access. Use - * gnutls_certificate_set_ocsp_status_request_function2() to fine-tune - * file accesses. - * * Note: the ability to set multiple OCSP responses per credential * structure via the index @idx was added in version 3.5.6. To keep * backwards compatibility, it requires using gnutls_certificate_set_flags() @@ -241,9 +237,9 @@ unsigned resp_matches_pcert(gnutls_ocsp_resp_t resp, const gnutls_pcert_st *cert * when multiple responses which apply to the chain are available. * If the response provided does not match any certificates present * in the chain, the code %GNUTLS_E_OCSP_MISMATCH_WITH_CERTS is returned. - * To force the previous behavior set the flag %GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK + * To revert to the previous behavior set the flag %GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK * in the certificate credentials structure. In that case, only the - * end-certificates OCSP response can be set. + * end-certificate's OCSP response can be set. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, * otherwise a negative error code is returned. @@ -255,72 +251,267 @@ gnutls_certificate_set_ocsp_status_request_file(gnutls_certificate_credentials_t const char *response_file, unsigned idx) { - unsigned i, found = 0; - gnutls_datum_t der = {NULL, 0}; - gnutls_ocsp_resp_t resp = NULL; int ret; - if (idx >= sc->ncerts) - return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); - - ret = gnutls_load_file(response_file, &der); - if (ret < 0) - return gnutls_assert_val(GNUTLS_E_FILE_ERROR); - - if (sc->flags & GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK) { - /* quick load of first response */ - gnutls_free(sc->certs[idx].ocsp_responses[0].data); - - sc->certs[idx].ocsp_responses[0].data = der.data; - der.data = NULL; - sc->certs[idx].ocsp_responses[0].size = der.size; - + ret = gnutls_certificate_set_ocsp_status_request_file2(sc, response_file, + idx, GNUTLS_X509_FMT_DER); + if (ret >= 0) return 0; - } + else + return ret; +} - ret = gnutls_ocsp_resp_init(&resp); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } +static int append_response(gnutls_certificate_credentials_t sc, unsigned idx, + gnutls_ocsp_resp_t resp, const gnutls_datum_t *der) +{ + int ret; + unsigned i, found = 0; + unsigned try_already_set = 0; + time_t t; - ret = gnutls_ocsp_resp_import(resp, &der); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } + retry: /* iterate through all certificates in chain, and add the response * to the certificate that it matches with. */ for (i=0;i<MIN(sc->certs[idx].cert_list_length, MAX_OCSP_RESPONSES);i++) { - if (sc->certs[idx].ocsp_responses[i].data) + if (!try_already_set && sc->certs[idx].ocsp_data[i].response.data) continue; if (!resp_matches_pcert(resp, &sc->certs[idx].cert_list[i])) continue; + t = _gnutls_ocsp_get_validity(resp); + /* if already invalid */ + if (t == (time_t)-1) { + gnutls_assert(); + continue; + } + + if (t >= 0) + sc->certs[idx].ocsp_data[i].exptime = t; + else + sc->certs[idx].ocsp_data[i].exptime = 0; + _gnutls_debug_log("associating OCSP response with chain %d on pos %d\n", idx, i); - sc->certs[idx].ocsp_responses[i].data = der.data; - der.data = NULL; - sc->certs[idx].ocsp_responses[i].size = der.size; + gnutls_free(sc->certs[idx].ocsp_data[i].response.data); - if (sc->certs[idx].ocsp_responses_length <= i) - sc->certs[idx].ocsp_responses_length = i+1; + ret = _gnutls_set_datum(&sc->certs[idx].ocsp_data[i].response, + der->data, + der->size); + if (ret < 0) { + gnutls_assert(); + sc->certs[idx].ocsp_data[i].response.data = NULL; + sc->certs[idx].ocsp_data[i].response.size = 0; + return ret; + } + + if (sc->certs[idx].ocsp_data_length <= i) + sc->certs[idx].ocsp_data_length = i+1; found = 1; break; } - if (!found) + if (!found) { + /* slow path; if we found no matching certificate for the OCSP + * response, try all the existing, even if a response is already + * given. */ + if (!try_already_set) { + try_already_set = 1; + goto retry; + } ret = GNUTLS_E_OCSP_MISMATCH_WITH_CERTS; - else + } else { ret = 0; - cleanup: + } + + return ret; +} + +/** + * gnutls_certificate_set_ocsp_status_request_file2: + * @sc: is a credentials structure. + * @response_file: a filename of the OCSP response + * @idx: is a certificate index as returned by gnutls_certificate_set_key() and friends + * @fmt: is PEM or DER + * + * This function loads the provided OCSP response. It will be + * sent to the client if requests an OCSP certificate status for + * the certificate chain specified by @idx. + * + * This function must be called after setting any certificates, and + * cannot be used for certificates that are provided via a callback -- + * that is when gnutls_certificate_set_retrieve_function() is used. In + * that case consider using gnutls_certificate_set_retrieve_function3(). + * + * This function can be called multiple times when multiple responses + * applicable to the certificate chain are available. + * If the response provided does not match any certificates present + * in the chain, the code %GNUTLS_E_OCSP_MISMATCH_WITH_CERTS is returned. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error code is returned. + * + * Since: 3.1.3 + **/ +int +gnutls_certificate_set_ocsp_status_request_file2(gnutls_certificate_credentials_t sc, + const char *response_file, + unsigned idx, + gnutls_x509_crt_fmt_t fmt) +{ + gnutls_datum_t raw = {NULL, 0}; + int ret; + + if (idx >= sc->ncerts) + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + + ret = gnutls_load_file(response_file, &raw); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + + ret = gnutls_certificate_set_ocsp_status_request_mem(sc, &raw, idx, fmt); + gnutls_free(raw.data); + return ret; +} + +#define PEM_OCSP_RESPONSE "OCSP RESPONSE" +#define FULL_PEM_OCSP_RESPONSE "-----BEGIN OCSP RESPONSE" + +/** + * gnutls_certificate_set_ocsp_status_request_mem: + * @sc: is a credentials structure. + * @resp_data: a memory buffer holding an OCSP response + * @idx: is a certificate index as returned by gnutls_certificate_set_key() and friends + * @fmt: is PEM or DER + * + * This function sets the filename of an OCSP response, that will be + * sent to the client if requests an OCSP certificate status for + * the certificate chain specified by @idx. + * + * Note: the ability to set multiple OCSP responses per credential + * structure via the index @idx was added in version 3.5.6. To keep + * backwards compatibility, it requires using gnutls_certificate_set_flags() + * with the %GNUTLS_CERTIFICATE_API_V2 flag to make the set certificate + * functions return an index usable by this function. + * + * This function must be called after setting any certificates, and + * cannot be used for certificates that are provided via a callback -- + * that is when gnutls_certificate_set_retrieve_function() is used. + * + * This function can be called multiple times when multiple responses which + * apply to the certificate chain are available. + * If the response provided does not match any certificates present + * in the chain, the code %GNUTLS_E_OCSP_MISMATCH_WITH_CERTS is returned. + * + * Returns: On success, the number of loaded responses is returned, + * otherwise a negative error code. + * + * Since: 3.6.xx + **/ +int +gnutls_certificate_set_ocsp_status_request_mem(gnutls_certificate_credentials_t sc, + const gnutls_datum_t *resp_data, + unsigned idx, + gnutls_x509_crt_fmt_t fmt) + +{ + gnutls_datum_t der = {NULL, 0}; + gnutls_ocsp_resp_t resp = NULL; + int ret; + unsigned int nresp = 0; + + ret = gnutls_ocsp_resp_init(&resp); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + if (fmt == GNUTLS_X509_FMT_PEM) { + /* load multiple responses */ + gnutls_datum_t p = {resp_data->data, resp_data->size}; + + p.data = memmem(p.data, p.size, FULL_PEM_OCSP_RESPONSE, + sizeof(FULL_PEM_OCSP_RESPONSE)-1); + if (p.data == NULL) { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + goto cleanup; + } + + p.size -= p.data - resp_data->data; + if (p.size <= 0) { + ret = gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + goto cleanup; + } + + do { + ret = gnutls_pem_base64_decode2(PEM_OCSP_RESPONSE, &p, &der); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_certificate_set_ocsp_status_request_mem(sc, &der, idx, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + nresp++; + + gnutls_free(der.data); + der.data = NULL; + + p.data++; + p.size--; + + p.data = memmem(p.data, p.size, FULL_PEM_OCSP_RESPONSE, + sizeof(FULL_PEM_OCSP_RESPONSE)-1); + if (p.data == NULL) + break; + p.size = resp_data->size - (p.data - resp_data->data); + } while(p.size > 0); + + ret = nresp; + } else { + /* DER: load a single response */ + if (sc->flags & GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK) { + /* quick load of first response */ + gnutls_free(sc->certs[idx].ocsp_data[0].response.data); + + ret = _gnutls_set_datum(&sc->certs[idx].ocsp_data[0].response, + resp_data->data, + resp_data->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + sc->certs[idx].ocsp_data[0].exptime = 0; + sc->certs[idx].ocsp_data_length = 1; + goto cleanup; + } + + ret = gnutls_ocsp_resp_import2(resp, resp_data, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = append_response(sc, idx, resp, resp_data); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 1; + } + cleanup: gnutls_free(der.data); if (resp) gnutls_ocsp_resp_deinit(resp); + return ret; } diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c index f9cddb8bc5..b8451220e9 100644 --- a/lib/tls13/certificate.c +++ b/lib/tls13/certificate.c @@ -121,26 +121,42 @@ int append_status_request(void *_ctx, gnutls_buffer_st *buf) gnutls_session_t session = ctx->session; int ret; gnutls_datum_t resp; + unsigned free_resp = 0; - assert(session->internals.selected_ocsp_func != NULL || ctx->cred->glob_ocsp_func != NULL); + assert(session->internals.selected_ocsp_func != NULL || ctx->cred->glob_ocsp_func != NULL || + session->internals.selected_ocsp_length != 0); /* The global ocsp callback function can only be used to return * a single certificate request */ - if (!session->internals.selected_ocsp_func && ctx->cert_index != 0) + if (session->internals.selected_ocsp_length == 1 && ctx->cert_index != 0) return 0; if (session->internals.selected_ocsp_length > 0) { if (ctx->cert_index < session->internals.selected_ocsp_length) { - resp.data = session->internals.selected_ocsp[ctx->cert_index].data; - resp.size = session->internals.selected_ocsp[ctx->cert_index].size; + if ((session->internals.selected_ocsp[ctx->cert_index].exptime != 0 && + gnutls_time(0) >= session->internals.selected_ocsp[ctx->cert_index].exptime) || + session->internals.selected_ocsp[ctx->cert_index].response.data == NULL) { + return 0; + } + + resp.data = session->internals.selected_ocsp[ctx->cert_index].response.data; + resp.size = session->internals.selected_ocsp[ctx->cert_index].response.size; ret = 0; } else { return 0; } } else if (session->internals.selected_ocsp_func) { - if (ctx->cert_index == 0) + if (ctx->cert_index == 0) { ret = session->internals.selected_ocsp_func(session, session->internals.selected_ocsp_func_ptr, &resp); - else { + free_resp = 1; + } else { + return 0; + } + } else if (ctx->cred->glob_ocsp_func) { + if (ctx->cert_index == 0) { + ret = ctx->cred->glob_ocsp_func(session, ctx->cred->glob_ocsp_func_ptr, &resp); + free_resp = 1; + } else { return 0; } } else @@ -166,7 +182,8 @@ int append_status_request(void *_ctx, gnutls_buffer_st *buf) ret = 0; cleanup: - gnutls_free(resp.data); + if (free_resp) + gnutls_free(resp.data); return ret; } @@ -243,8 +260,9 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) } #ifdef ENABLE_OCSP - if ((session->internals.selected_ocsp_func != NULL || - cred->glob_ocsp_func != NULL) && + if ((session->internals.selected_ocsp_length > 0 || + session->internals.selected_ocsp_func || + cred->glob_ocsp_func) && _gnutls_hello_ext_is_present(session, GNUTLS_EXTENSION_STATUS_REQUEST)) { /* append status response if available */ ret = _gnutls_extv_append_init(&buf); diff --git a/lib/x509.c b/lib/x509.c index cdca9cf549..6a536cd1e5 100644 --- a/lib/x509.c +++ b/lib/x509.c @@ -48,6 +48,7 @@ #include "read-file.h" #include "system-keys.h" #include "urls.h" +#include "ocsp.h" #ifdef _WIN32 #include <wincrypt.h> #endif @@ -70,7 +71,6 @@ certificate_credential_append_crt_list(gnutls_certificate_credentials_t res, } /* fifteen days */ -#define MAX_OCSP_VALIDITY_SECS (15*60*60*24) #ifdef ENABLE_OCSP /* If the certificate is revoked status will be GNUTLS_CERT_REVOKED. * diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 128306b95c..a10cd2f00c 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -79,7 +79,7 @@ libgnutls_x509_la_SOURCES = \ tls_features.c \ krb5.c krb5.h \ ip.c ip.h ip-in-cidr.h \ - supported_exts.h + supported_exts.h ocsp.h if ENABLE_OCSP libgnutls_x509_la_SOURCES += ocsp.c ocsp_output.c diff --git a/lib/x509/ocsp.c b/lib/x509/ocsp.c index 9edaa48022..0c57f7cf2e 100644 --- a/lib/x509/ocsp.c +++ b/lib/x509/ocsp.c @@ -32,6 +32,7 @@ #include "common.h" #include "verify-high.h" #include "x509.h" +#include "ocsp.h" #include <gnutls/ocsp.h> #include <auth/cert.h> @@ -2543,3 +2544,54 @@ gnutls_ocsp_resp_list_import2(gnutls_ocsp_resp_t **ocsps, gnutls_ocsp_resp_deinit(resp); return ret; } + +/* This returns -1 if the OCSP response is invalid (revoked) or its + * data are too old. Otherwise it returns the time after which that data + * is invalid. + */ +time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_t resp) +{ + unsigned int cert_status; + time_t rtime, vtime, ntime, now; + int ret; + + ret = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL, NULL, + &cert_status, &vtime, &ntime, + &rtime, NULL); + if (ret < 0) { + _gnutls_debug_log("There was an error parsing the OCSP response: %s\n", + gnutls_strerror(ret)); + return gnutls_assert_val(-1); + } + + if (cert_status != GNUTLS_OCSP_CERT_GOOD && + cert_status != GNUTLS_OCSP_CERT_UNKNOWN) { + _gnutls_debug_log("The OCSP response status (%d) is invalid\n", + cert_status); + return gnutls_assert_val(-1); + } + + now = gnutls_time(0); + + if (ntime == -1) { + /* This is a problematic case, and there is no concensus on how + * to treat these responses. It doesn't contain the time after which + * the response is invalid, thus it is an OCSP response effectively + * valid forever defeating the purpose of OCSP. We set here the same + * limit we apply when verifying responses. */ + if (now - vtime > MAX_OCSP_VALIDITY_SECS) { + _gnutls_debug_log("The OCSP response is old\n"); + return gnutls_assert_val(-1); + } + + return now + MAX_OCSP_VALIDITY_SECS; + } else { + /* there is a newer OCSP answer, don't trust this one */ + if (ntime < now) { + _gnutls_debug_log("There is a newer OCSP response\n"); + return gnutls_assert_val(-1); + } + + return ntime; + } +} diff --git a/lib/x509/ocsp.h b/lib/x509/ocsp.h new file mode 100644 index 0000000000..3d6418b184 --- /dev/null +++ b/lib/x509/ocsp.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ +#include <gnutls/ocsp.h> + +/* fifteen days */ +#define MAX_OCSP_VALIDITY_SECS (15*60*60*24) + +time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_t resp); |