Added SNI support to OPENSSL-CONNECT, with options no-sni, snihost

This commit is contained in:
Gerhard Rieger 2020-12-31 14:30:04 +01:00
parent d109e3131b
commit aa2b9c00b2
9 changed files with 179 additions and 2 deletions

View file

@ -145,6 +145,12 @@ New features:
Added option ip-transparent (socket option IP_TRANSPARENT) Added option ip-transparent (socket option IP_TRANSPARENT)
Thanks to Wang Shanker for sending a patch. Thanks to Wang Shanker for sending a patch.
OPENSSL-CONNECT now automatically uses the SNI feature, option
openssl-no-sni turns it off. Option openssl-snihost overrides the value
of option openssl-commonname or the server name.
Tests: OPENSSL_SNI OPENSSL_NO_SNI
Thanks to Travis Burtrum for providing the initial patch
####################### V 1.7.3.4: ####################### V 1.7.3.4:
Corrections: Corrections:

View file

@ -508,6 +508,9 @@
/* Define if you have the OpenSSL SSL_CTX_clear_mode macro or function */ /* Define if you have the OpenSSL SSL_CTX_clear_mode macro or function */
#undef HAVE_SSL_CTX_clear_mode #undef HAVE_SSL_CTX_clear_mode
/* 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 flock function */ /* Define if you have the flock function */
#undef HAVE_FLOCK #undef HAVE_FLOCK

View file

