summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-22 10:06:26 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-22 16:41:32 +0100
commit19f3da62b832ec4b714eae26dbc62ed106089575 (patch)
tree474aa0d67ed95a710ab6afb46339df11c92198ba
parent9e475488055577bc90d432e9ec216c747974019b (diff)
downloadgnutls-19f3da62b832ec4b714eae26dbc62ed106089575.tar.gz
ocsp: do not use revoked or expired OCSP responses
That is, if the server is provided a problematic OCSP response, or the OCSP response is not renewed before it is invalid, stop providing it to the clients. Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/auth/cert.h7
-rw-r--r--lib/cert.c2
-rw-r--r--lib/ocsp-api.c33
-rw-r--r--lib/x509.c3
-rw-r--r--lib/x509/ocsp.c52
-rw-r--r--lib/x509/ocsp.h30
6 files changed, 115 insertions, 12 deletions
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index 15a256a2ae..486d6fa747 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -35,6 +35,11 @@ typedef struct legacy_ocsp_func_st {
void *ptr; /* private data of legacy_ocsp_func */
} legacy_ocsp_func_st;
+typedef struct ocsp_data_st {
+ gnutls_datum_t response;
+ time_t exptime;
+} ocsp_data_st;
+
#define MAX_OCSP_RESPONSES 8
typedef struct {
@@ -47,7 +52,7 @@ typedef struct {
gnutls_status_request_ocsp_func2 ocsp_func;
void *ocsp_func_ptr; /* private data of ocsp_func */
- gnutls_datum_t ocsp_response[MAX_OCSP_RESPONSES]; /* corresponding OCSP response file */
+ ocsp_data_st ocsp_data[MAX_OCSP_RESPONSES]; /* corresponding OCSP response file */
/* the private key corresponding to certificate */
gnutls_privkey_t pkey;
diff --git a/lib/cert.c b/lib/cert.c
index d62ac9e057..1000b31d35 100644
--- a/lib/cert.c
+++ b/lib/cert.c
@@ -62,7 +62,7 @@ void gnutls_certificate_free_keys(gnutls_certificate_credentials_t sc)
gnutls_free(sc->certs[i].cert_list);
for (j = 0; j < MIN(sc->certs[i].cert_list_length, MAX_OCSP_RESPONSES); j++) {
- gnutls_free(sc->certs[i].ocsp_response[j].data);
+ gnutls_free(sc->certs[i].ocsp_data[j].response.data);
}
_gnutls_str_array_clear(&sc->certs[i].names);
gnutls_privkey_deinit(sc->certs[i].pkey);
diff --git a/lib/ocsp-api.c b/lib/ocsp-api.c
index dc30ff14b3..6fce125f03 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:
@@ -289,11 +290,15 @@ static int mem_ocsp_func(gnutls_session_t session,
certs_st *certs = ptr;
if (cinfo->cert_index >= MAX_OCSP_RESPONSES ||
- certs->ocsp_response[cinfo->cert_index].data == NULL)
+ certs->ocsp_data[cinfo->cert_index].response.data == NULL)
return GNUTLS_E_NO_CERTIFICATE_STATUS;
- ret = _gnutls_set_datum(ocsp_response, certs->ocsp_response[cinfo->cert_index].data,
- certs->ocsp_response[cinfo->cert_index].size);
+ if (certs->ocsp_data[cinfo->cert_index].exptime != 0 &&
+ gnutls_time(0) >= certs->ocsp_data[cinfo->cert_index].exptime)
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_STATUS);
+
+ ret = _gnutls_set_datum(ocsp_response, certs->ocsp_data[cinfo->cert_index].response.data,
+ certs->ocsp_data[cinfo->cert_index].response.size);
if (ret < 0)
return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_STATUS);
@@ -432,12 +437,13 @@ static int append_response(gnutls_certificate_credentials_t sc, unsigned idx,
{
int ret;
unsigned i, found = 0;
+ time_t t;
/* 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_response[i].data)
+ if (sc->certs[idx].ocsp_data[i].response.data)
continue;
if (!resp_matches_pcert(resp, &sc->certs[idx].cert_list[i]))
@@ -445,7 +451,18 @@ static int append_response(gnutls_certificate_credentials_t sc, unsigned idx,
_gnutls_debug_log("associating OCSP response with chain %d on pos %d\n", idx, i);
- ret = _gnutls_set_datum(&sc->certs[idx].ocsp_response[i], der->data, der->size);
+ t = _gnutls_ocsp_get_validity(resp);
+ /* if already invalid */
+ if (t == (time_t)-1)
+ continue;
+
+ if (t >= 0)
+ sc->certs[idx].ocsp_data[i].exptime = t;
+ else
+ sc->certs[idx].ocsp_data[i].exptime = 0;
+
+ ret = _gnutls_set_datum(&sc->certs[idx].ocsp_data[i].response,
+ der->data, der->size);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -561,7 +578,7 @@ gnutls_certificate_set_ocsp_status_request_mem(gnutls_certificate_credentials_t
p.data = memmem(p.data, p.size, FULL_PEM_OCSP_RESPONSE,
sizeof(FULL_PEM_OCSP_RESPONSE)-1);
if (p.data == NULL)
- return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+ break;
p.size -= p.data - resp_data->data;
} while(p.size > 0);
@@ -571,9 +588,9 @@ gnutls_certificate_set_ocsp_status_request_mem(gnutls_certificate_credentials_t
if (sc->flags & GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK) {
/* quick load of first response */
- gnutls_free(sc->certs[idx].ocsp_response[0].data);
+ gnutls_free(sc->certs[idx].ocsp_data[0].response.data);
- ret = _gnutls_set_datum(&sc->certs[idx].ocsp_response[0],
+ ret = _gnutls_set_datum(&sc->certs[idx].ocsp_data[0].response,
resp_data->data,
resp_data->size);
if (ret < 0) {
diff --git a/lib/x509.c b/lib/x509.c
index cdca9cf549..b72fc671f8 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 "x509/ocsp.h"
#ifdef _WIN32
#include <wincrypt.h>
#endif
@@ -69,8 +70,6 @@ certificate_credential_append_crt_list(gnutls_certificate_credentials_t res,
return 0; \
}
-/* 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/ocsp.c b/lib/x509/ocsp.c
index 84bf141079..62d54294fa 100644
--- a/lib/x509/ocsp.c
+++ b/lib/x509/ocsp.c
@@ -31,6 +31,7 @@
#include <pk.h>
#include "common.h"
#include "verify-high.h"
+#include "ocsp.h"
#include <gnutls/ocsp.h>
#include <auth/cert.h>
@@ -2410,3 +2411,54 @@ gnutls_ocsp_resp_verify(gnutls_ocsp_resp_t resp,
return rc;
}
+
+/* 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);