diff --git a/CHANGES b/CHANGES index 43c9cd8..c84d108 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,13 @@ Features: Test: PROXY_HTTPVERSION Feature inspired by Robin Palotai. + New options openssl-maxfraglen and openssl-maxsendfrag for + functions/macros SSL_CTX_set_tlsext_max_fragment_length() and + SSL_CTX_set_max_send_fragment(). + Thanks to James Tavares for his contribution. + + Added Info log of resulting OpenSSL max fragment length. + Corrections: When a sub process (EXEC, SYSTEM) terminated with exit code other than 0, its last sent data might have been lost depending on timing of read/ diff --git a/config.h.in b/config.h.in index 7ee8677..a80a5ba 100644 --- a/config.h.in +++ b/config.h.in @@ -532,6 +532,12 @@ /* Define if you have the OpenSSL SSL_set_tlsext_host_name define/function */ #undef HAVE_SSL_set_tlsext_host_name +/* Define if you have the OpenSSL SSL_CTX_set_tlsext_max_fragment_length define/function */ +#undef HAVE_SSL_CTX_set_tlsext_max_fragment_length + +/* Define if you have the OpenSSL SSL_CTX_set_max_send_fragment define/function */ +#undef HAVE_SSL_CTX_set_max_send_fragment + /* Define if you have the flock function */ #undef HAVE_FLOCK diff --git a/configure.ac b/configure.ac index 61bfca9..7fdbe2c 100644 --- a/configure.ac +++ b/configure.ac @@ -1578,6 +1578,23 @@ AC_CHECK_FUNC(ASN1_STRING_get0_data, AC_DEFINE(HAVE_ASN1_STRING_get0_data), AC_C AC_CHECK_FUNC(RAND_status, AC_DEFINE(HAVE_RAND_status)) AC_CHECK_FUNC(SSL_CTX_clear_mode, AC_DEFINE(HAVE_SSL_CTX_clear_mode)) AC_CHECK_FUNC(SSL_set_tlsext_host_name, AC_DEFINE(HAVE_SSL_set_tlsext_host_name)) +AC_CHECK_FUNC(SSL_CTX_set_tlsext_max_fragment_length, AC_DEFINE(HAVE_SSL_CTX_set_tlsext_max_fragment_length)) + +AC_MSG_CHECKING(if SSL_CTX_set_max_send_fragment exists) +AC_CACHE_VAL(ac_cv_have_SSL_CTX_set_max_send_fragment, +[AC_TRY_COMPILE([#include ],[ +#ifndef SSL_CTX_set_max_send_fragment +#error "SSL_CTX_set_max_send_fragment not found" +#endif +], +[ac_cv_have_SSL_CTX_set_max_send_fragment=yes], +[ac_cv_have_SSL_CTX_set_max_send_fragment=no])] +) +if test $ac_cv_have_SSL_CTX_set_max_send_fragment = yes; then + AC_DEFINE(HAVE_SSL_CTX_set_max_send_fragment) +fi +AC_MSG_RESULT($ac_cv_have_SSL_CTX_set_max_send_fragment) + AC_CHECK_FUNC(SSL_library_init, AC_DEFINE(HAVE_SSL_library_init)) AC_CHECK_FUNC(ERR_error_string, AC_DEFINE(HAVE_ERR_error_string)) diff --git a/doc/socat.yo b/doc/socat.yo index b2bd711..8cb488d 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -2896,6 +2896,15 @@ label(OPTION_OPENSSL_EGD)dit(bf(tt(egd=))) On some systems, openssl requires an explicit source of random data. Specify the socket name where an entropy gathering daemon like egd provides random data, e.g. /dev/egd-pool. +label(OPTION_OPENSSL_MAXFRAGLEN)dit(bf(tt(maxfraglen=, openssl-maxfraglen=))) + For client connections, make a Max Fragment Length Negotiation Request to the server to limit the + maximum size fragment the server will send to us. Supported lengths are: 512, 1024, 2048, or + 4096. Note that this option is not applicable for link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN). +label(OPTION_OPENSSL_MAXSENDFRAG)dit(bf(tt(maxsendfrag=, openssl-maxsendfrag=))) + Limit the maximum size of the fragment we will send to the other side. Supported length range: + 512 - 16384. Note that under link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN), the maximum fragment + size may be further limited by the client's Maximum Fragment Length Negotiation Request, if it + makes one. label(OPTION_OPENSSL_PSEUDO)dit(bf(tt(pseudo))) On systems where openssl cannot find an entropy source and where no entropy gathering daemon can be utilized, this option activates a mechanism for diff --git a/sslcls.c b/sslcls.c index 377a958..9784c8d 100644 --- a/sslcls.c +++ b/sslcls.c @@ -319,6 +319,26 @@ int sycSSL_CTX_set_tmp_dh(SSL_CTX *ctx, DH *dh) { return result; } +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) +int sycSSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode) { + int result; + Debug2("SSL_CTX_set_tlsext_max_fragment_length(%p, %u)", ctx, mode); + result = SSL_CTX_set_tlsext_max_fragment_length(ctx, mode); + Debug1("SSL_CTX_set_tlsext_max_fragment_length() -> %d", result); + return result; +} +#endif + +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) +int sycSSL_CTX_set_max_send_fragment(SSL_CTX *ctx, long msf) { + int result; + Debug2("SSL_CTX_set_max_send_fragment(%p, %ld)", ctx, msf); + result = SSL_CTX_set_max_send_fragment(ctx, msf); + Debug1("SSL_CTX_set_max_send_fragment() -> %d", result); + return result; +} +#endif + int sycSSL_set_cipher_list(SSL *ssl, const char *str) { int result; Debug2("SSL_set_cipher_list(%p, \"%s\")", ssl, str); diff --git a/sslcls.h b/sslcls.h index 1412f80..0a7fe0f 100644 --- a/sslcls.h +++ b/sslcls.h @@ -44,6 +44,12 @@ void sycSSL_CTX_set_verify(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *)); int sycSSL_CTX_set_tmp_dh(SSL_CTX *ctx, DH *dh); int sycSSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str); +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) +int sycSSL_CTX_set_tlsext_max_fragment_length(SSL_CTX *ctx, uint8_t mode); +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) +int sycSSL_CTX_set_max_send_fragment(SSL_CTX *ctx, long msf); +#endif int sycSSL_set_cipher_list(SSL *ssl, const char *str); long sycSSL_get_verify_result(SSL *ssl); int sycSSL_set_fd(SSL *ssl, int fd); @@ -108,6 +114,12 @@ const char *sycSSL_COMP_get_name(const COMP_METHOD *comp); #define sycSSL_CTX_set_verify(c,m,v) SSL_CTX_set_verify(c,m,v) #define sycSSL_CTX_set_tmp_dh(c,d) SSL_CTX_set_tmp_dh(c,d) #define sycSSL_CTX_set_cipher_list(c,s) SSL_CTX_set_cipher_list(c,s) +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) +#define sycSSL_CTX_set_tlsext_max_fragment_length(c,m) SSL_CTX_set_tlsext_max_fragment_length(c, m) +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) +#define sycSSL_CTX_set_max_send_fragment(c,m) SSL_CTX_set_max_send_fragment(c, m) +#endif #define sycSSL_set_cipher_list(s,t) SSL_set_cipher_list(s,t) #define sycSSL_get_verify_result(s) SSL_get_verify_result(s) #define sycSSL_set_fd(s,f) SSL_set_fd(s,f) diff --git a/xio-openssl.c b/xio-openssl.c index 29413ad..acd54f0 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -125,6 +125,12 @@ const struct optdesc opt_openssl_dhparam = { "openssl-dhparam", "dh", const struct optdesc opt_openssl_cafile = { "openssl-cafile", "cafile", OPT_OPENSSL_CAFILE, GROUP_OPENSSL, PH_SPEC, TYPE_FILENAME, OFUNC_SPEC }; const struct optdesc opt_openssl_capath = { "openssl-capath", "capath", OPT_OPENSSL_CAPATH, GROUP_OPENSSL, PH_SPEC, TYPE_FILENAME, OFUNC_SPEC }; const struct optdesc opt_openssl_egd = { "openssl-egd", "egd", OPT_OPENSSL_EGD, GROUP_OPENSSL, PH_SPEC, TYPE_FILENAME, OFUNC_SPEC }; +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) +const struct optdesc opt_openssl_maxfraglen = { "openssl-maxfraglen", "maxfraglen", OPT_OPENSSL_MAXFRAGLEN, GROUP_OPENSSL, PH_SPEC, TYPE_INT, OFUNC_SPEC }; +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) +const struct optdesc opt_openssl_maxsendfrag = { "openssl-maxsendfrag", "maxsendfrag", OPT_OPENSSL_MAXSENDFRAG, GROUP_OPENSSL, PH_SPEC, TYPE_INT, OFUNC_SPEC }; +#endif const struct optdesc opt_openssl_pseudo = { "openssl-pseudo", "pseudo", OPT_OPENSSL_PSEUDO, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC }; #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_COMP) const struct optdesc opt_openssl_compress = { "openssl-compress", "compress", OPT_OPENSSL_COMPRESS, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC }; @@ -138,7 +144,6 @@ const struct optdesc opt_openssl_no_sni = { "openssl-no-sni", "nosni", const struct optdesc opt_openssl_snihost = { "openssl-snihost", "snihost", OPT_OPENSSL_SNIHOST, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC }; #endif - /* If FIPS is compiled in, we need to track if the user asked for FIPS mode. * On forks, the FIPS mode must be reset by a disable, then enable since * FIPS tracks the process ID that initializes things. @@ -166,6 +171,7 @@ int xio_reset_fips_mode(void) { static void openssl_conn_loginfo(SSL *ssl) { const char *string; + SSL_SESSION *session; string = SSL_get_cipher_version(ssl); Notice1("SSL proto version used: %s", string); @@ -187,6 +193,31 @@ static void openssl_conn_loginfo(SSL *ssl) { Notice1("SSL connection expansion \"%s\"", expansion?sycSSL_COMP_get_name(expansion):"none"); } +#endif + session = SSL_get_session(ssl); + if (session == NULL) { + Warn1("SSL_get_session(%p) failed", ssl); + return; + } +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) + { + uint8_t fragcod; + int fraglen = -1; + fragcod = SSL_SESSION_get_max_fragment_length(session); + switch (fragcod) { + case TLSEXT_max_fragment_length_DISABLED: fraglen = 0; break; + case TLSEXT_max_fragment_length_512: fraglen = 512; break; + case TLSEXT_max_fragment_length_1024: fraglen = 1024; break; + case TLSEXT_max_fragment_length_2048: fraglen = 2048; break; + case TLSEXT_max_fragment_length_4096: fraglen = 4096; break; + default: Warn1("SSL_SESSION_get_max_fragment_length(): unknown code %u", + fragcod); + break; + } + if (fraglen > 0) { + Info1("OpenSSL: max fragment length is %d", fraglen); + } + } #endif } @@ -1405,6 +1436,60 @@ cont_out: NULL); } +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) + { + /* set client max fragment length negotiation (512, 1024, 2048, or 4096) */ + + int opt_maxfraglen = -1; + + retropt_int(opts, OPT_OPENSSL_MAXFRAGLEN, &opt_maxfraglen); + + if (!server) { + /* on client connection, ask the server not to send us packets bigger than our inbound buffer */ + uint8_t mfl_code = TLSEXT_max_fragment_length_DISABLED; + if (opt_maxfraglen == -1) { + /* max frag length is not specified, leave DISABLED */ + } else if (opt_maxfraglen == 512) { + mfl_code = TLSEXT_max_fragment_length_512; + } else if (opt_maxfraglen == 1024) { + mfl_code = TLSEXT_max_fragment_length_1024; + } else if (opt_maxfraglen == 2048) { + mfl_code = TLSEXT_max_fragment_length_2048; + } else if (opt_maxfraglen == 4096) { + mfl_code = TLSEXT_max_fragment_length_4096; + } else { + Error1("openssl: maxfraglen %d is not one of 512, 1024, 2048, or 4096", opt_maxfraglen); + return STAT_NORETRY; + } + + sycSSL_CTX_set_tlsext_max_fragment_length(ctx, mfl_code); + } else { + if (opt_maxfraglen != -1) { + Error("openssl: maxfraglen option not applicable to a server"); + return STAT_NORETRY; + } + } + } +#endif + +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) + { + /* limit the maximum size of sent packets */ + const int maxsendfrag_min = 512; /* per OpenSSL documentation */ + int opt_maxsendfrag = SSL3_RT_MAX_PLAIN_LENGTH; + + retropt_int(opts, OPT_OPENSSL_MAXSENDFRAG, &opt_maxsendfrag); + + if (opt_maxsendfrag < maxsendfrag_min || opt_maxsendfrag > SSL3_RT_MAX_PLAIN_LENGTH) { + Error2("openssl: maxsendfrag %d out of range 512 - %d", maxsendfrag_min, + SSL3_RT_MAX_PLAIN_LENGTH); + return STAT_NORETRY; + } + + sycSSL_CTX_set_max_send_fragment(ctx, opt_maxsendfrag); + } +#endif + return STAT_OK; } diff --git a/xio-openssl.h b/xio-openssl.h index 6ec3d3a..204aca6 100644 --- a/xio-openssl.h +++ b/xio-openssl.h @@ -26,6 +26,12 @@ extern const struct optdesc opt_openssl_dhparam; extern const struct optdesc opt_openssl_cafile; extern const struct optdesc opt_openssl_capath; extern const struct optdesc opt_openssl_egd; +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) +extern const struct optdesc opt_openssl_maxfraglen; +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) +extern const struct optdesc opt_openssl_maxsendfrag; +#endif extern const struct optdesc opt_openssl_pseudo; #if OPENSSL_VERSION_NUMBER >= 0x00908000L extern const struct optdesc opt_openssl_compress; diff --git a/xioopts.c b/xioopts.c index 750caa5..48058c3 100644 --- a/xioopts.c +++ b/xioopts.c @@ -936,10 +936,16 @@ const struct optname optionnames[] = { IF_OPENSSL("max-version", &opt_openssl_max_proto_version) #endif IF_LISTEN ("maxchildren", &opt_max_children) +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) + IF_OPENSSL("maxfraglen", &opt_openssl_maxfraglen) +#endif #ifdef TCP_MAXSEG IF_TCP ("maxseg", &opt_tcp_maxseg) IF_TCP ("maxseg-late", &opt_tcp_maxseg_late) #endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) + IF_OPENSSL("maxsendfrag", &opt_openssl_maxsendfrag) +#endif #ifdef TCP_MD5SUM IF_TCP ("md5sig", &opt_tcp_md5sig) #endif @@ -1190,6 +1196,12 @@ const struct optname optionnames[] = { #if HAVE_SSL_set_max_proto_version || defined(SSL_set_max_proto_version) IF_OPENSSL("openssl-max-proto-version", &opt_openssl_max_proto_version) #endif +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) + IF_OPENSSL("openssl-maxfraglen", &opt_openssl_maxfraglen) +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) + IF_OPENSSL("openssl-maxsendfrag", &opt_openssl_maxsendfrag) +#endif #if WITH_OPENSSL_METHOD IF_OPENSSL("openssl-method", &opt_openssl_method) #endif diff --git a/xioopts.h b/xioopts.h index 32c3ab3..f1bb5bc 100644 --- a/xioopts.h +++ b/xioopts.h @@ -506,6 +506,12 @@ enum e_optcode { OPT_OPENSSL_EGD, OPT_OPENSSL_FIPS, OPT_OPENSSL_KEY, +#if HAVE_SSL_CTX_set_tlsext_max_fragment_length || defined(SSL_CTX_set_tlsext_max_fragment_length) + OPT_OPENSSL_MAXFRAGLEN, +#endif +#if HAVE_SSL_CTX_set_max_send_fragment || defined(SSL_CTX_set_max_send_fragment) + OPT_OPENSSL_MAXSENDFRAG, +#endif OPT_OPENSSL_MAX_PROTO_VERSION, OPT_OPENSSL_METHOD, OPT_OPENSSL_MIN_PROTO_VERSION,