From cf72c7579201086cee303eadcb60bd28eff78dd9 Mon Sep 17 00:00:00 2001 From: FdaSilvaYY Date: Sun, 5 Nov 2017 17:46:48 +0100 Subject: [PATCH] Implement Maximum Fragment Length TLS extension. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on patch from Tomasz Moń: https://groups.google.com/forum/#!topic/mailing.openssl.dev/fQxXvCg1uQY Reviewed-by: Matt Caswell Reviewed-by: Bernd Edlinger (Merged from https://github.com/openssl/openssl/pull/1008) --- CHANGES | 5 + apps/s_client.c | 37 +++- crypto/err/openssl.txt | 7 + doc/man3/SSL_CTX_set_split_send_fragment.pod | 68 ++++++- include/openssl/ssl.h | 3 +- include/openssl/sslerr.h | 5 + include/openssl/tls1.h | 11 ++ ssl/record/rec_layer_d1.c | 4 +- ssl/record/rec_layer_s3.c | 28 +-- ssl/record/ssl3_buffer.c | 9 +- ssl/record/ssl3_record.c | 19 +- ssl/ssl_asn1.c | 8 +- ssl/ssl_err.c | 10 ++ ssl/ssl_lib.c | 27 +++ ssl/ssl_locl.h | 30 ++++ ssl/statem/extensions.c | 31 ++++ ssl/statem/extensions_clnt.c | 62 +++++++ ssl/statem/extensions_cust.c | 3 +- ssl/statem/extensions_srvr.c | 57 ++++++ ssl/statem/statem_locl.h | 11 +- ssl/t1_lib.c | 31 ++++ test/handshake_helper.c | 11 ++ test/recipes/80-test_ssl_new.t | 4 +- test/ssl-tests/13-fragmentation.conf | 176 ++++++++++++++++++- test/ssl-tests/13-fragmentation.conf.in | 85 ++++++++- test/ssl_test_ctx.c | 29 +++ test/ssl_test_ctx.h | 3 + test/ssl_test_ctx_test.c | 6 +- test/ssl_test_ctx_test.conf | 3 + test/sslapitest.c | 109 ++++++++++++ util/libssl.num | 3 + util/perl/TLSProxy/Message.pm | 1 + 32 files changed, 860 insertions(+), 36 deletions(-) diff --git a/CHANGES b/CHANGES index 392bcebb6f..71c700c2fd 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,11 @@ Changes between 1.1.0f and 1.1.1 [xx XXX xxxx] + *) Add 'Maximum Fragment Length' TLS extension negotiation and support + as documented in RFC6066. + Based on a patch from Tomasz Moń + [Filipe Raimundo da Silva] + *) Add SM4 implemented according to GB/T 32907-2016. [ Jack Lloyd , Ronald Tse , diff --git a/apps/s_client.c b/apps/s_client.c index 019e73535a..d4fb1a8554 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -590,8 +590,8 @@ typedef enum OPTION_choice { OPT_CHAINCAFILE, OPT_VERIFYCAFILE, OPT_NEXTPROTONEG, OPT_ALPN, OPT_SERVERINFO, OPT_STARTTLS, OPT_SERVERNAME, OPT_NOSERVERNAME, OPT_ASYNC, OPT_USE_SRTP, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN, OPT_PROTOHOST, - OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, OPT_READ_BUF, - OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE, + OPT_MAXFRAGLEN, OPT_MAX_SEND_FRAG, OPT_SPLIT_SEND_FRAG, OPT_MAX_PIPELINES, + OPT_READ_BUF, OPT_KEYLOG_FILE, OPT_EARLY_DATA, OPT_REQCAFILE, OPT_V_ENUM, OPT_X_ENUM, OPT_S_ENUM, @@ -665,6 +665,8 @@ const OPTIONS s_client_options[] = { "Export keying material using label"}, {"keymatexportlen", OPT_KEYMATEXPORTLEN, 'p', "Export len bytes of keying material (default 20)"}, + {"maxfraglen", OPT_MAXFRAGLEN, 'p', + "Enable Maximum Fragment Length Negotiation (len values: 512, 1024, 2048 and 4096)"}, {"fallback_scsv", OPT_FALLBACKSCSV, '-', "Send the fallback SCSV"}, {"name", OPT_PROTOHOST, 's', "Hostname to use for \"-starttls lmtp\", \"-starttls smtp\" or \"-starttls xmpp[-server]\""}, @@ -942,6 +944,7 @@ int s_client_main(int argc, char **argv) unsigned int split_send_fragment = 0, max_pipelines = 0; enum { use_inet, use_unix, use_unknown } connect_type = use_unknown; int count4or6 = 0; + uint8_t maxfraglen = 0; int c_nbio = 0, c_msg = 0, c_ign_eof = 0, c_brief = 0; int c_tlsextdebug = 0; #ifndef OPENSSL_NO_OCSP @@ -1424,6 +1427,28 @@ int s_client_main(int argc, char **argv) case OPT_ASYNC: async = 1; break; + case OPT_MAXFRAGLEN: + len = atoi(opt_arg()); + switch (len) { + case 512: + maxfraglen = TLSEXT_max_fragment_length_512; + break; + case 1024: + maxfraglen = TLSEXT_max_fragment_length_1024; + break; + case 2048: + maxfraglen = TLSEXT_max_fragment_length_2048; + break; + case 4096: + maxfraglen = TLSEXT_max_fragment_length_4096; + break; + default: + BIO_printf(bio_err, + "%s: Max Fragment Len %u is out of permitted values", + prog, len); + goto opthelp; + } + break; case OPT_MAX_SEND_FRAG: max_send_fragment = atoi(opt_arg()); break; @@ -1677,6 +1702,14 @@ int s_client_main(int argc, char **argv) SSL_CTX_set_default_read_buffer_len(ctx, read_buf_len); } + if (maxfraglen > 0 + && !SSL_CTX_set_tlsext_max_fragment_length(ctx, maxfraglen)) { + BIO_printf(bio_err, + "%s: Max Fragment Length code %u is out of permitted values" + "\n", prog, maxfraglen); + goto end; + } + if (!config_ctx(cctx, ssl_args, ctx)) goto end; diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 5705966fe9..d3e4a62e71 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1063,6 +1063,8 @@ SSL_F_SSL_CTX_SET_CLIENT_CERT_ENGINE:290:SSL_CTX_set_client_cert_engine SSL_F_SSL_CTX_SET_CT_VALIDATION_CALLBACK:396:SSL_CTX_set_ct_validation_callback SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT:219:SSL_CTX_set_session_id_context SSL_F_SSL_CTX_SET_SSL_VERSION:170:SSL_CTX_set_ssl_version +SSL_F_SSL_CTX_SET_TLSEXT_MAX_FRAGMENT_LENGTH:551:\ + SSL_CTX_set_tlsext_max_fragment_length SSL_F_SSL_CTX_USE_CERTIFICATE:171:SSL_CTX_use_certificate SSL_F_SSL_CTX_USE_CERTIFICATE_ASN1:172:SSL_CTX_use_certificate_ASN1 SSL_F_SSL_CTX_USE_CERTIFICATE_FILE:173:SSL_CTX_use_certificate_file @@ -1126,6 +1128,7 @@ SSL_F_SSL_SET_RFD:194:SSL_set_rfd SSL_F_SSL_SET_SESSION:195:SSL_set_session SSL_F_SSL_SET_SESSION_ID_CONTEXT:218:SSL_set_session_id_context SSL_F_SSL_SET_SESSION_TICKET_EXT:294:SSL_set_session_ticket_ext +SSL_F_SSL_SET_TLSEXT_MAX_FRAGMENT_LENGTH:550:SSL_set_tlsext_max_fragment_length SSL_F_SSL_SET_WFD:196:SSL_set_wfd SSL_F_SSL_SHUTDOWN:224:SSL_shutdown SSL_F_SSL_SRP_CTX_INIT:313:SSL_SRP_CTX_init @@ -1192,6 +1195,7 @@ SSL_F_TLS_CONSTRUCT_CTOS_ETM:469:tls_construct_ctos_etm SSL_F_TLS_CONSTRUCT_CTOS_HELLO:356:* SSL_F_TLS_CONSTRUCT_CTOS_KEY_EXCHANGE:357:* SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE:470:tls_construct_ctos_key_share +SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN:549:tls_construct_ctos_maxfragmentlen SSL_F_TLS_CONSTRUCT_CTOS_NPN:471:tls_construct_ctos_npn SSL_F_TLS_CONSTRUCT_CTOS_PADDING:472:tls_construct_ctos_padding SSL_F_TLS_CONSTRUCT_CTOS_PSK:501:tls_construct_ctos_psk @@ -1233,6 +1237,7 @@ SSL_F_TLS_CONSTRUCT_STOC_ETM:455:tls_construct_stoc_etm SSL_F_TLS_CONSTRUCT_STOC_HELLO:376:* SSL_F_TLS_CONSTRUCT_STOC_KEY_EXCHANGE:377:* SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE:456:tls_construct_stoc_key_share +SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN:548:tls_construct_stoc_maxfragmentlen SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG:457:tls_construct_stoc_next_proto_neg SSL_F_TLS_CONSTRUCT_STOC_PSK:504:tls_construct_stoc_psk SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE:458:tls_construct_stoc_renegotiate @@ -2443,6 +2448,8 @@ SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES:362:srtp could not allocate profiles SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG:363:\ srtp protection profile list too long SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE:364:srtp unknown protection profile +SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH:232:\ + ssl3 ext invalid max fragment length SSL_R_SSL3_EXT_INVALID_SERVERNAME:319:ssl3 ext invalid servername SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE:320:ssl3 ext invalid servername type SSL_R_SSL3_SESSION_ID_TOO_LONG:300:ssl3 session id too long diff --git a/doc/man3/SSL_CTX_set_split_send_fragment.pod b/doc/man3/SSL_CTX_set_split_send_fragment.pod index f65540fc28..ef5e7cda35 100644 --- a/doc/man3/SSL_CTX_set_split_send_fragment.pod +++ b/doc/man3/SSL_CTX_set_split_send_fragment.pod @@ -5,8 +5,10 @@ SSL_CTX_set_max_send_fragment, SSL_set_max_send_fragment, SSL_CTX_set_split_send_fragment, SSL_set_split_send_fragment, SSL_CTX_set_max_pipelines, SSL_set_max_pipelines, -SSL_CTX_set_default_read_buffer_len, SSL_set_default_read_buffer_len - Control -fragment sizes and pipelining operations +SSL_CTX_set_default_read_buffer_len, SSL_set_default_read_buffer_len, +SSL_CTX_set_tlsext_max_fragment_length, +SSL_set_tlsext_max_fragment_length, +SSL_SESSION_get_max_fragment_length - Control fragment size settings and pipelining operations =head1 SYNOPSIS @@ -24,6 +26,10 @@ fragment sizes and pipelining operations void SSL_CTX_set_default_read_buffer_len(SSL_CTX *ctx, size_t len); void SSL_set_default_read_buffer_len(SSL *s, size_t len); + int SSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode); + int SSL_set_tlsext_max_fragment_length(SSL *ssl, uint8_t mode); + uint8_t SSL_SESSION_get_max_fragment_length(SSL_SESSION *session); + =head1 DESCRIPTION Some engines are able to process multiple simultaneous crypto operations. This @@ -99,15 +105,62 @@ greater than the default that would have been used anyway. The normal default value depends on a number of factors but it will be at least SSL3_RT_MAX_PLAIN_LENGTH + SSL3_RT_MAX_ENCRYPTED_OVERHEAD (16704) bytes. +SSL_CTX_set_tlsext_max_fragment_length() sets the default maximum fragment +length negotiation mode via value B to B. +This setting affects only SSL instances created after this function is called. +It affects the client-side as only its side may initiate this extension use. + +SSL_set_tlsext_max_fragment_length() sets the maximum fragment length +negotiation mode via value B to B. +This setting will be used during a handshake when extensions are exchanged +between client and server. +So it only affects SSL sessions created after this function is called. +It affects the client-side as only its side may initiate this extension use. + +SSL_SESSION_get_max_fragment_length() gets the maximum fragment length +negotiated in B. + =head1 RETURN VALUES All non-void functions return 1 on success and 0 on failure. =head1 NOTES -With the exception of SSL_CTX_set_default_read_buffer_len() and -SSL_set_default_read_buffer_len() all these functions are implemented using -macros. +The Maximum Fragment Length extension support is optional on the server side. +If the server does not support this extension then +SSL_SESSION_get_max_fragment_length() will return: +TLSEXT_max_fragment_length_DISABLED. + +The following modes are available: + +=over 4 + +=item TLSEXT_max_fragment_length_DISABLED + +Disables Maximum Fragment Length Negotiation (default). + +=item TLSEXT_max_fragment_length_512 + +Sets Maximum Fragment Length to 512 bytes. + +=item TLSEXT_max_fragment_length_1024 + +Sets Maximum Fragment Length to 1024. + +=item TLSEXT_max_fragment_length_2048 + +Sets Maximum Fragment Length to 2048. + +=item TLSEXT_max_fragment_length_4096 + +Sets Maximum Fragment Length to 4096. + +=back + +With the exception of SSL_CTX_set_default_read_buffer_len() +SSL_set_default_read_buffer_len(), SSL_CTX_set_tlsext_max_fragment_length(), +SSL_set_tlsext_max_fragment_length() and SSL_SESSION_get_max_fragment_length() +all these functions are implemented using macros. =head1 HISTORY @@ -116,13 +169,16 @@ SSL_CTX_set_split_send_fragment(), SSL_set_split_send_fragment(), SSL_CTX_set_default_read_buffer_len() and SSL_set_default_read_buffer_len() functions were added in OpenSSL 1.1.0. +SSL_CTX_set_tlsext_max_fragment_length(), SSL_set_tlsext_max_fragment_length() +and SSL_SESSION_get_max_fragment_length() were added in OpenSSL 1.1.1. + =head1 SEE ALSO L, L =head1 COPYRIGHT -Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved. Licensed under the OpenSSL license (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 7bc409c494..176425a61d 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1915,10 +1915,11 @@ __owur size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, size_t outlen); __owur size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, size_t outlen); -__owur size_t SSL_SESSION_get_master_key(const SSL_SESSION *ssl, +__owur size_t SSL_SESSION_get_master_key(const SSL_SESSION *sess, unsigned char *out, size_t outlen); __owur int SSL_SESSION_set1_master_key(SSL_SESSION *sess, const unsigned char *in, size_t len); +uint8_t SSL_SESSION_get_max_fragment_length(const SSL_SESSION *sess); #define SSL_get_ex_new_index(l, p, newf, dupf, freef) \ CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, l, p, newf, dupf, freef) diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index 1df0dfa713..3450b4cbe2 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -129,6 +129,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_CTX_SET_CT_VALIDATION_CALLBACK 396 # define SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT 219 # define SSL_F_SSL_CTX_SET_SSL_VERSION 170 +# define SSL_F_SSL_CTX_SET_TLSEXT_MAX_FRAGMENT_LENGTH 551 # define SSL_F_SSL_CTX_USE_CERTIFICATE 171 # define SSL_F_SSL_CTX_USE_CERTIFICATE_ASN1 172 # define SSL_F_SSL_CTX_USE_CERTIFICATE_FILE 173 @@ -192,6 +193,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_SET_SESSION 195 # define SSL_F_SSL_SET_SESSION_ID_CONTEXT 218 # define SSL_F_SSL_SET_SESSION_TICKET_EXT 294 +# define SSL_F_SSL_SET_TLSEXT_MAX_FRAGMENT_LENGTH 550 # define SSL_F_SSL_SET_WFD 196 # define SSL_F_SSL_SHUTDOWN 224 # define SSL_F_SSL_SRP_CTX_INIT 313 @@ -257,6 +259,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_CTOS_HELLO 356 # define SSL_F_TLS_CONSTRUCT_CTOS_KEY_EXCHANGE 357 # define SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE 470 +# define SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN 549 # define SSL_F_TLS_CONSTRUCT_CTOS_NPN 471 # define SSL_F_TLS_CONSTRUCT_CTOS_PADDING 472 # define SSL_F_TLS_CONSTRUCT_CTOS_PSK 501 @@ -296,6 +299,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_TLS_CONSTRUCT_STOC_HELLO 376 # define SSL_F_TLS_CONSTRUCT_STOC_KEY_EXCHANGE 377 # define SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE 456 +# define SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN 548 # define SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG 457 # define SSL_F_TLS_CONSTRUCT_STOC_PSK 504 # define SSL_F_TLS_CONSTRUCT_STOC_RENEGOTIATE 458 @@ -551,6 +555,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES 362 # define SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG 363 # define SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE 364 +# define SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH 232 # define SSL_R_SSL3_EXT_INVALID_SERVERNAME 319 # define SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE 320 # define SSL_R_SSL3_SESSION_ID_TOO_LONG 300 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 9adb89eb30..d114fb5e4e 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -203,6 +203,17 @@ extern "C" { # define TLSEXT_curve_P_256 23 # define TLSEXT_curve_P_384 24 +/* OpenSSL value to disable maximum fragment length extension */ +# define TLSEXT_max_fragment_length_DISABLED 0 +/* Allowed values for max fragment length extension */ +# define TLSEXT_max_fragment_length_512 1 +# define TLSEXT_max_fragment_length_1024 2 +# define TLSEXT_max_fragment_length_2048 3 +# define TLSEXT_max_fragment_length_4096 4 + +int SSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode); +int SSL_set_tlsext_max_fragment_length(SSL *ssl, uint8_t mode); + # define TLSEXT_MAXLEN_host_name 255 __owur const char *SSL_get_servername(const SSL *s, const int type); diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c index 59285ed591..3eabf71cf6 100644 --- a/ssl/record/rec_layer_d1.c +++ b/ssl/record/rec_layer_d1.c @@ -1,5 +1,5 @@ /* - * Copyright 2005-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2005-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -774,7 +774,7 @@ int do_dtls1_write(SSL *s, int type, const unsigned char *buf, if (len == 0 && !create_empty_fragment) return 0; - if (len > s->max_send_fragment) { + if (len > ssl_get_max_send_fragment(s)) { SSLerr(SSL_F_DO_DTLS1_WRITE, SSL_R_EXCEEDS_MAX_FRAGMENT_SIZE); return 0; } diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c index 1225f9b4d8..980e5a4b8b 100644 --- a/ssl/record/rec_layer_s3.c +++ b/ssl/record/rec_layer_s3.c @@ -334,9 +334,9 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, { const unsigned char *buf = buf_; size_t tot; - size_t n, split_send_fragment, maxpipes; + size_t n, max_send_fragment, split_send_fragment, maxpipes; #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK - size_t max_send_fragment, nw; + size_t nw; #endif SSL3_BUFFER *wb = &s->rlayer.wbuf[0]; int i; @@ -403,7 +403,7 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, * compromise is considered worthy. */ if (type == SSL3_RT_APPLICATION_DATA && - len >= 4 * (max_send_fragment = s->max_send_fragment) && + len >= 4 * (max_send_fragment = ssl_get_max_send_fragment(s)) && s->compress == NULL && s->msg_callback == NULL && !SSL_WRITE_ETM(s) && SSL_USE_EXPLICIT_IV(s) && EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(s->enc_write_ctx)) & @@ -523,7 +523,7 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, tot += tmpwrit; } } else -#endif +#endif /* !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK */ if (tot == len) { /* done? */ if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s)) ssl3_release_write_buffer(s); @@ -534,7 +534,8 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, n = (len - tot); - split_send_fragment = s->split_send_fragment; + max_send_fragment = ssl_get_max_send_fragment(s); + split_send_fragment = ssl_get_split_send_fragment(s); /* * If max_pipelines is 0 then this means "undefined" and we default to * 1 pipeline. Similarly if the cipher does not support pipelined @@ -556,10 +557,10 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, & EVP_CIPH_FLAG_PIPELINE) || !SSL_USE_EXPLICIT_IV(s)) maxpipes = 1; - if (s->max_send_fragment == 0 || split_send_fragment > s->max_send_fragment - || split_send_fragment == 0) { + if (max_send_fragment == 0 || split_send_fragment == 0 + || split_send_fragment > max_send_fragment) { /* - * We should have prevented this when we set the split and max send + * We should have prevented this when we set/get the split and max send * fragments so we shouldn't get here */ SSLerr(SSL_F_SSL3_WRITE_BYTES, ERR_R_INTERNAL_ERROR); @@ -577,13 +578,13 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len, if (numpipes > maxpipes) numpipes = maxpipes; - if (n / numpipes >= s->max_send_fragment) { + if (n / numpipes >= max_send_fragment) { /* * We have enough data to completely fill all available * pipelines */ for (j = 0; j < numpipes; j++) { - pipelens[j] = s->max_send_fragment; + pipelens[j] = max_send_fragment; } } else { /* We can partially fill all available pipelines */ @@ -854,7 +855,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, } if (SSL_TREAT_AS_TLS13(s) && s->enc_write_ctx != NULL) { - size_t rlen; + size_t rlen, max_send_fragment; if (!WPACKET_put_bytes_u8(thispkt, type)) { SSLerr(SSL_F_DO_SSL3_WRITE, ERR_R_INTERNAL_ERROR); @@ -863,10 +864,11 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf, SSL3_RECORD_add_length(thiswr, 1); /* Add TLS1.3 padding */ + max_send_fragment = ssl_get_max_send_fragment(s); rlen = SSL3_RECORD_get_length(thiswr); - if (rlen < SSL3_RT_MAX_PLAIN_LENGTH) { + if (rlen < max_send_fragment) { size_t padding = 0; - size_t max_padding = SSL3_RT_MAX_PLAIN_LENGTH - rlen; + size_t max_padding = max_send_fragment - rlen; if (s->record_padding_cb != NULL) { padding = s->record_padding_cb(s, type, rlen, s->record_padding_arg); } else if (s->block_padding > 0) { diff --git a/ssl/record/ssl3_buffer.c b/ssl/record/ssl3_buffer.c index 8a6a922261..da23b36791 100644 --- a/ssl/record/ssl3_buffer.c +++ b/ssl/record/ssl3_buffer.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -93,7 +93,7 @@ int ssl3_setup_write_buffer(SSL *s, size_t numwpipes, size_t len) align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1); #endif - len = s->max_send_fragment + len = ssl_get_max_send_fragment(s) + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + headerlen + align; #ifndef OPENSSL_NO_COMP if (ssl_allow_compression(s)) @@ -107,6 +107,11 @@ int ssl3_setup_write_buffer(SSL *s, size_t numwpipes, size_t len) for (currpipe = 0; currpipe < numwpipes; currpipe++) { SSL3_BUFFER *thiswb = &wb[currpipe]; + if (thiswb->buf != NULL && thiswb->len != len) { + OPENSSL_free(thiswb->buf); + thiswb->buf = NULL; /* force reallocation */ + } + if (thiswb->buf == NULL) { p = OPENSSL_malloc(len); if (p == NULL) { diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c index 8d71cd3184..28a706f7ba 100644 --- a/ssl/record/ssl3_record.c +++ b/ssl/record/ssl3_record.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -685,6 +685,14 @@ int ssl3_get_record(SSL *s) goto f_err; } + /* If received packet overflows current Max Fragment Length setting */ + if (s->session != NULL && USE_MAX_FRAGMENT_LENGTH_EXT(s->session) + && thisrr->length > GET_MAX_FRAGMENT_LENGTH(s->session)) { + al = SSL_AD_RECORD_OVERFLOW; + SSLerr(SSL_F_SSL3_GET_RECORD, SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + thisrr->off = 0; /*- * So at this point the following is true @@ -1823,6 +1831,15 @@ int dtls1_get_record(SSL *s) goto again; } + /* If received packet overflows own-client Max Fragment Length setting */ + if (s->session != NULL && USE_MAX_FRAGMENT_LENGTH_EXT(s->session) + && rr->length > GET_MAX_FRAGMENT_LENGTH(s->session)) { + /* record too long, silently discard it */ + rr->length = 0; + RECORD_LAYER_reset_packet_length(&s->rlayer); + goto again; + } + /* now s->rlayer.rstate == SSL_ST_READ_BODY */ } diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c index f6019bca06..7258618d18 100644 --- a/ssl/ssl_asn1.c +++ b/ssl/ssl_asn1.c @@ -42,6 +42,7 @@ typedef struct { uint32_t max_early_data; ASN1_OCTET_STRING *alpn_selected; ASN1_OCTET_STRING *tick_nonce; + uint32_t tlsext_max_fragment_len_mode; } SSL_SESSION_ASN1; ASN1_SEQUENCE(SSL_SESSION_ASN1) = { @@ -71,7 +72,8 @@ ASN1_SEQUENCE(SSL_SESSION_ASN1) = { ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_tick_age_add, ZUINT32, 14), ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, max_early_data, ZUINT32, 15), ASN1_EXP_OPT(SSL_SESSION_ASN1, alpn_selected, ASN1_OCTET_STRING, 16), - ASN1_EXP_OPT(SSL_SESSION_ASN1, tick_nonce, ASN1_OCTET_STRING, 17) + ASN1_EXP_OPT(SSL_SESSION_ASN1, tick_nonce, ASN1_OCTET_STRING, 17), + ASN1_EXP_OPT_EMBED(SSL_SESSION_ASN1, tlsext_max_fragment_len_mode, ZUINT32, 18) } static_ASN1_SEQUENCE_END(SSL_SESSION_ASN1) IMPLEMENT_STATIC_ASN1_ENCODE_FUNCTIONS(SSL_SESSION_ASN1) @@ -196,6 +198,8 @@ int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp) ssl_session_oinit(&as.tick_nonce, &tick_nonce, in->ext.tick_nonce, in->ext.tick_nonce_len); + as.tlsext_max_fragment_len_mode = in->ext.max_fragment_len_mode; + return i2d_SSL_SESSION_ASN1(&as, pp); } @@ -370,6 +374,8 @@ SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp, ret->ext.tick_nonce_len = 0; } + ret->ext.max_fragment_len_mode = as->tlsext_max_fragment_len_mode; + M_ASN1_free_of(as, SSL_SESSION_ASN1); if ((a != NULL) && (*a == NULL)) diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 3eb89a3810..17a2413fa4 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -176,6 +176,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "SSL_CTX_set_session_id_context"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_SSL_VERSION, 0), "SSL_CTX_set_ssl_version"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_SET_TLSEXT_MAX_FRAGMENT_LENGTH, 0), + "SSL_CTX_set_tlsext_max_fragment_length"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_USE_CERTIFICATE, 0), "SSL_CTX_use_certificate"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_USE_CERTIFICATE_ASN1, 0), @@ -268,6 +270,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { "SSL_set_session_id_context"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_SESSION_TICKET_EXT, 0), "SSL_set_session_ticket_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_TLSEXT_MAX_FRAGMENT_LENGTH, 0), + "SSL_set_tlsext_max_fragment_length"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_WFD, 0), "SSL_set_wfd"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SHUTDOWN, 0), "SSL_shutdown"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SRP_CTX_INIT, 0), "SSL_SRP_CTX_init"}, @@ -377,6 +381,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_KEY_EXCHANGE, 0), ""}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_KEY_SHARE, 0), "tls_construct_ctos_key_share"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN, 0), + "tls_construct_ctos_maxfragmentlen"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_NPN, 0), "tls_construct_ctos_npn"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_CTOS_PADDING, 0), @@ -448,6 +454,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_KEY_EXCHANGE, 0), ""}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_KEY_SHARE, 0), "tls_construct_stoc_key_share"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN, 0), + "tls_construct_stoc_maxfragmentlen"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_NEXT_PROTO_NEG, 0), "tls_construct_stoc_next_proto_neg"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_TLS_CONSTRUCT_STOC_PSK, 0), @@ -882,6 +890,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "srtp protection profile list too long"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE), "srtp unknown protection profile"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH), + "ssl3 ext invalid max fragment length"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME), "ssl3 ext invalid servername"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index c151e7e27e..b1173f0c02 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -688,6 +688,8 @@ SSL *SSL_new(SSL_CTX *ctx) goto err; X509_VERIFY_PARAM_inherit(s->param, ctx->param); s->quiet_shutdown = ctx->quiet_shutdown; + + s->ext.max_fragment_len_mode = ctx->ext.max_fragment_len_mode; s->max_send_fragment = ctx->max_send_fragment; s->split_send_fragment = ctx->split_send_fragment; s->max_pipelines = ctx->max_pipelines; @@ -5160,3 +5162,28 @@ int ssl_randbytes(SSL *s, unsigned char *rnd, size_t size) } return RAND_bytes(rnd, (int)size); } + +__owur unsigned int ssl_get_max_send_fragment(const SSL *ssl) +{ + /* Return any active Max Fragment Len extension */ + if (ssl->session != NULL && USE_MAX_FRAGMENT_LENGTH_EXT(ssl->session)) + return GET_MAX_FRAGMENT_LENGTH(ssl->session); + + /* return current SSL connection setting */ + return ssl->max_send_fragment; +} + +__owur unsigned int ssl_get_split_send_fragment(const SSL *ssl) +{ + /* Return a value regarding an active Max Fragment Len extension */ + if (ssl->session != NULL && USE_MAX_FRAGMENT_LENGTH_EXT(ssl->session) + && ssl->split_send_fragment > GET_MAX_FRAGMENT_LENGTH(ssl->session)) + return GET_MAX_FRAGMENT_LENGTH(ssl->session); + + /* else limit |split_send_fragment| to current |max_send_fragment| */ + if (ssl->split_send_fragment > ssl->max_send_fragment) + return ssl->max_send_fragment; + + /* return current SSL connection setting */ + return ssl->split_send_fragment; +} diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 78511c287a..0acb39a14d 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -357,6 +357,14 @@ # define SSL_CLIENT_USE_SIGALGS(s) \ SSL_CLIENT_USE_TLS1_2_CIPHERS(s) +# define IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value) \ + (((value) >= TLSEXT_max_fragment_length_512) && \ + ((value) <= TLSEXT_max_fragment_length_4096)) +# define USE_MAX_FRAGMENT_LENGTH_EXT(session) \ + IS_MAX_FRAGMENT_LENGTH_EXT_VALID(session->ext.max_fragment_len_mode) +# define GET_MAX_FRAGMENT_LENGTH(session) \ + (512U << (session->ext.max_fragment_len_mode - 1)) + # define SSL_READ_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC_READ) # define SSL_WRITE_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE) @@ -558,6 +566,13 @@ struct ssl_session_st { /* The ALPN protocol selected for this session */ unsigned char *alpn_selected; size_t alpn_selected_len; + /* + * Maximum Fragment Length as per RFC 4366. + * If this value does not contain RFC 4366 allowed values (1-4) then + * either the Maximum Fragment Length Negotiation failed or was not + * performed at all. + */ + uint8_t max_fragment_len_mode; } ext; # ifndef OPENSSL_NO_SRP char *srp_username; @@ -669,6 +684,7 @@ typedef struct { typedef enum tlsext_index_en { TLSEXT_IDX_renegotiate, TLSEXT_IDX_server_name, + TLSEXT_IDX_max_fragment_length, TLSEXT_IDX_srp, TLSEXT_IDX_ec_point_formats, TLSEXT_IDX_supported_groups, @@ -895,6 +911,8 @@ struct ssl_ctx_st { void *status_arg; /* ext status type used for CSR extension (OCSP Stapling) */ int status_type; + /* RFC 4366 Maximum Fragment Length Negotiation */ + uint8_t max_fragment_len_mode; # ifndef OPENSSL_NO_EC /* EC extension values inherited by SSL structure */ @@ -1244,6 +1262,16 @@ struct ssl_st { /* May be sent by a server in HRR. Must be echoed back in ClientHello */ unsigned char *tls13_cookie; size_t tls13_cookie_len; + /* + * Maximum Fragment Length as per RFC 4366. + * If this member contains one of the allowed values (1-4) + * then we should include Maximum Fragment Length Negotiation + * extension in Client Hello. + * Please note that value of this member does not have direct + * effect. The actual (binding) value is stored in SSL_SESSION, + * as this extension is optional on server side. + */ + uint8_t max_fragment_len_mode; } ext; /* @@ -2175,6 +2203,8 @@ __owur EVP_PKEY *ssl_generate_pkey(EVP_PKEY *pm); __owur int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int genmaster); __owur EVP_PKEY *ssl_dh_to_pkey(DH *dh); +__owur unsigned int ssl_get_max_send_fragment(const SSL *ssl); +__owur unsigned int ssl_get_split_send_fragment(const SSL *ssl); __owur const SSL_CIPHER *ssl3_get_cipher_by_id(uint32_t id); __owur const SSL_CIPHER *ssl3_get_cipher_by_std_name(const char *stdname); diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c index 3153d3b376..1c3414127a 100644 --- a/ssl/statem/extensions.c +++ b/ssl/statem/extensions.c @@ -55,6 +55,7 @@ static int init_srtp(SSL *s, unsigned int context); #endif static int final_sig_algs(SSL *s, unsigned int context, int sent, int *al); static int final_early_data(SSL *s, unsigned int context, int sent, int *al); +static int final_maxfragmentlen(SSL *s, unsigned int context, int sent, int *al); /* Structure to define a built-in extension */ typedef struct extensions_definition_st { @@ -135,6 +136,14 @@ static const EXTENSION_DEFINITION ext_defs[] = { tls_construct_stoc_server_name, tls_construct_ctos_server_name, final_server_name }, + { + TLSEXT_TYPE_max_fragment_length, + SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO + | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, + NULL, tls_parse_ctos_maxfragmentlen, tls_parse_stoc_maxfragmentlen, + tls_construct_stoc_maxfragmentlen, tls_construct_ctos_maxfragmentlen, + final_maxfragmentlen + }, #ifndef OPENSSL_NO_SRP { TLSEXT_TYPE_srp, @@ -1475,3 +1484,25 @@ static int final_early_data(SSL *s, unsigned int context, int sent, int *al) return 1; } + +static int final_maxfragmentlen(SSL *ssl, unsigned int context, int sent, int *al) +{ + /* + * Session resumption on server-side with MFL extension active + * BUT MFL extension packet was not resent (i.e. sent == 0) + */ + if (ssl->server && ssl->hit && USE_MAX_FRAGMENT_LENGTH_EXT(ssl->session) + && !sent ) { + *al = SSL_AD_MISSING_EXTENSION; + return 0; + } + + /* Current SSL buffer is lower than requested MFL */ + if (ssl->session && USE_MAX_FRAGMENT_LENGTH_EXT(ssl->session) + && ssl->max_send_fragment < GET_MAX_FRAGMENT_LENGTH(ssl->session)) + /* trigger a larger buffer reallocation */ + if (!ssl3_setup_buffers(ssl)) + return 0; + + return 1; +} diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c index c1f98b42f7..ff2e0cf575 100644 --- a/ssl/statem/extensions_clnt.c +++ b/ssl/statem/extensions_clnt.c @@ -57,6 +57,31 @@ EXT_RETURN tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, return EXT_RETURN_SENT; } +/* Push a Max Fragment Len extension into ClientHello */ +EXT_RETURN tls_construct_ctos_maxfragmentlen(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx, int *al) +{ + if (s->ext.max_fragment_len_mode == TLSEXT_max_fragment_length_DISABLED) + return EXT_RETURN_NOT_SENT; + + /* Add Max Fragment Length extension if client enabled it. */ + /*- + * 4 bytes for this extension type and extension length + * 1 byte for the Max Fragment Length code value. + */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_max_fragment_length) + /* Sub-packet for Max Fragment Length extension (1 byte) */ + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, s->ext.max_fragment_len_mode) + || !WPACKET_close(pkt)) { + SSLerr(SSL_F_TLS_CONSTRUCT_CTOS_MAXFRAGMENTLEN, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + #ifndef OPENSSL_NO_SRP EXT_RETURN tls_construct_ctos_srp(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al) @@ -1115,6 +1140,43 @@ int tls_parse_stoc_renegotiate(SSL *s, PACKET *pkt, unsigned int context, return 1; } +/* Parse the server's max fragment len extension packet */ +int tls_parse_stoc_maxfragmentlen(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx, int *al) +{ + unsigned int value; + + if (PACKET_remaining(pkt) != 1 || !PACKET_get_1(pkt, &value)) { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + + /* |value| should contains a valid max-fragment-length code. */ + if (!IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value)) { + *al = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* Must be the same value as client-configured one who was sent to server */ + /*- + * RFC 6066: if a client receives a maximum fragment length negotiation + * response that differs from the length it requested, ... + * It must abort with SSL_AD_ILLEGAL_PARAMETER alert + */ + if (value != s->ext.max_fragment_len_mode) { + *al = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* + * Maximum Fragment Length Negotiation succeeded. + * The negotiated Maximum Fragment Length is binding now. + */ + s->session->ext.max_fragment_len_mode = value; + + return 1; +} + int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al) { diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c index 24b9909047..055c850abe 100644 --- a/ssl/statem/extensions_cust.c +++ b/ssl/statem/extensions_cust.c @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2014-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -489,6 +489,7 @@ int SSL_extension_supported(unsigned int ext_type) #endif case TLSEXT_TYPE_padding: case TLSEXT_TYPE_renegotiate: + case TLSEXT_TYPE_max_fragment_length: case TLSEXT_TYPE_server_name: case TLSEXT_TYPE_session_ticket: case TLSEXT_TYPE_signature_algorithms: diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c index 8bf3a7678f..1b56fa1b88 100644 --- a/ssl/statem/extensions_srvr.c +++ b/ssl/statem/extensions_srvr.c @@ -139,6 +139,40 @@ int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context, return 1; } +int tls_parse_ctos_maxfragmentlen(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx, int *al) +{ + unsigned int value; + + if (PACKET_remaining(pkt) != 1 || !PACKET_get_1(pkt, &value)) { + *al = TLS1_AD_DECODE_ERROR; + return 0; + } + + /* Received |value| should be a valid max-fragment-length code. */ + if (!IS_MAX_FRAGMENT_LENGTH_EXT_VALID(value)) { + *al = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* + * RFC 6066: The negotiated length applies for the duration of the session + * including session resumptions. + * We should receive the same code as in resumed session ! + */ + if (s->hit && s->session->ext.max_fragment_len_mode != value) { + *al = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* + * Store it in session, so it'll become binding for us + * and we'll include it in a next Server Hello. + */ + s->session->ext.max_fragment_len_mode = value; + return 1; +} + #ifndef OPENSSL_NO_SRP int tls_parse_ctos_srp(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al) @@ -844,6 +878,29 @@ EXT_RETURN tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, return EXT_RETURN_SENT; } +/* Add/include the server's max fragment len extension into ServerHello */ +EXT_RETURN tls_construct_stoc_maxfragmentlen(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx, int *al) +{ + if (!USE_MAX_FRAGMENT_LENGTH_EXT(s->session)) + return EXT_RETURN_NOT_SENT; + + /*- + * 4 bytes for this extension type and extension length + * 1 byte for the Max Fragment Length code value. + */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_max_fragment_length) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u8(pkt, s->session->ext.max_fragment_len_mode) + || !WPACKET_close(pkt)) { + SSLerr(SSL_F_TLS_CONSTRUCT_STOC_MAXFRAGMENTLEN, ERR_R_INTERNAL_ERROR); + return EXT_RETURN_FAIL; + } + + return EXT_RETURN_SENT; +} + #ifndef OPENSSL_NO_EC EXT_RETURN tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h index 9b76dc021a..0ec097cbe3 100644 --- a/ssl/statem/statem_locl.h +++ b/ssl/statem/statem_locl.h @@ -1,5 +1,5 @@ /* - * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2015-2017 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -187,6 +187,8 @@ int tls_parse_ctos_renegotiate(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); int tls_parse_ctos_server_name(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); +int tls_parse_ctos_maxfragmentlen(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx, int *al); #ifndef OPENSSL_NO_SRP int tls_parse_ctos_srp(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); @@ -237,6 +239,9 @@ EXT_RETURN tls_construct_stoc_server_name(SSL *s, WPACKET *pkt, EXT_RETURN tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); +EXT_RETURN tls_construct_stoc_maxfragmentlen(SSL *s, WPACKET *pkt, + unsigned int context, X509 *x, + size_t chainidx, int *al); #ifndef OPENSSL_NO_EC EXT_RETURN tls_construct_stoc_ec_pt_formats(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, @@ -287,6 +292,8 @@ EXT_RETURN tls_construct_ctos_renegotiate(SSL *s, WPACKET *pkt, unsigned int con X509 *x, size_t chainidx, int *al); EXT_RETURN tls_construct_ctos_server_name(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); +EXT_RETURN tls_construct_ctos_maxfragmentlen(SSL *s, WPACKET *pkt, unsigned int context, + X509 *x, size_t chainidx, int *al); #ifndef OPENSSL_NO_SRP EXT_RETURN tls_construct_ctos_srp(SSL *s, WPACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); @@ -353,6 +360,8 @@ int tls_parse_stoc_server_name(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); +int tls_parse_stoc_maxfragmentlen(SSL *s, PACKET *pkt, unsigned int context, + X509 *x, size_t chainidx, int *al); #ifndef OPENSSL_NO_EC int tls_parse_stoc_ec_pt_formats(SSL *s, PACKET *pkt, unsigned int context, X509 *x, size_t chainidx, int *al); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 28b25e122f..48f01ffe9d 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -2406,3 +2406,34 @@ int tls_choose_sigalg(SSL *s, int *al) s->s3->tmp.sigalg = lu; return 1; } + +int SSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode) +{ + if (mode != TLSEXT_max_fragment_length_DISABLED + && !IS_MAX_FRAGMENT_LENGTH_EXT_VALID(mode)) { + SSLerr(SSL_F_SSL_CTX_SET_TLSEXT_MAX_FRAGMENT_LENGTH, + SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH); + return 0; + } + + ctx->ext.max_fragment_len_mode = mode; + return 1; +} + +int SSL_set_tlsext_max_fragment_length(SSL *ssl, uint8_t mode) +{ + if (mode != TLSEXT_max_fragment_length_DISABLED + && !IS_MAX_FRAGMENT_LENGTH_EXT_VALID(mode)) { + SSLerr(SSL_F_SSL_SET_TLSEXT_MAX_FRAGMENT_LENGTH, + SSL_R_SSL3_EXT_INVALID_MAX_FRAGMENT_LENGTH); + return 0; + } + + ssl->ext.max_fragment_len_mode = mode; + return 1; +} + +uint8_t SSL_SESSION_get_max_fragment_length(const SSL_SESSION *session) +{ + return session->ext.max_fragment_len_mode; +} diff --git a/test/handshake_helper.c b/test/handshake_helper.c index be96abe3c9..ad1b7090a6 100644 --- a/test/handshake_helper.c +++ b/test/handshake_helper.c @@ -490,6 +490,17 @@ static int configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx, break; } + switch (extra->client.max_fragment_len_mode) { + case TLSEXT_max_fragment_length_512: + case TLSEXT_max_fragment_length_1024: + case TLSEXT_max_fragment_length_2048: + case TLSEXT_max_fragment_length_4096: + case TLSEXT_max_fragment_length_DISABLED: + TEST_true(SSL_CTX_set_tlsext_max_fragment_length( + client_ctx, extra->client.max_fragment_len_mode)); + break; + } + /* * Link the two contexts for SNI purposes. * Also do ClientHello callbacks here, as setting both ClientHello and SNI diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index 1ab8ef8d78..42bf4625d3 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -1,5 +1,5 @@ #! /usr/bin/env perl -# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 2015-2017 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the OpenSSL license (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -81,7 +81,7 @@ my %skip = ( # We could run some of these tests without TLS 1.2 if we had a per-test # disable instruction but that's a bizarre configuration not worth # special-casing for. - # We should review this once we have TLS 1.3. + # TODO(TLS 1.3): We should review this once we have TLS 1.3. "13-fragmentation.conf" => disabled("tls1_2"), "14-curves.conf" => disabled("tls1_2") || $no_ec || $no_ec2m, "15-certstatus.conf" => $no_tls || $no_ocsp, diff --git a/test/ssl-tests/13-fragmentation.conf b/test/ssl-tests/13-fragmentation.conf index 14aec20f70..649387c7b7 100644 --- a/test/ssl-tests/13-fragmentation.conf +++ b/test/ssl-tests/13-fragmentation.conf @@ -1,6 +1,6 @@ # Generated with generate_ssl_tests.pl -num_tests = 16 +num_tests = 22 test-0 = 0-one-fragment-minus-app-data test-1 = 1-one-fragment-app-data @@ -18,6 +18,12 @@ test-12 = 12-large-app-data-aes-sha1-multibuffer-odd-fragment test-13 = 13-large-app-data-aes-sha2-multibuffer-odd-fragment test-14 = 14-small-app-data-aes-sha1-multibuffer test-15 = 15-small-app-data-aes-sha2-multibuffer +test-16 = 16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled +test-17 = 17-Maximum Fragment Len extension equal FragmentSize to 2048 +test-18 = 18-Maximum Fragment Len extension 512 lower than FragmentSize 1024 +test-19 = 19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024 +test-20 = 20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048 +test-21 = 21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024 # =========================================================== [0-one-fragment-minus-app-data] @@ -401,3 +407,171 @@ ApplicationData = 4096 MaxFragmentSize = 4096 +# =========================================================== + +[16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled] +ssl_conf = 16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-ssl + +[16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-ssl] +server = 16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-server +client = 16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-client + +[16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-16] +ApplicationData = 3072 +MaxFragmentSize = 16384 +client = 16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-client-extra + +[16-Maximum Fragment Len extension set to 1024 w. FragmentSize disabled-client-extra] +MaxFragmentLenExt = 1024 + + +# =========================================================== + +[17-Maximum Fragment Len extension equal FragmentSize to 2048] +ssl_conf = 17-Maximum Fragment Len extension equal FragmentSize to 2048-ssl + +[17-Maximum Fragment Len extension equal FragmentSize to 2048-ssl] +server = 17-Maximum Fragment Len extension equal FragmentSize to 2048-server +client = 17-Maximum Fragment Len extension equal FragmentSize to 2048-client + +[17-Maximum Fragment Len extension equal FragmentSize to 2048-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[17-Maximum Fragment Len extension equal FragmentSize to 2048-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-17] +ApplicationData = 3072 +MaxFragmentSize = 2048 +client = 17-Maximum Fragment Len extension equal FragmentSize to 2048-client-extra + +[17-Maximum Fragment Len extension equal FragmentSize to 2048-client-extra] +MaxFragmentLenExt = 2048 + + +# =========================================================== + +[18-Maximum Fragment Len extension 512 lower than FragmentSize 1024] +ssl_conf = 18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-ssl + +[18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-ssl] +server = 18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-server +client = 18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-client + +[18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-18] +ApplicationData = 3072 +MaxFragmentSize = 1024 +client = 18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-client-extra + +[18-Maximum Fragment Len extension 512 lower than FragmentSize 1024-client-extra] +MaxFragmentLenExt = 512 + + +# =========================================================== + +[19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024] +ssl_conf = 19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-ssl + +[19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-ssl] +server = 19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-server +client = 19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-client + +[19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-19] +ApplicationData = 3072 +MaxFragmentSize = 1024 +client = 19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-client-extra + +[19-Maximum Fragment Len extension 1024 lower than FragmentSize 1024-client-extra] +MaxFragmentLenExt = 2048 + + +# =========================================================== + +[20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048] +ssl_conf = 20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-ssl + +[20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-ssl] +server = 20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-server +client = 20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-client + +[20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-20] +ApplicationData = 8196 +MaxFragmentSize = 2048 +client = 20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-client-extra + +[20-Maximum Fragment Len extension 4096 greater than FragmentSize 2048-client-extra] +MaxFragmentLenExt = 4096 + + +# =========================================================== + +[21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024] +ssl_conf = 21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-ssl + +[21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-ssl] +server = 21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-server +client = 21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-client + +[21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem + +[21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-client] +CipherString = DEFAULT +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-21] +ApplicationData = 3072 +MaxFragmentSize = 1024 +client = 21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-client-extra + +[21-Maximum Fragment Len extension 2048 greater than FragmentSize 1024-client-extra] +MaxFragmentLenExt = 2048 + + diff --git a/test/ssl-tests/13-fragmentation.conf.in b/test/ssl-tests/13-fragmentation.conf.in index ae6446ed34..1fa32002ff 100644 --- a/test/ssl-tests/13-fragmentation.conf.in +++ b/test/ssl-tests/13-fragmentation.conf.in @@ -1,5 +1,5 @@ # -*- mode: perl; -*- -# Copyright 2016-2016 The OpenSSL Project Authors. All Rights Reserved. +# Copyright 2016-2017 The OpenSSL Project Authors. All Rights Reserved. # # Licensed under the OpenSSL license (the "License"). You may not use # this file except in compliance with the License. You can obtain a copy @@ -7,7 +7,7 @@ # https://www.openssl.org/source/license.html -## Test version negotiation +## Test packet fragmentation use strict; use warnings; @@ -184,4 +184,85 @@ our @tests = ( MaxFragmentSize => 4 * 1024, } }, + ############################################ + # Default (Max) Fragment Size is 512. + # Default Application data size is 256. + { + name => "Maximum Fragment Len extension set to 1024 w. FragmentSize disabled", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 1024, + }, + }, + test => { + ApplicationData => 3072, + MaxFragmentSize => 16384, + } + }, + { + name => "Maximum Fragment Len extension equal FragmentSize to 2048", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 2048, + }, + }, + test => { + ApplicationData => 3072, + MaxFragmentSize => 2048, + } + }, + { + name => "Maximum Fragment Len extension 512 lower than FragmentSize 1024", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 512, + }, + }, + test => { + ApplicationData => 3072, + MaxFragmentSize => 1024, + } + }, + { + name => "Maximum Fragment Len extension 1024 lower than FragmentSize 1024", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 2048, + }, + }, + test => { + ApplicationData => 3072, + MaxFragmentSize => 1024, + } + }, + { + name => "Maximum Fragment Len extension 4096 greater than FragmentSize 2048", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 4096, + }, + }, + test => { + ApplicationData => 8196, + MaxFragmentSize => 2048, + } + }, + { + name => "Maximum Fragment Len extension 2048 greater than FragmentSize 1024", + server => { }, + client => { + extra => { + MaxFragmentLenExt => 2048, + }, + }, + test => { + ApplicationData => 3072, + MaxFragmentSize => 1024, + } + }, ); diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c index 569aef0fa7..62417ac2c1 100644 --- a/test/ssl_test_ctx.c +++ b/test/ssl_test_ctx.c @@ -472,6 +472,34 @@ IMPLEMENT_SSL_TEST_INT_OPTION(SSL_TEST_CTX, test, app_data_size) IMPLEMENT_SSL_TEST_INT_OPTION(SSL_TEST_CTX, test, max_fragment_size) +/* Maximum-Fragment-Length TLS extension mode */ +static const test_enum ssl_max_fragment_len_mode[] = { + {"None", TLSEXT_max_fragment_length_DISABLED}, + { "512", TLSEXT_max_fragment_length_512}, + {"1024", TLSEXT_max_fragment_length_1024}, + {"2048", TLSEXT_max_fragment_length_2048}, + {"4096", TLSEXT_max_fragment_length_4096} +}; + +__owur static int parse_max_fragment_len_mode(SSL_TEST_CLIENT_CONF *client_conf, + const char *value) +{ + int ret_value; + + if (!parse_enum(ssl_max_fragment_len_mode, + OSSL_NELEM(ssl_max_fragment_len_mode), &ret_value, value)) { + return 0; + } + client_conf->max_fragment_len_mode = ret_value; + return 1; +} + +const char *ssl_max_fragment_len_name(int MFL_mode) +{ + return enum_name(ssl_max_fragment_len_mode, + OSSL_NELEM(ssl_max_fragment_len_mode), MFL_mode); +} + /* Expected key and signature types */ @@ -639,6 +667,7 @@ static const ssl_test_client_option ssl_test_client_options[] = { { "RenegotiateCiphers", &parse_client_reneg_ciphers}, { "SRPUser", &parse_client_srp_user }, { "SRPPassword", &parse_client_srp_password }, + { "MaxFragmentLenExt", &parse_max_fragment_len_mode }, }; /* Nested server options. */ diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h index fea6527656..cec6b77f73 100644 --- a/test/ssl_test_ctx.h +++ b/test/ssl_test_ctx.h @@ -97,6 +97,8 @@ typedef struct { ssl_verify_callback_t verify_callback; /* One of a number of predefined server names use by the client */ ssl_servername_t servername; + /* Maximum Fragment Length extension mode */ + int max_fragment_len_mode; /* Supported NPN and ALPN protocols. A comma-separated list. */ char *npn_protocols; char *alpn_protocols; @@ -223,6 +225,7 @@ const char *ssl_test_method_name(ssl_test_method_t method); const char *ssl_handshake_mode_name(ssl_handshake_mode_t mode); const char *ssl_ct_validation_name(ssl_ct_validation_t mode); const char *ssl_certstatus_name(ssl_cert_status_t cert_status); +const char *ssl_max_fragment_len_name(int MFL_mode); /* * Load the test case context from |conf|. diff --git a/test/ssl_test_ctx_test.c b/test/ssl_test_ctx_test.c index 33a18428e8..d064511df2 100644 --- a/test/ssl_test_ctx_test.c +++ b/test/ssl_test_ctx_test.c @@ -40,7 +40,9 @@ static int clientconf_eq(SSL_TEST_CLIENT_CONF *conf1, || !TEST_int_eq(conf1->servername, conf2->servername) || !TEST_str_eq(conf1->npn_protocols, conf2->npn_protocols) || !TEST_str_eq(conf1->alpn_protocols, conf2->alpn_protocols) - || !TEST_int_eq(conf1->ct_validation, conf2->ct_validation)) + || !TEST_int_eq(conf1->ct_validation, conf2->ct_validation) + || !TEST_int_eq(conf1->max_fragment_len_mode, + conf2->max_fragment_len_mode)) return 0; return 1; } @@ -178,6 +180,7 @@ static int test_good_configuration(void) OPENSSL_strdup("foo,bar"); if (!TEST_ptr(fixture->expected_ctx->extra.client.npn_protocols)) goto err; + fixture->expected_ctx->extra.client.max_fragment_len_mode = 0; fixture->expected_ctx->extra.server.servername_callback = SSL_TEST_SERVERNAME_IGNORE_MISMATCH; @@ -215,6 +218,7 @@ static const char *bad_configurations[] = { "ssltest_unknown_handshake_mode", "ssltest_unknown_resumption_expected", "ssltest_unknown_ct_validation", + "ssltest_invalid_max_fragment_len", }; static int test_bad_configuration(int idx) diff --git a/test/ssl_test_ctx_test.conf b/test/ssl_test_ctx_test.conf index c85a4ba91b..91e1465ffe 100644 --- a/test/ssl_test_ctx_test.conf +++ b/test/ssl_test_ctx_test.conf @@ -92,3 +92,6 @@ client = ssltest_unknown_ct_validation_client [ssltest_unknown_ct_validation_client] CTCallback = Foo + +[ssltest_invalid_max_fragment_len] +MaxFragmentLenExt = 421 diff --git a/test/sslapitest.c b/test/sslapitest.c index 6267ce8e92..b5878570e2 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -40,6 +40,7 @@ static X509 *ocspcert = NULL; #endif #define NUM_EXTRA_CERTS 40 +#define CLIENT_VERSION_LEN 2 /* * This structure is used to validate that the correct number of log messages @@ -3108,6 +3109,113 @@ static int test_ssl_clear(int idx) return testresult; } +/* Parse CH and retrieve any MFL extension value if present */ +static int get_MFL_from_client_hello(BIO *bio, int *mfl_codemfl_code) +{ + long len; + unsigned char *data; + PACKET pkt = {0}, pkt2 = {0}, pkt3 = {0}; + unsigned int MFL_code = 0, type = 0; + + if (!TEST_uint_gt( len = BIO_get_mem_data( bio, (char **) &data ), 0 ) ) + goto end; + + if (!TEST_true( PACKET_buf_init( &pkt, data, len ) ) + /* Skip the record header */ + || !PACKET_forward(&pkt, SSL3_RT_HEADER_LENGTH) + /* Skip the handshake message header */ + || !TEST_true(PACKET_forward(&pkt, SSL3_HM_HEADER_LENGTH)) + /* Skip client version and random */ + || !TEST_true(PACKET_forward(&pkt, CLIENT_VERSION_LEN + + SSL3_RANDOM_SIZE)) + /* Skip session id */ + || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &pkt2)) + /* Skip ciphers */ + || !TEST_true(PACKET_get_length_prefixed_2(&pkt, &pkt2)) + /* Skip compression */ + || !TEST_true(PACKET_get_length_prefixed_1(&pkt, &pkt2)) + /* Extensions len */ + || !TEST_true(PACKET_as_length_prefixed_2(&pkt, &pkt2))) + goto end; + + /* Loop through all extensions */ + while (PACKET_remaining(&pkt2)) { + if (!TEST_true(PACKET_get_net_2(&pkt2, &type)) + || !TEST_true(PACKET_get_length_prefixed_2(&pkt2, &pkt3))) + goto end; + + if (type == TLSEXT_TYPE_max_fragment_length) { + if (!TEST_uint_ne(PACKET_remaining(&pkt3), 0) + || !TEST_true(PACKET_get_1(&pkt3, &MFL_code))) + goto end; + + *mfl_codemfl_code = MFL_code; + return 1; + } + } + + end: + return 0; +} + +/* Maximum-Fragment-Length TLS extension mode to test */ +static const unsigned char max_fragment_len_test[] = { + TLSEXT_max_fragment_length_512, + TLSEXT_max_fragment_length_1024, + TLSEXT_max_fragment_length_2048, + TLSEXT_max_fragment_length_4096 +}; + +static int test_max_fragment_len_ext(int idx_tst) +{ + SSL_CTX *ctx; + SSL *con = NULL; + int testresult = 0, MFL_mode = 0; + BIO *rbio, *wbio; + + ctx = SSL_CTX_new(TLS_method()); + if (!TEST_ptr(ctx)) + goto end; + + if (!TEST_true(SSL_CTX_set_tlsext_max_fragment_length( + ctx, max_fragment_len_test[idx_tst]))) + goto end; + + con = SSL_new(ctx); + if (!TEST_ptr(con)) + goto end; + + rbio = BIO_new(BIO_s_mem()); + wbio = BIO_new(BIO_s_mem()); + if (!TEST_ptr(rbio)|| !TEST_ptr(wbio)) { + BIO_free(rbio); + BIO_free(wbio); + goto end; + } + + SSL_set_bio(con, rbio, wbio); + SSL_set_connect_state(con); + + if (!TEST_int_le(SSL_connect(con), 0)) { + /* This shouldn't succeed because we don't have a server! */ + goto end; + } + + if (!TEST_true(get_MFL_from_client_hello(wbio, &MFL_mode))) + /* no MFL in client hello */ + goto end; + if (!TEST_true(max_fragment_len_test[idx_tst] == MFL_mode)) + goto end; + + testresult = 1; + +end: + SSL_free(con); + SSL_CTX_free(ctx); + + return testresult; +} + int setup_tests(void) { if (!TEST_ptr(cert = test_get_argument(0)) @@ -3159,6 +3267,7 @@ int setup_tests(void) ADD_ALL_TESTS(test_serverinfo, 8); ADD_ALL_TESTS(test_export_key_mat, 4); ADD_ALL_TESTS(test_ssl_clear, 2); + ADD_ALL_TESTS(test_max_fragment_len_ext, OSSL_NELEM(max_fragment_len_test)); return 1; } diff --git a/util/libssl.num b/util/libssl.num index 1d8f8ab525..243c1fb2cf 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -470,3 +470,6 @@ SSL_SESSION_set1_alpn_selected 470 1_1_1 EXIST::FUNCTION: SSL_SESSION_set1_hostname 471 1_1_1 EXIST::FUNCTION: SSL_SESSION_get0_alpn_selected 472 1_1_1 EXIST::FUNCTION: DTLS_set_timer_cb 473 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_tlsext_max_fragment_length 474 1_1_1 EXIST::FUNCTION: +SSL_set_tlsext_max_fragment_length 475 1_1_1 EXIST::FUNCTION: +SSL_SESSION_get_max_fragment_length 476 1_1_1 EXIST::FUNCTION: diff --git a/util/perl/TLSProxy/Message.pm b/util/perl/TLSProxy/Message.pm index a9002ec654..1c2bd2044b 100644 --- a/util/perl/TLSProxy/Message.pm +++ b/util/perl/TLSProxy/Message.pm @@ -63,6 +63,7 @@ my %message_type = ( use constant { EXT_SERVER_NAME => 0, + EXT_MAX_FRAGMENT_LENGTH => 1, EXT_STATUS_REQUEST => 5, EXT_SUPPORTED_GROUPS => 10, EXT_EC_POINT_FORMATS => 11,