Certificate patches for EAP TLS module

Keith Moores kmm6b at virginia.edu
Fri May 18 18:19:31 CEST 2007


In trying to come up with a our own solution to the same problem I  
discovered the following previous patch proposal by Michael Joosten  
from 2005.

Incorporating this functionality would be greatly appreciated:

> configurable checking of user identity (i.e. what the supplicant
> tells via EAP Identity) and the actual client/user certificate.

I couldn't find any comments on this (other than another person  
interested in seeing it adopted), any chance this could make it into  
a future version? 2.0?

-Keith


> From freeradius-devel at lists.freeradius.org  Thu Mar 10 05:16:40 2005
> From: freeradius-devel at lists.freeradius.org (Michael Joosten)
> Date: Thu, 10 Mar 2005 06:16:40 +0100
> Subject: certificate patches for EAP TLS module, plus some questions..
> Message-ID: <422FD838.30003 at c-lab.de>
>
> This is a multi-part message in MIME format.
> --------------010606020309030200040704
> Content-Type: text/plain; charset=ISO-8859-1; format=flowed
> Content-Transfer-Encoding: 7bit
>
> Hello,
>
> due to internal demand I'm providing a patch that provides the  
> following
> new functionality for EAP TLS:
>
> 1) configurable checking of user identity (i.e. what the supplicant
> tells via EAP Identity) and the actual client/user certificate.  
> There is
> already a check for commonName, but in many cases "Joe User" isn't
> unique enough - and some PKIs even uses different X509 attributes,  
> like
> those who want to implement a Microsoft SmartCard Login compatible
> infrastructure. And yes, stuff from Subject Alternative Name is also
> supported.
>
> This patch is implemented as additional config options for the EAP TLS
> module section in eap.conf, providing plain text names for attributes
> and even search lists, in case two different versions/generations of
> user certificates must be supported:
>
>       use_as_cert_cn = email,UPN,TCGID,CN
> # search the user cert for email (both in Subject Alt. Name and
> Subject), Microsoft Universal Principal Name, Trust Center Global ID
> (Guardeonic thingy), and commonName, in this order and return first  
> hit.
>
>      check_cert_cn = %{User-Name}
> # kept from previous impl., uses CN if use_as_cert_cn is not set,
> otherwise whatever was found above first
>
> 2) for accounting and informing the gateway/NAS, the most relevant  
> X509
> attributes of a verified user certificate can be exported as AV pairs.
> Similar to 1), a list can be specified or all defined attributes are
> 'exported':
> export_cert_attributes = *
> - or -
> export_cert_attributes = CN,email,UPN,TCGID
>
> This will end up as
> UserCert-CN = "Joe User"
> UserCert-Email = "juser at sweatshop.com"
> UserCert-UPN = "Joe.User at sweatshop.com"
> UserCert-TCGID = "USERJ0001234"
>
> and some other usual X.509 attributes. If I'm not mistaken, this has
> been requested a few times in the mailing lists, hasn't it ?
>
> These avpairs are created at the end of eaptls_authenticate() and  
> added
> to the reply list - I hope that's the right place?!
>
> My question is now under which namespace these attributes should go.
> They are not really company-specific and could go into the common  
> range
> < 255, but there are currently about 20 defined. I could also use some
> Siemens enterprise ID to fix them, though. Currently, I added a new
> dictionary file (dictionary.siemens) and put them there under some
> Siemens IANA enterprise number.
>
> And the prefix 'UserCert-' is also changeable, by using, e.g.,
> cert_attributes_prefix = X509-Attr-
>
> The patch adds a new file in the rlm_eap_tls directory and maked some
> minor mods to the existing files, and is therefore completely  
> restricted
> to rlm_eap_tls. Except of the changes in share/dictionary and
> share/dictionary.siemens and an update of the EAP TLS documentation
> in doc/rlm_eap.
>
> Adding additional X509 attributes is very simple, usually just adding
> them to an internal table in cert.c is sufficient. With some more
> work/time, this mapping table could even be read from a  
> configuration file.
>
> Looking forward for some comments,
>
> Michael
>
>
>
>
>
> --------------010606020309030200040704
> Content-Type: text/plain;
>  name="freeradius102-patch1.txt"
> Content-Transfer-Encoding: 7bit
> Content-Disposition: inline;
>  filename="freeradius102-patch1.txt"
>
> diff -urN -x '*~' ../orig/freeradius-1.0.2/doc/rlm_eap ./doc/rlm_eap
> --- ../orig/freeradius-1.0.2/doc/rlm_eap	Tue Dec 16 04:50:34 2003
> +++ ./doc/rlm_eap	Wed Mar  9 00:35:59 2005
> @@ -155,6 +155,96 @@
>
>    EAP-SIM will send WEP attributes to the resquestor.
>
> +EAP TLS server
> +
> +  EAP TLS, TTLS and PEAP use public key based certificates for the  
> server,
> +  while TLS even uses them for authentication of the client (aka
> +  supplicant). Consequently, TLS is usually employed for  
> deployments that
> +  intend or already have an organization-wide PKI (Public Key
> +  Infrastructure). Currently, provided that the supplicant (user  
> client)
> +  has a valid certificate, ANY identity that it provides in the
> +  EAP-Identity phase of the protocol is accepted, which clearly  
> make few
> +  sense for accounting and authorization. Whilst the rlm_eap_tls  
> module
> +  has an option to compare this identity with the certificate's
> +  commonName, many PKI implementations chose other attributes to  
> uniquely
> +  identify an user. Therefore, additional attributes can be used,  
> the most common are:
> +
> +   RADIUS name:       Explanation:
> +    
> ====================================================================== 
> =========
> +	Email	(in the cert's Subject as PKCS#9 legacy or in the Subject
> +		Alternative Name),
> +	URI	(in the Subject Alternative Name)
> +	DNS	(in the Subject Alternative Name)
> +	UPN	(Microsoft Universal Principal Name, used for Smartcard Logon,
> +		in the Subject Alternative Name)
> +	CN 	(X.509/X520 commonName, in the cert's Subject)
> +	TCGID	(Guardeonic's Global ID attribute, in the cert's Subject)
> +
> +  Other attributes which also available are:
> +
> +   RADIUS name:    X.509/X.520 name:
> +   ===================================================
> +
> +	SN 		(user's surname/family name)
> +	GN		(user's given/first name)
> +	O		(organizationName)
> +	OU		(organizationalUnitName)
> +	ST		(stateOrProvinceName)
> +	L		(localityName)
> +	Title		(title)
> +	Desc		(description)
> +	Name		(name)
> +	Initials	(initials)
> +	Pseudonym	(pseudonym)
> +	Role		(role)
> +
> +
> +  These attribute names can even be used in a search list, e.g. to  
> support
> +  multiple types or versions of user certificates. The first  
> attribute
> +  found with a value (hit) will be used in the same way as the CN
> +  (commonName) was used in the old implementation:
> +
> +  	use_as_cert_cn = UPN,TCGID,Email,CN
> +  	check_cert_cn = %{User-Name}
> +
> +  These example means that instead of just comparing the User-Name  
> RADIUS
> +  attribute with the commonName of the user's attribute, the  
> comparision
> +  first tries to use the value of the Universal Principal Name if
> +  available, then the TCGID, the Email and finally the commonName,
> +  whichever exists first.
> +
> +  To support the authenticator/NAS/Access point even more, these
> +  attributes of a user certicate (from the X.520 schema  
> definition) can be
> +  'exported' as RADIUS attributes. This is controlled by the
> +  'export_cert_attributes' option, which either takes '*' to  
> indicate that
> +  all available X509 attributes are to be exported or just a
> +  comma-separated list:
> +
> +	# return all 'known' attributes
> +	export_cert_attributes = *
> + 	# just return the named attrs  				
> +	export_cert_attributes = Email,CN,GN,SN,TCGID,UPN
> +
> +  The attributes are returned as attribute/value pairs in the  
> server's
> +  authentication reply, using a prefix with the RADIUS names in  
> the tables above:
> +
> +	UserCert-Email	= jdoe at circlecorp.com
> +	UserCert-CN	= John Doe
> +	UserCert-GN	= John
> +	UserCert-SN	= Doe
> +	UserCert-TCGID	= JDOE000123
> +	UserCert-UPN	= John.Doe at circlecorp.com
> +
> +  In addition, the prefix 'UserCert-' can be changed with the
> +  'cert_attributes_prefix' option. Keep in mind that in this case  
> you have
> +  to provide your own RADIUS dictionary entries!  Additional  
> attributes
> +  can configured in the source without requiring a deep  
> understanding of
> +  OpenSSL's X509 API, see cert.c. For a list of supported X509  
> attribute
> +  names, see <openssl/obj_mac.h> or, better, in OpenSSL's source
> +  crypto/objects/objects.txt.
> +
> +
> +
>  EAP CLIENTS
>
>    1. XSupplicant - freeradius (EAP/TLS) notes may be found at:
> @@ -323,6 +413,9 @@
>    For EAP-TLS, OPENSSL, <http://www.openssl.org/>, is required to  
> be installed.
>    Any version from 0.9.7, should fairly work with this module.
>
> +  EAP-TTLS and PEAP use the TLS module to provide the secure  
> tunnel for the
> +  chellenge/password exchange and, therefore, also requires OpenSSL.
> +
>    EAP-SIM should not require any additional packages.
>
>
> @@ -404,4 +497,8 @@
>  		    The development of the EAP/SIM support was funded by
>  		    Internet Foundation Austria (http://www.nic.at/ipa).
>
> +   Certificate check for EAP-TLS - Michael Joosten  
> <michael.joosten at c-lab.de>
> +		    The development of this extension was funded by
> +		    Siemens Business Services
> +
>
> diff -urN -x '*~' ../orig/freeradius-1.0.2/raddb/eap.conf ./raddb/ 
> eap.conf
> --- ../orig/freeradius-1.0.2/raddb/eap.conf	Thu Apr 15 20:34:41 2004
> +++ ./raddb/eap.conf	Wed Mar  9 01:17:19 2005
> @@ -175,6 +175,33 @@
>                         #  will fail rejecting the user.
>                         #
>                 #       check_cert_cn = %{User-Name}
> +                       #
> +                       #  If use_as_cert_cn is set, the value of  
> the first
> +                       #  available X509 attribute in the client  
> certificate
> +                       #  is used instead of CN (commonName) if  
> check_cert_cn
> +                       #  is set. That is, you can specify a sort  
> of 'search path'
> +                       #  in order to support different versions  
> of certificates.
> +                       #  See doc/rlm_eap, section EAP-TLS, for  
> more detailed documentation.
> +                       #
> +                       #  This example uses the values of the  
> Microsoft Universal
> +                       #  Principal Name, the email (both in  
> <Subject> and
> +                       #  <Subject Alternative Name> sections),  
> the Guardeonic
> +                       #  Trust Center Global ID and finally the  
> commonName, whatever
> +                       #  comes first. NO spaces in the list!
> +                       #
> +               #       use_as_cert_cn = UPN,Email,TCGID,CN
> +                       #
> +                       #  export_cert_attributes takes attribute  
> values from
> +                       #  verified client certificate and creates  
> RADIUS
> +                       #  attribute/value pairs in the  
> authentication reply.
> +                       #  See again doc/rlm_eap for documentation.
> +                       #
> +                       #  The example creates the following av  
> pairs, if the
> +                       #  attributes are present in the certificate:
> +                       #  UserCert-CN = "Joe User", UserCert-Email  
> = "juser at company.com"
> +                       #  UserCert-UPN = "Joe.User at company.com",  
> UserCert-TCGID = "JUSER001234"
> +                       #
> +               #       export_cert_attributes = CN,Email,UPN,TCGID
>  		#}
>
>  		#  The TTLS module implements the EAP-TTLS protocol,
> diff -urN -x '*~' ../orig/freeradius-1.0.2/share/dictionary ./share/ 
> dictionary
> --- ../orig/freeradius-1.0.2/share/dictionary	Wed Feb  9 18:50:53 2005
> +++ ./share/dictionary	Wed Mar  9 01:20:16 2005
> @@ -89,6 +89,7 @@
>  $INCLUDE dictionary.redcreek
>  $INCLUDE dictionary.shasta
>  $INCLUDE dictionary.shiva
> +$INCLUDE dictionary.siemens
>  $INCLUDE dictionary.sonicwall
>  $INCLUDE dictionary.springtide
>  $INCLUDE dictionary.telebit
> diff -urN -x '*~' ../orig/freeradius-1.0.2/share/ 
> dictionary.siemens ./share/dictionary.siemens
> --- ../orig/freeradius-1.0.2/share/dictionary.siemens	Thu Jan  1  
> 01:00:00 1970
> +++ ./share/dictionary.siemens	Wed Mar  9 01:05:36 2005
> @@ -0,0 +1,33 @@
> +#
> +#      Dictionary enries for export of X509 user certificate  
> attributes
> +#
> +#      $Id$
> +#
> +
> +
> +VENDOR  	Siemens			4329
> +
> +BEGIN-VENDOR 	Siemens
> +# general X509 subject/issuer attributes
> +ATTRIBUTE	UserCert-CN		221	string
> +ATTRIBUTE	UserCert-SN		222	string
> +ATTRIBUTE	UserCert-GN		223	string
> +ATTRIBUTE	UserCert-O		224	string
> +ATTRIBUTE	UserCert-OU		225	string
> +ATTRIBUTE	UserCert-ST		226	string
> +ATTRIBUTE	UserCert-L		227	string
> +ATTRIBUTE	UserCert-Title		228	string
> +ATTRIBUTE	UserCert-Desc		229	string
> +ATTRIBUTE	UserCert-Name		230	string
> +ATTRIBUTE	UserCert-Initials	231	string
> +ATTRIBUTE	UserCert-Pseudonym	232	string
> +ATTRIBUTE	UserCert-Role		233	string
> +
> +# some X509v3 extension attributes
> +ATTRIBUTE	UserCert-UPN		240	string
> +ATTRIBUTE	UserCert-URI		241	string
> +ATTRIBUTE	UserCert-DNS		242	string
> +ATTRIBUTE	UserCert-Email		243	string
> +ATTRIBUTE	UserCert-TCGID		244	string
> +
> +END-VENDOR	Siemens
> diff -urN -x '*~' ../orig/freeradius-1.0.2/src/modules/rlm_eap/ 
> types/rlm_eap_tls/Makefile.in ./src/modules/rlm_eap/types/ 
> rlm_eap_tls/Makefile.in
> --- ../orig/freeradius-1.0.2/src/modules/rlm_eap/types/rlm_eap_tls/ 
> Makefile.in	Fri Oct 31 23:32:32 2003
> +++ ./src/modules/rlm_eap/types/rlm_eap_tls/Makefile.in	Wed Mar  9  
> 01:17:19 2005
> @@ -1,5 +1,5 @@
>  TARGET      = @targetname@
> -SRCS        = rlm_eap_tls.c eap_tls.c cb.c tls.c mppe_keys.c
> +SRCS        = rlm_eap_tls.c eap_tls.c cb.c tls.c mppe_keys.c cert.c
>  RLM_CFLAGS  = $(INCLTDL) -I at srcdir@/../.. -I at srcdir@/../../libeap  
> @eap_tls_cflags@ -DOPENSSL_NO_KRB5
>  HEADERS     = rlm_eap_tls.h eap_tls.h ../../eap.h ../../rlm_eap.h
>  RLM_INSTALL =
> diff -urN -x '*~' ../orig/freeradius-1.0.2/src/modules/rlm_eap/ 
> types/rlm_eap_tls/cb.c ./src/modules/rlm_eap/types/rlm_eap_tls/cb.c
> --- ../orig/freeradius-1.0.2/src/modules/rlm_eap/types/rlm_eap_tls/ 
> cb.c	Fri May 28 00:10:17 2004
> +++ ./src/modules/rlm_eap/types/rlm_eap_tls/cb.c	Wed Mar  9  
> 01:17:19 2005
> @@ -55,6 +55,7 @@
>  	}
>  }
>
> +
>  /*
>   *	Before trusting a certificate, you must make sure that the
>   *	certificate is 'valid'. There are several steps that your
> @@ -119,12 +120,6 @@
>  	X509_NAME_oneline(X509_get_subject_name(client_cert), subject, 256);
>  	X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),  
> issuer, 256);
>
> -	/*
> -	 *	Get the Common Name
> -	 */
> -	X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
> -             NID_commonName, buf, 256);
> -
>  	switch (ctx->error) {
>
>  	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
> @@ -158,10 +153,29 @@
>  			/* if this fails, fail the verification */
>  			my_ok = 0;
>  		}
> -		DEBUG2("    rlm_eap_tls: checking certificate CN (%s) with  
> xlat'ed value (%s)", buf, cn_str);
> +
> +		/*
> +		 * Search for a specific attribute in a search order
> +		 */
> +		buf[0] = '\0';
> +		if (conf->use_as_cert_cn != NULL) {
> +		  if (certtls_get_attribute(client_cert, conf->use_as_cert_cn,
> +					 buf, 256) == 0)
> +		    radlog(L_ERR, "rlm_eap_tls: no such attributes found in user  
> certificate for checking: %s", conf->use_as_cert_cn);
> +		} else {
> +		
> +		  /*
> +		   *	Legacy case: Get the Common Name
> +		   */
> +		  X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
> +					    NID_commonName, buf, 256);
> +		
> +		}
> +
> +		DEBUG2("    rlm_eap_tls: checking certificate identity (%s) with  
> xlat'ed value (%s)", buf, cn_str);
>  		if (strncmp(cn_str, buf, sizeof(buf)) != 0) {
>  			my_ok = 0;
> -			radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match  
> specified value (%s)!", buf, cn_str);
> +			radlog(L_AUTH, "rlm_eap_tls: Certificate identity (%s) does not  
> match specified value (%s)!", buf, cn_str);
>  		}
>  	}
>
> @@ -180,6 +194,7 @@
>  		radlog(L_INFO, "--> issuer  = %s", issuer);
>  		radlog(L_INFO, "--> verify return:%d", my_ok);
>  	}
> +
>  	return my_ok;
>  }
>
> diff -urN -x '*~' ../orig/freeradius-1.0.2/src/modules/rlm_eap/ 
> types/rlm_eap_tls/cert.c ./src/modules/rlm_eap/types/rlm_eap_tls/ 
> cert.c
> --- ../orig/freeradius-1.0.2/src/modules/rlm_eap/types/rlm_eap_tls/ 
> cert.c	Thu Jan  1 01:00:00 1970
> +++ ./src/modules/rlm_eap/types/rlm_eap_tls/cert.c	Wed Mar  9  
> 01:17:19 2005
> @@ -0,0 +1,436 @@
> +/*
> + * cert.c
> + *
> + * Version:     $Id$
> + *
> + *   This program is free software; you can redistribute it and/or  
> modify
> + *   it under the terms of the GNU General Public License as  
> published by
> + *   the Free Software Foundation; either version 2 of the  
> License, or
> + *   (at your option) any later version.
> + *
> + *   This program is distributed in the hope that it will be useful,
> + *   but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *   GNU General Public License for more details.
> + *
> + *   You should have received a copy of the GNU General Public  
> License
> + *   along with this program; if not, write to the Free Software
> + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   
> 02111-1307  USA
> + *
> + *   Copyright 2005 Michael Joosten, Siemens Business Services  
> GmbH & Co OHG
> + */
> +
> +#include "eap_tls.h"
> +
> +#ifndef NO_OPENSSL
> +
> +#include <openssl/x509v3.h>
> +
> +/*
> + * default prefix used to prepend in front of X509 attribute names
> + * to form RADIUS attribute names. Make sure that these names are  
> still
> + * registered in any used dictionary !
> + */
> +
> +#define DEFAULT_RADATTR_PREFIX "UserCert-"
> +
> +/*
> + *    The following functions implement access and search methods for
> + *    X509 certificates. Based on the concept of named and IDentified
> + *    ASN.1 objects in OpenSSL's X509 module, these function are an
> + *    extensible basis to provide access to arbitrary RDNs (Relative
> + *    Distinguished Names) in the <subject> and in the  
> <subjectAltName>
> + *    fields, in order to check for the correctness of the user's  
> identity.
> + */
> +
> +typedef int (*x509_accessor_t)(X509 *cert, char *x509_attr, char  
> *buf, int len);
> +
> +struct x509_access_methods {
> +  char              *rad_attr_name;
> +  /*   int               rad_attr_id; */
> +  x509_accessor_t   attr_getter;
> +  char              *x509_attr_name;
> +};
> +
> +/*
> + * Add value pair to reply
> + */
> +static void add_reply(VALUE_PAIR** vp,
> +                      const char* name, const char* value, int len)
> +{
> +        VALUE_PAIR *reply_attr;
> +        reply_attr = pairmake(name, "", T_OP_EQ);
> +        if (!reply_attr) {
> +                DEBUG("rlm_eap_tls: "
> +                      "add_reply failed to create attribute %s: %s 
> \n",
> +                      name, librad_errstr);
> +                return;
> +        }
> +
> +        memcpy(reply_attr->strvalue, value, len);
> +        reply_attr->length = len;
> +        pairadd(vp, reply_attr);
> +}
> +
> +
> +/*
> + * This function returns attribute values from a X509 certificate's
> + * <subject> field. The subject consists of a sequence of attribute,
> + * denoted by an ASN.1 object id, and string value pairs.
> + *
> + * Returns 1 and the attribute value in buf if found, otherwise 0.
> + */
> +static int x509_getSubjectName(X509 *cert, char *x509_rdn_name,  
> char *buf, int len)
> +{
> +  X509_NAME *cert_subject;
> +  ASN1_OBJECT *rdn_object;
> +  static int NID_tcgid = NID_undef;
> +
> +  /* Old Siemens PKIv1 certificates contain a unique GID in a  
> company-specific
> +   * DN tagged by an enterpise OID from Guardeonic Solutions  
> object space. Newer PKI v2
> +   * certificates have the same GID encoded as standard serialNumber.
> +   */
> +  if (NID_tcgid == NID_undef)
> +   {
> +     NID_tcgid = OBJ_create("1.3.6.1.4.1.1201.1.1.2.2.75",  
> "TCGID", "Trust Center Global ID");
> +     if(NID_tcgid == NID_undef)
> +       {
> +         radlog(L_ERR,"rlm_eap_tls:cannot create TCGID ASN.1  
> object!");
> +         return 0;
> +       }
> +   }
> +
> +
> +  if ((cert_subject = X509_get_subject_name(cert)) == NULL) {
> +    radlog(L_ERR, "rlm_eap_tls: no subject in users's certificate?! 
> \n");
> +  }
> +
> +  if ((rdn_object = OBJ_txt2obj(x509_rdn_name, 0)) != NULL) {
> +    if (X509_NAME_get_text_by_OBJ(cert_subject, rdn_object, buf,  
> len) < 0)
> +      return 0;
> +    else return 1;
> +  } else {
> +    radlog(L_ERR, "rlm_eap_tls: X509 subject RDN \"%s\" unknown!",  
> x509_rdn_name);
> +    return 0;
> +  }
> +}
> +
> +
> +/*
> + * Mapping for X509v3 extension type names into their type ids.
> + */
> +
> +struct x509_subjAltName_type {
> +  char *name; int type; };
> +
> +static struct x509_subjAltName_type
> + x509_subjAltName_types[]  =
> +{
> +  { "otherName", GEN_OTHERNAME },
> +  { "X400Name",  GEN_X400 },
> +  { "EdiPartyName",  GEN_EDIPARTY },
> +  { "email",  GEN_EMAIL },
> +  { "DNS",  GEN_DNS },
> +  { "URI",  GEN_URI },
> +  { "DirName",  GEN_DIRNAME },
> +  { "IP",  GEN_IPADD },
> +  { "RegisteredID",  GEN_RID },
> +  { NULL, -1}
> +};
> +
> +
> +static int x509_find_subjAltName_type(const char* name)
> +{
> +  int i = 0;
> +
> +  while (x509_subjAltName_types[i].name != NULL) {
> +    if (strcmp(x509_subjAltName_types[i].name, name) == 0)
> +      return x509_subjAltName_types[i].type;
> +    i++;
> +  }
> +}
> +
> +
> +/*
> + * not sure which version of OpenSSL 0.97 introduced the Microsoft
> + * Universal Principal Name for Smartcard Logon, so be defensive here
> + * and define it yourself if necessary.
> + */
> +#ifndef NID_ms_upn
> +#define NID_ms_upn NID_undef
> +#endif
> +
> +/*
> + * This function returns attribute values from a X509 certificate's
> + * extension (X509v3 ext). The extensions are increasingly used to
> + * provide additional attributes instead of adding them to the usual
> + * <subject> or <issuer> attributes, and are known as Subject/Issuer
> + * Alternative Names (subjAltNames/issAltNames).
> + * Namely the <otherName> attribute is a sort of extensible variant
> + * record, using a ASN.1 object id to distinguish different attribute
> + * types and their values.
> + * Other, more common, attributes, are defined in RFC3280 with fixed,
> + * small integers, thus the use of the table above.
> + *
> + * Returns 1 and the attribute value in buf if found, otherwise 0.
> + */
> +
> +static int x509_getSubjectAltName(X509 *cert, char  
> *x509_rdn_namespec, char *buf, int len)
> +{
> +  ASN1_OBJECT    *rdn_object;
> +  X509_EXTENSION *subjAltNameExt = NULL;
> +  STACK_OF(GENERAL_NAME)   *subjAltNameDNs = NULL;
> +  GENERAL_NAME   *subjAltNameDN  = NULL;
> +  OTHERNAME      *otherName      = NULL;
> +  ASN1_STRING    *asn_string     = NULL;
> +  int i, j;
> +  int num_GNAMES;
> +  int type;
> +  char type_name[128];
> +  char *token_loc = NULL;
> +  char *rdn_object_name;
> +
> +  static int NID_msUPN = NID_ms_upn;
> +
> +  /* if MS UPN is not known, define it yourself... */
> +  if (NID_msUPN == NID_undef)
> +   {
> +     NID_msUPN = OBJ_create("1.3.6.1.4.1.311.20.2.3", "msUPN",  
> "Microsoft Universal Principal Name");
> +     if(NID_msUPN == NID_undef)
> +       {
> +         radlog(L_ERR, "rlm_eap_tls: cannot create MS UPN ASN.1  
> object!");
> +         return 0;
> +       }
> +   }
> +
> +  /* split namespec into extension name and object name, if  
> possible */
> +  type_name[0] = '\0';
> +  token_loc = strchr(x509_rdn_namespec,':');
> +  if (token_loc == 0) {
> +    strncpy(type_name, x509_rdn_namespec, sizeof(type_name)-1) 
> [sizeof(type_name)] = '\0';
> +  } else {
> +    strncpy(type_name, x509_rdn_namespec, token_loc- 
> x509_rdn_namespec);
> +    type_name[token_loc-x509_rdn_namespec] = '\0';
> +
> +    /* get trailing part of namespec, an ASN.1 object id */
> +    rdn_object_name = token_loc+1;
> +  }
> +
> +  /* map extension type name to its number */
> +  if (type_name[0] != '\0') {
> +    type = x509_find_subjAltName_type(type_name);
> +    if (type < 0) {
> +      radlog(L_ERR, "rlm_eap_tls: certificate subjectAltName type  
> name \"%s\" is not known!", type_name);
> +      return 0;
> +    }
> +  } else {
> +    radlog(L_ERR, "rlm_eap_tls: no certificate subjectAltName type  
> name found in \"%s\"", x509_rdn_namespec);
> +    return 0;
> +  }
> +
> +  /* iterate over subjAltName extensions, but there is only one... */
> +  i = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
> +  if (i < 0) {
> +        radlog(L_INFO,"rlm_eap_tls: certificate has no X509v3  
> extensions");
> +        return 0;
> +   }
> +
> +  /* get the extension and the attached RDNs (Relative  
> Distinguished Names) */
> +  if(!(subjAltNameExt = X509_get_ext(cert, i)) ||
> +      !(subjAltNameDNs = X509V3_EXT_d2i(subjAltNameExt)) ) {
> +        return 0;
> +  }
> +
> +
> +  /* now iterate over the RDNs represented as GENERAL_NAMES until
> +   * type and OID match:
> +   */
> +  num_GNAMES = sk_GENERAL_NAME_num(subjAltNameDNs);
> +  for (j = 0; j < num_GNAMES; j++) {
> +    subjAltNameDN = sk_GENERAL_NAME_value(subjAltNameDNs, j);
> +    if (subjAltNameDN->type == type) {
> +      switch (type) {
> +      case GEN_OTHERNAME:
> +	if (rdn_object_name == NULL) {
> +	  radlog(L_ERR,"rlm_eap_tls:  otherName in certificate  
> subjAltName requires an ASN.1 object name: \"%s\" after \': 
> \'",x509_rdn_namespec);
> +	  return 0;
> +	}
> +
> +	rdn_object = OBJ_txt2obj(rdn_object_name, 0);
> +	if (rdn_object == NULL) {
> +	  radlog(L_ERR,"rlm_eap_tls: otherName in certificate subjAltName  
> requires a known ASN.1 object name: \"%s\" not  
> registered",rdn_object_name);
> +	  return 0;
> +	}
> +
> +	otherName = subjAltNameDN->d.otherName;
> +	if (OBJ_cmp(otherName->type_id, rdn_object) == 0) {
> +	  /* finally, we got the right DN and can now assume that there is a
> +	   * UTF8 string inside.
> +	   */
> +	  asn_string = otherName->value->value.utf8string;
> +	  i = ASN1_STRING_length(asn_string);
> +	  if (i < len) {
> +	    strncpy(buf, ASN1_STRING_data(asn_string), i)[i] = 0;
> +	    radlog(L_INFO,"rlm_eap_tls: cert's subjAltName otherName = % 
> s", buf);
> +	    return 1;
> +	  } else {
> +	    radlog(L_ERR,"rlm_eap_tls: cert's otherName is too long!");
> +	  }
> +	}
> +	break;
> +      case GEN_EMAIL:
> +      case GEN_DNS:
> +      case GEN_URI:
> +	i = ASN1_STRING_length(subjAltNameDN->d.ia5);
> +	i = (i > len) ? len : i;
> +	strncpy(buf,ASN1_STRING_data(subjAltNameDN->d.ia5), i)[i]=0;
> +	radlog(L_INFO,"rlm_eap_tls: cert's subjAltName  %s = %s",  
> type_name, buf);
> +	return 1;
> +      default:
> +	radlog(L_ERR, "rlm_eap_tls: cert's subjAltName  %s not yet  
> implemented!", type_name);
> +	return 0;
> +      }
> +    }
> +  }
> +  return 0;
> +}
> +
> +
> +/*
> + * This table defines the necessary access methods (accessors) and
> + * names of ASN.1 objects as used in OpenSSL with the short-cut
> + * names used in FreeRADIUS.
> + * It can be easily extended by adding an appropriate line, using
> + * preferably the long names of ASN.1 object as defined in OpenSSL's
> + * <openssl/obj_mac.h> or, better, crypto/objects/objects.txt.
> + *
> + * Some attributes can appear in both the newer altName extension  
> as also
> + * in the older <subject>/<issuer> fields, but only as legacy.  
> Therefore,
> + * the altName extensions come first, so that these values are  
> returned!!
> + */
> +
> +static struct x509_access_methods x509_am_table[] =
> +  {
> +    { "UPN",       x509_getSubjectAltName,  
> "otherName:msUPN"         },
> +    { "URI",       x509_getSubjectAltName,  
> "URI"                     },
> +    { "DNS",       x509_getSubjectAltName,  
> "DNS"                     },
> +    { "Email",     x509_getSubjectAltName,  
> "email"                   },
> +    { "Email",     x509_getSubjectName,     
> "emailAddress"            },
> +    { "TCGID",     x509_getSubjectName,     
> "serialNumber"            },
> +    { "TCGID",     x509_getSubjectName,     
> "TCGID"                   },
> +    { "CN",        x509_getSubjectName,     
> "commonName"              },
> +    { "SN",        x509_getSubjectName,     
> "surname"                 },
> +    { "GN",        x509_getSubjectName,     
> "givenName"               },
> +    { "O",         x509_getSubjectName,     
> "organizationName"        },
> +    { "OU",        x509_getSubjectName,     
> "organizationalUnitName"  },
> +    { "ST",        x509_getSubjectName,     
> "stateOrProvinceName"     },
> +    { "L",         x509_getSubjectName,     
> "localityName"            },
> +    { "Title",     x509_getSubjectName,     
> "title"                   },
> +    { "Desc",      x509_getSubjectName,     
> "description"             },
> +    { "Name",      x509_getSubjectName,     
> "name"                    },
> +    { "Initials",  x509_getSubjectName,     
> "initials"                },
> +    { "Pseudonym", x509_getSubjectName,     
> "pseudonym"               },
> +    { "Role",      x509_getSubjectName,     
> "role"                    },
> +
> +
> +
> +  };
> +
> +
> +/*
> + * This is the main function to retrieve almost arbitrary attributes
> + * from a X509 certificate. Using the table above, it uses
> + * x509_getSubjectname() or x509_getSubjAltName()
> + * returns 1 is attribute is available, and its value in buf
> + * with max. len bytes, otherwise 0
> + */
> +int certtls_get_attribute(X509 *cert, char *rad_attr_name, char *  
> buf, int len)
> +{
> +  char single_attr_name[80];
> +  char *startp;
> +  int i, j;
> +
> +  /*
> +   * parse list of attr names and use them as search order list,
> +   * providing for "fallback attributes".
> +   */
> +  startp = rad_attr_name;
> +  i = 0;
> +  while (*startp != '\0') {
> +    while (*startp != '\0' && *startp != ',' &&
> +	   i < sizeof(single_attr_name)-1) {
> +      single_attr_name[i] = *startp;
> +      startp++; i++;
> +    }
> +    single_attr_name[i] = '\0';
> +    if (*startp == ',') startp++;
> +    i = 0;
> +
> +    /* look the value according to the table, return on the first  
> hit! */
> +    for (j = 0; j < sizeof(x509_am_table)/sizeof(struct  
> x509_access_methods); j++) {
> +      if (strcmp(single_attr_name, x509_am_table[j].rad_attr_name)  
> == 0) {
> +	if (x509_am_table[j].attr_getter(cert, x509_am_table 
> [j].x509_attr_name, buf, len) == 1)
> +	  return 1;
> +      }
> +    }
> +  }
> +  return 0;
> +}
> +
> +int certtls_export_attributes(X509 *cert, char *attr_list, char  
> *attr_prefix, EAP_HANDLER *eap)
> +{
> +  char single_attr_name[80];
> +  char *startp;
> +  int i, j;
> +  char *previousname = NULL;
> +  VALUE_PAIR **outvps = NULL;
> +  char avp_attr_name[128];
> +  char buf[128];
> +
> +  /*
> +   * Parse list of attr names and use them as list of attributes
> +   * to be retrieved from the certificate and stored as avpairs
> +   * in the reply queue.
> +   * '*' is the wild card character, adding all known (see table)
> +   * attributes to the reply queue.
> +   */
> +  startp = attr_list;
> +
> +  if (startp == NULL || *startp == '\0') return 0;
> +
> +  outvps = &eap->request->reply->vps;
> +
> +  if (attr_prefix == NULL || *attr_prefix == '\0') {
> +    attr_prefix =  DEFAULT_RADATTR_PREFIX;
> +  }
> +
> +  i = 0;
> +  while (*startp != '\0') {
> +    while (*startp != '\0' && *startp != ',' &&
> +	   i < sizeof(single_attr_name)-1) {
> +      single_attr_name[i] = *startp;
> +      startp++; i++;
> +    }
> +    single_attr_name[i] = '\0';
> +    if (*startp == ',') startp++;
> +    i = 0;
> +
> +    /* traverse table, but in case of success avoid duplicate  
> attrs ! */
> +    for (j = 0; j < sizeof(x509_am_table)/sizeof(struct  
> x509_access_methods); j++) {
> +      if (single_attr_name[0] == '*' || strcmp(single_attr_name,  
> x509_am_table[j].rad_attr_name) == 0) {
> +
> +	if (x509_am_table[j].attr_getter(cert, x509_am_table 
> [j].x509_attr_name, buf, sizeof(buf)-1) == 1) {
> +	  previousname = x509_am_table[j].rad_attr_name;
> +	  strcpy(avp_attr_name, "UserCert-");
> +	  strncat(avp_attr_name, x509_am_table[j].rad_attr_name, sizeof 
> (avp_attr_name)-10);
> +	  add_reply(outvps, avp_attr_name, buf, strlen(buf));
> +	
> +	}
> +      }
> +    }
> +  }
> +
> +
> +}
> +
> +#endif /* NO_OPENSSL */
> diff -urN -x '*~' ../orig/freeradius-1.0.2/src/modules/rlm_eap/ 
> types/rlm_eap_tls/eap_tls.h ./src/modules/rlm_eap/types/rlm_eap_tls/ 
> eap_tls.h
> --- ../orig/freeradius-1.0.2/src/modules/rlm_eap/types/rlm_eap_tls/ 
> eap_tls.h	Wed Apr  7 17:51:45 2004
> +++ ./src/modules/rlm_eap/types/rlm_eap_tls/eap_tls.h	Wed Mar  9  
> 01:17:19 2005
> @@ -200,6 +200,9 @@
>  	int		fragment_size;
>  	int		check_crl;
>  	char		*check_cert_cn;
> +        char            *use_as_cert_cn;
> +        char            *export_cert_attributes;
> +        char            *cert_attributes_prefix;
>  } EAP_TLS_CONF;
>
>
> @@ -242,4 +245,11 @@
>  			    unsigned int size);
>  unsigned int 	record_minus(record_t *buf, unsigned char *ptr,
>  			     unsigned int size);
> +
> +/* certificate check and export */
> +int certtls_get_attribute(X509 *cert, char *rad_attr_name,
> +			  char * buf, int len);
> +int certtls_export_attributes(X509 *cert, char *attr_list,
> +			      char *attr_prefix, EAP_HANDLER *eap);
> +
>  #endif /*_EAP_TLS_H*/
> diff -urN -x '*~' ../orig/freeradius-1.0.2/src/modules/rlm_eap/ 
> types/rlm_eap_tls/rlm_eap_tls.c ./src/modules/rlm_eap/types/ 
> rlm_eap_tls/rlm_eap_tls.c
> --- ../orig/freeradius-1.0.2/src/modules/rlm_eap/types/rlm_eap_tls/ 
> rlm_eap_tls.c	Wed Apr  7 17:51:45 2004
> +++ ./src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c	Wed Mar   
> 9 01:17:19 2005
> @@ -63,6 +63,12 @@
>  	  offsetof(EAP_TLS_CONF, check_crl), NULL, "no"},
>  	{ "check_cert_cn", PW_TYPE_STRING_PTR,
>  	  offsetof(EAP_TLS_CONF, check_cert_cn), NULL, NULL},
> +	{ "use_as_cert_cn", PW_TYPE_STRING_PTR,
> +	  offsetof(EAP_TLS_CONF, use_as_cert_cn), NULL, NULL},
> +	{ "export_cert_attributes", PW_TYPE_STRING_PTR,
> +	  offsetof(EAP_TLS_CONF, export_cert_attributes), NULL, NULL},
> +	{ "cert_attributes_prefix", PW_TYPE_STRING_PTR,
> +	  offsetof(EAP_TLS_CONF, cert_attributes_prefix), NULL, NULL},
>
>   	{ NULL, -1, 0, NULL, NULL }           /* end the list */
>  };
> @@ -480,10 +486,13 @@
>  /*
>   *	Do authentication, by letting EAP-TLS do most of the work.
>   */
> -static int eaptls_authenticate(void *arg UNUSED, EAP_HANDLER  
> *handler)
> +static int eaptls_authenticate(void *instance, EAP_HANDLER *handler)
>  {
>  	eaptls_status_t	status;
>  	tls_session_t *tls_session = (tls_session_t *) handler->opaque;
> +	eap_tls_t       *inst = (eap_tls_t *)instance;
> +	EAP_TLS_CONF    *conf = inst->conf;
> +	
>
>  	DEBUG2("  rlm_eap_tls: Authenticate");
>
> @@ -548,6 +557,16 @@
>  	eaptls_gen_mppe_keys(&handler->request->reply->vps,
>  			     tls_session->ssl,
>  			     "client EAP encryption");
> +
> +	/*
> +	 *      If configured, export attributes of user's certificate
> +	 */
> +	if (conf->export_cert_attributes != NULL) {
> +	  certtls_export_attributes(SSL_get_peer_certificate(tls_session- 
> >ssl),
> +				    conf->export_cert_attributes, conf->cert_attributes_prefix,
> +				    handler);
> +	}
> +
>  	return 1;
>  }
>



More information about the Freeradius-Users mailing list