Package: ssmtp Version: 2.64-8+b2 Severity: normal Tags: patch upstream During a TLS1.3 session, the client may see "post-handshake new session tickets". The SSL_read() emulation done by GnuTLS returns a zero-byte read for this case, and ssmtp thinks the session has been hung up.
For instance, on my machine (the server here is fairly stock qpsmtpd on buster, but since this is a TLS feature presumably it's not uncommon): $ ssmtp -v p...@peff.net <msg [<-] 220 peff.net ESMTP qpsmtpd 0.94 ready; send us your mail, but not your spam. [->] EHLO sigill.intra.peff.net [<-] 250 AUTH CRAM-MD5 [->] STARTTLS [<-] 220 Go ahead with TLS [->] HELO sigill.intra.peff.net [<-] ssmtp: (sigill.intra.peff.net) The client just hangs up the first time it tries to read any data after setting up the TLS session, and no mail is sent. If I connect with openssl, I see: $ openssl s_client -connect mail.intra.peff.net:587 -starttls smtp [...] --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 [...] --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 [...] Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 [...] So there are two re-ticketing events (which openssl handles just fine). Curiously, if I replace the use of libgnutls-openssl in ssmtp with actual libssl, then everything works fine. But I think gnutls can also handle this case. What happens is that our SSL_read() call returns 0, and ssmtp assumes that's an EOF and the session is done. If I hack around it like this: diff --git a/ssmtp.c b/ssmtp.c index 7ab79ab..6b2b9d0 100644 --- a/ssmtp.c +++ b/ssmtp.c @@ -1291,8 +1291,12 @@ fd_getc() -- Read a character from an fd ssize_t fd_getc(int fd, void *c) { #ifdef HAVE_SSL - if(use_tls == True) { - return(SSL_read(ssl, c, 1)); + if(use_tls == True) { + int attempt = 3; + int ret = 0; + while (attempt-- > 0 && ret == 0) + ret = SSL_read(ssl, c, 1); + return ret; } #endif return(read(fd, c, 1)); then we're able to skip past the re-ticketing and everything works as expected. It's not clear to me if the problem is in the gnutls-openssl wrappers, or if ssmtp needs to be more careful about retrying reads. This is somewhat similar to the ancient bug #312146, which was about checking for SSL_ERROR_WANT_READ. But it is a bit different, in that ssmtp has no way of knowing whether it's a real EOF or not. And empirically, real openssl SSL_read() does not need any retries here (it's all handled transparently). So the patch above does work for me, but it's a pretty awful hack (and it's not at all clear that 3 is enough; it just works for my case). -- System Information: Debian Release: bullseye/sid APT prefers unstable APT policy: (500, 'unstable'), (500, 'testing'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 4.19.0-5-amd64 (SMP w/8 CPU cores) Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_WARN, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) Versions of packages ssmtp depends on: ii debconf [debconf-2.0] 1.5.72 ii libc6 2.28-10 ii libgnutls-openssl27 3.6.8-2 ssmtp recommends no packages. ssmtp suggests no packages. -- debconf information excluded