Re: [ietf-smtp] TLS1.3

Sam Varshavchik <mrsam@courier-mta.com> Wed, 06 May 2020 00:04 UTC

Return-Path: <mrsam@courier-mta.com>
X-Original-To: ietf-smtp@ietfa.amsl.com
Delivered-To: ietf-smtp@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 6CB933A0C6A for <ietf-smtp@ietfa.amsl.com>; Tue, 5 May 2020 17:04:00 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.9
X-Spam-Level:
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 mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3Elu5u5aROZu for <ietf-smtp@ietfa.amsl.com>; Tue, 5 May 2020 17:03:58 -0700 (PDT)
Received: from mailx.courier-mta.com (mailx.courier-mta.com [68.166.206.83]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 92FA73A0C68 for <ietf-smtp@ietf.org>; Tue, 5 May 2020 17:03:58 -0700 (PDT)
Received: from monster.email-scan.com (monster.email-scan.com [::ffff:192.168.0.2]) (TLS: TLSv1.3,256bits,TLS_AES_256_GCM_SHA384) by www.courier-mta.com with UTF8ESMTPS id 00000000002A0091.000000005EB1FEEB.0000671B; Tue, 05 May 2020 20:03:55 -0400
Received: from monster.email-scan.com (localhost [127.0.0.1]) (IDENT: uid 1004) by monster.email-scan.com with UTF8ESMTP id 000000000005E9B6.000000005EB1FEEA.0000D25A; Tue, 05 May 2020 20:03:54 -0400
References: <3aab7ca5-27f1-1ba6-565f-650b67124fe8@wizmail.org> <cone.1588197733.557070.108041.1004@monster.email-scan.com> <20200505135020.GH76674@straasha.imrryr.org>
Message-ID: <cone.1588723433.721450.34142.1004@monster.email-scan.com>
X-Mailer: http://www.courier-mta.org/cone/
From: Sam Varshavchik <mrsam@courier-mta.com>
To: ietf-smtp@ietf.org
Date: Tue, 05 May 2020 20:03:53 -0400
Mime-Version: 1.0
Content-Type: multipart/signed; boundary="=_monster.email-scan.com-34142-1588723433-0001"; micalg=pgp-sha1; protocol="application/pgp-signature"
Archived-At: <https://mailarchive.ietf.org/arch/msg/ietf-smtp/blqL69f3H0unhKAUCzNoBkblgdw>
Subject: Re: [ietf-smtp] TLS1.3
X-BeenThere: ietf-smtp@ietf.org
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\]" <ietf-smtp.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/ietf-smtp>, <mailto:ietf-smtp-request@ietf.org?subject=unsubscribe>
List-Archive: <https://mailarchive.ietf.org/arch/browse/ietf-smtp/>
List-Post: <mailto:ietf-smtp@ietf.org>
List-Help: <mailto:ietf-smtp-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/ietf-smtp>, <mailto:ietf-smtp-request@ietf.org?subject=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:

220 poorly-configured-server.example.com ESMTP
EHLO hythere
250-poorly-configured-server.example.com Ok.
250-STARTTLS
250-PIPELINING
250-8BITMIME
250-SIZE
250 DSN
STARTTLS
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:
> >
> >       SSL_ERROR_SYSCALL
> >           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  
criteria.

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.