From 800fd903b16f8e69234e76f3b7f207ecc060c966 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Fri, 16 Nov 2012 21:10:44 +0100 Subject: [PATCH] OpenSSL option compress allows to disable compression --- CHANGES | 4 +++ doc/socat.yo | 9 ++++- doc/xio.help | 17 +++++++++ sslcls.c | 40 +++++++++++++++++++++- sslcls.h | 12 ++++++- test.sh | 59 ++++++++++++++++++++++++++++++++ xio-openssl.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++-- xio-openssl.h | 5 ++- xioopts.c | 6 ++++ xioopts.h | 3 ++ 10 files changed, 243 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 6029f00..13c1d3b 100644 --- a/CHANGES +++ b/CHANGES @@ -135,6 +135,10 @@ new features: Till Maas added support for tun/tap addresses without IP address + added an option openssl-compress that allows to disable the compression + feature of newer OpenSSL versions. Thanks to Michael Hanselmann for + providing this contribution (sponsored by Google Inc.) + ####################### V 2.0.0-b7: security: diff --git a/doc/socat.yo b/doc/socat.yo index 2c8b295..1a7cf78 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -10,7 +10,7 @@ def(Filan)(0)(bf(Filan)) def(procan)(0)(bf(procan)) def(Procan)(0)(bf(Procan)) -manpage(socat)(1)(Apr 2012)()() +manpage(socat)(1)(Nov 2012)()() whenhtml( label(CONTENTS) @@ -2737,6 +2737,13 @@ label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips))) This mode might require that the involved certificates are generated with a FIPS enabled version of openssl. Setting or clearing this option on one socat() address affects all OpenSSL addresses of this process. +label(OPTION_OPENSSL_COMPRESS)dit(bf(tt(compress))) + Enable or disable the use of compression for a connection. Setting this to + "none" disables compression, setting it to "auto" lets OpenSSL choose the best + available algorithm supported by both parties. The default is to not touch any + compression-related settings. + NOTE: Requires OpenSSL 0.9.8 or higher and disabling compression with + OpenSSL 0.9.8 affects all new connections in the process. enddit() startdit()enddit()nl() diff --git a/doc/xio.help b/doc/xio.help index a767b42..96082fb 100644 --- a/doc/xio.help +++ b/doc/xio.help @@ -4609,6 +4609,23 @@ This mode might require that the involved certificates are generated with a FIPS enabled version of openssl. Setting or clearing this option on one socat address affects all OpenSSL addresses of this process. + +Option: openssl-compress +Aliases: compress + +Type: STRING +Option group: OPENSSL +Phase: SPEC +Platforms: (depends on openssl installation) + +Enable or disable the use of compression for a connection. Setting this to +"none" disables compression, setting it to "auto" lets OpenSSL choose the best +available algorithm supported by both parties. The default is to not touch any +compression-related settings. +NOTE: Requires OpenSSL 0.9.8 or higher. +NOTE: Disabling compression with OpenSSL 0.9.8 affects all new connections in +the same process. + =============================================================================== Application specific address options diff --git a/sslcls.c b/sslcls.c index 076cc95..fd66a43 100644 --- a/sslcls.c +++ b/sslcls.c @@ -1,5 +1,5 @@ /* source: sslcls.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2012 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* explicit system call and C library trace function, for those who miss strace @@ -331,4 +331,42 @@ int sycFIPS_mode_set(int onoff) { } #endif /* WITH_FIPS */ +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +const COMP_METHOD *sycSSL_get_current_compression(SSL *ssl) { + const COMP_METHOD *result; + Debug1("SSL_get_current_compression(%p)", ssl); + result = SSL_get_current_compression(ssl); + if (result) { + Debug1("SSL_get_current_compression() -> %p", result); + } else { + Debug("SSL_get_current_compression() -> NULL"); + } + return result; +} + +const COMP_METHOD *sycSSL_get_current_expansion(SSL *ssl) { + const COMP_METHOD *result; + Debug1("SSL_get_current_expansion(%p)", ssl); + result = SSL_get_current_expansion(ssl); + if (result) { + Debug1("SSL_get_current_expansion() -> %p", result); + } else { + Debug("SSL_get_current_expansion() -> NULL"); + } + return result; +} + +const char *sycSSL_COMP_get_name(const COMP_METHOD *comp) { + const char *result; + Debug1("SSL_COMP_get_name(%p)", comp); + result = SSL_COMP_get_name(comp); + if (result) { + Debug1("SSL_COMP_get_name() -> \"%s\"", result); + } else { + Debug("SSL_COMP_get_name() -> NULL"); + } + return result; +} +#endif + #endif /* WITH_SYCLS && WITH_OPENSSL */ diff --git a/sslcls.h b/sslcls.h index 8d2d193..82058eb 100644 --- a/sslcls.h +++ b/sslcls.h @@ -1,5 +1,5 @@ /* source: sslcls.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2012 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __sslcls_h_included @@ -51,6 +51,12 @@ BIO *sycBIO_new_file(const char *filename, const char *mode); int sycFIPS_mode_set(int onoff); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +const COMP_METHOD *sycSSL_get_current_compression(SSL *ssl); +const COMP_METHOD *sycSSL_get_current_expansion(SSL *ssl); +const char *sycSSL_COMP_get_name(const COMP_METHOD *comp); +#endif + #endif /* WITH_OPENSSL */ #else /* !WITH_SYCLS */ @@ -96,6 +102,10 @@ int sycFIPS_mode_set(int onoff); #define sycBIO_new_file(f,m) BIO_new_file(f,m) +#define sycSSL_get_current_compression(s) SSL_get_current_compression(s) +#define sycSSL_get_current_expansion(s) SSL_get_current_expansion(s) +#define sycSSL_COMP_get_name(c) SSL_COMP_get_name(c) + #endif /* WITH_OPENSSL */ #define sycFIPS_mode_set(o) FIPS_mode_set(o) diff --git a/test.sh b/test.sh index 3f63806..db8229a 100755 --- a/test.sh +++ b/test.sh @@ -491,6 +491,9 @@ filloptionvalues() { case "$OPTS" in *,egd,*) OPTS=$(echo "$OPTS" |sed "s/,egd,/,egd=/tmp/hugo,/g");; esac + case "$OPTS" in + *,compress,*) OPTS=$(echo "$OPTS" |sed "s/,compress,/,compress=none,/g");; + esac # PROXY case "$OPTS" in *,proxyauth,*) OPTS=$(echo "$OPTS" |sed "s/,proxyauth,/,proxyauth=user:pass,/g");; @@ -4139,6 +4142,62 @@ PORT=$((PORT+1)) N=$((N+1)) +NAME=OPENSSL_COMPRESS +case "$TESTS" in +*%functions%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) +TEST="$NAME: OpenSSL compression" +if ! eval $NUMCOND; then :; +elif ! testaddrs openssl >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) +elif ! testaddrs listen tcp ip4 >/dev/null || ! runsip4 >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv4 not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) +elif ! testoptions openssl-compress >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL compression option not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) +else + gentestcert testsrv + printf "test $F_n $TEST... " $N + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + success=yes + for srccompr in '' compress=auto compress=none; do + for dstcompr in '' compress=auto compress=none; do + CMD2="$SOCAT $opts OPENSSL-LISTEN:$PORT,pf=ip4,reuseaddr,$SOCAT_EGD,cert=testsrv.crt,key=testsrv.key,verify=0,$dstcompr pipe" + CMD="$SOCAT $opts - openssl:$LOCALHOST:$PORT,pf=ip4,verify=0,$SOCAT_EGD,$srccompr" + eval "$CMD2 2>\"${te}1\" &" + pid=$! # background process id + waittcp4port $PORT + echo "$da" | $CMD >$tf 2>"${te}2" + kill $pid 2>/dev/null + if ! echo "$da" |diff - "$tf" >"$tdiff"; then + success= + break + fi + done + done + if test -z "$success"; then + $PRINTF "$FAILED: $SOCAT:\n" + echo "$CMD2 &" + echo "$CMD" + cat "${te}1" + cat "${te}2" + cat "$tdiff" + numFAIL=$((numFAIL+1)) + else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi + numOK=$((numOK+1)) + fi +fi ;; # NUMCOND, feats +esac +PORT=$((PORT+1)) +N=$((N+1)) + + NAME=SOCKS4CONNECT_TCP4 case "$TESTS" in *%functions%*|*%chain%*|*%socks%*|*%socks4%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) diff --git a/xio-openssl.c b/xio-openssl.c index ca40257..7cdb2e8 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -177,6 +177,9 @@ const struct optdesc opt_openssl_cafile = { "openssl-cafile", "cafile", 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 }; 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 +const struct optdesc opt_openssl_compress = { "openssl-compress", "compress", OPT_OPENSSL_COMPRESS, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC }; +#endif #if WITH_FIPS const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC }; #endif @@ -207,6 +210,24 @@ int xio_reset_fips_mode(void) { #define xio_reset_fips_mode() 0 #endif +static void openssl_conn_loginfo(SSL *ssl) { + Notice1("SSL connection using %s", SSL_get_cipher(ssl)); + +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + { + const COMP_METHOD *comp, *expansion; + + comp = sycSSL_get_current_compression(ssl); + expansion = sycSSL_get_current_expansion(ssl); + + Notice1("SSL connection compression \"%s\"", + comp?sycSSL_COMP_get_name(comp):"none"); + Notice1("SSL connection expansion \"%s\"", + expansion?sycSSL_COMP_get_name(expansion):"none"); + } +#endif +} + /* the open function for OpenSSL client */ static int xioopen_openssl_connect(int argc, @@ -415,7 +436,7 @@ static int break; } while (true); /* drop out on success */ - Notice1("SSL connection using %s", SSL_get_cipher(xfd->para.openssl.ssl)); + openssl_conn_loginfo(xfd->para.openssl.ssl); /* fill in the fd structure */ return STAT_OK; @@ -634,8 +655,7 @@ static int return result; } - Notice1("SSL connection using %s", - SSL_get_cipher(xfd->para.openssl.ssl)); + openssl_conn_loginfo(xfd->para.openssl.ssl); break; } /* drop out on success */ @@ -738,6 +758,59 @@ int _xioopen_openssl_listen(struct single *xfd, #endif /* WITH_LISTEN */ +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +/* In OpenSSL 0.9.7 compression methods could be added using + * SSL_COMP_add_compression_method(3), but the implemntation is not compatible + * with the standard (RFC3749). + */ +static int openssl_setup_compression(SSL_CTX *ctx, char *method) +{ + STACK_OF(SSL_COMP)* comp_methods; + + assert(method); + + /* Getting the stack of compression methods has the intended side-effect of + * initializing the SSL library's compression part. + */ + comp_methods = SSL_COMP_get_compression_methods(); + if (!comp_methods) { + Info("OpenSSL built without compression support"); + return STAT_OK; + } + + if (strcasecmp(method, "auto") == 0) { + Info("Using default OpenSSL compression"); + return STAT_OK; + } + + if (strcasecmp(method, "none") == 0) { + /* Disable compression */ +#ifdef SSL_OP_NO_COMPRESSION + Info("Disabling OpenSSL compression"); + SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION); +#else + /* SSL_OP_NO_COMPRESSION was only introduced in OpenSSL 0.9.9 (released + * as 1.0.0). Removing all compression methods is a work-around for + * earlier versions of OpenSSL, but it affects all SSL connections. + */ + Info("Disabling OpenSSL compression globally"); + sk_SSL_COMP_zero(comp_methods); +#endif + return STAT_OK; + } + + /* zlib compression in OpenSSL before version 0.9.8e-beta1 uses the libc's + * default malloc/free instead of the ones passed to OpenSSL. Should socat + * ever use custom malloc/free functions for OpenSSL, this must be taken + * into consideration. See OpenSSL bug #1468. + */ + + Error1("openssl-compress=\"%s\": unknown compression method", method); + return STAT_NORETRY; +} +#endif + + int _xioopen_openssl_prepare(struct opt *opts, struct single *xfd,/* a xio file descriptor @@ -757,6 +830,9 @@ int char *opt_cafile = NULL; /* certificate authority file */ char *opt_capath = NULL; /* certificate authority directory */ char *opt_egd = NULL; /* entropy gathering daemon socket path */ +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + char *opt_compress = NULL; /* compression method */ +#endif bool opt_pseudo = false; /* use pseudo entropy if nothing else */ unsigned long err; int result; @@ -773,6 +849,9 @@ int retropt_string(opts, OPT_OPENSSL_DHPARAM, &opt_dhparam); retropt_string(opts, OPT_OPENSSL_EGD, &opt_egd); retropt_bool(opts,OPT_OPENSSL_PSEUDO, &opt_pseudo); +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + retropt_string(opts, OPT_OPENSSL_COMPRESS, &opt_compress); +#endif #if WITH_FIPS if (opt_fips) { @@ -914,6 +993,16 @@ int } } +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + if (opt_compress) { + int result; + result = openssl_setup_compression(*ctx, opt_compress); + if (result != STAT_OK) { + return result; + } + } +#endif + if (opt_cafile != NULL || opt_capath != NULL) { if (sycSSL_CTX_load_verify_locations(*ctx, opt_cafile, opt_capath) != 1) { int result; diff --git a/xio-openssl.h b/xio-openssl.h index 5545ec6..94ba972 100644 --- a/xio-openssl.h +++ b/xio-openssl.h @@ -1,5 +1,5 @@ /* source: xio-openssl.h */ -/* Copyright Gerhard Rieger 2002-2007 */ +/* Copyright Gerhard Rieger 2002-2012 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_openssl_included @@ -23,6 +23,9 @@ extern const struct optdesc opt_openssl_cafile; extern const struct optdesc opt_openssl_capath; extern const struct optdesc opt_openssl_egd; extern const struct optdesc opt_openssl_pseudo; +#if OPENSSL_VERSION_NUMBER >= 0x00908000L +extern const struct optdesc opt_openssl_compress; +#endif #if WITH_FIPS extern const struct optdesc opt_openssl_fips; #endif diff --git a/xioopts.c b/xioopts.c index 598e77a..b9cecc2 100644 --- a/xioopts.c +++ b/xioopts.c @@ -300,6 +300,9 @@ const struct optname optionnames[] = { #if WITH_EXT2 && defined(EXT2_COMPR_FL) IF_ANY ("compr", &opt_ext2_compr) #endif +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + IF_OPENSSL("compress", &opt_openssl_compress) +#endif #ifdef TCP_CONN_ABORT_THRESHOLD /* HP_UX */ IF_TCP ("conn-abort-threshold", &opt_tcp_conn_abort_threshold) #endif @@ -1049,6 +1052,9 @@ const struct optname optionnames[] = { IF_OPENSSL("openssl-capath", &opt_openssl_capath) IF_OPENSSL("openssl-certificate", &opt_openssl_certificate) IF_OPENSSL("openssl-cipherlist", &opt_openssl_cipherlist) +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + IF_OPENSSL("openssl-compress", &opt_openssl_compress) +#endif IF_OPENSSL("openssl-dhparam", &opt_openssl_dhparam) IF_OPENSSL("openssl-egd", &opt_openssl_egd) #if WITH_FIPS diff --git a/xioopts.h b/xioopts.h index 57609d2..d60b27a 100644 --- a/xioopts.h +++ b/xioopts.h @@ -477,6 +477,9 @@ enum e_optcode { OPT_OPENSSL_CAPATH, OPT_OPENSSL_CERTIFICATE, OPT_OPENSSL_CIPHERLIST, +#if OPENSSL_VERSION_NUMBER >= 0x00908000L + OPT_OPENSSL_COMPRESS, +#endif OPT_OPENSSL_DHPARAM, OPT_OPENSSL_EGD, OPT_OPENSSL_FIPS,