]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
tls13: request OCSP responses as a server
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Wed, 15 Jan 2020 10:05:31 +0000 (11:05 +0100)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 20 Jan 2020 16:38:12 +0000 (17:38 +0100)
The TLS1.3 protocol requires the server to advertise an empty
OCSP status request extension on its certificate verify message
for an OCSP response to be sent by the client. We now always
send this extension to allow clients attaching those responses.

Resolves: #876

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
NEWS
lib/tls13/certificate_request.c
tests/Makefile.am
tests/set_x509_ocsp_multi_cli.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 23b4859682053d589670c80f2fbb15debd781a15..28afa6944b0c2e3b707086c377b600c95182ba4b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -42,7 +42,8 @@ See the end for copying conditions.
    have been marked as insecure otherwise (#877).
 
 ** libgnutls: On client side only send OCSP staples if they have been requested
-   by the server (#876).
+   by the server, and on server side always advertise that we support OCSP stapling
+   (#876).
 
 ** libgnutls: The default-priority-string added to system configuration
    to allow overriding compiled-in default-priority-string.
index 7c0eb04d9b346ff7be803801c8c7ac92a72f1052..37e7b41049ed815804ccb62efab7fa9ebafa5759 100644 (file)
@@ -266,6 +266,11 @@ int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
                                              size);
 }
 
+static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
+{
+       return GNUTLS_E_INT_RET_0;
+}
+
 int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
 {
        gnutls_certificate_credentials_t cred;
@@ -341,6 +346,17 @@ int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
                        goto cleanup;
                }
 
+#ifdef ENABLE_OCSP
+               /* We always advertise our support for OCSP stapling */
+               ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id, session,
+                                         append_empty_ext);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+               session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
+#endif
+
                ret = _gnutls_extv_append_final(&buf, init_pos, 0);
                if (ret < 0) {
                        gnutls_assert();
index c3c1780ad1dcd9b097ce4fe812a621b83ef93d8f..4e12bc802eb5dc2bde40df4e2aae45b9b23e8e1f 100644 (file)
@@ -216,7 +216,8 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
         resume-with-stek-expiration resume-with-previous-stek rawpk-api \
         tls-record-size-limit-asym dh-compute ecdh-compute sign-verify-data-newapi \
         sign-verify-newapi sign-verify-deterministic iov aead-cipher-vec \
-        tls13-without-timeout-func buffer status-request-revoked
+        tls13-without-timeout-func buffer status-request-revoked \
+        set_x509_ocsp_multi_cli
 
 if HAVE_SECCOMP_TESTS
 ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
diff --git a/tests/set_x509_ocsp_multi_cli.c b/tests/set_x509_ocsp_multi_cli.c
new file mode 100644 (file)
index 0000000..ae80ca3
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2020 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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 <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#ifdef ENABLE_OCSP
+
+#include "cert-common.h"
+#include "ocsp-common.h"
+#include "utils.h"
+
+/* Tests whether setting an OCSP response to a client
+ * is working as expected */
+
+static time_t mytime(time_t * t)
+{
+       time_t then = OCSP_RESP_DATE;
+       if (t)
+               *t = then;
+
+       return then;
+}
+
+static void check_cli(gnutls_session_t session, void *priv)
+{
+       assert((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SERV_REQUESTED_OCSP) != 0);
+}
+
+static void check_serv(gnutls_session_t session, void *priv)
+{
+       int ret;
+       unsigned int status;
+       gnutls_datum_t resp;
+       gnutls_datum_t *exp_resp = priv;
+
+       assert((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_SERV_REQUESTED_OCSP) != 0);
+
+       ret = gnutls_ocsp_status_request_get(session, &resp);
+       if (ret < 0) {
+               if (priv == NULL)
+                       return;
+               fail("no response was received\n");
+       }
+
+       if (priv == NULL) {
+               fail("not expected response, but received one\n");
+       }
+
+       if (resp.size != exp_resp->size || memcmp(resp.data, exp_resp->data, resp.size) != 0) {
+               fail("did not receive the expected response\n");
+       }
+
+       /* Check intermediate response */
+       if (gnutls_protocol_get_version(session) == GNUTLS_TLS1_3) {
+               ret = gnutls_ocsp_status_request_get2(session, 1, &resp);
+               if (ret < 0) {
+                       fail("no intermediate response was received\n");
+               }
+
+               if (resp.size != ocsp_subca3_unknown.size || memcmp(resp.data, ocsp_subca3_unknown.data, resp.size) != 0) {
+                       fail("did not receive the expected intermediate response\n");
+               }
+       }
+
+       ret = gnutls_certificate_verify_peers2(session, &status);
+       if (ret != 0)
+               fail("error in verification (%s)\n", gnutls_strerror(ret));
+
+       ret = gnutls_ocsp_status_request_is_checked(session, GNUTLS_OCSP_SR_IS_AVAIL);
+       if (ret == 0) {
+               fail("did not receive the expected value (%d)\n", ret);
+       }
+
+       ret = gnutls_ocsp_status_request_is_checked(session, 0);
+       if (ret == 0) {
+               fail("did not receive the expected value (%d)\n", ret);
+       }
+}
+
+static void tls_log_func(int level, const char *str)
+{
+       fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+void doit(void)
+{
+       int ret;
+       gnutls_certificate_credentials_t xcred;
+       gnutls_certificate_credentials_t clicred;
+       const char *certfile1;
+       const char *ocspfile1;
+       char certname1[TMPNAME_SIZE], ocspname1[TMPNAME_SIZE];
+       FILE *fp;
+       unsigned index1;
+       time_t t;
+
+       global_init();
+       gnutls_global_set_time_function(mytime);
+
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(4711);
+
+       assert(gnutls_certificate_allocate_credentials(&xcred) >= 0);
+       assert(gnutls_certificate_allocate_credentials(&clicred) >= 0);
+
+       gnutls_certificate_set_flags(xcred, GNUTLS_CERTIFICATE_API_V2);
+
+       certfile1 = get_tmpname(certname1);
+
+       /* set cert with localhost name */
+       fp = fopen(certfile1, "wb");
+       if (fp == NULL)
+               fail("error in fopen\n");
+       assert(fwrite(server_localhost_ca3_cert_chain_pem, 1, strlen(server_localhost_ca3_cert_chain_pem), fp)>0);
+       assert(fwrite(server_ca3_key_pem, 1, strlen((char*)server_ca3_key_pem), fp)>0);
+       fclose(fp);
+
+       ret = gnutls_certificate_set_x509_key_file2(xcred, certfile1, certfile1,
+                                                   GNUTLS_X509_FMT_PEM, NULL, 0);
+       if (ret < 0)
+               fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+
+       ret = gnutls_certificate_set_x509_key_file2(clicred, certfile1, certfile1,
+                                                   GNUTLS_X509_FMT_PEM, NULL, 0);
+       if (ret < 0)
+               fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+       index1 = ret;
+
+       /* set OCSP response1, include an unrelated OCSP response */
+       ocspfile1 = get_tmpname(ocspname1);
+       fp = fopen(ocspfile1, "wb");
+       if (fp == NULL)
+               fail("error in fopen\n");
+       assert(fwrite(ocsp_subca3_unknown_pem.data, 1, ocsp_subca3_unknown_pem.size, fp)>0);
+       assert(fwrite(ocsp_ca3_localhost_unknown_pem.data, 1, ocsp_ca3_localhost_unknown_pem.size, fp)>0);
+       assert(fwrite(ocsp_ca3_localhost6_unknown_pem.data, 1, ocsp_ca3_localhost6_unknown_pem.size, fp)>0);
+       fclose(fp);
+
+       ret = gnutls_certificate_set_ocsp_status_request_file2(clicred, ocspfile1, index1,
+                                                              GNUTLS_X509_FMT_PEM);
+       if (ret != GNUTLS_E_OCSP_MISMATCH_WITH_CERTS)
+               fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+       /* set OCSP response1, include correct responses */
+       remove(ocspfile1);
+       fp = fopen(ocspfile1, "wb");
+       if (fp == NULL)
+               fail("error in fopen\n");
+       assert(fwrite(ocsp_subca3_unknown_pem.data, 1, ocsp_subca3_unknown_pem.size, fp)>0);
+       assert(fwrite(ocsp_ca3_localhost_unknown_pem.data, 1, ocsp_ca3_localhost_unknown_pem.size, fp)>0);
+       fclose(fp);
+
+       ret = gnutls_certificate_set_ocsp_status_request_file2(clicred, ocspfile1, index1,
+                                                              GNUTLS_X509_FMT_PEM);
+       if (ret < 0)
+               fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+       ret = gnutls_certificate_set_x509_trust_mem(clicred, &ca3_cert, GNUTLS_X509_FMT_PEM);
+       if (ret < 0) {
+               fail("error in setting trust cert: %s\n", gnutls_strerror(ret));
+       }
+
+       t = gnutls_certificate_get_ocsp_expiration(clicred, 0, 0, 0);
+       if (t != 1509625639)
+               fail("error in OCSP validity time: %ld\n", (long int)t);
+
+       t = gnutls_certificate_get_ocsp_expiration(clicred, 0, 1, 0);
+       if (t != 1509625639)
+               fail("error in OCSP validity time: %ld\n", (long int)t);
+
+       t = gnutls_certificate_get_ocsp_expiration(clicred, 0, -1, 0);
+       if (t != 1509625639)
+               fail("error in OCSP validity time: %ld\n", (long int)t);
+
+#define PRIO "NORMAL:-ECDHE-ECDSA:-VERS-TLS-ALL:+VERS-TLS1.3"
+       _test_cli_serv(xcred, clicred, PRIO, PRIO, "localhost", &ocsp_ca3_localhost_unknown, check_cli,
+                      check_serv, 0, 1, 0, 0);
+
+       gnutls_certificate_free_credentials(xcred);
+       gnutls_certificate_free_credentials(clicred);
+       gnutls_global_deinit();
+       remove(ocspfile1);
+       remove(certfile1);
+}
+
+#else
+void doit(void)
+{
+       exit(77);
+}
+#endif