summaryrefslogtreecommitdiff
path: root/lib/ocsp-api.c
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-12-08 13:45:24 +0100
committerNikos Mavrogiannopoulos <nmav@redhat.com>2018-02-19 15:29:37 +0100
commit5623c86b5678ef93e9670a6f7bc412e2c8dda62a (patch)
tree35fe40a30e4da89c3410dcc477fce78282ae1983 /lib/ocsp-api.c
parent4a4f5b48a8db833adaaef9c0bbf02a9eb7b20700 (diff)
downloadgnutls-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>
Diffstat (limited to 'lib/ocsp-api.c')
-rw-r--r--lib/ocsp-api.c283
1 files changed, 237 insertions, 46 deletions
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;
}