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