diff options
author | Daiki Ueno <ueno@gnu.org> | 2019-01-24 15:03:30 +0000 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2019-01-24 15:03:30 +0000 |
commit | e7af375a1222694514be1999463493354fd6361f (patch) | |
tree | ffc63984c39f71c554eeb13d4110a66aa1e78565 | |
parent | cd73edf45d98b84c44a43d840de68f48e2e0a160 (diff) | |
parent | 0f567dc5b839a7506df4243da9fb0ed80819a505 (diff) | |
download | gnutls-e7af375a1222694514be1999463493354fd6361f.tar.gz |
Merge branch 'tmp-record-size-limit-fixes' into 'master'
Fix record_size_limit extension handling when resuming
See merge request gnutls/gnutls!886
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | lib/constate.c | 11 | ||||
-rw-r--r-- | lib/ext/record_size_limit.c | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/resume-with-record-size-limit.c | 424 | ||||
-rw-r--r-- | tests/suite/tls-fuzzer/gnutls-nocert.json | 22 | ||||
-rwxr-xr-x | tests/suite/tls-fuzzer/tls-fuzzer-common.sh | 2 | ||||
m--------- | tests/suite/tls-fuzzer/tlsfuzzer | 0 |
8 files changed, 461 insertions, 6 deletions
diff --git a/.gitignore b/.gitignore index 22b851ba89..98275484ad 100644 --- a/.gitignore +++ b/.gitignore @@ -625,6 +625,7 @@ tests/resume-lifetime tests/resume-psk tests/resume-with-false-start tests/resume-with-previous-stek +tests/resume-with-record-size-limit tests/resume-with-stek-expiration tests/resume-x509 tests/rng-fork diff --git a/lib/constate.c b/lib/constate.c index 11fedab533..fbbff886e9 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -738,8 +738,6 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t memcpy(dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \ dst->ext_master_secret = src->ext_master_secret; \ dst->etm = src->etm; \ - dst->max_record_recv_size = src->max_record_recv_size; \ - dst->max_record_send_size = src->max_record_send_size; \ dst->prf = src->prf; \ dst->grp = src->grp; \ dst->pversion = src->pversion; \ @@ -757,8 +755,15 @@ void _gnutls_set_resumed_parameters(gnutls_session_t session) security_parameters_st *src = &session->internals.resumed_security_parameters; security_parameters_st *dst = &session->security_parameters; + const version_entry_st *ver = get_version(session); + + CPY_COMMON(ver->tls13_sem); - CPY_COMMON(get_version(session)->tls13_sem); + if (!ver->tls13_sem && + !(session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_NEGOTIATED)) { + dst->max_record_recv_size = src->max_record_recv_size; + dst->max_record_send_size = src->max_record_send_size; + } } /* Sets the current connection session to conform with the diff --git a/lib/ext/record_size_limit.c b/lib/ext/record_size_limit.c index 811e2ea93f..c74ae81e4b 100644 --- a/lib/ext/record_size_limit.c +++ b/lib/ext/record_size_limit.c @@ -39,7 +39,7 @@ const hello_ext_entry_st ext_mod_record_size_limit = { .name = "Record Size Limit", .tls_id = 28, .gid = GNUTLS_EXTENSION_RECORD_SIZE_LIMIT, - .parse_type = GNUTLS_EXT_TLS, + .parse_type = GNUTLS_EXT_MANDATORY, .validity = GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_DTLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE | GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO, .recv_func = _gnutls_record_size_limit_recv_params, @@ -54,6 +54,8 @@ _gnutls_record_size_limit_recv_params(gnutls_session_t session, ssize_t data_size = _data_size; DECR_LEN(data_size, 2); + if (data_size != 0) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); new_size = _gnutls_read_uint16(data); /* protocol error */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 3e77add14d..148f09fa4e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -400,7 +400,8 @@ endif if HAVE_FORK ctests += x509self x509dn anonself pskself dhepskself \ setcredcrash tls12-resume-x509 tls12-resume-psk tls12-resume-anon \ - tls13-resume-x509 tls13-resume-psk tls13-early-data tls13-early-data-neg + tls13-resume-x509 tls13-resume-psk tls13-early-data tls13-early-data-neg \ + resume-with-record-size-limit endif gc_CPPFLAGS = $(AM_CPPFLAGS) \ diff --git a/tests/resume-with-record-size-limit.c b/tests/resume-with-record-size-limit.c new file mode 100644 index 0000000000..49801a2f64 --- /dev/null +++ b/tests/resume-with-record-size-limit.c @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2004-2016 Free Software Foundation, Inc. + * Copyright (C) 2013 Adam Sampson <ats@offog.org> + * Copyright (C) 2016-2019 Red Hat, Inc. + * + * Author: Simon Josefsson, Nikos Mavrogiannopoulos, Daiki Ueno + * + * 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 <http://www.gnu.org/licenses/> + */ + +/* Parts copied from GnuTLS example programs. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if defined(_WIN32) + +/* socketpair isn't supported on Win32. */ +int main(int argc, char **argv) +{ + exit(77); +} + +#else + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/socket.h> +#if !defined(_WIN32) +#include <sys/wait.h> +#endif +#include <unistd.h> +#include <gnutls/gnutls.h> +#include <sys/wait.h> +#include <signal.h> +#include <assert.h> +#include "utils.h" +#include "cert-common.h" +#include "virt-time.h" + +#define SKIP8(pos, total) { \ + uint8_t _s; \ + if (pos+1 > total) fail("error\n"); \ + _s = msg->data[pos]; \ + if ((size_t)(pos+1+_s) > total) fail("error\n"); \ + pos += 1+_s; \ + } + +pid_t child; + +/* A very basic TLS client, with anonymous authentication. + */ + +#define SESSIONS 2 +#define MAX_BUF 5*1024 +#define MSG "Hello TLS" + +/* 2^13, which is not supported by max_fragment_length */ +#define MAX_DATA_SIZE 8192 + +#define HANDSHAKE_SESSION_ID_POS (2+32) + +static void tls_log_func(int level, const char *str) +{ + fprintf(stderr, "%s |<%d>| %s", child ? "server" : "client", level, + str); +} + +static int ext_callback(void *ctx, unsigned tls_id, const unsigned char *data, unsigned size) +{ + if (tls_id == 28) { /* record size limit */ + uint16_t max_data_size; + + assert(size == 2); + max_data_size = (data[0] << 8) | data[1]; + if (max_data_size == MAX_DATA_SIZE) + fail("record_size_limit is not reset: %u == %u\n", + max_data_size, MAX_DATA_SIZE); + } + return 0; +} + +static int handshake_callback(gnutls_session_t session, unsigned int htype, + unsigned post, unsigned int incoming, const gnutls_datum_t *msg) +{ + int ret; + unsigned pos; + gnutls_datum_t mmsg; + + if (!post) + return 0; + + switch (htype) { + case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: + ret = gnutls_ext_raw_parse(NULL, ext_callback, msg, 0); + assert(ret >= 0); + break; + case GNUTLS_HANDSHAKE_SERVER_HELLO: + assert(msg->size >= HANDSHAKE_SESSION_ID_POS); + pos = HANDSHAKE_SESSION_ID_POS; + SKIP8(pos, msg->size); + pos += 3; + + mmsg.data = &msg->data[pos]; + mmsg.size = msg->size - pos; + ret = gnutls_ext_raw_parse(NULL, ext_callback, &mmsg, 0); + assert(ret >= 0); + break; + default: + break; + } + return 0; +} + +static void client(int sds[], const char *prio) +{ + int ret, ii; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + gnutls_certificate_credentials_t clientx509cred; + + /* variables used in session resuming + */ + int t; + gnutls_datum_t session_data = {NULL, 0}; + + if (debug) { + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4); + } + + gnutls_certificate_allocate_credentials(&clientx509cred); + + assert(gnutls_certificate_set_x509_key_mem(clientx509cred, + &cli_cert, &cli_key, + GNUTLS_X509_FMT_PEM) >= 0); + + for (t = 0; t < SESSIONS; t++) { + int sd = sds[t]; + + assert(gnutls_init(&session, GNUTLS_CLIENT)>=0); + + ret = gnutls_priority_set_direct(session, prio, NULL); + if (ret < 0) { + fail("prio: %s\n", gnutls_strerror(ret)); + } + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, clientx509cred); + + if (t == 0) { + ret = gnutls_record_set_max_size(session, MAX_DATA_SIZE); + if (ret < 0) + fail("gnutls_set_max_size: %s\n", gnutls_strerror(ret)); + } + + if (t > 0) { + /* if this is not the first time we connect */ + gnutls_session_set_data(session, session_data.data, + session_data.size); + + gnutls_handshake_set_hook_function(session, + GNUTLS_HANDSHAKE_ANY, + GNUTLS_HOOK_POST, + handshake_callback); + } + + gnutls_transport_set_int(session, sd); + + /* Perform the TLS handshake + */ + gnutls_handshake_set_timeout(session, 20 * 1000); + do { + ret = gnutls_handshake(session); + } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + + if (ret < 0) { + fail("client: Handshake failed\n"); + gnutls_perror(ret); + break; + } else { + if (debug) + success + ("client: Handshake was completed\n"); + } + + if (t == 0) { + /* get the session data size */ + ret = + gnutls_session_get_data2(session, + &session_data); + if (ret < 0) + fail("Getting resume data failed\n"); + + } else { /* the second time we connect */ + /* check if we actually resumed the previous session */ + if (gnutls_session_is_resumed(session) == 0) { + fail("- Previous session was resumed but NOT expected\n"); + } + } + + gnutls_record_send(session, MSG, strlen(MSG)); + + do { + ret = gnutls_record_recv(session, buffer, MAX_BUF); + } while (ret == GNUTLS_E_AGAIN); + if (ret == 0) { + if (debug) + success + ("client: Peer has closed the TLS connection\n"); + break; + } else if (ret < 0) { + fail("client: Error: %s\n", gnutls_strerror(ret)); + } + + if (debug) { + printf("- Received %d bytes: ", ret); + for (ii = 0; ii < ret; ii++) { + fputc(buffer[ii], stdout); + } + fputs("\n", stdout); + } + + gnutls_bye(session, GNUTLS_SHUT_RDWR); + + close(sd); + + gnutls_deinit(session); + } + gnutls_free(session_data.data); + + gnutls_certificate_free_credentials(clientx509cred); +} + +/* These are global */ +static gnutls_datum_t session_ticket_key = { NULL, 0 }; + + +gnutls_certificate_credentials_t serverx509cred; + +static void global_stop(void) +{ + if (debug) + success("global stop\n"); + + gnutls_certificate_free_credentials(serverx509cred); +} + +static void server(int sds[], const char *prio) +{ + int t; + int ret; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + unsigned iflags = GNUTLS_SERVER; + + virt_time_init(); + + /* this must be called once in the program, it is mostly for the server. + */ + if (debug) { + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(4); + } + + gnutls_certificate_allocate_credentials(&serverx509cred); + assert(gnutls_certificate_set_x509_key_mem(serverx509cred, + &server_cert, &server_key, GNUTLS_X509_FMT_PEM) >= 0); + + gnutls_session_ticket_key_generate(&session_ticket_key); + + for (t = 0; t < SESSIONS; t++) { + int sd = sds[t]; + + assert(gnutls_init(&session, iflags) >= 0); + + /* avoid calling all the priority functions, since the defaults + * are adequate. + */ + assert(gnutls_priority_set_direct(session, prio, NULL) >= 0); + + + gnutls_session_ticket_enable_server(session, + &session_ticket_key); + + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred); + gnutls_transport_set_int(session, sd); + gnutls_handshake_set_timeout(session, 20 * 1000); + + do { + ret = gnutls_handshake(session); + } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); + if (ret < 0) { + close(sd); + gnutls_deinit(session); + kill(child, SIGTERM); + fail("server: Handshake has failed (%s)\n\n", + gnutls_strerror(ret)); + return; + } + if (debug) + success("server: Handshake was completed\n"); + + if (t > 0) { + ret = gnutls_session_is_resumed(session); + if (ret == 0) { + fail("server: session_is_resumed error (%d)\n", t); + } + } + + /* see the Getting peer's information example */ + /* print_info(session); */ + + for (;;) { + memset(buffer, 0, MAX_BUF + 1); + ret = gnutls_record_recv(session, buffer, MAX_BUF); + + if (ret == 0) { + if (debug) + success + ("server: Peer has closed the GnuTLS connection\n"); + break; + } else if (ret < 0) { + kill(child, SIGTERM); + fail("server: Received corrupted data(%d). Closing...\n", ret); + break; + } else if (ret > 0) { + /* echo data back to the client + */ + gnutls_record_send(session, buffer, + strlen(buffer)); + } + } + /* do not wait for the peer to close the connection. + */ + gnutls_bye(session, GNUTLS_SHUT_WR); + + close(sd); + + gnutls_deinit(session); + } + + gnutls_free(session_ticket_key.data); + session_ticket_key.data = NULL; + + if (debug) + success("server: finished\n"); +} + +static void run(const char *prio) +{ + int client_sds[SESSIONS], server_sds[SESSIONS]; + int j; + int err; + + signal(SIGCHLD, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + for (j = 0; j < SESSIONS; j++) { + int sockets[2]; + + err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); + if (err == -1) { + perror("socketpair"); + fail("socketpair failed\n"); + return; + } + + server_sds[j] = sockets[0]; + client_sds[j] = sockets[1]; + } + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork\n"); + return; + } + + if (child) { + int status = 0; + /* parent */ + for (j = 0; j < SESSIONS; j++) + close(client_sds[j]); + server(server_sds, prio); + + waitpid(child, &status, 0); + check_wait_status(status); + global_stop(); + } else { + for (j = 0; j < SESSIONS; j++) + close(server_sds[j]); + client(client_sds, prio); + exit(0); + } +} + +void doit(void) +{ + run("NORMAL:-VERS-ALL:+VERS-TLS1.2"); + run("NORMAL:-VERS-ALL:+VERS-TLS1.3"); +} + +#endif /* _WIN32 */ diff --git a/tests/suite/tls-fuzzer/gnutls-nocert.json b/tests/suite/tls-fuzzer/gnutls-nocert.json index 6e2a2ea47c..04376f40ea 100644 --- a/tests/suite/tls-fuzzer/gnutls-nocert.json +++ b/tests/suite/tls-fuzzer/gnutls-nocert.json @@ -230,6 +230,28 @@ "-e", "non fragmented, over fragmentation limit: 65535 fragment - 16332B extension", "-e", "small, maximum fragmentation: 1 fragment - 20B extension", "-e", "medium, maximum fragmentation: 1 fragment - 1024B extension"]}, + {"name" : "test-record-size-limit.py", + "comment" : "These tests rely on too small lower limit we don't support; TLS 1.3 high limit is not what we expect; 1/n-1 splitting is not supported in TLS 1.0; we don't reject too large appliation_data records in TLS 1.2 #676", + "arguments" : ["-p", "@PORT@", "--reply-AD-size", "{expected_size}", + "-e", "change size in TLS 1.2 resumption", + "-e", "change size in TLS 1.3 session resumption", + "-e", "check if server accepts maximum size in TLS 1.0", + "-e", "check if server accepts maximum size in TLS 1.3", + "-e", "check if server accepts minimal size in TLS 1.0", + "-e", "check if server accepts minimal size in TLS 1.1", + "-e", "check if server accepts minimal size in TLS 1.2", + "-e", "check if server accepts minimal size in TLS 1.3", + "-e", "check interaction with sha256 prf", + "-e", "check interaction with sha384 prf", + "-e", "check server sent size in TLS 1.0", + "-e", "check server sent size in TLS 1.3", + "-e", "drop extension in TLS 1.3 session resumption", + "-e", "HRR sanity", + "-e", "modified extension in 2nd CH in HRR handshake", + "-e", "renegotiation with changed limit", + "-e", "renegotiation with dropped extension", + "-e", "too large record in TLS 1.2", + "-e", "too large record payload in TLS 1.3"] }, {"name" : "test-sessionID-resumption.py", "arguments" : ["-p", "@PORT@"] }, {"name" : "test-serverhello-random.py", diff --git a/tests/suite/tls-fuzzer/tls-fuzzer-common.sh b/tests/suite/tls-fuzzer/tls-fuzzer-common.sh index 111fd44970..b41f068a07 100755 --- a/tests/suite/tls-fuzzer/tls-fuzzer-common.sh +++ b/tests/suite/tls-fuzzer/tls-fuzzer-common.sh @@ -44,7 +44,7 @@ retval=0 tls_fuzzer_prepare -PYTHONPATH=. python tests/scripts_retention.py ${TMPFILE} ${SERV} +PYTHONPATH=. python tests/scripts_retention.py ${TMPFILE} ${SERV} 821 retval=$? rm -f ${TMPFILE} diff --git a/tests/suite/tls-fuzzer/tlsfuzzer b/tests/suite/tls-fuzzer/tlsfuzzer -Subproject b9dec4fde7bedfac90850b86c2c3f644349f6c3 +Subproject 7b2ebe4c8bd06e5a1059a8aeb5bfe2b014e2b52 |