summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-10-18 09:54:29 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2017-12-04 21:17:25 +0100
commitf6af904f3ce37ac3050d59f88e406b16fed4e763 (patch)
tree756ea2d0194673da2e44967d88f40ed014fe1145
parent5b0f07dce29e455cbffb66329e5d29cf73075792 (diff)
downloadgnutls-f6af904f3ce37ac3050d59f88e406b16fed4e763.tar.gz
gnutls_certificate_set_ocsp_status_request_file: match input response to certificates
That is, iterate through the certificate chain to figure to which certificate the response corresponds to, and assign it to it. That allows for applications to re-use this function to set multiple responses when available. Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/auth/cert.h4
-rw-r--r--lib/cert.c5
-rw-r--r--lib/errors.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in1
-rw-r--r--lib/ocsp-api.c105
5 files changed, 107 insertions, 10 deletions
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index 0f8aba162b..90bed40de3 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -35,6 +35,8 @@ typedef struct legacy_ocsp_func_st {
void *ptr; /* private data of legacy_ocsp_func */
} legacy_ocsp_func_st;
+#define MAX_OCSP_RESPONSE_FILES 8
+
typedef struct {
gnutls_pcert_st *cert_list; /* a certificate chain */
unsigned int cert_list_length; /* its length */
@@ -45,7 +47,7 @@ typedef struct {
gnutls_status_request_ocsp_func2 ocsp_func;
void *ocsp_func_ptr; /* private data of ocsp_func */
- char *ocsp_response_file; /* corresponding OCSP response file */
+ char *ocsp_response_files[MAX_OCSP_RESPONSE_FILES]; /* 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 9e3a00f484..a1529bae69 100644
--- a/lib/cert.c
+++ b/lib/cert.c
@@ -60,7 +60,10 @@ void gnutls_certificate_free_keys(gnutls_certificate_credentials_t sc)
gnutls_pcert_deinit(&sc->certs[i].cert_list[j]);
}
gnutls_free(sc->certs[i].cert_list);
- gnutls_free(sc->certs[i].ocsp_response_file);
+
+ for (j = 0; j < MIN(sc->certs[i].cert_list_length, MAX_OCSP_RESPONSE_FILES); j++) {
+ gnutls_free(sc->certs[i].ocsp_response_files[j]);
+ }
_gnutls_str_array_clear(&sc->certs[i].names);
gnutls_privkey_deinit(sc->certs[i].pkey);
}
diff --git a/lib/errors.c b/lib/errors.c
index ea444c3e12..ad67aaba6a 100644
--- a/lib/errors.c
+++ b/lib/errors.c
@@ -375,6 +375,8 @@ static const gnutls_error_entry error_entries[] = {
GNUTLS_E_CERTIFICATE_LIST_UNSORTED),
ERROR_ENTRY(N_("The OCSP response is invalid"),
GNUTLS_E_OCSP_RESPONSE_ERROR),
+ ERROR_ENTRY(N_("The OCSP response provided doesn't match the available certificates"),
+ GNUTLS_E_OCSP_MISMATCH_WITH_CERTS),
ERROR_ENTRY(N_("There is no certificate status (OCSP)."),
GNUTLS_E_NO_CERTIFICATE_STATUS),
ERROR_ENTRY(N_("Error in the system's randomness device."),
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 1ac4fa106f..29385f6309 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -3003,6 +3003,7 @@ unsigned gnutls_fips140_mode_enabled(void);
#define GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY -419
#define GNUTLS_E_PK_INVALID_PUBKEY_PARAMS -420
#define GNUTLS_E_PK_NO_VALIDATION_PARAMS -421
+#define GNUTLS_E_OCSP_MISMATCH_WITH_CERTS -422
#define GNUTLS_E_NO_COMMON_KEY_SHARE -420
diff --git a/lib/ocsp-api.c b/lib/ocsp-api.c
index 70431fbfe6..3c2e77ff55 100644
--- a/lib/ocsp-api.c
+++ b/lib/ocsp-api.c
@@ -30,9 +30,11 @@
#include <auth.h>
#include <auth/cert.h>
#include <handshake.h>
+#include <minmax.h>
#ifdef ENABLE_OCSP
+#include <gnutls/ocsp.h>
/**
* gnutls_ocsp_status_request_get:
@@ -284,18 +286,48 @@ static int file_ocsp_func(gnutls_session_t session,
gnutls_datum_t * ocsp_response)
{
int ret;
- const char *file = ptr;
+ certs_st *certs = ptr;
- if (cinfo->cert_index > 0)
+ if (cinfo->cert_index >= MAX_OCSP_RESPONSE_FILES ||
+ certs->ocsp_response_files[cinfo->cert_index] == NULL)
return GNUTLS_E_NO_CERTIFICATE_STATUS;
- ret = gnutls_load_file(file, ocsp_response);
+ ret = gnutls_load_file(certs->ocsp_response_files[cinfo->cert_index], ocsp_response);
if (ret < 0)
return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_STATUS);
return 0;
}
+static
+unsigned resp_matches_pcert(gnutls_ocsp_resp_t resp, const gnutls_pcert_st *cert)
+{
+ gnutls_x509_crt_t crt;
+ int ret;
+ unsigned retval;
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0)
+ return 0;
+
+ ret = gnutls_x509_crt_import(crt, &cert->cert, GNUTLS_X509_FMT_DER);
+ if (ret < 0) {
+ gnutls_assert();
+ retval = 0;
+ goto cleanup;
+ }
+
+ ret = gnutls_ocsp_resp_check_crt(resp, 0, crt);
+ if (ret == 0)
+ retval = 1;
+ else
+ retval = 0;
+
+ cleanup:
+ gnutls_x509_crt_deinit(crt);
+ return retval;
+}
+
/**
* gnutls_certificate_set_ocsp_status_request_file:
* @sc: is a credentials structure.
@@ -321,6 +353,11 @@ static int file_ocsp_func(gnutls_session_t session,
* 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 since GnuTLS 3.6.xx
+ * 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.
+ *
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned,
* otherwise a negative error code is returned.
*
@@ -331,15 +368,67 @@ 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);
- gnutls_free(sc->certs[idx].ocsp_response_file);
- sc->certs[idx].ocsp_response_file = gnutls_strdup(response_file);
- if (sc->certs[idx].ocsp_response_file == NULL)
- return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ ret = gnutls_load_file(response_file, &der);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+
+ ret = gnutls_ocsp_resp_init(&resp);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = gnutls_ocsp_resp_import(resp, &der);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* 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_RESPONSE_FILES);i++) {
+ if (sc->certs[idx].ocsp_response_files[i])
+ continue;
+
+ if (!resp_matches_pcert(resp, &sc->certs[idx].cert_list[i]))
+ continue;
+
+ _gnutls_debug_log("associating OCSP response with chain %d on pos %d\n", idx, i);
+ sc->certs[idx].ocsp_response_files[i] = gnutls_strdup(response_file);
+ if (sc->certs[idx].ocsp_response_files[i] == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ ret = gnutls_certificate_set_ocsp_status_request_function3(sc, idx,
+ file_ocsp_func,
+ &sc->certs[idx], 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ found = 1;
+ break;
+ }
- return gnutls_certificate_set_ocsp_status_request_function3(sc, idx, file_ocsp_func, sc->certs[idx].ocsp_response_file, 0);
+ if (!found)
+ ret = GNUTLS_E_OCSP_MISMATCH_WITH_CERTS;
+ else
+ ret = 0;
+ cleanup:
+ gnutls_free(der.data);
+ if (resp)
+ gnutls_ocsp_resp_deinit(resp);
+ return ret;
}
/**