From feeb91998a29ca040f6e5dd103e09507a6355e32 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Fri, 11 Dec 2020 18:39:46 +0100 Subject: libinterimap: deprecate SSL_protocols and introduce SSL_protocol_{min,max}. Using the libssl interface simplifies our protocol black/whitelist greatly; this only allows simple min/max bounds, but holes are arguably not very useful here. Using the new settings bumps the required libssl version to 1.1.0. --- Changelog | 8 +++++- doc/interimap.1.md | 9 +++++++ doc/pullimap.1.md | 9 +++++++ lib/Net/IMAP/InterIMAP.pm | 32 +++++++++++++++++++++--- tests/tls-protocols/t | 64 +++++++++++++++++++++++++++++++++++++++++++++-- tests/tls-rsa+ecdsa/t | 2 +- 6 files changed, 117 insertions(+), 7 deletions(-) diff --git a/Changelog b/Changelog index 01e272c..c2f60dc 100644 --- a/Changelog +++ b/Changelog @@ -5,6 +5,11 @@ interimap (0.5.5) upstream; Buster (OpenSSL 1.1.1) this does not make a difference, however using the system default provides better compatibility with future libssl versions. + * libinterimap: deprecate SSL_protocols, obsoleted by new settings + SSL_protocol_{min,max}. Using the libssl interface simplifies our + protocol black/whilelist greatly; this only allows simple min/max + bounds, but holes are arguably not very useful here. Using the new + settings bumps the required libssl version to 1.1.0. - libinterimap: make $OPENSSL_VERSION global. - libinterimap: use Net::SSLeay::get_version() to get the protocol version string. @@ -24,7 +29,8 @@ interimap (0.5.4) upstream; Subject Alternative Name (SAN) or Subject CommonName (CN) matches the hostname or IP literal specified by the 'host' option. Previously it was only checking the chain of trust. This bumps the minimum - Net::SSLeay version to 1.83 and OpenSSL version to 1.0.2. + Net::SSLeay version to 1.83 and OpenSSL version to 1.0.2 (when + SSL_verify is used). * libinterimap: add support for the TLS SNI (Server Name Indication) extension, controlled by the new 'SSL_hostname' option. The default value of that option is the value of the 'host' option when it is diff --git a/doc/interimap.1.md b/doc/interimap.1.md index 9cfec7a..9b14a49 100644 --- a/doc/interimap.1.md +++ b/doc/interimap.1.md @@ -389,6 +389,15 @@ Valid options are: Enabling a protocol is a short-hand for disabling all other protocols. + *Deprecacted*: Use *SSL_protocol_min* and/or *SSL_protocol_max* + instead. + +*SSL_protocol_min*, *SSL_protocol_max* + +: Set minimum resp. maximum SSL/TLS protocol version to use for the + connection. Accepted values are `SSLv3`, `TLSv1`, `TLSv1.1`, + `TLSv1.2`, and `TLSv1.3`. + *SSL_cipher_list* : The cipher list to send to the server. Although the server diff --git a/doc/pullimap.1.md b/doc/pullimap.1.md index 84cae46..028cbaa 100644 --- a/doc/pullimap.1.md +++ b/doc/pullimap.1.md @@ -208,6 +208,15 @@ Valid options are: Enabling a protocol is a short-hand for disabling all other protocols. + *Deprecacted*: Use *SSL_protocol_min* and/or *SSL_protocol_max* + instead. + +*SSL_protocol_min*, *SSL_protocol_max* + +: Set minimum resp. maximum SSL/TLS protocol version to use for the + connection. Accepted values are `SSLv3`, `TLSv1`, `TLSv1.1`, + `TLSv1.2`, and `TLSv1.3`. + *SSL_cipher_list* : The cipher list to send to the server. Although the server diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index e2b89ec..49ea343 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -63,7 +63,9 @@ my %OPTIONS = ( command => qr/\A(\P{Control}+)\z/, 'null-stderr' => qr/\A(YES|NO)\z/i, compress => qr/\A(YES|NO)\z/i, - SSL_protocols => qr/\A(!?$RE_SSL_PROTO(?: !?$RE_SSL_PROTO)*)\z/, + SSL_protocols => qr/\A(!?$RE_SSL_PROTO(?: !?$RE_SSL_PROTO)*)\z/, # TODO deprecated, remove in 0.6 + SSL_protocol_min => qr/\A(\P{Control}+)\z/, + SSL_protocol_max => qr/\A(\P{Control}+)\z/, SSL_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+(?: (?:[A-Za-z0-9]+\$)?\p{AHex}+)*)\z/, SSL_cipherlist => qr/\A(\P{Control}+)\z/, SSL_hostname => qr/\A(\P{Control}*)\z/, @@ -1676,7 +1678,7 @@ sub _ssl_verify($$$) { } my %SSL_proto; -BEGIN { +BEGIN { # TODO deprecated, remove in 0.6 sub _append_ssl_proto($$) { my ($k, $v) = @_; $SSL_proto{$k} = $v if defined $v; @@ -1689,6 +1691,15 @@ BEGIN { _append_ssl_proto( "TLSv1.3", eval { Net::SSLeay::OP_NO_TLSv1_3() } ); } +# see ssl/ssl_conf.c:protocol_from_string() in the OpenSSL source tree +my %SSL_protocol_versions = ( + "SSLv3" => eval { Net::SSLeay::SSL3_VERSION() } + , "TLSv1" => eval { Net::SSLeay::TLS1_VERSION() } + , "TLSv1.1" => eval { Net::SSLeay::TLS1_1_VERSION() } + , "TLSv1.2" => eval { Net::SSLeay::TLS1_2_VERSION() } + , "TLSv1.3" => eval { Net::SSLeay::TLS1_3_VERSION() } +); + # $self->_start_ssl($socket) # Upgrade the $socket to SSL/TLS. sub _start_ssl($$) { @@ -1703,7 +1714,22 @@ sub _start_ssl($$) { my $ssl_options = Net::SSLeay::OP_SINGLE_DH_USE() | Net::SSLeay::OP_SINGLE_ECDH_USE(); $ssl_options |= Net::SSLeay::OP_NO_COMPRESSION(); - if (defined (my $protos = $self->{SSL_protocols})) { + if (defined $self->{SSL_protocol_min} or defined $self->{SSL_protocol_max}) { + $self->panic("Failed requirement libssl >=1.1.0") if $OPENSSL_VERSION < 0x1010000f; + my ($min, $max) = @$self{qw/SSL_protocol_min SSL_protocol_max/}; + if (defined $min) { + my $v = $SSL_protocol_versions{$min} // $self->panic("Unknown protocol version: $min"); + $self->_ssl_error("CTX_set_min_proto_version()") unless Net::SSLeay::CTX_set_min_proto_version($ctx, $v) == 1; + $self->log("Minimum SSL/TLS protocol version: ", $min) if $self->{debug}; + } + if (defined $max) { + my $v = $SSL_protocol_versions{$max} // $self->panic("Unknown protocol version: $max"); + $self->_ssl_error("CTX_set_max_proto_version()") unless Net::SSLeay::CTX_set_max_proto_version($ctx, $v) == 1; + $self->log("Maximum SSL/TLS protocol version: ", $max) if $self->{debug}; + } + } elsif (defined (my $protos = $self->{SSL_protocols})) { # TODO deprecated, remove in 0.6 + $self->warn("SSL_protocols is deprecated and will be removed in a future release! ", + "Use SSL_protocol_{min,max} instead."); my ($proto_include, $proto_exclude) = (0, 0); foreach (split /\s+/, $protos) { my $neg = s/^!// ? 1 : 0; diff --git a/tests/tls-protocols/t b/tests/tls-protocols/t index 5444658..b65d93c 100644 --- a/tests/tls-protocols/t +++ b/tests/tls-protocols/t @@ -1,3 +1,10 @@ +# system default +interimap --debug || error +! grep -E "^remote: Disabling SSL protocols: " <"$STDERR" || error # TODO deprecated +! grep -E "^remote: Minimum SSL/TLS protocol version: " <"$STDERR" || error +! grep -E "^remote: Maximum SSL/TLS protocol version: " <"$STDERR" || error +grep -E "^remote: SSL protocol: TLSv" <"$STDERR" || error + # backup config install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~" with_remote_tls_protocols() { @@ -5,12 +12,15 @@ with_remote_tls_protocols() { printf "SSL_protocols = %s\\n" "$*" >>"$XDG_CONFIG_HOME/interimap/config" } -# disable TLSv1.2 and earlier versions +# disable TLSv1.2 and earlier with_remote_tls_protocols "!SSLv2" "!SSLv3" "!TLSv1" "!TLSv1.1" "!TLSv1.2" interimap --debug || error grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1, TLSv1.1, TLSv1.2" <"$STDERR" || error grep -E "^remote: SSL protocol: TLSv1\.3 " <"$STDERR" || error +interimap || error +grep -E "^remote: SSL_protocols is deprecated " <"$STDERR" || error "no deprecation message" + # force TLSv1.2 with_remote_tls_protocols "TLSv1.2" interimap --debug || error @@ -23,7 +33,7 @@ interimap --debug || error grep -Fx "remote: Disabling SSL protocols: SSLv3, TLSv1.3" <"$STDERR" || error grep -E "^remote: SSL protocol: TLSv(1\.[12])? " <"$STDERR" || error -# force SSLv2 and SSLv3, fails as it's disabled server side +# force SSLv2 and SSLv3; this fails due to dovecot's ssl_min_protocol=TLSv1 with_remote_tls_protocols "SSLv2" "SSLv3" ! interimap --debug || error grep -Fx "remote: Disabling SSL protocols: TLSv1, TLSv1.1, TLSv1.2, TLSv1.3" <"$STDERR" || error @@ -31,4 +41,54 @@ grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error # make sure we didn't send any credentials ! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error + +# new interface: SSL_protocol_{min,max} +with_remote_tls_protocol_min_max() { + install -m0600 "$XDG_CONFIG_HOME/interimap/config~" "$XDG_CONFIG_HOME/interimap/config" + if [ -n "${1-}" ]; then + printf "SSL_protocol_min = %s\\n" "$1" >>"$XDG_CONFIG_HOME/interimap/config" + fi + if [ -n "${2-}" ]; then + printf "SSL_protocol_max = %s\\n" "$2" >>"$XDG_CONFIG_HOME/interimap/config" + fi +} + +# disable TLSv1.2 and earlier +# XXX this test assumes that TLSv1.3 is the highest version supported +with_remote_tls_protocol_min_max "TLSv1.3" +interimap --debug || error +grep -Fx "remote: Minimum SSL/TLS protocol version: TLSv1.3" <"$STDERR" || error +! grep -E "^remote: Maximum SSL/TLS protocol version: " <"$STDERR" || error +grep -E "^remote: SSL protocol: TLSv1\.3 " <"$STDERR" || error + +# force TLSv1.2 +with_remote_tls_protocol_min_max "TLSv1.2" "TLSv1.2" +interimap --debug || error +grep -Fx "remote: Minimum SSL/TLS protocol version: TLSv1.2" <"$STDERR" || error +grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.2" <"$STDERR" || error +grep -E "^remote: SSL protocol: TLSv1\.2 " <"$STDERR" || error + +# disable TLSv1.2 and later +with_remote_tls_protocol_min_max "" "TLSv1.1" +interimap --debug || error +! grep -E "^remote: Minimum SSL/TLS protocol version: " <"$STDERR" || error +grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.1" <"$STDERR" || error +grep -E "^remote: SSL protocol: TLSv1\.1 " <"$STDERR" || error + +# force SSLv3 to to TLSv1.1 +with_remote_tls_protocol_min_max "SSLv3" "TLSv1.1" +interimap --debug || error +grep -Fx "remote: Minimum SSL/TLS protocol version: SSLv3" <"$STDERR" || error +grep -Fx "remote: Maximum SSL/TLS protocol version: TLSv1.1" <"$STDERR" || error +grep -E "^remote: SSL protocol: TLSv1(\.1)? " <"$STDERR" || error + +# force SSLv3; this fails due to dovecot's ssl_min_protocol=TLSv1 +with_remote_tls_protocol_min_max "SSLv3" "SSLv3" +! interimap --debug || error +grep -Fx "remote: Minimum SSL/TLS protocol version: SSLv3" <"$STDERR" || error +grep -Fx "remote: Maximum SSL/TLS protocol version: SSLv3" <"$STDERR" || error +grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error +# make sure we didn't send any credentials +! grep -E "^remote: C: .* (AUTHENTICATE|LOGIN) " <"$STDERR" || error + # vim: set filetype=sh : diff --git a/tests/tls-rsa+ecdsa/t b/tests/tls-rsa+ecdsa/t index 2adf930..8b811fd 100644 --- a/tests/tls-rsa+ecdsa/t +++ b/tests/tls-rsa+ecdsa/t @@ -38,7 +38,7 @@ grep -Fx -e "remote: Peer certificate matches pinned SPKI digest sha256\$$PKEY_S # force RSA (XXX do we really have to force TLSv1.2 here?) cat >>"$XDG_CONFIG_HOME/interimap/config" <<-EOF - SSL_protocols = TLSv1.2 + SSL_protocol_max = TLSv1.2 SSL_cipherlist = EECDH+AESGCM+aRSA EOF interimap --debug || error -- cgit v1.2.3