[openssl-commits] [openssl] master update

bernd.edlinger at hotmail.de bernd.edlinger at hotmail.de
Sun Nov 5 16:50:18 UTC 2017


The branch master has been updated
       via  cf72c7579201086cee303eadcb60bd28eff78dd9 (commit)
      from  b82acc3c1a7f304c9df31841753a0fa76b5b3cda (commit)


- Log -----------------------------------------------------------------
commit cf72c7579201086cee303eadcb60bd28eff78dd9
Author: FdaSilvaYY <fdasilvayy at gmail.com>
Date:   Sun Nov 5 17:46:48 2017 +0100

    Implement Maximum Fragment Length TLS extension.
    
    Based on patch from Tomasz Moń:
    https://groups.google.com/forum/#!topic/mailing.openssl.dev/fQxXvCg1uQY
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Bernd Edlinger <bernd.edlinger at hotmail.de>
    (Merged from https://github.com/openssl/openssl/pull/1008)

-----------------------------------------------------------------------

Summary of changes:
 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 392bceb..71c700c 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 <jack.lloyd at ribose.com>,
        Ronald Tse <ronald.tse at ribose.com>,
diff --git a/apps/s_client.c b/apps/s_client.c
index 019e735..d4fb1a8 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 5705966..d3e4a62 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 f65540f..ef5e7cd 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<mode> to B<ctx>.
+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<mode> to B<ssl>.
+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<session>.
+
 =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<SSL_CTX_set_read_ahead(3)>, L<SSL_pending(3)>
 
 =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 7bc409c..176425a 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 1df0dfa..3450b4c 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 9adb89e..d114fb5 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 59285ed..3eabf71 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 1225f9b..980e5a4 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 8a6a922..da23b36 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 8d71cd3..28a706f 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 f6019bc..7258618 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 3eb89a3..17a2413 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 c151e7e..b1173f0 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 78511c2..0acb39a 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 3153d3b..1c34141 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 c1f98b4..ff2e0cf 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 24b9909..055c850 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 8bf3a76..1b56fa1 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 9b76dc0..0ec097c 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 28b25e1..48f01ff 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 be96abe..ad1b709 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 1ab8ef8..42bf462 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 14aec20..649387c 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 ae6446e..1fa3200 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 569aef0..62417ac 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 fea6527..cec6b77f 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 33a1842..d064511 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 c85a4ba..91e1465 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 6267ce8..b587857 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 1d8f8ab..243c1fb 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 a9002ec..1c2bd20 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,


More information about the openssl-commits mailing list