Mailing List Archive

TLS SNI possibly breaking "verify = certificate"
"verify = certificate" no longer passes after some system upgrade.

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

2) some other TLS change which is invisible in the Exim log; or

3) some other cause at the client which still allows working TLS but is
not visible to exim.

I'm unsure to what extent (2) or (3) are even possible.

If I move on to testing (1) I'll need to modify the client code, but how
likely is this if Exim is documented as ignoring this?

Or something different entirely.

Thanks

--
Mark


tls_privatekey = /etc/ssl/local.key
tls_certificate = /etc/ssl/lets.crt
tls_advertise_hosts = *
tls_verify_certificates = /etc/ssl/certs/
tls_try_verify_hosts = *

RELAY_FROM_CERTS =\
702B4xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: \
EA37Exxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

acl_check_rcpt:

accept verify = certificate
condition = ${if inlist{${sha256:$tls_in_peercert}}{RELAY_FROM_CERTS}}
control = dkim_disable_verify
logwrite = accepting message from certified connection ${substr{0}{6}{${sha256:$tls_in_peercert}}}

# end

--
## 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: TLS SNI possibly breaking "verify = certificate" [ In reply to ]
On 28/04/2024 22:32, Mark Hills via Exim-users wrote:
> "verify = certificate" no longer passes after some system upgrade.

Some? What, precisely?

> 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



I've hacked this out of my own config. It should give a hint as to what stage
in the cert chain the verify fails, if it's not the leaf. If it's the leaf
then it'll point the finger at a cert-name problem, if that's it. Otherwise
we'll have to think harder.

You may need to tweak the notion of a "user cert" embedded in "inbound_user_cert" -
I don't know what yours look like.
--
Cheers,
Jeremy


--
## 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: TLS SNI possibly breaking "verify = certificate" [ In reply to ]
D?a 29. 4. o 0:52 Jeremy Harris via Exim-users napísal(a):

>   accept set acl_m_tmp = \${certextract {subj_altname,dns}{\$$acl_arg2}}

please, can you explain me why the $ are escaped?

If i understand it correctly, the "\$$acl_arg2" part is about sending
variable name in acl_arg2, but the escaping by backslash confuses me in
both places...

regards

--
Slavko
https://www.slavino.sk/


--
## 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: TLS SNI possibly breaking "verify = certificate" [ In reply to ]
On 29/04/2024 07:36, Slavko via Exim-users wrote:
> please, can you explain me why the $ are escaped?
So that they are not taken as expansion-markers on that line,
in turn so that they are available for the line right after.

You could play with "exim -d" and see what happens in detail
if you care enough.
--
Cheers,
Jeremy


--
## 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: TLS SNI possibly breaking "verify = certificate" [ In reply to ]
D?a 29. apríla 2024 11:48:18 UTC používate? Jeremy Harris via Exim-users <exim-users@lists.exim.org> napísal:

>So that they are not taken as expansion-markers on that line,

Ah, they are expanded later, many thanks

regards


--
Slavko
https://www.slavino.sk/

--
## 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/
Re: TLS SNI possibly breaking "verify = certificate" [ In reply to ]
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@xxxx.org> to=<xxxxxx@gmail.com> 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@lists.exim.org
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/