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