From freeradius-devel@lists.freeradius.org Thu Mar 10 05:16:40 2005
From: freeradius-devel@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@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@sweatshop.com"
UserCert-UPN = "Joe.User@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@circlecorp.com
+ UserCert-CN = John Doe
+ UserCert-GN = John
+ UserCert-SN = Doe
+ UserCert-TCGID = JDOE000123
+ UserCert-UPN = John.Doe@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@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@company.com"
+ # UserCert-UPN = "Joe.User@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@srcdir@/../.. -I@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;
}