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 [EMAIL PROTECTED] Thu Mar 10 05:16:40 2005 > From: [EMAIL PROTECTED] (Michael Joosten) > Date: Thu, 10 Mar 2005 06:16:40 +0100 > Subject: certificate patches for EAP TLS module, plus some questions.. > Message-ID: <[EMAIL PROTECTED]> > > 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 = "[EMAIL PROTECTED]" > UserCert-UPN = "[EMAIL PROTECTED]" > 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 = [EMAIL PROTECTED] > + UserCert-CN = John Doe > + UserCert-GN = John > + UserCert-SN = Doe > + UserCert-TCGID = JDOE000123 > + UserCert-UPN = [EMAIL PROTECTED] > + > + 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 > <[EMAIL PROTECTED]> > + 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 > = "[EMAIL PROTECTED]" > + # UserCert-UPN = "[EMAIL PROTECTED]", > 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) [EMAIL PROTECTED]@/../.. [EMAIL > PROTECTED]@/../../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; > } > - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html