From: Alan T. DeKok Date: Fri, 3 Sep 2010 10:44:11 +0000 (+0200) Subject: Added ability to verify client certificates X-Git-Tag: release_3_0_0_beta0~1272 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=freeradius.git;a=commitdiff_plain;h=c895d8f36a10ea79862f2b29e33a7abd9626bc54 Added ability to verify client certificates Disabled in the default build. --- diff --git a/raddb/eap.conf b/raddb/eap.conf index 0de4884..4e769ee 100644 --- a/raddb/eap.conf +++ b/raddb/eap.conf @@ -225,7 +225,7 @@ # 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 @@ -320,6 +320,44 @@ # 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, diff --git a/share/dictionary.freeradius.internal b/share/dictionary.freeradius.internal index b7022e8..933bfee 100644 --- a/share/dictionary.freeradius.internal +++ b/share/dictionary.freeradius.internal @@ -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 diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 1ae5292..a2086d6 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -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; diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h index 470dbcd..12ea52c 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h @@ -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 */