From 90d926f6f32dd3ff06e5c49e6a982777ead9f691 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Tue, 15 Sep 2015 16:48:29 +0200 Subject: Remove support for the Binary Content extension [RFC3516]. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “If the server does not know how to decode the section's CTE, it MUST fail the request and issue a "NO" response that contains the "UNKNOWN-CTE" extended response code.” — [RFC3516 section 4.3] Unfortunately the client doesn't know which message couldn't be decoded, so it can't fallback and use BODY instead. This made ‘use-binary=NO’ pretty much mandatory. Hence we remove support for BINARY [RFC3516]. Instead, we increase the thresold for when to add Zlib full flush points from 4096 to the buffer size (32768). --- Changelog | 4 ---- README | 5 ++--- interimap | 13 ++++--------- interimap.1 | 18 ++---------------- interimap.sample | 1 - lib/Net/IMAP/InterIMAP.pm | 39 +++++++++++---------------------------- 6 files changed, 19 insertions(+), 61 deletions(-) diff --git a/Changelog b/Changelog index 9d864f8..67f6833 100644 --- a/Changelog +++ b/Changelog @@ -6,10 +6,6 @@ interimap (0.2) upstream; server. * Add a configuration option 'null-stderr=YES' to send STDERR to /dev/null for type=tunnel. - * Add support for the Binary Content extension [RFC3516]. Enabled by - default if both the local and remote servers advertize "BINARY". - Can be disabled by adding 'use-binary=NO' to the default section in - the configuration file. * Exit with return value 0 when receiving a SIGTERM. * Add SSL options SINGLE_ECDH_USE, SINGLE_DH_USE, NO_SSLv2, NO_SSLv3 and NO_COMPRESSION to the compiled-in CTX options. diff --git a/README b/README index 2809ccb..bf2e052 100644 --- a/README +++ b/README @@ -28,9 +28,8 @@ extensions are: * LITERAL+ [RFC2088] non-synchronizing literals (recommended), * MULTIAPPEND [RFC3502] (recommended), * COMPRESS=DEFLATE [RFC4978] (recommended), - * SASL-IR [RFC4959] SASL Initial Client Response, - * UNSELECT [RFC3691], and - * BINARY [RFC3516]. + * SASL-IR [RFC4959] SASL Initial Client Response, and + * UNSELECT [RFC3691]. ####################################################################### diff --git a/interimap b/interimap index b3a7342..45a6643 100755 --- a/interimap +++ b/interimap @@ -74,7 +74,6 @@ my $CONF = read_config( delete $CONFIG{config} // $NAME , 'list-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/ , 'list-select-opts' => qr/\A([\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]+)\z/ , 'ignore-mailbox' => qr/\A([\x01-\x09\x0B\x0C\x0E-\x7F]+)\z/ - , 'use-binary' => qr/\A(YES|NO)\z/i, ); my ($DBFILE, $LOCKFILE, $LOGGER_FD); @@ -512,10 +511,7 @@ sub sync_mailbox_list() { sync_mailbox_list(); ($lIMAP, $rIMAP) = map {$IMAP->{$_}->{client}} qw/local remote/; -my $ATTRS = 'MODSEQ FLAGS INTERNALDATE '. - (((!defined $CONF->{_} or $CONF->{_}->{'use-binary'} // 1) and - !$lIMAP->incapable('BINARY') and !$rIMAP->incapable('BINARY')) - ? 'BINARY' : 'BODY').'.PEEK[]'; +my $ATTRS = join ' ', qw/MODSEQ FLAGS INTERNALDATE BODY.PEEK[]/; ############################################################################# @@ -600,7 +596,7 @@ sub download_missing($$$@) { my $attrs = $ATTRS.' ENVELOPE'; ($source eq 'local' ? $lIMAP : $rIMAP)->fetch(compact_set(@set), "($attrs)", sub($) { my $mail = shift; - return unless exists $mail->{RFC822} or exists $mail->{BINARY}; # not for us + return unless exists $mail->{RFC822}; # not for us my $uid = $mail->{UID}; my $from = first { defined $_ and @$_ } @{$mail->{ENVELOPE}}[2,3,4]; @@ -969,10 +965,9 @@ sub sync_known_messages($$) { # after the FETCH. sub callback_new_message($$$$;$$$) { my ($idx, $mailbox, $name, $mail, $UIDs, $buff, $bufflen) = @_; + return unless exists $mail->{RFC822}; # not for us - my $length = defined $mail->{RFC822} ? length(${$mail->{RFC822}}) - : defined $mail->{BINARY} ? length(${$mail->{BINARY}}) - : return; # not for us + my $length = length ${$mail->{RFC822}}; if ($length == 0) { msg("$name($mailbox)", "WARNING: Ignoring new 0-length message (UID $mail->{UID})"); return; diff --git a/interimap.1 b/interimap.1 index 621d968..d0c5474 100644 --- a/interimap.1 +++ b/interimap.1 @@ -304,18 +304,6 @@ Whether to redirect \fIcommand\fR's standard error to \(lq/dev/null\(rq for type \fItype\fR=tunnel. (Default: \(lqNO\(rq.) -.TP -.I use-binary -Whether to use the Binary Content extension [RFC3516] in FETCH and -APPEND commands. -This is useful for binary attachments for instance, as it avoids the -overhead caused by base64 encodings. Moreover if the IMAP COMPRESS -extension is enabled, full flush points are placed around large non-text -literals to empty the compression dictionary. -This option is only available in the default section, and is ignored if -either server does not advertize \(lqBINARY\(rq in its capability list. -(Default: \(lqYES\(rq.) - .TP .I SSL_cipher_list The cipher list to send to the server. Although the server determines @@ -364,11 +352,9 @@ MULTIAPPEND [RFC3502] (recommended), .IP \[bu] COMPRESS=DEFLATE [RFC4978] (recommended), .IP \[bu] -SASL-IR [RFC4959] SASL Initial Client Response, -.IP \[bu] -UNSELECT [RFC3691], and +SASL-IR [RFC4959] SASL Initial Client Response, and .IP \[bu] -BINARY [RFC3516]. +UNSELECT [RFC3691]. .SH KNOWN BUGS AND LIMITATIONS diff --git a/interimap.sample b/interimap.sample index 5d9d6d2..6d52f91 100644 --- a/interimap.sample +++ b/interimap.sample @@ -2,7 +2,6 @@ #list-mailbox = "*" list-select-opts = SUBSCRIBED ignore-mailbox = ^virtual/ -#use-binary = YES [local] type = tunnel diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 6f44879..a761614 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -772,9 +772,7 @@ sub remove_message($@) { # Issue an APPEND command with the given mails. Croak if the server # did not advertise "UIDPLUS" (RFC 4315) in its CAPABILITY list. # Each $mail is a hash reference with key 'RFC822' and optionally -# 'FLAGS' and 'INTERNALDATE'. If the server supports the "BINARY" -# extension (RFC 3516), the key 'RFC822' can be replaced with 'BINARY' -# to send the mail body as a binary literal. +# 'FLAGS' and 'INTERNALDATE'. # Providing multiple mails is only allowed for servers supporting # "MULTIAPPEND" (RFC 3502). # Return the list of UIDs allocated for the new messages. @@ -801,11 +799,8 @@ sub append($$@) { my $str = ' '; $str .= '('.join(' ', grep {lc $_ ne '\recent'} @{$mail->{FLAGS}}).') ' if defined $mail->{FLAGS}; $str .= '"'.$mail->{INTERNALDATE}.'" ' if defined $mail->{INTERNALDATE}; - my ($body, $t) = defined $mail->{RFC822} ? ($mail->{RFC822}, 0) - : defined $mail->{BINARY} ? ($mail->{BINARY}, 1) - : $self->panic("Missing message body in APPEND"); $self->_cmd_extend(\$str); - $self->_cmd_extend_lit($body, $t); + $self->_cmd_extend_lit($mail->{RFC822} // $self->panic("Missing message body in APPEND")); } $self->_cmd_flush(); @@ -1075,8 +1070,7 @@ sub pull_updates($;$) { # FETCH new messages since the UIDNEXT found in the persistent cache # (or 1 in no such UIDNEXT is found), and process each response on the # fly with the callback. -# The list of attributes to FETCH, $attr, much contain either BODY or -# BINARY. +# The list of attributes to FETCH, $attr, must contain BODY[]. # If an @ignore list is supplied, then these messages are ignored from # the UID FETCH range. # Finally, update the UIDNEXT from the persistent cache to the value @@ -1582,20 +1576,17 @@ sub _cmd_extend($$) { } -# $self->_cmd_extend_lit($lit, [$lit8]) +# $self->_cmd_extend_lit($lit) # Append the literal $lit to the command buffer. $lit must be a -# scalar reference. If $lit8 is true, a literal8 is sent instead [RFC -# 3516]. -sub _cmd_extend_lit($$;$) { - my ($self, $lit, $lit8) = @_; +# scalar reference. +sub _cmd_extend_lit($$) { + my ($self, $lit) = @_; my $len = length($$lit); my $d = $self->{_Z_DEFLATE}; - # create a full flush point for long binary literals - my $z_flush = ($len > 4096 and !($self->{'use-binary'} // 1 and !$lit8)) ? 1 : 0; - $lit8 = $lit8 ? '~' : ''; # literal8, RFC 3516 BINARY - - my $strlen = $lit8.'{'.$len.$self->{_LITPLUS}.'}'.$CRLF; + # create a full flush point for long literals, cf. RFC 4978 section 4 + my $z_flush = $len > $BUFSIZE ? 1 : 0; + my $strlen = "{$len$self->{_LITPLUS}}$CRLF"; if ($self->{_LITPLUS} ne '') { $self->_cmd_extend_(\$strlen); @@ -2086,14 +2077,6 @@ sub _resp($$;$$$) { elsif (s/\A(?:RFC822|BODY\[\]) //) { $mail{RFC822} = \$self->_nstring(\$_); } - elsif (s/\ABINARY\[\] //) { - if (s/\A~\{([0-9]+)\}\z//) { # literal8, RFC 3516 BINARY - (my $lit, $_) = $self->_getline($1); - $mail{BINARY} = $lit; - } else { - $mail{RFC822} = \$self->_nstring(\$_); - } - } elsif (s/\AFLAGS \((\\?$RE_ATOM_CHAR+(?: \\?$RE_ATOM_CHAR+)*)?\)//) { $mail{FLAGS} = defined $1 ? [ split / /, $1 ] : []; } @@ -2103,7 +2086,7 @@ sub _resp($$;$$$) { my $uid = $mail{UID} // $self->panic(); # sanity check $self->panic() unless defined $mail{MODSEQ} or !$self->_enabled('QRESYNC'); # sanity check - if (!exists $mail{RFC822} and !exists $mail{BINARY} and !exists $mail{ENVELOPE} and # ignore new mails + if (!exists $mail{RFC822} and !exists $mail{ENVELOPE} and # ignore new mails (!exists $self->{_MODIFIED}->{$uid} or $self->{_MODIFIED}->{$uid}->[0] < $mail{MODSEQ} or ($self->{_MODIFIED}->{$uid}->[0] == $mail{MODSEQ} and !defined $self->{_MODIFIED}->{$uid}->[1]))) { my $flags = join ' ', sort(grep {lc $_ ne '\recent'} @{$mail{FLAGS}}) if defined $mail{FLAGS}; -- cgit v1.2.3 From 40864537f86e31e037a1232f015a06e9d73bf1e6 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Tue, 15 Sep 2015 18:24:43 +0200 Subject: Don't set SO_KEEPALIVE on the socket. This is most likely useless in our case since the TCP keepalive time is usually much higher than the IMAP timeout. --- Changelog | 5 ++++- lib/Net/IMAP/InterIMAP.pm | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Changelog b/Changelog index 67f6833..cf11878 100644 --- a/Changelog +++ b/Changelog @@ -13,7 +13,7 @@ interimap (0.2) upstream; handshake. * Rename the 'SSL_verify_trusted_peer', 'SSL_ca_path', and 'SSL_cipher_list' options to 'SSL_CApath', 'SSL_verify' and - 'SSL_cipherlist', respectively. + 'SSL_cipherlist', respectively. * Add an option 'SSL_CAfile' to specify a file containing trusted certificates to use during server certificate authentication. * Replace IO::Socket::SSL dependency by the lower level Net::SSLeay. @@ -22,6 +22,9 @@ interimap (0.2) upstream; IPv6. (Both are core Perl module.) * Add a configuration option 'proxy' to proxy TCP connections to the IMAP server. + * Don't set SO_KEEPALIVE on the socket. This is most likely useless + in our case since the TCP keepalive time is usually much higher than + the IMAP timeout. -- Guilhem Moulin Wed, 09 Sep 2015 00:44:35 +0200 diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index a761614..a0be91e 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -26,7 +26,7 @@ use IO::Select (); use Net::SSLeay (); use List::Util 'first'; use POSIX ':signal_h'; -use Socket qw/SOL_SOCKET SO_KEEPALIVE SOCK_STREAM IPPROTO_TCP AF_INET AF_INET6 SOCK_RAW :addrinfo/; +use Socket qw/SOCK_STREAM IPPROTO_TCP AF_INET AF_INET6 SOCK_RAW :addrinfo/; use Exporter 'import'; BEGIN { @@ -289,7 +289,6 @@ sub new($%) { } my $socket = defined $self->{proxy} ? $self->_proxify(@$self{qw/proxy host port/}) : $self->_tcp_connect(@$self{qw/host port/}); - setsockopt($socket, SOL_SOCKET, SO_KEEPALIVE, 1) or $self->fail("Can't setsockopt SO_KEEPALIVE: $!"); $self->_start_ssl($socket) if $self->{type} eq 'imaps'; $self->{$_} = $socket for qw/STDOUT STDIN/; -- cgit v1.2.3 From 0e1e8e06debc4d7b00670eaa981ca5b382d90591 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 16 Sep 2015 16:49:00 +0200 Subject: Set X.509 certificate purpose to 'SSL Server' for SSL_verify=YES. --- Changelog | 1 + lib/Net/IMAP/InterIMAP.pm | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Changelog b/Changelog index cf11878..820ee6f 100644 --- a/Changelog +++ b/Changelog @@ -25,6 +25,7 @@ interimap (0.2) upstream; * Don't set SO_KEEPALIVE on the socket. This is most likely useless in our case since the TCP keepalive time is usually much higher than the IMAP timeout. + * Set X.509 certificate purpose to 'SSL Server' for SSL_verify=YES. -- Guilhem Moulin Wed, 09 Sep 2015 00:44:35 +0200 diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index a0be91e..53fddec 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -1398,6 +1398,8 @@ sub _start_ssl($$) { or $self->_ssl_error("Can't load verify locations"); } Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER()); + Net::SSLeay::CTX_set_purpose($ctx, Net::SSLeay::X509_PURPOSE_SSL_SERVER()) + or $self->_ssl_error("Can't set purpose"); } else { Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_NONE()); -- cgit v1.2.3 From cad0e125728658e4e899201e7cedc86036908057 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 16 Sep 2015 18:05:29 +0200 Subject: Display the certificate chain, SSL protocol and cipher in debug mode. --- Changelog | 2 ++ lib/Net/IMAP/InterIMAP.pm | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 820ee6f..79a7ea4 100644 --- a/Changelog +++ b/Changelog @@ -26,6 +26,8 @@ interimap (0.2) upstream; in our case since the TCP keepalive time is usually much higher than the IMAP timeout. * Set X.509 certificate purpose to 'SSL Server' for SSL_verify=YES. + * Display the certificate chain, SSL protocol and cipher in debug + mode. -- Guilhem Moulin Wed, 09 Sep 2015 00:44:35 +0200 diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index 53fddec..f54f239 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -1364,6 +1364,35 @@ sub _proxify($$$$) { } +# $self->_ssl_verify($self, $preverify_ok, $x509_ctx) +# SSL verify callback function, see +# https://www.openssl.org/docs/manmaster/ssl/SSL_CTX_set_verify.html +sub _ssl_verify($$$) { + my ($self, $ok, $x509_ctx) = @_; + return 0 unless $x509_ctx; # reject + + my $depth = Net::SSLeay::X509_STORE_CTX_get_error_depth($x509_ctx); + my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($x509_ctx) + or $self->_ssl_error("Can't get current certificate"); + if ($self->{debug}) { + $self->log("[$depth] preverify=$ok"); + $self->log(' Issuer Name: ', Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($cert))); + $self->log(' Subject Name: ', Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert))); + } + + if ($depth == 0) { + if ($self->{debug}) { + my $algo = 'sha256'; + my $type = Net::SSLeay::EVP_get_digestbyname($algo) + or $self->_ssl_error("Can't find MD value for name '$algo'"); + $self->log('Peer certificate fingerprint: ' + .$algo.'$'.unpack('H*', Net::SSLeay::X509_digest($cert, $type))); + } + } + return $ok; # 1=accept cert, 0=reject +} + + # $self->_start_ssl($socket) # Upgrade the $socket to SSL/TLS. sub _start_ssl($$) { @@ -1397,7 +1426,7 @@ sub _start_ssl($$) { Net::SSLeay::CTX_load_verify_locations($ctx, $file, $path) or $self->_ssl_error("Can't load verify locations"); } - Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER()); + Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER(), sub($$) {$self->_ssl_verify(@_)}); Net::SSLeay::CTX_set_purpose($ctx, Net::SSLeay::X509_PURPOSE_SSL_SERVER()) or $self->_ssl_error("Can't set purpose"); } @@ -1409,6 +1438,20 @@ sub _start_ssl($$) { Net::SSLeay::set_fd($ssl, fileno $socket) or $self->fail("SSL filehandle association failed"); $self->_ssl_error("Can't initiate TLS/SSL handshake") unless Net::SSLeay::connect($ssl) == 1; + if ($self->{debug}) { + my $v = Net::SSLeay::version($ssl); + $self->log(sprintf('SSL protocol: %s (0x%x)', ($v == 0x0002 ? 'SSLv2' : + $v == 0x0300 ? 'SSLv3' : + $v == 0x0301 ? 'TLSv1' : + $v == 0x0302 ? 'TLSv1.1' : + $v == 0x0303 ? 'TLSv1.2' : + '??'), + $v)); + $self->log(sprintf('SSL cipher: %s (%d bits)' + , Net::SSLeay::get_cipher($ssl) + , Net::SSLeay::get_cipher_bits($ssl))); + } + if (defined (my $fpr = $self->{SSL_fingerprint})) { # ensure we're talking to the right server (my $algo, $fpr) = $fpr =~ /^([^\$]+)\$(.*)/ ? ($1, $2) : ('sha256', $fpr); -- cgit v1.2.3 From 683a3973a32ee3618824d08ed7ee6cfc7ee9ab02 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Wed, 16 Sep 2015 18:28:10 +0200 Subject: Move SSL fingerprint verification to the the verify callback. --- interimap.1 | 4 ++-- lib/Net/IMAP/InterIMAP.pm | 43 ++++++++++++++++++++++--------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/interimap.1 b/interimap.1 index d0c5474..60493f3 100644 --- a/interimap.1 +++ b/interimap.1 @@ -317,8 +317,8 @@ Fingerprint of the server certificate (or its public key) in the form \fIALGO\fR$\fIDIGEST_HEX\fR, where \fIALGO\fR is the used algorithm (default \(lqsha256\(rq). Attempting to connect to a server with a non-matching certificate -fingerprint causes \fBInterIMAP\fR to abort the connection immediately -after the SSL/TLS handshake. +fingerprint causes \fBInterIMAP\fR to abort the connection during the +SSL/TLS handshake. .TP .I SSL_verify diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index f54f239..bf33294 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -1380,7 +1380,8 @@ sub _ssl_verify($$$) { $self->log(' Subject Name: ', Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($cert))); } - if ($depth == 0) { + $ok = 1 unless $self->{SSL_verify} // 1; + if ($depth == 0 and !exists $self->{_SSL_PEER_VERIFIED}) { if ($self->{debug}) { my $algo = 'sha256'; my $type = Net::SSLeay::EVP_get_digestbyname($algo) @@ -1388,6 +1389,21 @@ sub _ssl_verify($$$) { $self->log('Peer certificate fingerprint: ' .$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); + + my $type = Net::SSLeay::EVP_get_digestbyname($algo) + or $self->_ssl_error("Can't find MD value for name '$algo'"); + + if (Net::SSLeay::X509_digest($cert, $type) ne $digest and + Net::SSLeay::X509_pubkey_digest($cert, $type) ne $digest) { + $self->warn("Fingerprint doesn't match! MiTM in action?"); + $ok = 0; + } + } + $self->{_SSL_PEER_VERIFIED} = $ok; } return $ok; # 1=accept cert, 0=reject } @@ -1426,17 +1442,18 @@ sub _start_ssl($$) { Net::SSLeay::CTX_load_verify_locations($ctx, $file, $path) or $self->_ssl_error("Can't load verify locations"); } - Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER(), sub($$) {$self->_ssl_verify(@_)}); - Net::SSLeay::CTX_set_purpose($ctx, Net::SSLeay::X509_PURPOSE_SSL_SERVER()) - or $self->_ssl_error("Can't set purpose"); } else { - Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_NONE()); + Net::SSLeay::CTX_set_verify_depth($ctx, 0); } + Net::SSLeay::CTX_set_purpose($ctx, Net::SSLeay::X509_PURPOSE_SSL_SERVER()) + or $self->_ssl_error("Can't set purpose"); + Net::SSLeay::CTX_set_verify($ctx, Net::SSLeay::VERIFY_PEER(), sub($$) {$self->_ssl_verify(@_)}); my $ssl = Net::SSLeay::new($ctx) or $self->fail("Can't create new SSL structure"); Net::SSLeay::set_fd($ssl, fileno $socket) or $self->fail("SSL filehandle association failed"); $self->_ssl_error("Can't initiate TLS/SSL handshake") unless Net::SSLeay::connect($ssl) == 1; + $self->panic("Couldn't verify") unless $self->{_SSL_PEER_VERIFIED}; # sanity check if ($self->{debug}) { my $v = Net::SSLeay::version($ssl); @@ -1452,22 +1469,6 @@ sub _start_ssl($$) { , Net::SSLeay::get_cipher_bits($ssl))); } - if (defined (my $fpr = $self->{SSL_fingerprint})) { - # ensure we're talking to the right server - (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 $cert = Net::SSLeay::get_peer_certificate($ssl) - or $self->_ssl_error("Can't get peer certificate"); - - $self->fail("Fingerprint doesn't match! MiTM in action?") - if Net::SSLeay::X509_digest($cert, $type) ne $digest and - Net::SSLeay::X509_pubkey_digest($cert, $type) ne $digest; - } - @$self{qw/_SSL _SSL_CTX/} = ($ssl, $ctx); } -- cgit v1.2.3 From 612b9e2102e1907709dde325f91d5fdf70ed2534 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Thu, 17 Sep 2015 22:05:09 +0200 Subject: Use TCP keepalive to detect dead peers. --- Changelog | 3 --- interimap | 1 + lib/Net/IMAP/InterIMAP.pm | 20 ++++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Changelog b/Changelog index 79a7ea4..8cd8be2 100644 --- a/Changelog +++ b/Changelog @@ -22,9 +22,6 @@ interimap (0.2) upstream; IPv6. (Both are core Perl module.) * Add a configuration option 'proxy' to proxy TCP connections to the IMAP server. - * Don't set SO_KEEPALIVE on the socket. This is most likely useless - in our case since the TCP keepalive time is usually much higher than - the IMAP timeout. * Set X.509 certificate purpose to 'SSL Server' for SSL_verify=YES. * Display the certificate chain, SSL protocol and cipher in debug mode. diff --git a/interimap b/interimap index 45a6643..54ae0aa 100755 --- a/interimap +++ b/interimap @@ -248,6 +248,7 @@ foreach my $name (qw/local remote/) { $config{name} = $name; $config{'logger-fd'} = $LOGGER_FD if defined $LOGGER_FD; $config{'compress'} //= ($name eq 'local' ? 0 : 1); + $config{keepalive} = 1 if $CONFIG{watch} and $config{type} ne 'tunnel'; $IMAP->{$name} = { client => Net::IMAP::InterIMAP::->new(%config) }; my $client = $IMAP->{$name}->{client}; diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm index bf33294..d6c46a8 100644 --- a/lib/Net/IMAP/InterIMAP.pm +++ b/lib/Net/IMAP/InterIMAP.pm @@ -228,6 +228,9 @@ our $IMAP_text; # # - 'logger-fd': An optional filehandle to use for debug output. # +# - 'keepalive': Whether to enable sending of keep-alive messages. +# (type=imap or type=imaps). +# sub new($%) { my $class = shift; my $self = { @_ }; @@ -289,6 +292,23 @@ sub new($%) { } my $socket = defined $self->{proxy} ? $self->_proxify(@$self{qw/proxy host port/}) : $self->_tcp_connect(@$self{qw/host port/}); + my ($cnt, $intvl) = (3, 5); + if (defined $self->{keepalive}) { + # detect dead peers and drop the connection after 60 secs + $cnt*$intvl + setsockopt($socket, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1) + or $self->fail("Can't setsockopt SO_KEEPALIVE: $!"); + setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE, 60) + or $self->fail("Can't setsockopt TCP_KEEPIDLE: $!"); + setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT, $cnt) + or $self->fail("Can't setsockopt TCP_KEEPCNT: $!"); + setsockopt($socket, Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL, $intvl) + or $self->fail("Can't setsockopt TCP_KEEPINTVL: $!"); + } + # Abort after 15secs if write(2) isn't acknowledged + # XXX Socket::TCP_USER_TIMEOUT isn't defined. + # `grep TCP_USER_TIMEOUT /usr/include/linux/tcp.h` gives 18 + setsockopt($socket, Socket::IPPROTO_TCP, 18, 1000 * $cnt * $intvl) + or $self->fail("Can't setsockopt TCP_USER_TIMEOUT: $!"); $self->_start_ssl($socket) if $self->{type} eq 'imaps'; $self->{$_} = $socket for qw/STDOUT STDIN/; -- cgit v1.2.3