]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls_certificate_set_ocsp_status_request_file: match input response to certificates
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Thu, 7 Dec 2017 15:16:55 +0000 (16:16 +0100)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:37 +0000 (15:29 +0100)
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>
lib/auth/cert.c
lib/auth/cert.h
lib/cert.c
lib/errors.c
lib/includes/gnutls/gnutls.h.in
lib/ocsp-api.c

index 9b9cd39c3ca0babf8eeb435a379252812447d810..89d0aa248e48076a76562a0b17bb4c9eb5dce584 100644 (file)
@@ -491,7 +491,8 @@ _gnutls_select_client_cert(gnutls_session_t session,
                                           cert_list[0],
                                           cred->certs[indx].
                                           cert_list_length,
-                                          NULL, 0,
+                                          cred->certs[indx].ocsp_responses,
+                                          cred->certs[indx].ocsp_responses_length,
                                           cred->certs[indx].pkey, 0,
                                           NULL, 0);
                } else {
@@ -1426,13 +1427,23 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
         */
  finished:
        if (idx >= 0) {
-               selected_certs_set(session,
-                                  &cred->certs[idx].cert_list[0],
-                                  cred->certs[idx].cert_list_length,
-                                  NULL, 0,
-                                  cred->certs[idx].pkey, 0,
-                                  cred->certs[idx].ocsp_func,
-                                  cred->certs[idx].ocsp_func_ptr);
+               if (cred->certs[idx].ocsp_func) {
+                       selected_certs_set(session,
+                                          &cred->certs[idx].cert_list[0],
+                                          cred->certs[idx].cert_list_length,
+                                          NULL, 0,
+                                          cred->certs[idx].pkey, 0,
+                                          cred->certs[idx].ocsp_func,
+                                          cred->certs[idx].ocsp_func_ptr);
+               } else {
+                       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].pkey, 0,
+                                          NULL, NULL);
+               }
        } else {
                gnutls_assert();
                /* Certificate does not support REQUESTED_ALGO.  */
index f890f4862427d4944c99389ee18e27fc3d7a1981..9a3ecb6434b730700e3634e179363c6ff57459a3 100644 (file)
@@ -30,6 +30,8 @@
 #include <gnutls/compat.h>
 #include <str_array.h>
 
+#define MAX_OCSP_RESPONSES 8
+
 typedef struct {
        gnutls_pcert_st *cert_list;     /* a certificate chain */
        unsigned int cert_list_length;  /* its length */
@@ -37,7 +39,8 @@ typedef struct {
 
        gnutls_status_request_ocsp_func ocsp_func;
        void *ocsp_func_ptr; /* corresponding OCSP response function + ptr */
-       char *ocsp_response_file; /* corresponding OCSP response file */
+       gnutls_datum_t ocsp_responses[MAX_OCSP_RESPONSES]; /* corresponding OCSP response file */
+       unsigned int ocsp_responses_length;
 
        /* the private key corresponding to certificate */
        gnutls_privkey_t pkey;
index 617ad8e64496848f6779f089b2eefa4e5734e343..1b514e9367ff899bb185d98a338109f6dadec6ad 100644 (file)
@@ -60,7 +60,11 @@ 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 < sc->certs[i].ocsp_responses_length; j++) {
+                       gnutls_free(sc->certs[i].ocsp_responses[j].data);
+                       sc->certs[i].ocsp_responses[j].data = NULL;
+               }
                _gnutls_str_array_clear(&sc->certs[i].names);
                gnutls_privkey_deinit(sc->certs[i].pkey);
        }
index 77cba34fc7a184a5cb9f93898636bf6154693abf..16ade637499f5f2409224cb3b7c49352a73b5f70 100644 (file)
@@ -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."),
index 8e0c764564093dbf6275ebcd3e59fbe365dd7b79..755ec781278b787961ed09af8e1e405cfe81dcb1 100644 (file)
@@ -3021,6 +3021,7 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
 #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 -423
 #define GNUTLS_E_REAUTH_REQUEST -424
index b7604f789e22d2c8e7f4cab11b59a33b12d69bba..1150bd4b84d76a31267b8c90c345c7d17e9c53ec 100644 (file)
 #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:
  * @session: is a #gnutls_session_t type.
@@ -184,17 +187,33 @@ gnutls_certificate_set_ocsp_status_request_function2
        return 0;
 }
 
-static int file_ocsp_func(gnutls_session_t session, void *ptr,
-                         gnutls_datum_t * ocsp_response)
+static
+unsigned resp_matches_pcert(gnutls_ocsp_resp_t resp, const gnutls_pcert_st *cert)
 {
+       gnutls_x509_crt_t crt;
        int ret;
-       const char *file = ptr;
+       unsigned retval;
 
-       ret = gnutls_load_file(file, ocsp_response);
+       ret = gnutls_x509_crt_init(&crt);
        if (ret < 0)
-               return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_STATUS);
+               return 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;
 }
 
 /**
@@ -218,6 +237,11 @@ static int file_ocsp_func(gnutls_session_t session, void *ptr,
  * with the %GNUTLS_CERTIFICATE_API_V2 flag to make the set certificate
  * functions return an index usable by this function.
  *
+ * 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.
  *
@@ -228,17 +252,62 @@ 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;
+       }
 
-       gnutls_certificate_set_ocsp_status_request_function2(sc, idx, file_ocsp_func, sc->certs[idx].ocsp_response_file);
+       ret = gnutls_ocsp_resp_import(resp, &der);
+       if (ret < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
 
-       return 0;
+       /* 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)
+                       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_responses[i].data = der.data;
+               der.data = NULL;
+               sc->certs[idx].ocsp_responses[i].size = der.size;
+
+               if (sc->certs[idx].ocsp_responses_length <= i)
+                       sc->certs[idx].ocsp_responses_length = i+1;
+
+               found = 1;
+               break;
+       }
+
+       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;
 }
 
 /**