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/fbee1e9b4ce93c15d0f074a... Comments? Cheers, Matthew
From fbee1e9b4ce93c15d0f074ad3fdfb71ba095a4ed Mon Sep 17 00:00:00 2001 From: Matthew Newton <mcn4@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@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@le.ac.uk>