Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to client certificate support #1164

Open
2 of 4 tasks
jbr opened this issue Jan 12, 2023 · 5 comments
Open
2 of 4 tasks

Improvements to client certificate support #1164

jbr opened this issue Jan 12, 2023 · 5 comments

Comments

@jbr
Copy link
Contributor

jbr commented Jan 12, 2023

Within webpki and prior to a client cert being accepted,

  • CRL support
  • Path validation, likely incorporating webpki CRL support

After a connection has been established,

  • Provide pub api access to the [unparsed?] client certificate as well as issuer chain
  • Provide pub api access to the parsed certificate entities/fields/extensions, including the Subject entity
@jsha
Copy link
Contributor

jsha commented Jan 13, 2023

The current status quo: rustls offers the ClientCertVerifier trait, which takes as input rustls::Certificate, which is a newtype around Vec<u8> - an unparsed, raw certificate. It is up to implementors of ClientCertVerifier to bring their own X.509 parser, path builder, and verifier. For instance, implementors might choose to use the webpki crate for those purposes.

rustls does offer two validating implementations of ClientCertVerifier that use webpki: AllowAnyAuthenticatedClient and AllowAnyAuthenticatedOrAnonymousClient. These say "if the client presents a certificate that chains to one of the configured roots, the client may connect."

For some uses that may suffice, but it's also common for servers to have more complex access requirements. For instance a server might want to say "the client must have a certificate that chains to one of the configured roots, and it must have SPIFFE ID xyz encoded in the Subject Alternative Names extension."

Or a server might want to say "the client may have any certificate or no certificate, but in the HTTP layer, if I'm handling a request for GET /admin, I will check the connection state. If the connection state contains a certificate with admin@example.com in the Subject, I will return HTTP 200; otherwise I will return HTTP 403 to that request." (Note there are good arguments for why mixing TLS-level authentication with HTTP-level authorization is a bad idea - see Colm's arguments on the Security, Cryptography, Whatever podcast and this Twitter thread).

Also, one downstream user of rustls, via rustls-ffi, is Apache's mod_tls. Right now it doesn't support client certificates. One possible goal would be to support what's needed so mod_tls can reach feature parity with mod_ssl (which uses OpenSSL). Some of mod_ssl's client certificate related features:

Environment variables - https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#envvars

Variable Name Value Type Description
SSL_CLIENT_M_VERSION string The version of the client certificate
SSL_CLIENT_M_SERIAL string The serial of the client certificate
SSL_CLIENT_S_DN string Subject DN in client's certificate
SSL_CLIENT_S_DN_x509 string Component of client's Subject DN
SSL_CLIENT_SAN_Email_n string Client certificate's subjectAltName extension entries of type rfc822Name
SSL_CLIENT_SAN_DNS_n string Client certificate's subjectAltName extension entries of type dNSName
SSL_CLIENT_SAN_OTHER_msUPN_n string Client certificate's subjectAltName extension entries of type otherName, Microsoft User Principal Name form (OID 1.3.6.1.4.1.311.20.2.3)
SSL_CLIENT_I_DN string Issuer DN of client's certificate
SSL_CLIENT_I_DN_x509 string Component of client's Issuer DN
SSL_CLIENT_V_START string Validity of client's certificate (start time)
SSL_CLIENT_V_END string Validity of client's certificate (end time)
SSL_CLIENT_V_REMAIN string Number of days until client's certificate expires
SSL_CLIENT_A_SIG string Algorithm used for the signature of client's certificate
SSL_CLIENT_A_KEY string Algorithm used for the public key of client's certificate
SSL_CLIENT_CERT string PEM-encoded client certificate
SSL_CLIENT_CERT_CHAIN_n string PEM-encoded certificates in client certificate chain
SSL_CLIENT_CERT_RFC4523_CEA string Serial number and issuer of the certificate. The format matches that of the CertificateExactAssertion in RFC4523
SSL_CLIENT_VERIFY string NONE, SUCCESS, GENEROUS or FAILED:reason

SSLCACertificateFile
SSLCACertificatePath
SSLCADNRequestFile
SSLCADNRequestPath
SSLCARevocationCheck
SSLCARevocationFile
SSLCARevocationPath
SSLVerifyClient
SSLVerifyDepth

Note: we almost certainly don't want to support all of these, but it's a useful reference for what Apache has implemented over the years.

CRLs / revocation

Is revocation more important for client certificates than for server certificates? Probably. One reason: client certificates are often used for authorization of individuals or machines, and authorization frequently has to be revoked. For instance if an administrator uses a client certificate to get access to a system, and they leave the organization or no longer have a trusted role, their certificate needs to be revoked.

Revocation and path building

Note there are two subtly different things: path building and path verifying. See sleevi's blog post for tons of details, and RFC 4518. For validating server certificates, there is a good argument that path building must take into account revocation status of intermediates. This allows clients to find alternate paths even if one of the paths they might find passes through a revoked certificate. This allows flexibility in the public Web PKI. Note that in practice, as described in sleevi's post, many implementations don't do this. For instance, the webpki crate doesn't yet take into account revocation status because it doesn't support revocation. OpenSSL does not take into account revocation status during path building because it does not do path building - it simply treats the chain from the TLS handshake as the only possible path.

For client certificates, do we need path building to take into account revocation status of intermediates? I think not. Usually the root certificates used for client certificates are simpler (more tree-like), and more locally controlled. For instance, a company might run an internal CA for issuing client certificates for their various services. These issuance hierarchies have less need to be flexible in the face of intermediate revocation, because they are fully controlled by a single entity. If an internal intermediate is revoked, the company can create a new intermediate and reissue and redeploy end entity certificates as needed.

@jbr
Copy link
Contributor Author

jbr commented Jan 13, 2023

@jsha Thanks for this writeup, it helps a lot with context and background!

@cpu
Copy link
Member

cpu commented Mar 31, 2023

Just wanted to mention I'm likely going to start looking at this problem space in the coming week. @jbr Happy to collaborate if you have thoughts or any work-in-progress on the go!

@cpu
Copy link
Member

cpu commented Apr 12, 2023

CRL support

I've started to sketch out the first piece for CRL support, parsing the DER representation: rustls/webpki#44

@cpu
Copy link
Member

cpu commented Jul 7, 2023

CRL support in webpki landed w/ v0.101.0, the Rustls support for using it with the provided webpki-based client verifiers landed in v0.21.3. I'm going to leave this issue open because we haven't yet tackled the second half of the described improvements.

I think we can probably manage to expose the client certificate and associated unparsed attributes pretty easily in a future release. I believe my marching orders from my funding source are to switch gears to supporting the pluggable crypto backend work but I'd like to pursue this as well when time permits. It's something folks have been asking for separate from this issue (rustls/webpki#28).

Exposing the issuer chain from EE -> trust anchor (e.g. briansmith/webpki#68) is trickier. I've made a couple attempts but haven't been able to arrive at a good solution I could retrofit into the path building implementation that exists in webpki today. The biggest "catch" I've been bumping into is trying to avoid needing to return a reference to a stack variable (and with alloc a feature flag we want to avoid heap allocation too). For example we can't return a ref to the potential_issuer) that can live beyond the frame, but constructing it from outside the frame is tricky because it's a node in a linked list being constructed by the path building recursion. I haven't given up yet, but was nervous about making substantial changes to this thoughtfully written (and security load bearing) algorithm. I'm a bit less nervous on that front now that we're improving the path building test coverage (e.g. rustls/webpki#116).

@cpu cpu removed their assignment Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants