OpenSSL options min-version, max-version

This commit is contained in:
Gerhard Rieger 2020-12-27 20:25:10 +01:00
parent beed38655f
commit de2f2c471b
10 changed files with 248 additions and 23 deletions

View file

@ -45,6 +45,11 @@ Porting:
<linux/ext2_fs.h>. Now they are called fs-* and depend on <linux/fs.h>.
These fs-* options are also available on old systems with ext2_fs.h
New options openssl-min-proto-version (min-version) and
openssl-max-proto-version (max-version) give access to the related
OpenSSL set-macros and substitute deprecated version-specific methods.
Test: OPENSSL_MIN_VERSION
Testing:
test.sh now produces a list of tests that could not be performed for
any reason. This helps to analyse these cases.

2
RESEARCH Normal file
View file

@ -0,0 +1,2 @@
Applying an openssl-max-proto-version lower than openssl-min-proto-version results in an OpenSSL internal error only as late as during handshake (openssl-1.1.1-1ubuntu2.amd64)

View file

@ -464,6 +464,12 @@
#undef HAVE_DTLSv1_client_method
#undef HAVE_DTLSv1_server_method
/* Define if you have the SSL CTX_set_min_proto_version function/macro */
#undef HAVE_SSL_CTX_set_min_proto_version
/* Define if you have the SSL_CTX_set_max_proto_version function/macro */
#undef HAVE_SSL_CTX_set_max_proto_version
/* Define if you have the EC_KEY type */
#undef HAVE_TYPE_EC_KEY

View file

