aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog6
-rw-r--r--doc/interimap.1.md12
-rw-r--r--doc/pullimap.1.md12
-rw-r--r--lib/Net/IMAP/InterIMAP.pm23
-rw-r--r--tests/tls-pin-fingerprint/t37
5 files changed, 75 insertions, 15 deletions
diff --git a/Changelog b/Changelog
index 341d5f7..e400b37 100644
--- a/Changelog
+++ b/Changelog
@@ -1,5 +1,11 @@
interimap (0.5.3) upstream;
+ * libinterimap: SSL_fingerprint now supports a space-separate list of
+ digests to pin, and succeeds if, and only if, the peer certificate
+ SPKI matches one of the pinned digest values. Specifying multiple
+ digest values can key useful in key rollover scenarios and/or when
+ the server supports certificates of different types (for instance
+ RSA+ECDSA).
- libinterimap: 'null-stderr' is now ignored when the 'debug' flag is
set (the standard error is never sent to /dev/null).
- test suite: use a RSA certificate rather than ECDSA.
diff --git a/doc/interimap.1.md b/doc/interimap.1.md
index c70698b..9b53a69 100644
--- a/doc/interimap.1.md
+++ b/doc/interimap.1.md
@@ -397,9 +397,10 @@ Valid options are:
*SSL_fingerprint*
-: Fingerprint of the server certificate's Subject Public Key Info, in
- the form `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm
- (by default `sha256`).
+: Space-separated list of acceptable fingerprints for the server
+ certificate's Subject Public Key Info, in the form
+ `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm (by default
+ `sha256`).
Attempting to connect to a server with a non-matching certificate
SPKI fingerprint causes `interimap` to abort the connection during
the SSL/TLS handshake.
@@ -410,6 +411,11 @@ Valid options are:
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256
+ Specifying multiple digest values can be useful in key rollover
+ scenarios and/or when the server supports certificates of different
+ types (for instance RSA+ECDSA). In that case the connection is
+ aborted when none of the specified digests matches.
+
*SSL_verify*
: Whether to verify the server certificate chain.
diff --git a/doc/pullimap.1.md b/doc/pullimap.1.md
index 87cafbf..2bc4212 100644
--- a/doc/pullimap.1.md
+++ b/doc/pullimap.1.md
@@ -216,9 +216,10 @@ Valid options are:
*SSL_fingerprint*
-: Fingerprint of the server certificate's Subject Public Key Info, in
- the form `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm
- (by default `sha256`).
+: Space-separated list of acceptable fingerprints for the server
+ certificate's Subject Public Key Info, in the form
+ `[ALGO$]DIGEST_HEX` where `ALGO` is the digest algorithm (by default
+ `sha256`).
Attempting to connect to a server with a non-matching certificate
SPKI fingerprint causes `pullimap` to abort the connection during
the SSL/TLS handshake.
@@ -229,6 +230,11 @@ Valid options are:
| openssl pkey -pubin -outform DER \
| openssl dgst -sha256
+ Specifying multiple digest values can be useful in key rollover
+ scenarios and/or when the server supports certificates of different
+ types (for instance RSA+ECDSA). In that case the connection is
+ aborted when none of the specified digests matches.
+
*SSL_verify*
: Whether to verify the server certificate chain.
diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm
index bd14625..1a71f59 100644
--- a/lib/Net/IMAP/InterIMAP.pm
+++ b/lib/Net/IMAP/InterIMAP.pm
@@ -63,7 +63,7 @@ my %OPTIONS = (
'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_fingerprint => qr/\A((?:[A-Za-z0-9]+\$)?\p{AHex}+)\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_verify => qr/\A(YES|NO)\z/i,
SSL_CApath => qr/\A(\P{Control}+)\z/,
@@ -1624,15 +1624,22 @@ sub _ssl_verify($$$) {
.$algo.'$'.unpack('H*', Net::SSLeay::X509_digest($cert, $type)));
}
- if (defined (my $fpr = $self->{SSL_fingerprint})) {
- (my $algo, $fpr) = $fpr =~ /^([^\$]+)\$(.*)/ ? ($1, $2) : ('sha256', $fpr);
- my $digest = pack 'H*', ($fpr =~ tr/://rd);
+ if (defined (my $fprs = $self->{SSL_fingerprint})) {
+ my $rv = 0;
+ foreach my $fpr (split /\s+/, $fprs) {
+ (my $algo, $fpr) = $fpr =~ /^([^\$]+)\$(.*)/ ? ($1, $2) : ('sha256', $fpr);
+ my $digest = pack 'H*', ($fpr =~ tr/://rd);
- my $type = Net::SSLeay::EVP_get_digestbyname($algo)
- or $self->_ssl_error("Can't find MD value for name '$algo'");
+ my $type = Net::SSLeay::EVP_get_digestbyname($algo)
+ or $self->_ssl_error("Can't find MD value for name '$algo'");
- my $pkey = Net::SSLeay::X509_get_X509_PUBKEY($cert);
- unless (defined $pkey and Net::SSLeay::EVP_Digest($pkey, $type) eq $digest) {
+ my $pkey = Net::SSLeay::X509_get_X509_PUBKEY($cert);
+ if (defined $pkey and Net::SSLeay::EVP_Digest($pkey, $type) eq $digest) {
+ $rv = 1;
+ last;
+ }
+ }
+ unless ($rv) {
$self->warn("Fingerprint doesn't match! MiTM in action?");
$ok = 0;
}
diff --git a/tests/tls-pin-fingerprint/t b/tests/tls-pin-fingerprint/t
index 612bc44..d3830e2 100644
--- a/tests/tls-pin-fingerprint/t
+++ b/tests/tls-pin-fingerprint/t
@@ -1,6 +1,8 @@
PKEY_SHA256="$(doveconf -c "$HOME_remote/.dovecot/config" -hx ssl_cert \
| openssl x509 -pubkey | openssl pkey -pubin -outform DER \
| openssl dgst -sha256 | sed -rn "/^.*=\\s*/ {s///p;q}")"
+INVALID_FPR="sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+INVALID_FPR2="sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee2"
# backup config
install -m0600 "$XDG_CONFIG_HOME/interimap/config" "$XDG_CONFIG_HOME/interimap/config~"
@@ -22,9 +24,28 @@ interimap_init
check_mailbox_status "INBOX"
+# with default algorithm (SHA256)
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR $PKEY_SHA256
+EOF
+interimap || error
+
+
# and now an invalid one
with_remote_config <<-EOF
- SSL_fingerprint = sha256\$deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+ SSL_fingerprint = $INVALID_FPR
+EOF
+! interimap --debug || error
+
+grep -Fx "remote: ERROR: Can't initiate TLS/SSL handshake" <"$STDERR" || error
+grep -Fx "remote: WARNING: Fingerprint doesn't match! MiTM in action?" <"$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
+
+# two invalid ones
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR $INVALID_FPR2
EOF
! interimap --debug || error
@@ -34,4 +55,18 @@ 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
+
+# valid + invalid
+with_remote_config <<-EOF
+ SSL_fingerprint = sha256\$$PKEY_SHA256 $INVALID_FPR
+EOF
+interimap || error
+
+
+# invalid + valid
+with_remote_config <<-EOF
+ SSL_fingerprint = $INVALID_FPR sha256\$$PKEY_SHA256
+EOF
+interimap || error
+
# vim: set filetype=sh :