diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2020-01-20 17:44:59 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2020-01-20 17:44:59 +0000 |
commit | ad93addd6db2dfd703888b24f7a9948d1f593c90 (patch) | |
tree | 02342b8bd55356702e70461b70c5b35fb237bae7 | |
parent | 999d1697ec4a4da261c6e4a9a741b6dc5043ea4e (diff) | |
parent | f39b85db96c099c5f851f000cb74fb5200e05919 (diff) | |
download | gnutls-ad93addd6db2dfd703888b24f7a9948d1f593c90.tar.gz |
Merge branch 'tmp-tls13-ocsp' into 'master'
tls13: fix issues with client OCSP responses
Closes #876
See merge request gnutls/gnutls!1169
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | lib/gnutls_int.h | 1 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 4 | ||||
-rw-r--r-- | lib/state.c | 2 | ||||
-rw-r--r-- | lib/tls13/certificate.c | 3 | ||||
-rw-r--r-- | lib/tls13/certificate_request.c | 25 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/set_x509_ocsp_multi_cli.c | 218 |
8 files changed, 258 insertions, 3 deletions
@@ -44,6 +44,10 @@ 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, 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. @@ -59,6 +63,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..37e7b41049 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); @@ -257,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; @@ -332,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 <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 |