@ -1450,6 +1450,8 @@ AC_CHECK_FUNC(setenv, AC_DEFINE(HAVE_SETENV),
dnl Search for unsetenv()
AC_CHECK_FUNC(unsetenv, AC_DEFINE(HAVE_UNSETENV))
AC_CHECK_FUNC(SSL_CTX_set_min_proto_version, AC_DEFINE(HAVE_SSL_CTX_set_min_proto_version))
AC_CHECK_FUNC(SSL_CTX_set_max_proto_version, AC_DEFINE(HAVE_SSL_CTX_set_max_proto_version))
AC_CHECK_FUNC(TLS_client_method, AC_DEFINE(HAVE_TLS_client_method) ac_cv_have_tls_client_method=yes, AC_CHECK_LIB(crypt, TLS_client_method, [LIBS=-lcrypt $LIBS]))
AC_CHECK_FUNC(TLS_server_method, AC_DEFINE(HAVE_TLS_server_method) ac_cv_have_tls_server_method=yes, AC_CHECK_LIB(crypt, TLS_server_method, [LIBS=-lcrypt $LIBS]))
if test -n "$WITH_OPENSSL_METHOD" -o -z "$ac_cv_have_tls_client_method" -o -z "$ac_cv_have_tls_server_method" ; then

30
test.sh
View file

@ -12910,6 +12910,36 @@ N=$((N+1))
done
# test security of option openssl-set-min-proto-version
OPENSSL_LATEST_PROTO_VERSION=$(openssl s_server --help 2>&1 |grep -e -ssl[1-9] -e -tls[1-9] |awk '{print($1);}' |cut -c 2- |tr '[a-z_]' '[A-Z.]' |sort |tail -n 1)
OPENSSL_BEFORELAST_PROTO_VERSION=$(openssl s_server --help 2>&1 |grep -e -ssl[1-9] -e -tls[1-9] |awk '{print($1);}' |cut -c 2- |tr '[a-z_]' '[A-Z.]' |sort |tail -n 2 |head -n 1)
NAME=OPENSSL_MIN_VERSION
case "$TESTS" in
*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: security of OpenSSL server with openssl-min-proto-version"
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-min-proto-version); then
$PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! [ "$OPENSSL_LATEST_PROTO_VERSION" -a "$OPENSSL_BEFORELAST_PROTO_VERSION" -a \
"$OPENSSL_LATEST_PROTO_VERSION" != "$OPENSSL_BEFORELAST_PROTO_VERSION" ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}cannot determine two available SSL/TLS versions${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -4" "SSL-L:$PORT,pf=ip4,reuseaddr,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "openssl-min-proto-version=$OPENSSL_LATEST_PROTO_VERSION" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,$SOCAT_EGD,openssl-max-proto-version=$OPENSSL_BEFORELAST_PROTO_VERSION" 4 tcp $PORT -1
fi ;; # NUMCOND, $fets
esac
PORT=$((PORT+1))
N=$((N+1))
# Address options fdin and fdout were silently ignored when not applicable
# due to -u or -U option. Now these combinations are caught as errors.

View file

@ -104,6 +104,12 @@ const struct optdesc opt_openssl_cipherlist = { "openssl-cipherlist", "ciphers",
#if WITH_OPENSSL_METHOD
const struct optdesc opt_openssl_method = { "openssl-method", "method", OPT_OPENSSL_METHOD, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC };
#endif
#if HAVE_SSL_CTX_set_min_proto_version || defined(SSL_CTX_set_min_proto_version)
const struct optdesc opt_openssl_min_proto_version = { "openssl-min-proto-version", "min-version", OPT_OPENSSL_MIN_PROTO_VERSION, GROUP_OPENSSL, PH_INIT, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.openssl.min_proto_version) };
#endif
#if HAVE_SSL_CTX_set_max_proto_version || defined(SSL_CTX_set_max_proto_version)
const struct optdesc opt_openssl_max_proto_version = { "openssl-max-proto-version", "max-version", OPT_OPENSSL_MAX_PROTO_VERSION, GROUP_OPENSSL, PH_INIT, TYPE_STRING, OFUNC_OFFSET, XIO_OFFSETOF(para.openssl.max_proto_version) };
#endif
const struct optdesc opt_openssl_verify = { "openssl-verify", "verify", OPT_OPENSSL_VERIFY, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC };
const struct optdesc opt_openssl_certificate = { "openssl-certificate", "cert", OPT_OPENSSL_CERTIFICATE, GROUP_OPENSSL, PH_SPEC, TYPE_FILENAME, OFUNC_SPEC };
const struct optdesc opt_openssl_key = { "openssl-key", "key", OPT_OPENSSL_KEY, GROUP_OPENSSL, PH_SPEC, TYPE_FILENAME, OFUNC_SPEC };
@ -147,7 +153,15 @@ int xio_reset_fips_mode(void) {
#endif
static void openssl_conn_loginfo(SSL *ssl) {
Notice1("SSL connection using %s", SSL_get_cipher(ssl));
const char *string;
string = SSL_get_cipher_version(ssl);
Notice1("SSL proto version used: %s", string);
xiosetenv("OPENSSL_PROTO_VERSION", string, 1, NULL);
string = SSL_get_cipher(ssl);
Notice1("SSL connection using %s", string);
xiosetenv("OPENSSL_CIPHER", string, 1, NULL);
#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_COMP)
{
@ -706,6 +720,116 @@ static int openssl_setup_compression(SSL_CTX *ctx, char *method)
#endif
#if HAVE_CTX_SSL_set_min_proto_version || defined(SSL_CTX_set_min_proto_version) || \
HAVE_SSL_CTX_set_max_proto_version || defined(SSL_CTX_set_max_proto_version)
#define XIO_OPENSSL_VERSIONGROUP_TLS 1
#define XIO_OPENSSL_VERSIONGROUP_DTLS 2
static struct wordent _xio_openssl_versions[] = {
#ifdef DTLS1_VERSION
{ "DTLS1", (void *)DTLS1_VERSION },
{ "DTLS1.0", (void *)DTLS1_VERSION },
#endif
#ifdef DTLS1_2_VERSION
{ "DTLS1.2", (void *)DTLS1_2_VERSION },
#endif
#ifdef DTLS1_VERSION
{ "DTLSv1", (void *)DTLS1_VERSION },
{ "DTLSv1.0", (void *)DTLS1_VERSION },
#endif
#ifdef DTLS1_2_VERSION
{ "DTLSv1.2", (void *)DTLS1_2_VERSION },
#endif
#ifdef SSL2_VERSION
{ "SSL2", (void *)SSL2_VERSION },
#endif
#ifdef SSL3_VERSION
{ "SSL3", (void *)SSL3_VERSION },
#endif
#ifdef SSL2_VERSION
{ "SSLv2", (void *)SSL2_VERSION },
#endif
#ifdef SSL3_VERSION
{ "SSLv3", (void *)SSL3_VERSION },
#endif
#ifdef TLS1_VERSION
{ "TLS1", (void *)TLS1_VERSION },
{ "TLS1.0", (void *)TLS1_VERSION },
#endif
#ifdef TLS1_1_VERSION
{ "TLS1.1", (void *)TLS1_1_VERSION },
#endif
#ifdef TLS1_2_VERSION
{ "TLS1.2", (void *)TLS1_2_VERSION },
#endif
#ifdef TLS1_3_VERSION
{ "TLS1.3", (void *)TLS1_3_VERSION },
#endif
#ifdef TLS1_VERSION
{ "TLSv1", (void *)TLS1_VERSION },
{ "TLSv1.0", (void *)TLS1_VERSION },
#endif
#ifdef TLS1_1_VERSION
{ "TLSv1.1", (void *)TLS1_1_VERSION },
#endif
#ifdef TLS1_2_VERSION
{ "TLSv1.2", (void *)TLS1_2_VERSION },
#endif
#ifdef TLS1_3_VERSION
{ "TLSv1.3", (void *)TLS1_3_VERSION },
#endif
} ;
static int _xio_openssl_parse_version(const char *verstring, int vergroups) {
int sslver;
const struct wordent *we;
we = keyw(_xio_openssl_versions, verstring,
sizeof(_xio_openssl_versions)/sizeof(struct wordent));
if (we == 0) {
Error1("Unknown SSL/TLS version \"%s\"", verstring);
return -1;
}
sslver = (size_t)we->desc;
switch (sslver) {
#ifdef SSL2_VERSION
case SSL2_VERSION:
#endif
#ifdef SSL3_VERSION
case SSL3_VERSION:
#endif
#ifdef TLS1_VERSION
case TLS1_VERSION:
#endif
#ifdef TLS1_1_VERSION
case TLS1_1_VERSION:
#endif
#ifdef TLS1_2_VERSION
case TLS1_2_VERSION:
#endif
#ifdef TLS1_3_VERSION
case TLS1_3_VERSION:
#endif
if (!(vergroups & XIO_OPENSSL_VERSIONGROUP_TLS)) {
Error1("Wrong type of TLS/DTLS version \"%s\"", verstring);
return -1;
}
#ifdef DTLS1_VERSION
case DTLS1_VERSION:
#endif
#ifdef DTLS1_2_VERSION
case DTLS1_2_VERSION:
#endif
if (!(vergroups & XIO_OPENSSL_VERSIONGROUP_DTLS)) {
Error1("Wrong type of TLS/DTLS version \"%s\"", verstring);
return -1;
}
break;
}
return sslver;
}
#endif /* defined(SSL_CTX_set_min_proto_version) || defined(SSL_CTX_set_max_proto_version) */
int
_xioopen_openssl_prepare(struct opt *opts,
struct single *xfd,/* a xio file descriptor
@ -714,8 +838,9 @@ int
bool server, /* SSL client: false */
bool *opt_ver,
const char *opt_cert,
SSL_CTX **ctx)
SSL_CTX **ctxp)
{
SSL_CTX *ctx;
bool opt_fips = false;
const SSL_METHOD *method = NULL;
char *me_str = NULL; /* method string */
@ -910,7 +1035,7 @@ int
}
}
if ((*ctx = sycSSL_CTX_new(method)) == NULL) {
if ((ctx = sycSSL_CTX_new(method)) == NULL) {
if (ERR_peek_error() == 0) Error("SSL_CTX_new()");
while (err = ERR_get_error()) {
Error1("SSL_CTX_new(): %s", ERR_error_string(err, NULL));
@ -919,6 +1044,39 @@ int
/*ERR_clear_error;*/
return STAT_RETRYLATER;
}
xfd->para.openssl.ctx = ctx;
*ctxp = ctx;
#if HAVE_SSL_CTX_set_min_proto_version || defined(SSL_CTX_set_min_proto_version)
if (xfd->para.openssl.min_proto_version != NULL) {
int sslver, rc;
sslver = _xio_openssl_parse_version(xfd->para.openssl.min_proto_version,
XIO_OPENSSL_VERSIONGROUP_TLS|XIO_OPENSSL_VERSIONGROUP_DTLS);
if (sslver < 0)
return STAT_NORETRY;
if ((rc = SSL_CTX_set_min_proto_version(ctx, sslver)) <= 0) {
Debug1("version: %d", SSL_CTX_get_min_proto_version(ctx));
Error3("_xioopen_openssl_prepare(): SSL_CTX_set_min_proto_version(\"%s\"->%d): failed (%d)",
xfd->para.openssl.min_proto_version, sslver, rc);
return STAT_NORETRY;
}
Debug1("version: %d", SSL_CTX_get_min_proto_version(ctx));
}
#endif /* HAVE_SSL_set_min_proto_version || defined(SSL_set_min_proto_version) */
#if HAVE_SSL_CTX_set_max_proto_version || defined(SSL_CTX_set_max_proto_version)
if (xfd->para.openssl.max_proto_version != NULL) {
int sslver;
sslver = _xio_openssl_parse_version(xfd->para.openssl.max_proto_version,
XIO_OPENSSL_VERSIONGROUP_TLS|XIO_OPENSSL_VERSIONGROUP_DTLS);
if (sslver < 0)
return STAT_NORETRY;
if (SSL_CTX_set_max_proto_version(ctx, sslver) <= 0) {
Error2("_xioopen_openssl_prepare(): SSL_CTX_set_max_proto_version(\"%s\"->%d): failed",
xfd->para.openssl.max_proto_version, sslver);
return STAT_NORETRY;
}
}
#endif /* HAVE_SSL_set_max_proto_version || defined(SSL_set_max_proto_version) */
{
static unsigned char dh2048_p[] = {
@ -976,12 +1134,12 @@ int
dh->p = p;
dh->g = g;
#endif /* HAVE_DH_set0_pqg */
if (sycSSL_CTX_set_tmp_dh(*ctx, dh) <= 0) {
if (sycSSL_CTX_set_tmp_dh(ctx, dh) <= 0) {
while (err = ERR_get_error()) {
Warn3("SSL_CTX_set_tmp_dh(%p, %p): %s", *ctx, dh,
Warn3("SSL_CTX_set_tmp_dh(%p, %p): %s", ctx, dh,
ERR_error_string(err, NULL));
}
Error2("SSL_CTX_set_tmp_dh(%p, %p) failed", *ctx, dh);
Error2("SSL_CTX_set_tmp_dh(%p, %p) failed", ctx, dh);
}
/* p & g are freed by DH_free() once attached */
DH_free(dh);
@ -1009,14 +1167,14 @@ cont_out:
return -1;
}
SSL_CTX_set_tmp_ecdh(*ctx, ecdh);
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
}
#endif /* HAVE_TYPE_EC_KEY */
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
if (opt_compress) {
int result;
result = openssl_setup_compression(*ctx, opt_compress);
result = openssl_setup_compression(ctx, opt_compress);
if (result != STAT_OK) {
return result;
}
@ -1028,17 +1186,17 @@ cont_out:
Without correction socat might hang in SSL_read() */
{
long mode = 0;
mode = SSL_CTX_get_mode(*ctx);
mode = SSL_CTX_get_mode(ctx);
if (mode & SSL_MODE_AUTO_RETRY) {
Info("SSL_CTX mode has SSL_MODE_AUTO_RETRY set. Correcting..");
Debug1("SSL_CTX_clean_mode(%p, SSL_MODE_AUTO_RETRY)", *ctx);
SSL_CTX_clear_mode(*ctx, SSL_MODE_AUTO_RETRY);
Debug1("SSL_CTX_clean_mode(%p, SSL_MODE_AUTO_RETRY)", ctx);
SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY);
}
}
#endif /* defined(HAVE_SSL_CTX_clear_mode) || defined(SSL_CTX_clear_mode) */
if (opt_cafile != NULL || opt_capath != NULL) {
if (sycSSL_CTX_load_verify_locations(*ctx, opt_cafile, opt_capath) != 1) {
if (sycSSL_CTX_load_verify_locations(ctx, opt_cafile, opt_capath) != 1) {
int result;
if ((result =
@ -1050,7 +1208,7 @@ cont_out:
}
#ifdef HAVE_SSL_CTX_set_default_verify_paths
} else {
SSL_CTX_set_default_verify_paths(*ctx);
SSL_CTX_set_default_verify_paths(ctx);
#endif
}
@ -1058,12 +1216,12 @@ cont_out:
BIO *bio;
DH *dh;
if (sycSSL_CTX_use_certificate_chain_file(*ctx, opt_cert) <= 0) {
if (sycSSL_CTX_use_certificate_chain_file(ctx, opt_cert) <= 0) {
/*! trace functions */
/*0 ERR_print_errors_fp(stderr);*/
if (ERR_peek_error() == 0)
Error2("SSL_CTX_use_certificate_file(%p, \"%s\", SSL_FILETYPE_PEM) failed",
*ctx, opt_cert);
ctx, opt_cert);
while (err = ERR_get_error()) {
Error1("SSL_CTX_use_certificate_file(): %s",
ERR_error_string(err, NULL));
@ -1071,7 +1229,7 @@ cont_out:
return STAT_RETRYLATER;
}
if (sycSSL_CTX_use_PrivateKey_file(*ctx, opt_key?opt_key:opt_cert, SSL_FILETYPE_PEM) <= 0) {
if (sycSSL_CTX_use_PrivateKey_file(ctx, opt_key?opt_key:opt_cert, SSL_FILETYPE_PEM) <= 0) {
/*ERR_print_errors_fp(stderr);*/
openssl_SSL_ERROR_SSL(E_ERROR/*!*/, "SSL_CTX_use_PrivateKey_file");
return STAT_RETRYLATER;
@ -1088,12 +1246,12 @@ cont_out:
Info1("PEM_read_bio_DHparams(%p, NULL, NULL, NULL): error", bio);
} else {
BIO_free(bio);
if (sycSSL_CTX_set_tmp_dh(*ctx, dh) <= 0) {
if (sycSSL_CTX_set_tmp_dh(ctx, dh) <= 0) {
while (err = ERR_get_error()) {
Warn3("SSL_CTX_set_tmp_dh(%p, %p): %s", *ctx, dh,
Warn3("SSL_CTX_set_tmp_dh(%p, %p): %s", ctx, dh,
ERR_error_string(err, NULL));
}
Error2("SSL_CTX_set_tmp_dh(%p, %p): error", *ctx, dh);
Error2("SSL_CTX_set_tmp_dh(%p, %p): error", ctx, dh);
}
}
}
@ -1102,7 +1260,7 @@ cont_out:
/* set pre openssl-connect options */
/* SSL_CIPHERS */
if (ci_str != NULL) {
if (sycSSL_CTX_set_cipher_list(*ctx, ci_str) <= 0) {
if (sycSSL_CTX_set_cipher_list(ctx, ci_str) <= 0) {
if (ERR_peek_error() == 0)
Error1("SSL_set_cipher_list(, \"%s\") failed", ci_str);
while (err = ERR_get_error()) {
@ -1115,11 +1273,11 @@ cont_out:
}
if (*opt_ver) {
sycSSL_CTX_set_verify(*ctx,
sycSSL_CTX_set_verify(ctx,
SSL_VERIFY_PEER| SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
} else {
sycSSL_CTX_set_verify(*ctx,
sycSSL_CTX_set_verify(ctx,
SSL_VERIFY_NONE,
NULL);
}

View file

@ -15,6 +15,8 @@ extern const struct addrdesc addr_openssl_listen;
extern const struct optdesc opt_openssl_cipherlist;
extern const struct optdesc opt_openssl_method;
extern const struct optdesc opt_openssl_min_proto_version;
extern const struct optdesc opt_openssl_max_proto_version;
extern const struct optdesc opt_openssl_verify;
extern const struct optdesc opt_openssl_certificate;
extern const struct optdesc opt_openssl_key;

8
xio.h
View file

@ -229,8 +229,14 @@ typedef struct single {
#if WITH_OPENSSL
struct {
struct timeval connect_timeout; /* how long to hang in connect() */
SSL_CTX* ctx; /* for freeing on close */
SSL *ssl;
SSL_CTX* ctx;
#if HAVE_SSL_CTX_set_min_proto_version || defined(SSL_CTX_set_min_proto_version)
char *min_proto_version;
#endif
#if HAVE_SSL_CTX_set_max_proto_version || defined(SSL_CTX_set_max_proto_version)
char *max_proto_version;
#endif
} openssl;
#endif /* WITH_OPENSSL */
#if WITH_TUN

View file

@ -904,6 +904,9 @@ const struct optname optionnames[] = {
#endif
IF_TUN ("master", &opt_iff_master)
IF_LISTEN ("max-children", &opt_max_children)
#if HAVE_SSL_set_max_proto_version || defined(SSL_set_max_proto_version)
IF_OPENSSL("max-version", &opt_openssl_max_proto_version)
#endif
IF_LISTEN ("maxchildren", &opt_max_children)
#ifdef TCP_MAXSEG
IF_TCP ("maxseg", &opt_tcp_maxseg)
@ -919,6 +922,9 @@ const struct optname optionnames[] = {
IF_OPENSSL("method", &opt_openssl_method)
#endif
IF_TERMIOS("min", &opt_vmin)
#if HAVE_SSL_set_min_proto_version || defined(SSL_set_min_proto_version)
IF_OPENSSL("min-version", &opt_openssl_min_proto_version)
#endif
IF_ANY ("mode", &opt_perm)
#ifdef TCP_MAXSEG
IF_TCP ("mss", &opt_tcp_maxseg)
@ -1147,8 +1153,14 @@ const struct optname optionnames[] = {
IF_OPENSSL("openssl-fips", &opt_openssl_fips)
#endif
IF_OPENSSL("openssl-key", &opt_openssl_key)
#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 WITH_OPENSSL_METHOD
IF_OPENSSL("openssl-method", &opt_openssl_method)
#endif
#if HAVE_SSL_set_min_proto_version || defined(SSL_set_min_proto_version)
IF_OPENSSL("openssl-min-proto-version", &opt_openssl_min_proto_version)
#endif
IF_OPENSSL("openssl-pseudo", &opt_openssl_pseudo)
IF_OPENSSL("openssl-verify", &opt_openssl_verify)

View file

@ -481,7 +481,9 @@ enum e_optcode {
OPT_OPENSSL_EGD,
OPT_OPENSSL_FIPS,
OPT_OPENSSL_KEY,
OPT_OPENSSL_MAX_PROTO_VERSION,
OPT_OPENSSL_METHOD,
OPT_OPENSSL_MIN_PROTO_VERSION,
OPT_OPENSSL_PSEUDO,
OPT_OPENSSL_VERIFY,
OPT_OPOST, /* termios.c_oflag */