From 19f3da62b832ec4b714eae26dbc62ed106089575 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 22 Nov 2017 10:06:26 +0100 Subject: 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 --- lib/auth/cert.h | 7 ++++++- lib/cert.c | 2 +- lib/ocsp-api.c | 33 +++++++++++++++++++++++++-------- lib/x509.c | 3 +-- lib/x509/ocsp.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/x509/ocsp.h | 30 ++++++++++++++++++++++++++++++ 6 files changed, 115 insertions(+), 12 deletions(-) create mode 100644 lib/x509/ocsp.h 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 +#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;icerts[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 #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 #include "common.h" #include "verify-high.h" +#include "ocsp.h" #include #include @@ -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 + * + */ + +/* Online Certificate Status Protocol - RFC 2560 + */ +#include + +/* fifteen days */ +#define MAX_OCSP_VALIDITY_SECS (15*60*60*24) + +time_t _gnutls_ocsp_get_validity(gnutls_ocsp_resp_t resp); -- cgit v1.2.1