From 6ab20d77120f818522863bd43cab20541e0afa57 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 15 Jan 2020 09:46:38 +0100 Subject: tls13: do not send OCSP responses as client without server requesting In client side ensure we see a request for OCSP from servers before sending one. Relates: #876 Signed-off-by: Nikos Mavrogiannopoulos --- NEWS | 4 ++++ lib/gnutls_int.h | 1 + lib/includes/gnutls/gnutls.h.in | 4 +++- lib/state.c | 2 ++ lib/tls13/certificate.c | 3 ++- lib/tls13/certificate_request.c | 9 +++++++++ 6 files changed, 21 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 68a36e7ec1..23b4859682 100644 --- a/NEWS +++ b/NEWS @@ -41,6 +41,9 @@ See the end for copying conditions. to accepting it. This addresses the problem of accepting CAs which would have been marked as insecure otherwise (#877). +** libgnutls: On client side only send OCSP staples if they have been requested + by the server (#876). + ** libgnutls: The default-priority-string added to system configuration to allow overriding compiled-in default-priority-string. @@ -56,6 +59,7 @@ See the end for copying conditions. ** API and ABI modifications: GNUTLS_SFLAGS_CLI_REQUESTED_OCSP: Added +GNUTLS_SFLAGS_SERV_REQUESTED_OCSP: Added gnutls_ocsp_req_const_t: Added diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index f7cf830ca1..b48805190a 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1383,6 +1383,7 @@ typedef struct { #define HSK_RECORD_SIZE_LIMIT_SENT (1<<25) /* record_size_limit extension was sent */ #define HSK_RECORD_SIZE_LIMIT_RECEIVED (1<<26) /* server: record_size_limit extension was seen but not accepted yet */ #define HSK_OCSP_REQUESTED (1<<27) /* server: client requested OCSP stapling */ +#define HSK_CLIENT_OCSP_REQUESTED (1<<28) /* client: server requested OCSP stapling */ /* The hsk_flags are for use within the ongoing handshake; * they are reset to zero prior to handshake start by gnutls_handshake. */ diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 1d0f924c26..61232de01b 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1570,6 +1570,7 @@ unsigned gnutls_session_etm_status(gnutls_session_t session); * @GNUTLS_SFLAGS_EARLY_START: The TLS1.3 server session returned early. * @GNUTLS_SFLAGS_EARLY_DATA: The TLS1.3 early data has been received by the server. * @GNUTLS_SFLAGS_CLI_REQUESTED_OCSP: Set when the client has requested OCSP staple during handshake. + * @GNUTLS_SFLAGS_SERV_REQUESTED_OCSP: Set when the server has requested OCSP staple during handshake. * * Enumeration of different session parameters. */ @@ -1585,7 +1586,8 @@ typedef enum { GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH = 1<<8, GNUTLS_SFLAGS_EARLY_START = 1<<9, GNUTLS_SFLAGS_EARLY_DATA = 1<<10, - GNUTLS_SFLAGS_CLI_REQUESTED_OCSP = 1<<11 + GNUTLS_SFLAGS_CLI_REQUESTED_OCSP = 1<<11, + GNUTLS_SFLAGS_SERV_REQUESTED_OCSP = 1<<12 } gnutls_session_flags_t; unsigned gnutls_session_get_flags(gnutls_session_t session); diff --git a/lib/state.c b/lib/state.c index 5e3a7f95aa..dff7312a87 100644 --- a/lib/state.c +++ b/lib/state.c @@ -1576,6 +1576,8 @@ unsigned gnutls_session_get_flags(gnutls_session_t session) flags |= GNUTLS_SFLAGS_EARLY_DATA; if (session->internals.hsk_flags & HSK_OCSP_REQUESTED) flags |= GNUTLS_SFLAGS_CLI_REQUESTED_OCSP; + if (session->internals.hsk_flags & HSK_CLIENT_OCSP_REQUESTED) + flags |= GNUTLS_SFLAGS_SERV_REQUESTED_OCSP; return flags; } diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c index 82a45af336..7483251a53 100644 --- a/lib/tls13/certificate.c +++ b/lib/tls13/certificate.c @@ -273,7 +273,8 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again) #ifdef ENABLE_OCSP if ((session->internals.selected_ocsp_length > 0 || session->internals.selected_ocsp_func) && - (session->internals.hsk_flags & HSK_OCSP_REQUESTED)) { + (((session->internals.hsk_flags & HSK_OCSP_REQUESTED) && IS_SERVER(session)) || + ((session->internals.hsk_flags & HSK_CLIENT_OCSP_REQUESTED) && !IS_SERVER(session)))) { /* append status response if available */ ret = _gnutls_extv_append_init(&buf); if (ret < 0) { diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c index 58fdbbc187..7c0eb04d9b 100644 --- a/lib/tls13/certificate_request.c +++ b/lib/tls13/certificate_request.c @@ -26,6 +26,7 @@ #include "handshake.h" #include "tls13/certificate_request.h" #include "ext/signature.h" +#include "ext/status_request.h" #include "mbuffers.h" #include "algorithms.h" #include "auth/cert.h" @@ -108,6 +109,14 @@ int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsig ctx->pk_algos[ctx->pk_algos_length++] = se->pk; } +#ifdef ENABLE_OCSP + } else if (tls_id == ext_mod_status_request.tls_id) { + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR); + + /* we are now allowed to send OCSP staples */ + session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED; +#endif } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) { if (data_size < 3) { return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR); -- cgit v1.2.1 From f39b85db96c099c5f851f000cb74fb5200e05919 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 15 Jan 2020 11:05:31 +0100 Subject: tls13: request OCSP responses as a server 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 --- NEWS | 3 +- lib/tls13/certificate_request.c | 16 +++ tests/Makefile.am | 3 +- tests/set_x509_ocsp_multi_cli.c | 218 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 tests/set_x509_ocsp_multi_cli.c diff --git a/NEWS b/NEWS index 23b4859682..28afa6944b 100644 --- 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. diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c index 7c0eb04d9b..37e7b41049 100644 --- a/lib/tls13/certificate_request.c +++ b/lib/tls13/certificate_request.c @@ -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(); diff --git a/tests/Makefile.am b/tests/Makefile.am index c3c1780ad1..4e12bc802e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 0000000000..ae80ca3b0b --- /dev/null +++ b/tests/set_x509_ocsp_multi_cli.c @@ -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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#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 -- cgit v1.2.1