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

Inconsistent/confusing use of certificates in tls configuration #5012

Closed
gvde opened this issue May 25, 2023 · 7 comments
Closed

Inconsistent/confusing use of certificates in tls configuration #5012

gvde opened this issue May 25, 2023 · 7 comments
Labels
close state: auto close the issue

Comments

@gvde
Copy link

gvde commented May 25, 2023

Message

When reading the comments for the tls configuration in the eap module for version 3.0 or 3.2 or even for the latest on the master branch, it still seems to me as if there is a huge mixup of certificate/ca configuration options for server and client certificates. To me, it is more than unclear how to correctly and securely set this up. From what I read in the comments it seems impossible to separate the client certificate verification from the necessary certificates to build the chain for the server certificate.

Conceptually, server and client certificates can be completely different using completely different ca chains.

The server has a certificate. It may require one or more intermediate CAs to verify the server certificate to a trusted root. Thus configuration options necessary are either:

  1. a single certificate_file containing the server certificate followed by intermediates.
  2. a certificate_file with the server certificate and a ca_file containing the intermediates.
  3. a certificate_file with the server certificate and a ca_path which contains all intermediates and hash links to build the chain.

The client also has a certificate. Again, it may require one or more intermediate CAs to verify the client certificate to a trusted root. It may use the same CAs as the servers but may also completely different intermediate CAs and even a completely different root. So for client certificate verification there are again another two options:

  1. a ca_file containing all the intermediate CAs required to verify the client certificates to a trusted root.
  2. a ca_path containing all the intermediate CAs

These may be completely different from those for the server certificate. However, as there is only a single ca_file and ca_path configuration there is no way to separate those. For instance, if your CA puts in server and client authentication into the key usage for all created server certificates, any server certificate could be used for client authentication and freeradius would successfully verify that, even if your client certificates you want to accept are on a completely different chain.

In addition, for client certificates you'll usually have the option to configure a set of CAs you'll accept client certificates from, i.e. a list of CAs which issue acceptable client certificates and which are sent to the client. Unlike the ca_file/ca_path before used to verify the client certificate to a trusted root, this is only the issuing CA(s). This would allow you to securely lock down accepted client certificates to those issued by that CA(s).

For instance compare the configuration options of httpd mod_ssl: there is SSLCertificateFile and SSLCertificateChainFile for server certificates,SSLCACertificateFile, SSLCACertificatePath, SSLCADNRequestFile and SSLCADNRequestPath for client certificates. That is a clear separation in configuration. If client and server certificates use the same CAs they are easily shared, but if not it's possible to properly separate them.

@gvde gvde added the close state: auto close the issue label May 25, 2023
@alandekok
Copy link
Member

The server supports this today. It's possible to put the entire server certificate chain into the file pointed to by certificate_file. The documentation describes how to do this.

The client certificates can then use ca_dir etc. to get the CAs for the client certs.

i.e. the server certificate is only signed by one CA (or chain of CAs). So those can just be dumped into one file, and you're done.

In contrast, client certificates can be issued from multiple CAs, in which case ca_dir is useful to get one of N different CAs.

There's only a need for the server to use multiple CAs when the server is dynamically presenting different identities, such as in a hosted environment. In that case, the various server certificate files can still contain the entire CA chain.

Are your comments here that the documentation is confusing, or that it is wrong?

It is possible to use different CAs for client and server certificates. Did you test this? Does it not work?

@gvde
Copy link
Author

gvde commented May 25, 2023

Well, I am currently setting up our new radius servers on AlmaLinux 9 using 3.0.21 and reading the eap module comments:

                #  If Private key & Certificate are located in
                #  the same file, then private_key_file &
                #  certificate_file must contain the same file
                #  name.
                #
                #  If ca_file (below) is not used, then the
                #  certificate_file below SHOULD also include all of
                #  the intermediate CA certificates used to sign the
                #  server certificate, but NOT the root CA.
                #
                #  Including the ROOT CA certificate is not useful and
                #  merely inflates the exchanged data volume during
                #  the TLS negotiation.
                #
                #  This file should contain the server certificate,
                #  followed by intermediate certificates, in order.
                #  i.e. If we have a server certificate signed by CA1,
                #  which is signed by CA2, which is signed by a root
                #  CA, then the "certificate_file" should contain
                #  server.pem, followed by CA1.pem, followed by
                #  CA2.pem.
                #
                #  When using "ca_file" or "ca_dir", the
                #  "certificate_file" should contain only
                #  "server.pem".  And then you may (or may not) need
                #  to set "auto_chain", depending on your version of
                #  OpenSSL.
                #
                #  In short, SSL / TLS certificates are complex.
                #  There are many versions of software, each of which
                #  behave slightly differently.  It is impossible to
                #  give advice which will work everywhere.  Instead,
                #  we give general guidelines.
                #
                certificate_file = ${certdir}/server.pem

                #  Trusted Root CA list
                #
                #  This file can contain multiple CA certificates.
                #  ALL of the CA's in this list will be trusted to
                #  issue client certificates for authentication.
                #
                #  In general, you should use self-signed
                #  certificates for 802.1x (EAP) authentication.
                #  In that case, this CA file should contain
                #  *one* CA certificate.
                #
                ca_file = ${cadir}/ca.pem

