[exim] Re: TLS SNI possibly breaking "verify = certificate"

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Mark Hills
Datum:  
To: Jeremy Harris
CC: exim-users
Betreff: [exim] Re: TLS SNI possibly breaking "verify = certificate"
On Sun, 28 Apr 2024, Jeremy Harris via Exim-users wrote:

> On 28/04/2024 22:32, Mark Hills via Exim-users wrote:
> > "verify = certificate" no longer passes after some system upgrade.
>
> Some? What, precisely?


Sadly I don't have the information. Being pushed for time, I blitzed a
series of upgrades, inclulding FreeBSD to 13.2 (Exim smarthost) and Alpine
Linux 3.18->3.19 (client). So both client and server changed OS, and then
the version of their respective mailers.

> > My systems running OpenSMTPD submit mail to an Exim smarthost.
> >
> > Authentication used "verify = certificate", then checks for known
> > certificate fingerprint. This has worked for many years. Extracts from
> > configuration at the end of this mail.
> >
> > But now "verify = certificate" no longer passes in _one_ case (others are
> > fine), which becomes my test case:
> >
> > - client: opensmtpd 7.4.0p1-r1 (Alpine Linux 3.19.1)
> >
> > - smarthost: exim 4.97.1_4 (FreeBSD 13.2-RELEASE-p11)
> >
> > The client certificate has not been changed.
> >
> > I also tested copying over the key + cert (and HELO identity) from a
> > working system, and it did not work on this system.
> >
> > Using exim's +all logging is not very insightful; the result of a 'diff'
> > is that authentication simply disappeared:
> >
> > SSL SSL_accept,state_chg: SSLv3/TLS write finished
> > SSL SSL_accept,state_chg: TLSv1.3 early data
> > SSL SSL_accept,state_chg: TLSv1.3 early data
> > -SSL authenticated verify ok: depth=0 SN=/C=GB/L=London/CN=xxxxxx
> > SSL SSL_accept,state_chg: SSLv3/TLS read client certificate
> > -SSL SSL_accept,state_chg: SSLv3/TLS read certificate verify
> >
> > Further up the logs, is the only tangible difference I can see is SNI
> > being sent by the (newer) OpenSMTPd client:
> >
> > SSL SSL_accept,state_chg: before SSL initialization
> > SSL SSL_accept,state_chg: before SSL initialization
> > +Received TLS SNI "mail.xxxxxxxxxx.uk" (unused for certificate selection)
> > SSL SSL_accept,state_chg: SSLv3/TLS read client hello
> >
> > The only other differences appear to be secrets, hostnames, PIDs, memory
> > addresses etc..
> >
> > So where to go from here:
> >
> > 1) sending SNI is breaking "verify = certificate" at Exim? or
>
> Unlikely, on it's own
>
> > 2) some other TLS change which is invisible in the Exim log; or
>
> I'd go for this
>
>
> Lets log some more stuff.
>
> a new main-section option:
>
> event_action = ${acl {tls_inbound_event}}
>
>
> and new ACLs:
>
> # 2 args: name that cert should apply for, name of cert variable
> # DO NOT CALL with unsafe data for arg2
> is_certname_verify:
>   accept set acl_m_tmp = \${certextract {subj_altname,dns}{\$$acl_arg2}}
>          set acl_m_tmp = ${expand:$acl_m_tmp}
>          condition =    ${if def:acl_m_tmp}
>          endpass
>          acl =          is_c_altname_v $acl_arg1 ${lc:$acl_m_tmp}
>          set acl_m_tmp =
>   accept set acl_m_tmp = \${certextract {subject,CN}{\$$acl_arg2}}
>          acl =          is_name_match $acl_arg1 ${lc:${expand:$acl_m_tmp}}
>          set acl_m_tmp =
>   deny   set acl_m_tmp =
> #         logwrite =    $event_name $acl_arg1: name DOES NOT verify

>
>  inbound_user_cert:
>    accept set acl_m_tmp = ${certextract {subj_altname,mail}
> {$tls_in_peercert}}
>         condition =    ${if def:acl_m_tmp}
>         logwrite =     potential user cert <$acl_m_tmp>
>         set acl_m_tmp =

>
> tls_inbound_event:
>   accept condition =    ${if !eq {tls:cert} {$event_name}}

