Checking TLS-Cert-* and and accept/reject based on them
Matthew Newton
mcn4 at leicester.ac.uk
Thu Feb 2 17:27:41 CET 2012
Hi,
An question came up recently on freeradius-users whether it was
possible to reject a request based on the TLS-Client-Cert-* type
attributes. The reply being that it would be quite hard to
allow these checks to happen in the authorize section of the code.
I had a look at doing that, but it does seem pretty nasty, as it
would involve doing EAP-TLS things at a different place from, say
PEAP or TTLS. At least, from what I could tell.
However, I looked at and implemented a different solution, which
seems to work really well. Taking ideas from inner-tunnels and the
SoH check method, I added a virtual_server option to the tls
configuration. The rlm_eap_tls code then, just before returning
success, runs through this virtual server (if given) allowing it
to check the TLS-Cert* vps, and reject if it wants to based on
them.
It turns out to be a pretty small patch, and the code path isn't
touched at all if the virtual_server option is not set. I've
tested with EAP-TLS and PEAP/EAP-TLS, and both work well.
I think the next step would be to move the client certificate
validation, ocsp and other checks as 'modules' in this virtual
server, but that looks a bit harder as they are done in a openssl
callback function (cbtls_verify IIRC) - but may not be past the
realms of possibility.
Patch below, and also pushed to github:
https://github.com/mcnewton/freeradius-server/commit/fbee1e9b4ce93c15d0f074ad3fdfb71ba095a4ed
Comments?
Cheers,
Matthew
>From fbee1e9b4ce93c15d0f074ad3fdfb71ba095a4ed Mon Sep 17 00:00:00 2001
From: Matthew Newton <mcn4 at leicester.ac.uk>
Date: Thu, 2 Feb 2012 16:00:37 +0000
Subject: [PATCH] Add virtual-server option for EAP-TLS to allow certificate field checks
Normally attributes such as TLS-Client-Cert-Common-Name can be seen in
Post-Auth only, which is too late to act if the return to the client should
be changed. This code adds a virtual-server option to EAP-TLS to allow
these values to be examined, and the return status updated accordingly.
---
raddb/eap.conf | 8 ++
raddb/sites-available/check-eap-tls | 54 +++++++++++++++
.../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c | 69 +++++++++++++++++++-
.../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h | 2 +
4 files changed, 132 insertions(+), 1 deletions(-)
create mode 100644 raddb/sites-available/check-eap-tls
diff --git a/raddb/eap.conf b/raddb/eap.conf
index 087f7f7..81d9074 100644
--- a/raddb/eap.conf
+++ b/raddb/eap.conf
@@ -291,6 +291,14 @@
ecdh_curve = "prime256v1"
#
+ # As part of checking a client certificate, the EAP-TLS
+ # sets some attributes such as TLS-Client-Cert-CN. This
+ # virtual server has access to these attributes, and can
+ # be used to accept or reject the request.
+ #
+ # virtual_server = check-eap-tls
+
+ #
# Session resumption / fast reauthentication
# cache.
#
diff --git a/raddb/sites-available/check-eap-tls b/raddb/sites-available/check-eap-tls
new file mode 100644
index 0000000..15dc628
--- /dev/null
+++ b/raddb/sites-available/check-eap-tls
@@ -0,0 +1,54 @@
+# This virtual server allows EAP-TLS to reject access requests
+# based on some certificate attributes.
+
+# Value-pairs that are available for checking include:
+#
+# TLS-Client-Cert-Subject
+# TLS-Client-Cert-Issuer
+# TLS-Client-Cert-Common-Name
+# TLS-Client-Cert-Subject-Alt-Name-Email
+#
+# To see a full list of attributes, run the server in debug mode
+# with this virtual server configured, and look at the attributes
+# passed in to this virtual server.
+
+server check-eap-tls {
+
+
+# Authorize - this is the only section required.
+#
+# To accept the access request, set Auth-Type = Accept, otherwise
+# set it to Reject.
+
+authorize {
+ if ("%{TLS-Client-Cert-Common-Name}" == "client.example.com") {
+ update config {
+ Auth-Type = Accept
+ }
+ }
+ else {
+ update config {
+ Auth-Type = Reject
+ }
+ update reply {
+ Reply-Message = "Your certificate is not valid."
+ }
+ }
+
+# if ("host/%{TLS-Client-Cert-Common-Name}" == "%{User-Name}") {
+# update config {
+# Auth-Type = Accept
+# }
+# }
+# else {
+# update config {
+# Auth-Type = Reject
+# }
+# }
+
+}
+
+
+
+}
+
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 cbe61ad..2ffd6eb 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
@@ -130,6 +130,8 @@ static CONF_PARSER module_config[] = {
offsetof(EAP_TLS_CONF, check_cert_issuer), NULL, NULL},
{ "make_cert_command", PW_TYPE_STRING_PTR,
offsetof(EAP_TLS_CONF, make_cert_command), NULL, NULL},
+ { "virtual_server", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, virtual_server), NULL, NULL},
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#ifndef OPENSSL_NO_ECDH
@@ -1611,6 +1613,7 @@ static int eaptls_authenticate(void *arg, EAP_HANDLER *handler)
tls_session_t *tls_session = (tls_session_t *) handler->opaque;
REQUEST *request = handler->request;
eap_tls_t *inst = (eap_tls_t *) arg;
+ EAP_TLS_CONF *conf = inst->conf;
RDEBUG2("Authenticate");
@@ -1619,9 +1622,73 @@ static int eaptls_authenticate(void *arg, EAP_HANDLER *handler)
switch (status) {
/*
* EAP-TLS handshake was successful, return an
- * EAP-TLS-Success packet here.
+ * EAP-TLS-Success packet here. If a virtual-server
+ * is configured, check that is happy, first.
*/
case EAPTLS_SUCCESS:
+ if (conf->virtual_server) {
+ VALUE_PAIR *vp;
+ VALUE_PAIR *copy;
+ REQUEST *fake;
+
+ /* create a fake request, and copy current request vps in */
+ fake = request_alloc_fake(request);
+ rad_assert(fake->packet->vps == NULL);
+
+ vp = pairmake("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
+ if (vp) {
+ pairadd(&fake->packet->vps, vp);
+ }
+
+ for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
+ if (pairfind(fake->packet->vps, vp->attribute)) {
+ continue;
+ }
+ copy = paircopy2(vp, vp->attribute);
+ pairadd(&fake->packet->vps, copy);
+ }
+
+ /* copy the TLS verification vps in to the request */
+ for (vp = handler->certs; vp != NULL; vp = vp->next) {
+ if (pairfind(fake->packet->vps, vp->attribute)) {
+ continue;
+ }
+ copy = paircopy2(vp, vp->attribute);
+ pairadd(&fake->packet->vps, copy);
+ }
+
+ /* set the virtual server to use */
+ if ((vp = pairfind(request->config_items, PW_VIRTUAL_SERVER)) != NULL) {
+ fake->server = vp->vp_strvalue;
+ } else {
+ fake->server = conf->virtual_server;
+ }
+
+ RDEBUG("Processing EAP-TLS check:");
+ debug_pair_list(fake->packet->vps);
+
+ RDEBUG("server %s {", fake->server);
+
+ rad_authenticate(fake);
+
+ RDEBUG("} # server %s", fake->server);
+
+ /* copy the reply vps back to our reply */
+ pairadd(&request->reply->vps, fake->reply->vps);
+ fake->reply->vps = NULL;
+
+ /* reject if virtual server didn't return accept */
+ if (fake->reply->code != PW_AUTHENTICATION_ACK) {
+ RDEBUG2("EAP-TLS rejected by virtual server");
+ request_free(&fake);
+ eaptls_fail(handler, 0);
+ return 0;
+ }
+
+ request_free(&fake);
+ /* success */
+ }
+
break;
/*
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 34c917f..6840714 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
@@ -71,6 +71,8 @@ typedef struct eap_tls_conf {
char *verify_tmp_dir;
char *verify_client_cert_cmd;
+ char *virtual_server;
+
#ifdef HAVE_OPENSSL_OCSP_H
/*
* OCSP Configuration
--
1.7.2.5
--
Matthew Newton, Ph.D. <mcn4 at le.ac.uk>
Systems Architect (UNIX and Networks), Network Services,
I.T. Services, University of Leicester, Leicester LE1 7RH, United Kingdom
For IT help contact helpdesk extn. 2253, <ithelp at le.ac.uk>
More information about the Freeradius-Devel
mailing list