@ -1484,6 +1484,7 @@ AC_CHECK_FUNC(DH_set0_pqg, AC_DEFINE(HAVE_DH_set0_pqg), AC_CHECK_LIB(crypt, DH_s
AC_CHECK_FUNC(ASN1_STRING_get0_data, AC_DEFINE(HAVE_ASN1_STRING_get0_data), AC_CHECK_LIB(crypt, ASN1_STRING_get0_data, [LIBS=-lcrypt $LIBS])) AC_CHECK_FUNC(ASN1_STRING_get0_data, AC_DEFINE(HAVE_ASN1_STRING_get0_data), AC_CHECK_LIB(crypt, ASN1_STRING_get0_data, [LIBS=-lcrypt $LIBS]))
AC_CHECK_FUNC(RAND_status, AC_DEFINE(HAVE_RAND_status)) 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_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_MSG_CHECKING(for type EC_KEY) AC_MSG_CHECKING(for type EC_KEY)
AC_CACHE_VAL(sc_cv_type_EC_TYPE, AC_CACHE_VAL(sc_cv_type_EC_TYPE,

View file

@ -2777,6 +2777,17 @@ label(OPTION_OPENSSL_COMMONNAME)dit(bf(tt(commonname=<string>)))
certificates commonname. This option has only meaning when option certificates commonname. This option has only meaning when option
link(verify)(OPTION_OPENSSL_VERIFY) is not disabled and the chosen cipher link(verify)(OPTION_OPENSSL_VERIFY) is not disabled and the chosen cipher
provides a peer certificate. provides a peer certificate.
label(OPTION_OPENSSL_NO_SNI)dit(bf(tt(no-sni=<bool>)))
Do not use the client side Server Name Indication (SNI) feature that selects
the desired server certificate.nl()
Note: SNI is automatically used since socat() version 1.7.4.0 and uses
link(commonname)(OPTION_OPENSSL_COMMONNAME) or the given host name.
label(OPTION_OPENSSL_SNIHOST)dit(bf(tt(snihost=<string>)))
Set the client side Server Name Indication (SNI) host name different from
the addressed server name or common name. This might be useful when the
server certificate has multiple host names or wildcard names because the
SNI host name is passed in cleartext to the server and might be eavesdropped;
with this option a mock name of the desired certificate may be transferred.
label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips))) label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips)))
Enables FIPS mode if compiled in. For info about the FIPS encryption Enables FIPS mode if compiled in. For info about the FIPS encryption
implementation standard see lurl(http://oss-institute.org/fips-faq.html). implementation standard see lurl(http://oss-institute.org/fips-faq.html).

97
test.sh
View file

@ -15,6 +15,7 @@ val_t=0.1
NUMCOND=true NUMCOND=true
#NUMCOND="test \$N -gt 70" #NUMCOND="test \$N -gt 70"
VERBOSE= VERBOSE=
FOREIGN=
while [ "$1" ]; do while [ "$1" ]; do
case "X$1" in case "X$1" in
X-t?*) val_t="${1#-t}" ;; X-t?*) val_t="${1#-t}" ;;
@ -25,6 +26,7 @@ while [ "$1" ]; do
X-N?*) NUMCOND="test \$N -gt ${1#-N}" ;; X-N?*) NUMCOND="test \$N -gt ${1#-N}" ;;
X-N) shift; NUMCOND="test \$N -ge $1" ;; X-N) shift; NUMCOND="test \$N -ge $1" ;;
X-C) rm -f testcert*.conf testcert.dh testcli*.* testsrv*.* ;; X-C) rm -f testcert*.conf testcert.dh testcli*.* testsrv*.* ;;
X-foreign) FOREIGN=1 ;; # allow access to 3rd party Internet hosts
*) break; *) break;
esac esac
shift shift
@ -14309,6 +14311,101 @@ esac
N=$((N+1)) N=$((N+1))
# Test the OpenSSL SNI feature
NAME=OPENSSL_SNI
case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: Test the OpenSSL SNI feature"
# Connect to a server that is known to use SNI. Use an SNI name, not the
# certifications default name. When the TLS connection is established
# the test succeeded.
SNISERVER=badssl.com
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))
listCANT="$listCANT $N"
elif ! feat=$(testoptions openssl-snihost); then
$PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ -z "$FOREIGN" ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}use test.sh option -foreign${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts FILE:/dev/null OPENSSL-CONNECT:$SNISERVER:443"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0"
rc0=$?
if [ $rc0 -eq 0 ]; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0" >&2
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# Test the openssl-no-sni option
NAME=OPENSSL_NO_SNI
case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: Test the openssl-no-sni option"
# Connect to a server that is known to use SNI. Use an SNI name, not the
# certifications default name, and use option openssl-no-sni.
# When the TLS connection failed the test succeeded.
# Please note that this test is only relevant when test OPENSSL_SNI succeeded.
SNISERVER=badssl.com
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))
listCANT="$listCANT $N"
elif ! feat=$(testoptions openssl-no-sni); then
$PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ -z "$FOREIGN" ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}use test.sh option -foreign${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts FILE:/dev/null OPENSSL-CONNECT:$SNISERVER:443,openssl-no-sni"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0"
rc0=$?
if [ $rc0 -ne 0 ]; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0" >&2
cat "${te}0" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
################################################################################## ##################################################################################
#================================================================================= #=================================================================================
# here come tests that might affect your systems integrity. Put normal tests # here come tests that might affect your systems integrity. Put normal tests

View file

@ -131,6 +131,10 @@ const struct optdesc opt_openssl_compress = { "openssl-compress", "compress
const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC }; const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC };
#endif #endif
const struct optdesc opt_openssl_commonname = { "openssl-commonname", "cn", OPT_OPENSSL_COMMONNAME, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC }; const struct optdesc opt_openssl_commonname = { "openssl-commonname", "cn", OPT_OPENSSL_COMMONNAME, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC };
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
const struct optdesc opt_openssl_no_sni = { "openssl-no-sni", "nosni", OPT_OPENSSL_NO_SNI, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC };
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. /* If FIPS is compiled in, we need to track if the user asked for FIPS mode.
@ -220,6 +224,8 @@ static int
bool opt_ver = true; /* verify peer certificate */ bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */ char *opt_cert = NULL; /* file name of client certificate */
const char *opt_commonname = NULL; /* for checking peer certificate */ const char *opt_commonname = NULL; /* for checking peer certificate */
bool opt_no_sni;
const char *opt_snihost = NULL; /* for SNI host */
int result; int result;
if (!(xioflags & XIO_MAYCONVERT)) { if (!(xioflags & XIO_MAYCONVERT)) {
@ -249,11 +255,27 @@ static int
retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert); retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert);
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname); retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
retropt_bool(opts, OPT_OPENSSL_NO_SNI, &opt_no_sni);
retropt_string(opts, OPT_OPENSSL_SNIHOST, (char **)&opt_snihost);
#endif
if (opt_commonname == NULL) { if (opt_commonname == NULL) {
opt_commonname = hostname; opt_commonname = strdup(hostname);
if (opt_commonname == NULL) {
Error1("strdup("F_Zu"): out of memory", strlen(hostname)+1);
}
} }
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
if (opt_snihost == NULL) {
opt_snihost = strdup(opt_commonname);
if (opt_snihost == NULL) {
Error1("strdup("F_Zu"): out of memory", strlen(opt_commonname)+1);
}
}
#endif
result = result =
_xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert, &ctx, (bool *)&use_dtls); _xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert, &ctx, (bool *)&use_dtls);
if (result != STAT_OK) return STAT_NORETRY; if (result != STAT_OK) return STAT_NORETRY;
@ -319,7 +341,8 @@ static int
return result; return result;
} }
result = _xioopen_openssl_connect(xfd, opt_ver, opt_commonname, ctx, level); result = _xioopen_openssl_connect(xfd, opt_ver, opt_commonname,
opt_no_sni, opt_snihost, ctx, level);
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
@ -376,6 +399,9 @@ static int
openssl_conn_loginfo(xfd->para.openssl.ssl); openssl_conn_loginfo(xfd->para.openssl.ssl);
free((void *)opt_commonname);
free((void *)opt_snihost);
/* fill in the fd structure */ /* fill in the fd structure */
return STAT_OK; return STAT_OK;
} }
@ -388,6 +414,8 @@ static int
int _xioopen_openssl_connect(struct single *xfd, int _xioopen_openssl_connect(struct single *xfd,
bool opt_ver, bool opt_ver,
const char *opt_commonname, const char *opt_commonname,
bool no_sni,
const char *snihost,
SSL_CTX *ctx, SSL_CTX *ctx,
int level) { int level) {
SSL *ssl; SSL *ssl;
@ -412,6 +440,17 @@ int _xioopen_openssl_connect(struct single *xfd,
return result; return result;
} }
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
if (!no_sni) {
if (!SSL_set_tlsext_host_name(ssl, snihost)) {
Error1("Failed to set SNI host \"%s\"", snihost);
sycSSL_free(xfd->para.openssl.ssl);
xfd->para.openssl.ssl = NULL;
return STAT_NORETRY;
}
}
#endif
result = xioSSL_connect(xfd, opt_commonname, opt_ver, level); result = xioSSL_connect(xfd, opt_commonname, opt_ver, level);
if (result != STAT_OK) { if (result != STAT_OK) {
sycSSL_free(xfd->para.openssl.ssl); sycSSL_free(xfd->para.openssl.ssl);

View file

@ -34,6 +34,8 @@ extern const struct optdesc opt_openssl_compress;
extern const struct optdesc opt_openssl_fips; extern const struct optdesc opt_openssl_fips;
#endif #endif
extern const struct optdesc opt_openssl_commonname; extern const struct optdesc opt_openssl_commonname;
extern const struct optdesc opt_openssl_no_sni;
extern const struct optdesc opt_openssl_snihost;
extern int extern int
_xioopen_openssl_prepare(struct opt *opts, struct single *xfd, _xioopen_openssl_prepare(struct opt *opts, struct single *xfd,
@ -42,6 +44,7 @@ extern int
extern int extern int
_xioopen_openssl_connect(struct single *xfd, bool opt_ver, _xioopen_openssl_connect(struct single *xfd, bool opt_ver,
const char *opt_commonname, const char *opt_commonname,
bool no_sni, const char *snihost,
SSL_CTX *ctx, int level); SSL_CTX *ctx, int level);
extern int extern int
_xioopen_openssl_listen(struct single *xfd, bool opt_ver, _xioopen_openssl_listen(struct single *xfd, bool opt_ver,

View file

@ -964,6 +964,9 @@ const struct optname optionnames[] = {
IF_SOCKET ("no-check", &opt_so_no_check) IF_SOCKET ("no-check", &opt_so_no_check)
#endif #endif
IF_TUN ("no-pi", &opt_iff_no_pi) IF_TUN ("no-pi", &opt_iff_no_pi)
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("no-sni", &opt_openssl_no_sni)
#endif
IF_TUN ("noarp", &opt_iff_noarp) IF_TUN ("noarp", &opt_iff_noarp)
#ifdef O_NOATIME #ifdef O_NOATIME
IF_OPEN ("noatime", &opt_o_noatime) IF_OPEN ("noatime", &opt_o_noatime)
@ -1000,6 +1003,9 @@ const struct optname optionnames[] = {
#ifdef SO_NOREUSEADDR /* AIX 4.3.3 */ #ifdef SO_NOREUSEADDR /* AIX 4.3.3 */
IF_SOCKET ("noreuseaddr", &opt_so_noreuseaddr) IF_SOCKET ("noreuseaddr", &opt_so_noreuseaddr)
#endif /* SO_NOREUSEADDR */ #endif /* SO_NOREUSEADDR */
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("nosni", &opt_openssl_no_sni)
#endif
IF_TUN ("notrailers", &opt_iff_notrailers) IF_TUN ("notrailers", &opt_iff_notrailers)
#ifdef O_NSHARE #ifdef O_NSHARE
IF_OPEN ("nshare", &opt_o_nshare) IF_OPEN ("nshare", &opt_o_nshare)
@ -1164,8 +1170,14 @@ const struct optname optionnames[] = {
#endif #endif
#if HAVE_SSL_set_min_proto_version || defined(SSL_set_min_proto_version) #if HAVE_SSL_set_min_proto_version || defined(SSL_set_min_proto_version)
IF_OPENSSL("openssl-min-proto-version", &opt_openssl_min_proto_version) IF_OPENSSL("openssl-min-proto-version", &opt_openssl_min_proto_version)
#endif
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("openssl-no-sni", &opt_openssl_no_sni)
#endif #endif
IF_OPENSSL("openssl-pseudo", &opt_openssl_pseudo) IF_OPENSSL("openssl-pseudo", &opt_openssl_pseudo)
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("openssl-snihost", &opt_openssl_snihost)
#endif
IF_OPENSSL("openssl-verify", &opt_openssl_verify) IF_OPENSSL("openssl-verify", &opt_openssl_verify)
IF_TERMIOS("opost", &opt_opost) IF_TERMIOS("opost", &opt_opost)
#if HAVE_TERMIOS_OSPEED #if HAVE_TERMIOS_OSPEED
@ -1434,6 +1446,9 @@ const struct optname optionnames[] = {
#ifdef SO_SNDLOWAT #ifdef SO_SNDLOWAT
IF_SOCKET ("sndlowat", &opt_so_sndlowat) IF_SOCKET ("sndlowat", &opt_so_sndlowat)
#endif #endif
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("snihost", &opt_openssl_snihost)
#endif
#ifdef SO_ACCEPTCONN /* AIX433 */ #ifdef SO_ACCEPTCONN /* AIX433 */
IF_SOCKET ("so-acceptconn", &opt_so_acceptconn) IF_SOCKET ("so-acceptconn", &opt_so_acceptconn)
#endif /* SO_ACCEPTCONN */ #endif /* SO_ACCEPTCONN */

View file

@ -490,7 +490,9 @@ enum e_optcode {
OPT_OPENSSL_MAX_PROTO_VERSION, OPT_OPENSSL_MAX_PROTO_VERSION,
OPT_OPENSSL_METHOD, OPT_OPENSSL_METHOD,
OPT_OPENSSL_MIN_PROTO_VERSION, OPT_OPENSSL_MIN_PROTO_VERSION,
OPT_OPENSSL_NO_SNI,
OPT_OPENSSL_PSEUDO, OPT_OPENSSL_PSEUDO,
OPT_OPENSSL_SNIHOST,
OPT_OPENSSL_VERIFY, OPT_OPENSSL_VERIFY,
OPT_OPOST, /* termios.c_oflag */ OPT_OPOST, /* termios.c_oflag */
OPT_OSPEED, /* termios.c_ospeed */ OPT_OSPEED, /* termios.c_ospeed */