summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-12-08 13:45:24 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-02-05 08:57:22 +0100
commit3f67616af92cdfabeb6ecc27e50d387a6dabb963 (patch)
tree9fed8b1c103431c65dc679434bac2dc67b597fe9
parentd05093aeef58cfdd93b0dd8b253f0adb4d6f93d9 (diff)
downloadgnutls-3f67616af92cdfabeb6ecc27e50d387a6dabb963.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.c16
-rw-r--r--lib/auth/cert.h5
-rw-r--r--lib/cert.c10
-rw-r--r--lib/ext/status_request.c19
-rw-r--r--lib/gnutls_int.h2
-rw-r--r--lib/includes/gnutls/abstract.h2
-rw-r--r--lib/includes/gnutls/gnutls.h.in19
-rw-r--r--lib/libgnutls.map4
-rw-r--r--lib/ocsp-api.c283
-rw-r--r--lib/tls13/certificate.c36
-rw-r--r--lib/x509.c2
-rw-r--r--lib/x509/Makefile.am2
-rw-r--r--lib/x509/ocsp.c52
-rw-r--r--lib/x509/ocsp.h30
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 81eeb2208b..4ebb6bb57e 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 f32a90e333..4024ee8fcc 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1210,7 +1210,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);