]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add reject_unknown_intermediate_ca
authorAlan T. DeKok <aland@freeradius.org>
Tue, 13 Jul 2021 15:57:01 +0000 (11:57 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 13 Jul 2021 15:57:35 +0000 (11:57 -0400)
raddb/mods-available/eap
src/include/tls-h
src/main/tls.c

index 0127de6421af08d2b7e3033d57762dd5f7109dad..92be2179e9bd783e9274c076836384194780d0b6 100644 (file)
@@ -366,6 +366,34 @@ eap {
                #
        #       check_cert_cn = %{User-Name}
 
+
+               #
+               #  This configuration item only applies when there is
+               #  an intermediate CA between the "root" CA, and the
+               #  client certificate.  If we trust the root CA, then
+               #  by definition we also trust ANY intermediate CA
+               #  which is signed by that root.  This means ANOTHER
+               #  intermediate CA can issue client certificates, and
+               #  have them accepted by the EAP module.
+               #
+               #  The solution is to list ONLY the trusted CAs in the
+               #  FreeRADIUS configuration, and then set this
+               #  configuration item to "yes".
+               #
+               #  Then, when the server receives a client certificate
+               #  from an untrusted CA, that authentication request
+               #  can be rejected.
+               #
+               #  It is possible to do these checks in "unlang", by
+               #  checking for unknown names in the
+               #  TLS-Client-Cert-Common-Name attribute, but that is
+               #  more complex.  So we add a configuration option
+               #  which can be set once, and which works for all
+               #  possible intermediate CAs, no matter what their
+               #  value.
+               #
+       #       reject_unknown_intermediate_ca = no
+
                #  Set this option to specify the allowed
                #  TLS cipher suites.  The format is listed
                #  in "man 1 ciphers".
index cea6f7d0f6bfc22d1032748dafaa06f1d42e6e09..3e62393fd5fdac8e322bd3d14329b8af5a443719 100644 (file)
@@ -368,6 +368,7 @@ struct fr_tls_server_conf_t {
        bool            disable_tlsv1_1;
        bool            disable_tlsv1_2;
        bool            tls13_enable_magic;
+       bool            disallow_untrusted;             //!< allow untrusted CAs to issue client certificates
 
        int             min_version;
        int             max_version;
index 0d888eb144faa370dd7b4b54e237ab4371c23cbb..7343b43c134d2a58993837c6199e22262b00fb9d 100644 (file)
@@ -1485,6 +1485,8 @@ static CONF_PARSER tls_server_config[] = {
        { "check_cert_issuer", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_issuer), NULL },
        { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, require_client_cert), NULL },
 
+       { "reject_unknown_intermediate_ca", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, disallow_untrusted), .dflt = "no", },
+
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
        { "ecdh_curve", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ecdh_curve), "prime256v1" },
@@ -3094,14 +3096,45 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         */
        if (depth == 0) {
                tls_session_t *ssn = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_SSN);
+               STACK_OF(X509)* untrusted = NULL;
+
                rad_assert(ssn != NULL);
 
+               /*
+                *      See if there are any untrusted certificates.
+                *      If so, complain about them.
+                */
+               untrusted = X509_STORE_CTX_get0_untrusted(ctx);
+               if (untrusted) {
+                       if (conf->disallow_untrusted || RDEBUG_ENABLED2) {
+                               int  i;
+
+                               WARN("Certificate chain - %i cert(s) untrusted",
+                                    X509_STORE_CTX_get_num_untrusted(ctx));
+                               for (i = sk_X509_num(untrusted); i > 0 ; i--) {
+                                       X509 *this_cert = sk_X509_value(untrusted, i - 1);
+
+                                       X509_NAME_oneline(X509_get_subject_name(this_cert), subject, sizeof(subject));
+                                       subject[sizeof(subject) - 1] = '\0';
+
+                                       WARN("(TLS) untrusted certificate with depth [%i] subject name %s",
+                                            i - 1, subject);
+                               }
+                       }
+
+                       if (conf->disallow_untrusted) {
+                               AUTH(LOG_PREFIX ": There are untrusted certificates in the certificate chain.  Rejecting.",
+                                    issuer, conf->check_cert_issuer);
+                               my_ok = 0;
+                       }
+               }
+
                /*
                 *      If the conf tells us to, check cert issuer
                 *      against the specified value and fail
                 *      verification if they don't match.
                 */
-               if (conf->check_cert_issuer &&
+               if (my_ok && conf->check_cert_issuer &&
                    (strcmp(issuer, conf->check_cert_issuer) != 0)) {
                        AUTH(LOG_PREFIX ": Certificate issuer (%s) does not match specified value (%s)!",
                             issuer, conf->check_cert_issuer);