Re: [ietf-smtp] TLS1.3

Sam Varshavchik <> Wed, 06 May 2020 00:04 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 6CB933A0C6A for <>; Tue, 5 May 2020 17:04:00 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -1.9
X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=ham autolearn_force=no
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id 3Elu5u5aROZu for <>; Tue, 5 May 2020 17:03:58 -0700 (PDT)
Received: from ( []) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by (Postfix) with ESMTPS id 92FA73A0C68 for <>; Tue, 5 May 2020 17:03:58 -0700 (PDT)
Received: from ( [::ffff:]) (TLS: TLSv1.3,256bits,TLS_AES_256_GCM_SHA384) by with UTF8ESMTPS id 00000000002A0091.000000005EB1FEEB.0000671B; Tue, 05 May 2020 20:03:55 -0400
Received: from (localhost []) (IDENT: uid 1004) by with UTF8ESMTP id 000000000005E9B6.000000005EB1FEEA.0000D25A; Tue, 05 May 2020 20:03:54 -0400
References: <> <> <>
Message-ID: <>
From: Sam Varshavchik <>
Date: Tue, 05 May 2020 20:03:53 -0400
Mime-Version: 1.0
Content-Type: multipart/signed; boundary=""; micalg=pgp-sha1; protocol="application/pgp-signature"
Archived-At: <>
Subject: Re: [ietf-smtp] TLS1.3
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: "Discussion of issues related to Simple Mail Transfer Protocol \(SMTP\) \[RFC 821, RFC 2821, RFC 5321\]" <>
List-Unsubscribe: <>, <>
List-Archive: <>
List-Post: <>
List-Help: <>
List-Subscribe: <>, <>
X-List-Received-Date: Wed, 06 May 2020 00:04:01 -0000

Viktor Dukhovni writes:

> On Wed, Apr 29, 2020 at 06:02:13PM -0400, Sam Varshavchik wrote:
> > > It emits a fatal "unexpected message" alert.  Turn off the
> > > client-cert request, and it works fine.
> > >
> > > Name withheld to protect the guilty.
> >
> > I wouldn't necessarily blame the operator. It's almost a sure bet that
> > they're using OpenSSL, and this developer's opinion is that OpenSSL is one
> > of the worst, most confusing, and the most poorly documented API libraries
> > in existence.
> I are of course free to vent, but on this particular issue the
> frustration is misdirected.  The OpenSSL TLS client silently ignores
> solications for client certificates when none are configured at the
> client.  It is up to the server to then accept or deny the handshake.
> [ Rare applications that want to select certificates dynamically can
> register for a callback, but then the decisions are theirs to make. ]

So after reading the following:

# When a certificate was set using the SSL_CTX_use_certificate(3) family of  
# functions, it will be sent to the server. The TLS standard requires that  
# only a certificate is sent, if it matches the list of acceptable CAs sent by  
# the server. This constraint is violated by the default behavior of the  
# OpenSSL library.

This appeared to me like a client that was configured with a certificate for  
some other host, but sending it in error, here, and the server rejecting the  
connection for that reason.

Problematic TLS implementation have been quite common, historically. For a  
long time I was seeing frequently TLS-related misconfigurations with SMTP  
servers. I lost count how many servers I saw advertising STARTTLS but  
choking when taken up on their offer:

EHLO hythere Ok.
250 DSN
450 Not now, maybe someday.

At one point I had to carefully implement an adaptive, transient TLS  
blacklist: ignore the server's STARTTLS if you have a prior record of the  
server failing to negotiate a TLS connection.

> > Things
> > have reached the stage where it's been determined that the OpenSSL library
> > is reporting an error code that its own documentation describes thusly:
> >
> >           Some non-recoverable, fatal I/O error occurred. The OpenSSL error
> >           queue may contain more information on the error. For socket I/O  
> on
> >           Unix systems, consult errno for details.
> >
> > And, immediately after that, the code in question does "consult errno", and
> > finds that it's zero. No error has occured, according to the operating
> > system.
> This is likely TLS session termination without a TLS close notify, i.e.
> potentially vulnerable to truncation attacks.  An empty read on EOF does
> not set errno.  In OpenSSL 1.1.1e there was a fix for that, to make it
> an SSL_ERROR_SSL, but that turned to have too many backwards compatibility
> issues, so it got rolled back in 1.1.1f.

I did not find this called out in the documentation, just a blanket  
statement: check errno, here's your error. This behavior was observed not  
with an active connection, but in a middle of a handshake, inside  
SSL_accept. The connection failed with no other indication of what the issue  
was. I'm too lazy to dig into openssl's source, but it seems that SSL_accept  
uses BIO_read, where "no data was successfully read or written if the result  
is 0 or -1". Then the default BIO_should_retry returns an indication  
calculated as follows: the read or write failed with -1 and errno was EAGAIN  
or EWOULDBLOCK. A closed socket connection (obviously) does not meet that  

So, I surmised that SSL_accept was likely calling BIO_read, and if it  
returns 0 or -1 BIO_should_retry gets called; and SSL_ERROR_WANT_READ if so,  
otherwise it must be SSL_ERROR_SYSCALL. And in this sequence, I was getting  
an SSL_ERROR_SYSCALL but errno remained unset. I could not see how this was  
documented behavior, from any public documentation.