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

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Jeremy Harris
Datum:  
To: exim-users
Betreff: [exim] Re: TLS SNI possibly breaking "verify = certificate"
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@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/