Added ability to verify client certificates
authorAlan T. DeKok <aland@freeradius.org>
Fri, 3 Sep 2010 10:44:11 +0000 (12:44 +0200)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 3 Sep 2010 10:49:15 +0000 (12:49 +0200)
Disabled in the default build.

raddb/eap.conf
share/dictionary.freeradius.internal
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h

index 0de4884..4e769ee 100644 (file)
                        #  3) uncomment the line below.
                        #  5) Restart radiusd
                #       check_crl = yes
-               #       CA_path = /path/to/directory/with/ca_certs/and/crls/
+                       CA_path = ${cadir}
 
                       #
                       #  If check_cert_issuer is set, the value will
                              #
                              max_entries = 255
                        }
+
+                       #
+                       #  As of version 2.1.10, client certificates can be
+                       #  validated via an external command.  This allows
+                       #  dynamic CRLs or OCSP to be used.
+                       #
+                       #  This configuration is commented out in the
+                       #  default configuration.  Uncomment it, and configure
+                       #  the correct paths below to enable it.
+                       #
+                       verify {
+                               #  A temporary directory where the client
+                               #  certificates are stored.  This directory
+                               #  MUST be owned by the UID of the server,
+                               #  and MUST not be accessible by any other
+                               #  users.  When the server starts, it will do
+                               #  "chmod go-rwx" on the directory, for
+                               #  security reasons.  The directory MUST
+                               #  exist when the server starts.
+                               #
+                               #  You should also delete all of the files
+                               #  in the directory when the server starts.
+               #               tmpdir = /tmp/radiusd
+
+                               #  The command used to verify the client cert.
+                               #  We recommend using the OpenSSL command-line
+                               #  tool.
+                               #
+                               #  The ${..CA_path} text is a reference to
+                               #  the CA_path variable defined above.
+                               #
+                               #  The %{TLS-Client-Cert-Filename} is the name
+                               #  of the temporary file containing the cert
+                               #  in PEM format.  This file is automatically
+                               #  deleted by the server when the command
+                               #  returns.
+               #               client = "/path/to/openssl verify -CApath ${..CA_path} %{TLS-Client-Cert-Filename}"
+                       }
                }
 
                #  The TTLS module implements the EAP-TTLS protocol,
index b7022e8..933bfee 100644 (file)
@@ -355,6 +355,7 @@ ATTRIBUTE   TLS-Client-Cert-Expiration              1921    string
 ATTRIBUTE      TLS-Client-Cert-Issuer                  1922    string
 ATTRIBUTE      TLS-Client-Cert-Subject                 1923    string
 ATTRIBUTE      TLS-Client-Cert-Common-Name             1924    string
+ATTRIBUTE      TLS-Client-Cert-Filename                1925    string
 
 #
 #      Range:  1910-2999
index 1ae5292..a2086d6 100644 (file)
@@ -55,6 +55,14 @@ static CONF_PARSER cache_config[] = {
        { NULL, -1, 0, NULL, NULL }           /* end the list */
 };
 
+static CONF_PARSER verify_config[] = {
+       { "tmpdir", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, verify_tmp_dir), NULL, NULL},
+       { "client", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, verify_client_cert_cmd), NULL, NULL},
+       { NULL, -1, 0, NULL, NULL }           /* end the list */
+};
+
 static CONF_PARSER module_config[] = {
        { "rsa_key_exchange", PW_TYPE_BOOLEAN,
          offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" },
@@ -99,6 +107,8 @@ static CONF_PARSER module_config[] = {
 
        { "cache", PW_TYPE_SUBSECTION, 0, NULL, (const void *) cache_config },
 
+       { "verify", PW_TYPE_SUBSECTION, 0, NULL, (const void *) verify_config },
+
        { NULL, -1, 0, NULL, NULL }           /* end the list */
 };
 
@@ -408,6 +418,59 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                }
                        }
                } /* check_cert_cn */
+
+               while (conf->verify_client_cert_cmd) {
+                       char filename[256];
+                       FILE *fp;
+
+                       snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX",
+                                conf->verify_tmp_dir, progname);
+                       if (mkstemp(filename) < 0) {
+                               RDEBUG("Failed creating file in %s: %s",
+                                      conf->verify_tmp_dir, strerror(errno));
+                               break;                                 
+                       }
+
+                       fp = fopen(filename, "w");
+                       if (!fp) {
+                               RDEBUG("Failed opening file %s: %s",
+                                      filename, strerror(errno));
+                               break;
+                       }
+
+                       if (!PEM_write_X509(fp, client_cert)) {
+                               fclose(fp);
+                               RDEBUG("Failed writing certificate to file");
+                               goto do_unlink;
+                       }
+                       fclose(fp);
+
+                       if (!radius_pairmake(request, &request->packet->vps,
+                                            "TLS-Client-Cert-Filename",
+                                            filename, T_OP_SET)) {
+                               RDEBUG("Failed creating TLS-Client-Cert-Filename");
+                               
+                               goto do_unlink;
+                       }
+
+                       RDEBUG("Verifying client certificate: %s",
+                              conf->verify_client_cert_cmd);
+                       if (radius_exec_program(conf->verify_client_cert_cmd,
+                                               request, 1, NULL, 0, 
+                                               request->packet->vps,
+                                               NULL, 1) != 0) {
+                               radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name);
+                               my_ok = 0;
+                       } else {
+                               RDEBUG("Client certificate CN %s passed external validation", common_name);
+                       }
+
+               do_unlink:
+                       unlink(filename);
+                       break;
+               }
+
+
        } /* depth == 0 */
 
        if (debug_flag > 0) {
@@ -873,9 +936,26 @@ static int eaptls_attach(CONF_SECTION *cs, void **instance)
        }
 
         if (generate_eph_rsa_key(inst->ctx) < 0) {
+               eaptls_detach(inst);
                 return -1;
         }
 
+       if (conf->verify_tmp_dir) {
+               char filename[256];
+
+               if (chmod(conf->verify_tmp_dir, S_IRWXU) < 0) {
+                       radlog(L_ERR, "rlm_eap_tls: Failed changing permissions on %s: %s", conf->verify_tmp_dir, strerror(errno));
+                       eaptls_detach(inst);
+                       return -1;
+               }
+       }
+
+       if (conf->verify_client_cert_cmd && !conf->verify_tmp_dir) {
+               radlog(L_ERR, "rlm_eap_tls: You MUST set the verify directory in order to use verify_client_cmd");
+               eaptls_detach(inst);
+               return -1;
+       }
+
        *instance = inst;
 
        return 0;
index 470dbcd..12ea52c 100644 (file)
@@ -66,6 +66,10 @@ typedef struct eap_tls_conf {
        char            *session_id_name;
        char            session_context_id[128];
        time_t          session_last_flushed;
+
+       char            *verify_tmp_dir;
+       char            *verify_client_cert_cmd;
+
 } EAP_TLS_CONF;
 
 /* This structure gets stored in arg */