If something is complex here it's those comments. It says to put intermediates into certificate_file if you don't use ca_file. "Don't use" means to me empty/unconfigured and not "don't use for the intermediates of the server certificate".

Likewise

                #  When using "ca_file" or "ca_dir", the
                #  "certificate_file" should contain only
                #  "server.pem".

sounds to me as "if you configure ca_file or ca_dir the certificate_file should contain the server certificate".

The comment for ca_file says Trusted Root CA list. But all considering, I suspect this is not a root ca list but used just like any -CAfile parameter to openssl would do. And if you don't include the intermediates into the server certificate_file it'll contain the intermediates.

So knowing openssl, I would map ca_file to -CAfile and ca_path to -CApath, but reading those comments it's really unclear and you seem to use those options at different places for very different purposes which makes it difficult to understand, how to configure it correctly.

So what you have suggested sounds to me at first like something those comments explicitly say you shouldn't do.

And I know I can configure different CAs for client and server certificates. That's not the problem. The thing is how can I make sure that client certificates issued by the server cert issuing CA are not accepted. Or how can I make sure that client certificates issued by a intermediate CA not issuing my client certificates are not accepted.

If someone puts all required CAs for client and server chains into ca_file or ca_dir it'll accept a lot more client certificates than it should. Of course, it works but for a lot more than it should...

@alandekok
Copy link
Member

If the documentation is unclear, please suggest fixes in a PR.

how can I make sure that client certificates issued by the server cert issuing CA are not accepted

Don't put that CA into ca_dir.

how can I make sure that client certificates issued by a intermediate CA not issuing my client certificates are not accepted.

That sentence is a bit unclear.

If a CA (intermediate or otherwise) has permission to issue client certificates, then OpenSSL will accept client certificates issued by that CA. This is how TLS in general works, even for https.

The problem there isn't FreeRADIUS. For TLS, the CA cert which signs the client cert must have the keyCertSign OID set. The issue is that this OID isn't for "client" certificates, it's for "child" certifictes. So all intermediate Ca certs also have a keyCertSign OID set. Which means that they can issue client certificates, too.

The solution to that particular problem is to have policy checks. When the server receives a client certificate, the issuing CA is placed into a variable (see radiusd -X for the name). You can then check whether the issuing CA is the correct one. If it isn't, reject the session.

@arr2036
Copy link
Member

arr2036 commented May 25, 2023

It's worth checking out the configuration format for v4 (master branch), we completely reworked client certificate validation to use distinct certificate chains for building the presented certificate chain and the certificate chain(s) used to validate clients. I think v3 config is based around the basic API OpenSSL exposes for setting up client and server CAs, which did seem to smush the two chains/concepts together. I found that extremely confusing and ended up redoing a lot of the validation logic manually.

@alandekok
Copy link
Member

Unless there is a documentation PR coming, I'm going to close this issue. While the documentation isn't perfect, the configuration allows you to do exactly what you want to do. It just takes a little bit of work.

@gvde
Copy link
Author

gvde commented May 25, 2023

Unless there is a documentation PR coming, I'm going to close this issue. While the documentation isn't perfect, the configuration allows you to do exactly what you want to do. It just takes a little bit of work.

I cannot write documentation on something I don't know exactly. To write that accurately I would need to know where each of those options is used exactly in what way for what purpose. Otherwise any change would be a guess as good as the current text.

And as I wrote are highly unclear. I cannot tell if it really exactly works the way I want. Only because it allows what I want doesn't mean it prevents everything I don't want.

The docs should guide you to a secure configuration. It only so "complex" because it's not clear.

I also do wonder: the ca_path configuration options ends in the OpenSSL CApath option? If that's the case then it's missing any indication of the required hash symlinks... Without those it's a pretty pointless option.

@alandekok
Copy link
Member

alandekok commented May 25, 2023

I explained how it worked. You don't need to understand every option and exactly how they work. This isn't complicated:

  1. CAs are taken from ca_path

  2. if you need a different CA for server certs, put it into certificate_file, and not into the ca_path directory.

I don't see how that isn't clear. What is very clear is that you don't believe my explanation, and/or you don't trust my explanation to be true. I'm not sure why tho...

I don't see why your comment about the "pointless option" is helpful. Clearly the software has worked for 20 years, so the option isn't pointless.

What is needed (as per the OpenSSL documentation) is that you can run the OpenSSL command "c_rehash" to update the links. This is documented by OpenSSL. We do not include (or rewrite) all of the OpenSSL documentation in FreeRADIUS.

Given all of the above issues, I'm going to close this issue. While the documentation isn't perfect, it's clear enough for most people. Since there are no positive contributions here, and unnecessary negative comments, I'm also going to lock the issue.

You've been given sufficient information to understand the issues you've raised. It's up to you to do something productive with that information.

@FreeRADIUS FreeRADIUS locked as resolved and limited conversation to collaborators May 25, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
close state: auto close the issue
Projects
None yet
Development

No branches or pull requests

3 participants