>
>   # cert logging
>   warn   logwrite =     [$sender_host_address] $sender_host_name \
>                         $event_name depth=$event_data \
>                         <${certextract {subject} {$tls_in_peercert}}>\
>                         ${if ={0}{$event_data} \
>                           { <${certextract
> {subj_altname}{$tls_in_peercert}}>}}
>   accept condition =    ${if !={0}{$event_data}}
>   accept acl =          inbound_user_cert
>   deny   condition =    ${if !def:sender_host_name}
>          logwrite =     [$sender_host_address] no rDNS - can't verify
> client-cert
>          message =      fail
>   deny   !acl =         is_certname_verify ${lc:$sender_host_name}
> tls_in_peercert
>          logwrite =     [$sender_host_address] $sender_host_name client-cert
> name mismmatch; try relaxed-rDNS names
>          !condition =   ${if forany {${lookup dnsdb{>:
> ptr=$sender_host_address}}} \
>                             {and {{!eqi {$sender_host_name}{$item}} \
>                                   {acl{{is_certname_verify}{${lc:$item}}{tls_in_peercert}}}
> \
>                          }  }    }
> #        message =      client-cert hostname mismatch
>          logwrite =     [$sender_host_address] client-cert hostname mismatch
>   accept logwrite =     [$sender_host_address] $sender_host_name client-cert
> relaxed-rDNS name ok


Much of the syntax above is unfamiliar to me, so testing this and I'm
faced with this error from the 'good' client (but it does continue to a
successful delivery):

2024-05-12 13:12:49 [x.x.x.x] xxxx.cable.virginm.net tls:cert depth=0 <CN=xxxx,L=London,C=GB> <>
2024-05-12 13:12:49 failed to expand event_action tls:cert in main: ERROR from acl "tls_inbound_event"
2024-05-12 13:12:49 accepting message from certified connection 702B4B
2024-05-12 13:12:49 1s684P-00000000Ln7-2MVg accepting message content from certified connection

But... what's interesting is that the "failed to expand" error is not
present at all when sending mail from the bad client.

So I took that to mean some part of the whole TLS process was not
happening with the bad client.

Yet, from the client's point of view it appeared to be a sercured
connection:

May 12 13:00:27 tamla mail.info smtpd[14968]: 9ffd7c651047ce54 mta connecting address=smtp+tls://x.x.x.x:587 host=mail.xxxxxxxx.uk
May 12 13:00:27 tamla mail.info smtpd[14968]: 9ffd7c651047ce54 mta connected
May 12 13:00:27 tamla mail.info smtpd[14968]: 9ffd7c651047ce54 mta tls ciphers=TLSv1.3:TLS_AES_256_GCM_SHA384:256
May 12 13:00:27 tamla mail.info smtpd[14968]: 9ffd7c651047ce54 mta cert-check result="valid" fingerprint="SHA256:27cda0b7xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
May 12 13:00:28 tamla mail.info smtpd[14968]: 9ffd7c651047ce54 mta delivery evpid=0651daf188b89719 from=<mark@???> to=<xxxxxx@???> rcpt=<-> source="x.x.x.x" relay="x.x.x.x (mail.xxxxxxxx.uk)" delay=1s result="PermFail" stat="550 relay not permitted"

I believe I've found my problem, which is that the client connection is
encrypted with TLS but not 'certified' in some way.

This may be off-topic for Exim list, but for completeness, here are the
relevant fragments of configuration in OpenSMTPd:

pki myname key "/etc/ssl/local.key"
pki myname cert "/etc/ssl/self.crt"

# Outbound HELO must match our SSL certificate
action "relay" relay host smtp+tls://mail.xxxxxxxx.uk:587 helo "myname"

And changing it to:

action "relay" relay host smtp+tls://mail.xxxxxxxx.uk:587 pki myname helo "myname"

got things working again. So it seems this used to be implicit in earlier
versions (my own comment left many years ago also suggests this) and now
must be explicitly stated.

So I think the summary is:

* Upgraded OpenSMTPd at the client no longer offers its certificate
without "pki" keyword

* The use of SNI appearing in Exim log is probably accurate, but unrelated
side effect of the client change

Many thanks for your assistance which helped me find this.

--
Mark

--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-users.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-users-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/