2023-10-25
There are at least two ways to 'verify' TLS client host certificates
One of the lesser used aspects of TLS is that TLS clients can send a certificate to the TLS server, in addition to the server sending one to clients. In private deployments, these client certificates are often issued out of a private Certificate Authority, possibly with custom fields that are understood by the software involved. However, you can also use conventional public TLS certificates for hosts as client certificates, and there are situations where you might want to do this; for a non-hypothetical example, you might want to verify some sort of 'identity' of third party SMTP mail sending machines that are contacting your (public) SMTP server in order to give them extra privileges.
(The advantage of not using a private Certificate Authority for this is that you don't have to run a CA or validate the identity of clients when they request certificates from you; you delegate all of those hassles to the public 'web PKI' infrastructure.)
If you get sent a TLS client certificate that is a host certificate, there are at least two decent approaches to verifying the identity (as well as an obvious third terrible one). First, in all cases you need to verify the TLS certificate chain and perhaps check for revocation and so on. But then, just as with servers, you need to verify the hostname (or host names). To do this, you could have a list of allowed host names and check that the TLS client certificate is for one of them, or you could check that the TLS client certificate verifies for a particular allowed host name.
One difference between these two is wild card TLS certificates. A wild card TLS certificate for '*.example.org' will validate for 'host.example.org', but its host name isn't 'host.example.org'. Another difference is that you might wind up with easier code for validation, because you can simply ask your TLS library 'does this TLS certificate chain validate for host <X>' rather than having to ask 'does this certificate chain validate' and then checking the DNS names in the host certificate.
On the other hand, if you have a lot of host names to accept you probably want to validate the certificate chain only once, since it's expensive, and then do the cheap host name checks. And provided that you're careful, you can handle matching wild card TLS certificate names yourself, or perhaps your TLS library will have explicit support for it.
In general I suspect that people mostly use the 'verify the TLS chain and then check the host name separately' approach unless they have only a single host name they accept (and are confident that it will stay that way). Wild card TLS certificates are probably uncommon enough that you can get away with ignoring them here.
(All of this only occurred to me recently when I needed to deal with TLS client host certificates for reasons outside the scope of this entry, and wound up doing some tooling work so that I could see what my test machine was sending as a TLS client certificate.)
Sidebar: The terrible approach
The terrible approach is to (securely) look up the DNS name for the IP address that contacted you and then verify the TLS certificate it gave you against that hostname. This is terrible primarily for operational reasons; people often have many outgoing IP addresses, each of which will usually have a unique name, but they probably don't want to give all of them a wild card TLS certificate and you probably don't want to have to list (and update) all of those individual names. Just like people load balance inbound HTTP (or even SMTP) connections to a pool of servers, all of which may have the same TLS certificate, it's sensible to have multiple outgoing IPs all use a TLS certificate for a specific (and possibly generic) host name.
(The other issue is that it converts DNS lookup problems into TLS certificate validation failures.)