Additional EAP-TLS Logging Option
Hello, I customized a freeradius installation to include an option to log information from the client certificates (subject, issuer, serial, expiration) when doing eap-tls authentication. This is done through a new option, log_certificates, in the tls section of the eap.conf file. Is there any interest in adding this capability into the baseline freeradius? I'm happy to make any changes that would be required to do that. Below are the updated rlm_eap_tls.h and rlm_eap_tls.c. Thanks, Mike Ross /* * rlm_eap_tls.h * * Version: $Id: rlm_eap_tls.h,v 1.13 2007/04/25 14:19:26 aland Exp $ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project * *----------------------------------------------------------------------------- * Modified by The Boeing Company, August 26, 2008 * * Added int log_certificates to the eap_tls_conf struct * * Copyright 2008 The Boeing Company <Michael.Ross2@boeing.com> *----------------------------------------------------------------------------- */ #ifndef _RLM_EAP_TLS_H #define _RLM_EAP_TLS_H #include <freeradius-devel/ident.h> RCSIDH(rlm_eap_tls_h, "$Id: rlm_eap_tls.h,v 1.13 2007/04/25 14:19:26 aland Exp $") #include "eap_tls.h" #include <freeradius-devel/radiusd.h> #include <freeradius-devel/modules.h> /* configured values goes right here */ typedef struct eap_tls_conf { char *private_key_password; char *private_key_file; char *certificate_file; char *random_file; char *ca_path; char *ca_file; char *dh_file; char *rsa_file; char *make_cert_command; int rsa_key; int dh_key; int rsa_key_length; int dh_key_length; int verify_depth; int file_type; int include_length; /* * Always < 4096 (due to radius limit), 0 by default = 2048 */ int fragment_size; int check_crl; char *check_cert_cn; char *cipher_list; char *check_cert_issuer; int log_certificates; /* log_certificates added by Boeing, 9/26/2008 */ } EAP_TLS_CONF; /* This structure gets stored in arg */ typedef struct _eap_tls_t { EAP_TLS_CONF *conf; SSL_CTX *ctx; } eap_tls_t; #endif /* _RLM_EAP_TLS_H */ /* * rlm_eap_tls.c contains the interfaces that are called from eap * * Version: $Id: rlm_eap_tls.c,v 1.54 2008/03/22 08:31:03 aland Exp $ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project * *----------------------------------------------------------------------------- * Modified by The Boeing Company, August 26, 2008 * * Added log_certificates to module_config CONF_PARSER * Added additional authentication logging into cbtls_verify * Added log_full_cert function to write certificate into log file * * Copyright 2008 The Boeing Company <Michael.Ross2@boeing.com> *----------------------------------------------------------------------------- */ #include <freeradius-devel/ident.h> RCSID("$Id: rlm_eap_tls.c,v 1.54 2008/03/22 08:31:03 aland Exp $") #include <freeradius-devel/autoconf.h> #ifdef HAVE_OPENSSL_RAND_H #include <openssl/rand.h> #endif #include "rlm_eap_tls.h" #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif static CONF_PARSER module_config[] = { { "rsa_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" }, { "dh_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" }, { "rsa_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" }, { "dh_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" }, { "verify_depth", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" }, { "CA_path", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, ca_path), NULL, NULL }, { "pem_file_type", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, file_type), NULL, "yes" }, { "private_key_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL }, { "certificate_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL }, { "CA_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, ca_file), NULL, NULL }, { "private_key_password", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL }, { "dh_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, dh_file), NULL, NULL }, { "random_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, random_file), NULL, NULL }, { "fragment_size", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" }, { "include_length", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, include_length), NULL, "yes" }, { "check_crl", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, check_crl), NULL, "no"}, { "check_cert_cn", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_cn), NULL, NULL}, { "cipher_list", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, cipher_list), NULL, NULL}, { "check_cert_issuer", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_issuer), NULL, NULL}, { "make_cert_command", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, make_cert_command), NULL, NULL}, /* log_certificates added by Boeing, 9/26/2008 */ { "log_certificates", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, log_certificates), NULL, "no" }, { NULL, -1, 0, NULL, NULL } /* end the list */ }; /* * log_full_cert added by Boeing, 9/26/2008 * * Write the certificate serial number, expiration * date, issuer, and subject to the log file */ static int log_full_cert(int ok, X509 *cert) { const int ub_serial = 20; const int ub_time = 15; char serialNumber[2*ub_serial+1]; char expiration[ub_time+1]; char issuer[ub_name+1]; char subject[ub_name+1]; char *pTemp = NULL; int local_ok = ok; int i = 0; ASN1_INTEGER *pSn = NULL; ASN1_TIME *pTime = NULL; /* * Get the Serial Number and format for display */ serialNumber[0] = '\0'; pSn = X509_get_serialNumber(cert); pTemp = serialNumber; if ( pSn == NULL || pSn->length > ub_serial ) { radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Serial Number"); local_ok = 0; } else { for (i = 0; i < pSn->length; i++) { sprintf(pTemp, "%02x", (int)pSn->data[i]); pTemp += 2; } } /* * Get the Expiration Date and format for display */ expiration[0] = '\0'; pTime = X509_get_notAfter(cert); if ( pTime == NULL || pTime->length > ub_time ) { radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Expiration"); local_ok = 0; } else { strncpy(expiration, pTime->data, pTime->length); expiration[pTime->length] = '\0'; } /* * Get the Issuer */ issuer[0] = '\0'; X509_NAME_oneline(X509_get_issuer_name(cert), issuer, ub_name); issuer[0] = ' '; issuer[ub_name] = '\0'; for (i = 0; i < ub_name; i++) { if (issuer[i] == '/') { issuer[i] = ','; } } /* * Get the Subject */ subject[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(cert), subject, ub_name); subject[0] = ' '; subject[ub_name] = '\0'; for (i = 0; i < ub_name; i++) { if (subject[i] == '/') { subject[i] = ','; } } /* * Log required attributes */ radlog(L_AUTH, "Certificate: Serial=%s; Expiration=%s; Issuer:%s; Subject:%s", serialNumber, expiration, issuer, subject); return local_ok; } /* * TODO: Check for the type of key exchange * like conf->dh_key */ static int load_dh_params(SSL_CTX *ctx, char *file) { DH *dh = NULL; BIO *bio; if ((bio = BIO_new_file(file, "r")) == NULL) { radlog(L_ERR, "rlm_eap_tls: Unable to open DH file - %s", file); return -1; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dh) { DEBUG2("WARNING: rlm_eap_tls: Unable to set DH parameters. DH cipher suites may not work!"); DEBUG2("WARNING: Fix this by running the OpenSSL command listed in eap.conf"); return 0; } if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) { radlog(L_ERR, "rlm_eap_tls: Unable to set DH parameters"); DH_free(dh); return -1; } DH_free(dh); return 0; } /* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your * application can take in determining if a certificate is * valid. Commonly used steps are: * * 1.Verifying the certificate's signature, and verifying that * the certificate has been issued by a trusted Certificate * Authority. * * 2.Verifying that the certificate is valid for the present date * (i.e. it is being presented within its validity dates). * * 3.Verifying that the certificate has not been revoked by its * issuing Certificate Authority, by checking with respect to a * Certificate Revocation List (CRL). * * 4.Verifying that the credentials presented by the certificate * fulfill additional requirements specific to the application, * such as with respect to access control lists or with respect * to OCSP (Online Certificate Status Processing). * * NOTE: This callback will be called multiple times based on the * depth of the root certificate chain */ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) { char subject[1024]; /* Used for the subject name */ char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; EAP_HANDLER *handler = NULL; X509 *client_cert; SSL *ssl; int err, depth; EAP_TLS_CONF *conf; int my_ok = ok; client_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); /* * Moved below the conf check by Boeing, 9/26/2008 */ /* *if (!my_ok) { * radlog(L_ERR,"--> verify error:num=%d:%s\n",err, * X509_verify_cert_error_string(err)); * return my_ok; *} */ /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); /* * Copied from above by Boeing, 9/26/2008 */ if (!my_ok) { radlog(L_ERR,"--> verify error:num=%d:%s\n",err, X509_verify_cert_error_string(err)); /* * Added by Boeing, 9/26/2008 * If the config file is set to log certificates * log the certificate if there has been an authentication * error */ if (conf->log_certificates) { log_full_cert(my_ok, client_cert); } return my_ok; } /* * Get the Subject & Issuer */ subject[0] = issuer[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(client_cert), subject, sizeof(subject)); X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, sizeof(issuer)); subject[sizeof(subject) - 1] = '\0'; issuer[sizeof(issuer) - 1] = '\0'; /* * Get the Common Name */ X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0'; switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: radlog(L_ERR, "issuer= %s\n", issuer); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: radlog(L_ERR, "notBefore="); #if 0 ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert)); #endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: radlog(L_ERR, "notAfter="); #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); #endif break; } /* * If we're at the actual client cert, apply additional * checks. */ if (depth == 0) { /* * If the conf tells us to, check cert issuer * against the specified value and fail * verification if they don't match. */ if (conf->check_cert_issuer && (strcmp(issuer, conf->check_cert_issuer) != 0)) { radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer); my_ok = 0; } /* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the * previous checks passed. */ if (my_ok && conf->check_cert_cn) { if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) { radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", conf->check_cert_cn); /* if this fails, fail the verification */ my_ok = 0; } else { DEBUG2(" rlm_eap_tls: checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str); if (strcmp(cn_str, common_name) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str); my_ok = 0; } } } /* check_cert_cn */ /* * Added by Boeing, 9/26/2008 * If the config file is set to log certificates * log the certificate */ if (conf->log_certificates) { my_ok = log_full_cert(my_ok, client_cert); } } /* depth == 0 */ if (debug_flag > 0) { DEBUG2("chain-depth=%d, ", depth); DEBUG2("error=%d", err); DEBUG2("--> User-Name = %s", handler->identity); DEBUG2("--> BUF-Name = %s", common_name); DEBUG2("--> subject = %s", subject); DEBUG2("--> issuer = %s", issuer); DEBUG2("--> verify return:%d", my_ok); } return my_ok; } /* * Create Global context SSL and use it in every new session * * - Load the trusted CAs * - Load the Private key & the certificate * - Set the Context options & Verify options */ static SSL_CTX *init_tls_ctx(EAP_TLS_CONF *conf) { SSL_METHOD *meth; SSL_CTX *ctx; X509_STORE *certstore; int verify_mode = SSL_VERIFY_NONE; int ctx_options = 0; int type; /* * Add all the default ciphers and message digests * Create our context. */ SSL_library_init(); SSL_load_error_strings(); meth = TLSv1_method(); ctx = SSL_CTX_new(meth); /* * Identify the type of certificates that needs to be loaded */ if (conf->file_type) { type = SSL_FILETYPE_PEM; } else { type = SSL_FILETYPE_ASN1; } /* * Set the password to load private key */ if (conf->private_key_password) { SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->private_key_password); SSL_CTX_set_default_passwd_cb(ctx, cbtls_password); } /* * Load our keys and certificates * * If certificates are of type PEM then we can make use * of cert chain authentication using openssl api call * SSL_CTX_use_certificate_chain_file. Please see how * the cert chain needs to be given in PEM from * openSSL.org */ if (type == SSL_FILETYPE_PEM) { if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading certificate file %s", conf->certificate_file); return NULL; } } else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading certificate file %s", conf->certificate_file); return NULL; } /* Load the CAs we trust */ if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading Trusted root CA list %s",conf->ca_file ); return NULL; } if (conf->ca_file && *conf->ca_file) SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(conf->ca_file)); if (!(SSL_CTX_use_PrivateKey_file(ctx, conf->private_key_file, type))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading private key file %s", conf->private_key_file); return NULL; } /* * Check if the loaded private key is the right one */ if (!SSL_CTX_check_private_key(ctx)) { radlog(L_ERR, "rlm_eap_tls: Private key does not match the certificate public key"); return NULL; } /* * Set ctx_options */ ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; /* * SSL_OP_SINGLE_DH_USE must be used in order to prevent * small subgroup attacks and forward secrecy. Always * using * * SSL_OP_SINGLE_DH_USE has an impact on the computer * time needed during negotiation, but it is not very * large. */ ctx_options |= SSL_OP_SINGLE_DH_USE; /* * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS to work around issues * in Windows Vista client. * http://www.openssl.org/~bodo/tls-cbc.txt * http://www.nabble.com/(RADIATOR)-Radiator-Version-3.16-released-t2600070.htm... */ ctx_options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; SSL_CTX_set_options(ctx, ctx_options); /* * TODO: Set the RSA & DH * SSL_CTX_set_tmp_rsa_callback(ctx, cbtls_rsa); * SSL_CTX_set_tmp_dh_callback(ctx, cbtls_dh); */ /* * set the message callback to identify the type of * message. For every new session, there can be a * different callback argument. * * SSL_CTX_set_msg_callback(ctx, cbtls_msg); */ /* Set Info callback */ SSL_CTX_set_info_callback(ctx, cbtls_info); /* * Check the certificates for revocation. */ #ifdef X509_V_FLAG_CRL_CHECK if (conf->check_crl) { certstore = SSL_CTX_get_cert_store(ctx); if (certstore == NULL) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading Certificate Store"); return NULL; } X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK); } #endif /* * Set verify modes * Always verify the peer certificate */ verify_mode |= SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; SSL_CTX_set_verify(ctx, verify_mode, cbtls_verify); if (conf->verify_depth) { SSL_CTX_set_verify_depth(ctx, conf->verify_depth); } /* Load randomness */ if (!(RAND_load_file(conf->random_file, 1024*1024))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error loading randomness"); return NULL; } /* * Set the cipher list if we were told to */ if (conf->cipher_list) { if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) { radlog(L_ERR, "rlm_eap_tls: Error setting cipher list"); return NULL; } } return ctx; } /* * Detach the EAP-TLS module. */ static int eaptls_detach(void *arg) { EAP_TLS_CONF *conf; eap_tls_t *inst; inst = (eap_tls_t *) arg; conf = inst->conf; if (conf) { memset(conf, 0, sizeof(*conf)); free(inst->conf); inst->conf = NULL; } if (inst->ctx) SSL_CTX_free(inst->ctx); inst->ctx = NULL; free(inst); return 0; } /* * Attach the EAP-TLS module. */ static int eaptls_attach(CONF_SECTION *cs, void **instance) { EAP_TLS_CONF *conf; eap_tls_t *inst; /* Store all these values in the data structure for later references */ inst = (eap_tls_t *)malloc(sizeof(*inst)); if (!inst) { radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(inst, 0, sizeof(*inst)); /* * Parse the config file & get all the configured values */ conf = (EAP_TLS_CONF *)malloc(sizeof(*conf)); if (conf == NULL) { free(inst); radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(conf, 0, sizeof(*conf)); inst->conf = conf; if (cf_section_parse(cs, conf, module_config) < 0) { eaptls_detach(inst); return -1; } /* * The EAP RFC's say 1020, but we're less picky. */ if (conf->fragment_size < 100) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too small."); eaptls_detach(inst); return -1; } /* * The maximum size for a RADIUS packet is 4096, * minus the header (20), Message-Authenticator (18), * and State (18), etc. results in about 4000 bytes of data * that can be devoted *solely* to EAP. */ if (conf->fragment_size > 4000) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too large."); eaptls_detach(inst); return -1; } /* * Account for the EAP header (4), and the EAP-TLS header * (6), as per Section 4.2 of RFC 2716. What's left is * the maximum amount of data we read from a TLS buffer. */ conf->fragment_size -= 10; /* * This magic makes the administrators life HUGELY easier * on initial deployments. * * If the server starts up in debugging mode, AND the * bootstrap command is configured, AND it exists, AND * there is no server certificate */ if (conf->make_cert_command && (debug_flag >= 2)) { struct stat buf; if ((stat(conf->make_cert_command, &buf) == 0) && (stat(conf->certificate_file, &buf) < 0) && (errno == ENOENT) && (radius_exec_program(conf->make_cert_command, NULL, 1, NULL, 0, NULL, NULL, 0) != 0)) { eaptls_detach(inst); return -1; } } /* * Initialize TLS */ inst->ctx = init_tls_ctx(conf); if (inst->ctx == NULL) { eaptls_detach(inst); return -1; } if (load_dh_params(inst->ctx, conf->dh_file) < 0) { eaptls_detach(inst); return -1; } *instance = inst; return 0; } /* * Send an initial eap-tls request to the peer. * * Frame eap reply packet. * len = header + type + tls_typedata * tls_typedata = flags(Start (S) bit set, and no data) * * Once having received the peer's Identity, the EAP server MUST * respond with an EAP-TLS/Start packet, which is an * EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit * set, and no data. The EAP-TLS conversation will then begin, * with the peer sending an EAP-Response packet with * EAP-Type = EAP-TLS. The data field of that packet will * be the TLS data. * * Fragment length is Framed-MTU - 4. * * http://mail.frascone.com/pipermail/public/eap/2003-July/001426.html */ static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler) { int status; tls_session_t *ssn; eap_tls_t *inst; VALUE_PAIR *vp; int client_cert = TRUE; int verify_mode = 0; inst = (eap_tls_t *)type_arg; /* * If we're TTLS or PEAP, then do NOT require a client * certificate. * * FIXME: This should be more configurable. */ if (handler->eap_type != PW_EAP_TLS) { vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT); if (!vp) { client_cert = FALSE; } else { client_cert = vp->vp_integer; } } /* * Every new session is started only from EAP-TLS-START. * Before Sending EAP-TLS-START, open a new SSL session. * Create all the required data structures & store them * in Opaque. So that we can use these data structures * when we get the response */ ssn = eaptls_new_session(inst->ctx, client_cert); if (!ssn) { return 0; } /* * Verify the peer certificate, if asked. */ if (client_cert) { DEBUG2(" rlm_eap_tls: Requiring client certificate"); verify_mode = SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; } SSL_set_verify(ssn->ssl, verify_mode, cbtls_verify); /* * Create a structure for all the items required to be * verified for each client and set that as opaque data * structure. * * NOTE: If we want to set each item sepearately then * this index should be global. */ SSL_set_ex_data(ssn->ssl, 0, (void *)handler); SSL_set_ex_data(ssn->ssl, 1, (void *)inst->conf); ssn->length_flag = inst->conf->include_length; /* * We use default fragment size, unless the Framed-MTU * tells us it's too big. Note that we do NOT account * for the EAP-TLS headers if conf->fragment_size is * large, because that config item looks to be confusing. * * i.e. it should REALLY be called MTU, and the code here * should figure out what that means for TLS fragment size. * asking the administrator to know the internal details * of EAP-TLS in order to calculate fragment sizes is * just too much. */ ssn->offset = inst->conf->fragment_size; vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU); if (vp && ((vp->vp_integer - 14) < ssn->offset)) { /* * Discount the Framed-MTU by: * 4 : EAPOL header * 4 : EAP header (code + id + length) * 1 : EAP type == EAP-TLS * 1 : EAP-TLS Flags * 4 : EAP-TLS Message length * (even if conf->include_length == 0, * just to be lazy). * --- * 14 */ ssn->offset = vp->vp_integer - 14; } handler->opaque = ((void *)ssn); handler->free_opaque = session_free; DEBUG2(" rlm_eap_tls: Initiate"); /* * PEAP-specific breakage. */ if (handler->eap_type == PW_EAP_PEAP) { /* * As it is a poorly designed protocol, PEAP uses * bits in the TLS header to indicate PEAP * version numbers. For now, we only support * PEAP version 0, so it doesn't matter too much. * However, if we support later versions of PEAP, * we will need this flag to indicate which * version we're currently dealing with. */ ssn->peap_flag = 0x00; /* * PEAP version 0 requires 'include_length = no', * so rather than hoping the user figures it out, * we force it here. */ ssn->length_flag = 0; } /* * TLS session initialization is over. Now handle TLS * related handshaking or application data. */ status = eaptls_start(handler->eap_ds, ssn->peap_flag); DEBUG2(" rlm_eap_tls: Start returned %d", status); if (status == 0) return 0; /* * The next stage to process the packet. */ handler->stage = AUTHENTICATE; return 1; } /* * Do authentication, by letting EAP-TLS do most of the work. */ static int eaptls_authenticate(void *arg UNUSED, EAP_HANDLER *handler) { eaptls_status_t status; tls_session_t *tls_session = (tls_session_t *) handler->opaque; DEBUG2(" rlm_eap_tls: Authenticate"); status = eaptls_process(handler); DEBUG2(" eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, return an * EAP-TLS-Success packet here. */ case EAPTLS_SUCCESS: break; /* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case EAPTLS_HANDLED: return 1; /* * Handshake is done, proceed with decoding tunneled * data. */ case EAPTLS_OK: DEBUG2(" rlm_eap_tls: Received unexpected tunneled data after successful handshake."); #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { unsigned int i; unsigned int data_len; unsigned char buffer[1024]; data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer)); log_debug(" Tunneled data (%u bytes)\n", data_len); for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, " %x: ", i); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); fprintf(fr_log_fp, "%02x ", buffer[i]); } fprintf(fr_log_fp, "\n"); } #endif eaptls_fail(handler->eap_ds, 0); return 0; break; /* * Anything else: fail. */ default: return 0; } /* * Success: Return MPPE keys. */ eaptls_success(handler->eap_ds, 0); eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, "client EAP encryption"); return 1; } /* * The module name should be the only globally exported symbol. * That is, everything else should be 'static'. */ EAP_TYPE rlm_eap_tls = { "eap_tls", eaptls_attach, /* attach */ eaptls_initiate, /* Start the initial request */ NULL, /* authorization */ eaptls_authenticate, /* authentication */ eaptls_detach /* detach */ };
Something I've thought would be nice is if there was an option to stuff this sort of information into FreeRADIUS internal attributes so that modules - like mod_jradius, then therefore the JRadius server - could use the info. Perhaps this is now possible, but wasn't the last time I checked. On Tue, 2010-07-27 at 07:55 -0700, Ross, Michael wrote:
Hello,
I customized a freeradius installation to include an option to log information from the client certificates (subject, issuer, serial, expiration) when doing eap-tls authentication. This is done through a new option, log_certificates, in the tls section of the eap.conf file. Is there any interest in adding this capability into the baseline freeradius? I'm happy to make any changes that would be required to do that. Below are the updated rlm_eap_tls.h and rlm_eap_tls.c.
Thanks, Mike Ross
/* * rlm_eap_tls.h * * Version: $Id: rlm_eap_tls.h,v 1.13 2007/04/25 14:19:26 aland Exp $ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project * *----------------------------------------------------------------------------- * Modified by The Boeing Company, August 26, 2008 * * Added int log_certificates to the eap_tls_conf struct * * Copyright 2008 The Boeing Company <Michael.Ross2@boeing.com> *----------------------------------------------------------------------------- */ #ifndef _RLM_EAP_TLS_H #define _RLM_EAP_TLS_H
#include <freeradius-devel/ident.h> RCSIDH(rlm_eap_tls_h, "$Id: rlm_eap_tls.h,v 1.13 2007/04/25 14:19:26 aland Exp $")
#include "eap_tls.h"
#include <freeradius-devel/radiusd.h> #include <freeradius-devel/modules.h>
/* configured values goes right here */ typedef struct eap_tls_conf { char *private_key_password; char *private_key_file; char *certificate_file; char *random_file; char *ca_path; char *ca_file; char *dh_file; char *rsa_file; char *make_cert_command; int rsa_key; int dh_key; int rsa_key_length; int dh_key_length; int verify_depth; int file_type; int include_length;
/* * Always < 4096 (due to radius limit), 0 by default = 2048 */ int fragment_size; int check_crl; char *check_cert_cn; char *cipher_list; char *check_cert_issuer; int log_certificates; /* log_certificates added by Boeing, 9/26/2008 */ } EAP_TLS_CONF;
/* This structure gets stored in arg */ typedef struct _eap_tls_t { EAP_TLS_CONF *conf; SSL_CTX *ctx; } eap_tls_t;
#endif /* _RLM_EAP_TLS_H */
/* * rlm_eap_tls.c contains the interfaces that are called from eap * * Version: $Id: rlm_eap_tls.c,v 1.54 2008/03/22 08:31:03 aland Exp $ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project * *----------------------------------------------------------------------------- * Modified by The Boeing Company, August 26, 2008 * * Added log_certificates to module_config CONF_PARSER * Added additional authentication logging into cbtls_verify * Added log_full_cert function to write certificate into log file * * Copyright 2008 The Boeing Company <Michael.Ross2@boeing.com> *----------------------------------------------------------------------------- */
#include <freeradius-devel/ident.h> RCSID("$Id: rlm_eap_tls.c,v 1.54 2008/03/22 08:31:03 aland Exp $")
#include <freeradius-devel/autoconf.h>
#ifdef HAVE_OPENSSL_RAND_H #include <openssl/rand.h> #endif
#include "rlm_eap_tls.h"
#ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif
static CONF_PARSER module_config[] = { { "rsa_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" }, { "dh_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" }, { "rsa_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" }, { "dh_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" }, { "verify_depth", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" }, { "CA_path", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, ca_path), NULL, NULL }, { "pem_file_type", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, file_type), NULL, "yes" }, { "private_key_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL }, { "certificate_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL }, { "CA_file", PW_TYPE_FILENAME, offsetof(EAP_TLS_CONF, ca_file), NULL, NULL }, { "private_key_password", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL }, { "dh_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, dh_file), NULL, NULL }, { "random_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, random_file), NULL, NULL }, { "fragment_size", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" }, { "include_length", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, include_length), NULL, "yes" }, { "check_crl", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, check_crl), NULL, "no"}, { "check_cert_cn", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_cn), NULL, NULL}, { "cipher_list", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, cipher_list), NULL, NULL}, { "check_cert_issuer", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_issuer), NULL, NULL}, { "make_cert_command", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, make_cert_command), NULL, NULL}, /* log_certificates added by Boeing, 9/26/2008 */ { "log_certificates", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, log_certificates), NULL, "no" },
{ NULL, -1, 0, NULL, NULL } /* end the list */ };
/* * log_full_cert added by Boeing, 9/26/2008 * * Write the certificate serial number, expiration * date, issuer, and subject to the log file */ static int log_full_cert(int ok, X509 *cert) { const int ub_serial = 20; const int ub_time = 15;
char serialNumber[2*ub_serial+1]; char expiration[ub_time+1]; char issuer[ub_name+1]; char subject[ub_name+1]; char *pTemp = NULL; int local_ok = ok; int i = 0; ASN1_INTEGER *pSn = NULL; ASN1_TIME *pTime = NULL;
/* * Get the Serial Number and format for display */ serialNumber[0] = '\0'; pSn = X509_get_serialNumber(cert); pTemp = serialNumber; if ( pSn == NULL || pSn->length > ub_serial ) { radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Serial Number"); local_ok = 0; } else { for (i = 0; i < pSn->length; i++) { sprintf(pTemp, "%02x", (int)pSn->data[i]); pTemp += 2; } }
/* * Get the Expiration Date and format for display */ expiration[0] = '\0'; pTime = X509_get_notAfter(cert); if ( pTime == NULL || pTime->length > ub_time ) { radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Expiration"); local_ok = 0; } else { strncpy(expiration, pTime->data, pTime->length); expiration[pTime->length] = '\0'; }
/* * Get the Issuer */ issuer[0] = '\0'; X509_NAME_oneline(X509_get_issuer_name(cert), issuer, ub_name); issuer[0] = ' '; issuer[ub_name] = '\0'; for (i = 0; i < ub_name; i++) { if (issuer[i] == '/') { issuer[i] = ','; } }
/* * Get the Subject */ subject[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(cert), subject, ub_name); subject[0] = ' '; subject[ub_name] = '\0'; for (i = 0; i < ub_name; i++) { if (subject[i] == '/') { subject[i] = ','; } }
/* * Log required attributes */ radlog(L_AUTH, "Certificate: Serial=%s; Expiration=%s; Issuer:%s; Subject:%s", serialNumber, expiration, issuer, subject);
return local_ok; }
/* * TODO: Check for the type of key exchange * like conf->dh_key */ static int load_dh_params(SSL_CTX *ctx, char *file) { DH *dh = NULL; BIO *bio;
if ((bio = BIO_new_file(file, "r")) == NULL) { radlog(L_ERR, "rlm_eap_tls: Unable to open DH file - %s", file); return -1; }
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (!dh) { DEBUG2("WARNING: rlm_eap_tls: Unable to set DH parameters. DH cipher suites may not work!"); DEBUG2("WARNING: Fix this by running the OpenSSL command listed in eap.conf"); return 0; }
if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) { radlog(L_ERR, "rlm_eap_tls: Unable to set DH parameters"); DH_free(dh); return -1; }
DH_free(dh); return 0; }
/* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your * application can take in determining if a certificate is * valid. Commonly used steps are: * * 1.Verifying the certificate's signature, and verifying that * the certificate has been issued by a trusted Certificate * Authority. * * 2.Verifying that the certificate is valid for the present date * (i.e. it is being presented within its validity dates). * * 3.Verifying that the certificate has not been revoked by its * issuing Certificate Authority, by checking with respect to a * Certificate Revocation List (CRL). * * 4.Verifying that the credentials presented by the certificate * fulfill additional requirements specific to the application, * such as with respect to access control lists or with respect * to OCSP (Online Certificate Status Processing). * * NOTE: This callback will be called multiple times based on the * depth of the root certificate chain */ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) { char subject[1024]; /* Used for the subject name */ char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; EAP_HANDLER *handler = NULL; X509 *client_cert; SSL *ssl; int err, depth; EAP_TLS_CONF *conf; int my_ok = ok;
client_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx);
/* * Moved below the conf check by Boeing, 9/26/2008 */ /* *if (!my_ok) { * radlog(L_ERR,"--> verify error:num=%d:%s\n",err, * X509_verify_cert_error_string(err)); * return my_ok; *} */
/* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1);
/* * Copied from above by Boeing, 9/26/2008 */ if (!my_ok) { radlog(L_ERR,"--> verify error:num=%d:%s\n",err, X509_verify_cert_error_string(err)); /* * Added by Boeing, 9/26/2008 * If the config file is set to log certificates * log the certificate if there has been an authentication * error */ if (conf->log_certificates) { log_full_cert(my_ok, client_cert); } return my_ok; }
/* * Get the Subject & Issuer */ subject[0] = issuer[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(client_cert), subject, sizeof(subject)); X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, sizeof(issuer));
subject[sizeof(subject) - 1] = '\0'; issuer[sizeof(issuer) - 1] = '\0';
/* * Get the Common Name */ X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0';
switch (ctx->error) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: radlog(L_ERR, "issuer= %s\n", issuer); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: radlog(L_ERR, "notBefore="); #if 0 ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert)); #endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: radlog(L_ERR, "notAfter="); #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); #endif break; }
/* * If we're at the actual client cert, apply additional * checks. */ if (depth == 0) { /* * If the conf tells us to, check cert issuer * against the specified value and fail * verification if they don't match. */ if (conf->check_cert_issuer && (strcmp(issuer, conf->check_cert_issuer) != 0)) { radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer); my_ok = 0; }
/* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the * previous checks passed. */ if (my_ok && conf->check_cert_cn) { if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) { radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", conf->check_cert_cn); /* if this fails, fail the verification */ my_ok = 0; } else { DEBUG2(" rlm_eap_tls: checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str); if (strcmp(cn_str, common_name) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str); my_ok = 0; } } } /* check_cert_cn */
/* * Added by Boeing, 9/26/2008 * If the config file is set to log certificates * log the certificate */ if (conf->log_certificates) { my_ok = log_full_cert(my_ok, client_cert); }
} /* depth == 0 */
if (debug_flag > 0) { DEBUG2("chain-depth=%d, ", depth); DEBUG2("error=%d", err);
DEBUG2("--> User-Name = %s", handler->identity); DEBUG2("--> BUF-Name = %s", common_name); DEBUG2("--> subject = %s", subject); DEBUG2("--> issuer = %s", issuer); DEBUG2("--> verify return:%d", my_ok); } return my_ok; }
/* * Create Global context SSL and use it in every new session * * - Load the trusted CAs * - Load the Private key & the certificate * - Set the Context options & Verify options */ static SSL_CTX *init_tls_ctx(EAP_TLS_CONF *conf) { SSL_METHOD *meth; SSL_CTX *ctx; X509_STORE *certstore; int verify_mode = SSL_VERIFY_NONE; int ctx_options = 0; int type;
/* * Add all the default ciphers and message digests * Create our context. */ SSL_library_init(); SSL_load_error_strings();
meth = TLSv1_method(); ctx = SSL_CTX_new(meth);
/* * Identify the type of certificates that needs to be loaded */ if (conf->file_type) { type = SSL_FILETYPE_PEM; } else { type = SSL_FILETYPE_ASN1; }
/* * Set the password to load private key */ if (conf->private_key_password) { SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->private_key_password); SSL_CTX_set_default_passwd_cb(ctx, cbtls_password); }
/* * Load our keys and certificates * * If certificates are of type PEM then we can make use * of cert chain authentication using openssl api call * SSL_CTX_use_certificate_chain_file. Please see how * the cert chain needs to be given in PEM from * openSSL.org */ if (type == SSL_FILETYPE_PEM) { if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading certificate file %s", conf->certificate_file); return NULL; }
} else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading certificate file %s", conf->certificate_file); return NULL; }
/* Load the CAs we trust */ if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading Trusted root CA list %s",conf->ca_file ); return NULL; } if (conf->ca_file && *conf->ca_file) SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(conf->ca_file)); if (!(SSL_CTX_use_PrivateKey_file(ctx, conf->private_key_file, type))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading private key file %s", conf->private_key_file); return NULL; }
/* * Check if the loaded private key is the right one */ if (!SSL_CTX_check_private_key(ctx)) { radlog(L_ERR, "rlm_eap_tls: Private key does not match the certificate public key"); return NULL; }
/* * Set ctx_options */ ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3;
/* * SSL_OP_SINGLE_DH_USE must be used in order to prevent * small subgroup attacks and forward secrecy. Always * using * * SSL_OP_SINGLE_DH_USE has an impact on the computer * time needed during negotiation, but it is not very * large. */ ctx_options |= SSL_OP_SINGLE_DH_USE;
/* * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS to work around issues * in Windows Vista client. * http://www.openssl.org/~bodo/tls-cbc.txt * http://www.nabble.com/(RADIATOR)-Radiator-Version-3.16-released-t2600070.htm... */ ctx_options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
SSL_CTX_set_options(ctx, ctx_options);
/* * TODO: Set the RSA & DH * SSL_CTX_set_tmp_rsa_callback(ctx, cbtls_rsa); * SSL_CTX_set_tmp_dh_callback(ctx, cbtls_dh); */
/* * set the message callback to identify the type of * message. For every new session, there can be a * different callback argument. * * SSL_CTX_set_msg_callback(ctx, cbtls_msg); */
/* Set Info callback */ SSL_CTX_set_info_callback(ctx, cbtls_info);
/* * Check the certificates for revocation. */ #ifdef X509_V_FLAG_CRL_CHECK if (conf->check_crl) { certstore = SSL_CTX_get_cert_store(ctx); if (certstore == NULL) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error reading Certificate Store"); return NULL; } X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK); } #endif
/* * Set verify modes * Always verify the peer certificate */ verify_mode |= SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; SSL_CTX_set_verify(ctx, verify_mode, cbtls_verify);
if (conf->verify_depth) { SSL_CTX_set_verify_depth(ctx, conf->verify_depth); }
/* Load randomness */ if (!(RAND_load_file(conf->random_file, 1024*1024))) { radlog(L_ERR, "rlm_eap: SSL error %s", ERR_error_string(ERR_get_error(), NULL)); radlog(L_ERR, "rlm_eap_tls: Error loading randomness"); return NULL; }
/* * Set the cipher list if we were told to */ if (conf->cipher_list) { if (!SSL_CTX_set_cipher_list(ctx, conf->cipher_list)) { radlog(L_ERR, "rlm_eap_tls: Error setting cipher list"); return NULL; } }
return ctx; }
/* * Detach the EAP-TLS module. */ static int eaptls_detach(void *arg) { EAP_TLS_CONF *conf; eap_tls_t *inst;
inst = (eap_tls_t *) arg; conf = inst->conf;
if (conf) { memset(conf, 0, sizeof(*conf)); free(inst->conf); inst->conf = NULL; }
if (inst->ctx) SSL_CTX_free(inst->ctx); inst->ctx = NULL;
free(inst);
return 0; }
/* * Attach the EAP-TLS module. */ static int eaptls_attach(CONF_SECTION *cs, void **instance) { EAP_TLS_CONF *conf; eap_tls_t *inst;
/* Store all these values in the data structure for later references */ inst = (eap_tls_t *)malloc(sizeof(*inst)); if (!inst) { radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(inst, 0, sizeof(*inst));
/* * Parse the config file & get all the configured values */ conf = (EAP_TLS_CONF *)malloc(sizeof(*conf)); if (conf == NULL) { free(inst); radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(conf, 0, sizeof(*conf));
inst->conf = conf; if (cf_section_parse(cs, conf, module_config) < 0) { eaptls_detach(inst); return -1; }
/* * The EAP RFC's say 1020, but we're less picky. */ if (conf->fragment_size < 100) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too small."); eaptls_detach(inst); return -1; }
/* * The maximum size for a RADIUS packet is 4096, * minus the header (20), Message-Authenticator (18), * and State (18), etc. results in about 4000 bytes of data * that can be devoted *solely* to EAP. */ if (conf->fragment_size > 4000) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too large."); eaptls_detach(inst); return -1; }
/* * Account for the EAP header (4), and the EAP-TLS header * (6), as per Section 4.2 of RFC 2716. What's left is * the maximum amount of data we read from a TLS buffer. */ conf->fragment_size -= 10;
/* * This magic makes the administrators life HUGELY easier * on initial deployments. * * If the server starts up in debugging mode, AND the * bootstrap command is configured, AND it exists, AND * there is no server certificate */ if (conf->make_cert_command && (debug_flag >= 2)) { struct stat buf;
if ((stat(conf->make_cert_command, &buf) == 0) && (stat(conf->certificate_file, &buf) < 0) && (errno == ENOENT) && (radius_exec_program(conf->make_cert_command, NULL, 1, NULL, 0, NULL, NULL, 0) != 0)) { eaptls_detach(inst); return -1; } }
/* * Initialize TLS */ inst->ctx = init_tls_ctx(conf); if (inst->ctx == NULL) { eaptls_detach(inst); return -1; }
if (load_dh_params(inst->ctx, conf->dh_file) < 0) { eaptls_detach(inst); return -1; }
*instance = inst;
return 0; }
/* * Send an initial eap-tls request to the peer. * * Frame eap reply packet. * len = header + type + tls_typedata * tls_typedata = flags(Start (S) bit set, and no data) * * Once having received the peer's Identity, the EAP server MUST * respond with an EAP-TLS/Start packet, which is an * EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit * set, and no data. The EAP-TLS conversation will then begin, * with the peer sending an EAP-Response packet with * EAP-Type = EAP-TLS. The data field of that packet will * be the TLS data. * * Fragment length is Framed-MTU - 4. * * http://mail.frascone.com/pipermail/public/eap/2003-July/001426.html */ static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler) { int status; tls_session_t *ssn; eap_tls_t *inst; VALUE_PAIR *vp; int client_cert = TRUE; int verify_mode = 0;
inst = (eap_tls_t *)type_arg;
/* * If we're TTLS or PEAP, then do NOT require a client * certificate. * * FIXME: This should be more configurable. */ if (handler->eap_type != PW_EAP_TLS) { vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT); if (!vp) { client_cert = FALSE; } else { client_cert = vp->vp_integer; } }
/* * Every new session is started only from EAP-TLS-START. * Before Sending EAP-TLS-START, open a new SSL session. * Create all the required data structures & store them * in Opaque. So that we can use these data structures * when we get the response */ ssn = eaptls_new_session(inst->ctx, client_cert); if (!ssn) { return 0; }
/* * Verify the peer certificate, if asked. */ if (client_cert) { DEBUG2(" rlm_eap_tls: Requiring client certificate"); verify_mode = SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; } SSL_set_verify(ssn->ssl, verify_mode, cbtls_verify);
/* * Create a structure for all the items required to be * verified for each client and set that as opaque data * structure. * * NOTE: If we want to set each item sepearately then * this index should be global. */ SSL_set_ex_data(ssn->ssl, 0, (void *)handler); SSL_set_ex_data(ssn->ssl, 1, (void *)inst->conf);
ssn->length_flag = inst->conf->include_length;
/* * We use default fragment size, unless the Framed-MTU * tells us it's too big. Note that we do NOT account * for the EAP-TLS headers if conf->fragment_size is * large, because that config item looks to be confusing. * * i.e. it should REALLY be called MTU, and the code here * should figure out what that means for TLS fragment size. * asking the administrator to know the internal details * of EAP-TLS in order to calculate fragment sizes is * just too much. */ ssn->offset = inst->conf->fragment_size; vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU); if (vp && ((vp->vp_integer - 14) < ssn->offset)) { /* * Discount the Framed-MTU by: * 4 : EAPOL header * 4 : EAP header (code + id + length) * 1 : EAP type == EAP-TLS * 1 : EAP-TLS Flags * 4 : EAP-TLS Message length * (even if conf->include_length == 0, * just to be lazy). * --- * 14 */ ssn->offset = vp->vp_integer - 14; }
handler->opaque = ((void *)ssn); handler->free_opaque = session_free;
DEBUG2(" rlm_eap_tls: Initiate");
/* * PEAP-specific breakage. */ if (handler->eap_type == PW_EAP_PEAP) { /* * As it is a poorly designed protocol, PEAP uses * bits in the TLS header to indicate PEAP * version numbers. For now, we only support * PEAP version 0, so it doesn't matter too much. * However, if we support later versions of PEAP, * we will need this flag to indicate which * version we're currently dealing with. */ ssn->peap_flag = 0x00;
/* * PEAP version 0 requires 'include_length = no', * so rather than hoping the user figures it out, * we force it here. */ ssn->length_flag = 0; }
/* * TLS session initialization is over. Now handle TLS * related handshaking or application data. */ status = eaptls_start(handler->eap_ds, ssn->peap_flag); DEBUG2(" rlm_eap_tls: Start returned %d", status); if (status == 0) return 0;
/* * The next stage to process the packet. */ handler->stage = AUTHENTICATE;
return 1; }
/* * Do authentication, by letting EAP-TLS do most of the work. */ static int eaptls_authenticate(void *arg UNUSED, EAP_HANDLER *handler) { eaptls_status_t status; tls_session_t *tls_session = (tls_session_t *) handler->opaque;
DEBUG2(" rlm_eap_tls: Authenticate");
status = eaptls_process(handler); DEBUG2(" eaptls_process returned %d\n", status); switch (status) { /* * EAP-TLS handshake was successful, return an * EAP-TLS-Success packet here. */ case EAPTLS_SUCCESS: break;
/* * The TLS code is still working on the TLS * exchange, and it's a valid TLS request. * do nothing. */ case EAPTLS_HANDLED: return 1;
/* * Handshake is done, proceed with decoding tunneled * data. */ case EAPTLS_OK: DEBUG2(" rlm_eap_tls: Received unexpected tunneled data after successful handshake."); #ifndef NDEBUG if ((debug_flag > 2) && fr_log_fp) { unsigned int i; unsigned int data_len; unsigned char buffer[1024];
data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer)); log_debug(" Tunneled data (%u bytes)\n", data_len); for (i = 0; i < data_len; i++) { if ((i & 0x0f) == 0x00) fprintf(fr_log_fp, " %x: ", i); if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
fprintf(fr_log_fp, "%02x ", buffer[i]); } fprintf(fr_log_fp, "\n"); } #endif
eaptls_fail(handler->eap_ds, 0); return 0; break;
/* * Anything else: fail. */ default: return 0; }
/* * Success: Return MPPE keys. */ eaptls_success(handler->eap_ds, 0); eaptls_gen_mppe_keys(&handler->request->reply->vps, tls_session->ssl, "client EAP encryption"); return 1; }
/* * The module name should be the only globally exported symbol. * That is, everything else should be 'static'. */ EAP_TYPE rlm_eap_tls = { "eap_tls", eaptls_attach, /* attach */ eaptls_initiate, /* Start the initial request */ NULL, /* authorization */ eaptls_authenticate, /* authentication */ eaptls_detach /* detach */ };
- List info/subscribe/unsubscribe? See http://www.freeradius.org/list/devel.html
Ross, Michael wrote:
I customized a freeradius installation to include an option to log information from the client certificates (subject, issuer, serial, expiration) when doing eap-tls authentication. This is done through a new option, log_certificates, in the tls section of the eap.conf file. Is there any interest in adding this capability into the baseline freeradius? I'm happy to make any changes that would be required to do that. Below are the updated rlm_eap_tls.h and rlm_eap_tls.c.
Patches are *very* much preferred. It's easier to see what's changed. Github makes this easier. See http://git.freeradius.org/ for more instructions. I'll take a look at the code. Alan DeKok.
Alan DeKok wrote:
Patches are *very* much preferred. It's easier to see what's changed. Github makes this easier. See http://git.freeradius.org/ for more instructions.
I'll take a look at the code.
If there is interest in a feature like this, I'm happy to try and convert it into a patch. Thanks Mike Ross
Ross, Michael wrote:
Alan DeKok wrote:
Patches are *very* much preferred. It's easier to see what's changed. Github makes this easier. See http://git.freeradius.org/ for more instructions.
I'll take a look at the code.
If there is interest in a feature like this, I'm happy to try and convert it into a patch.
Yes, there's interest. It's hard to easily see what the feature is without a patch. There are a lot of features that get discussed, but the patches do something else entirely. Use "git" (http://git.freeradius.org). Or save a copy of the original file in the same directory, and do a "diff". Patches should be simple to create. Alan DeKok.
Alan DeKok wrote:
Yes, there's interest. It's hard to easily see what the feature is without a patch. There are a lot of features that get discussed, but the patches do something else entirely.
Use "git" (http://git.freeradius.org). Or save a copy of the original file in the same directory, and do a "diff". Patches should be simple to create.
Here is the patch for this feature.
From 1a10cbf69b07b049e1e452b63d1fb333650b10ac Mon Sep 17 00:00:00 2001 From: Mike Ross <Michael.Ross2@Boeing.com> Date: Sun, 15 Aug 2010 15:49:46 -0700 Subject: [PATCH] Added option for certificate logging to EAP TLS authentication
--- raddb/eap.conf | 9 ++ .../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c | 97 ++++++++++++++++++- .../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h | 3 + 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/raddb/eap.conf b/raddb/eap.conf index 75098b1..cf31708 100644 --- a/raddb/eap.conf +++ b/raddb/eap.conf @@ -249,6 +249,15 @@ # # check_cert_cn = %{User-Name} # + + # + # If log_certificates is set, the Issuer, + # Serial Number, Expiration Date, and + # Subject of the client certificate are + # written to the log file. + # + # log_certificates = yes + # Set this option to specify the allowed # TLS cipher suites. The format is listed # in "man 1 ciphers". diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 11ed96a..466df22 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -20,6 +20,7 @@ * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project + * Copyright 2010 The Boeing Company * */ @@ -94,6 +95,8 @@ static CONF_PARSER module_config[] = { offsetof(EAP_TLS_CONF, cipher_list), NULL, NULL}, { "check_cert_issuer", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, check_cert_issuer), NULL, NULL}, + { "log_certificates", PW_TYPE_BOOLEAN, + offsetof(EAP_TLS_CONF, log_certificates), NULL, "no" }, { "make_cert_command", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, make_cert_command), NULL, NULL}, @@ -102,6 +105,77 @@ static CONF_PARSER module_config[] = { { NULL, -1, 0, NULL, NULL } /* end the list */ }; +/* + * + * Write the certificate serial number, expiration + * date, issuer, and subject to the log file + */ +static int log_full_cert(int ok, X509 *cert) +{ + const int ub_serial = 20; + const int ub_time = 15; + + char serialNumber[2*ub_serial+1]; + char expiration[ub_time+1]; + char issuer[ub_name+1]; + char subject[ub_name+1]; + char *pTemp = NULL; + int local_ok = ok; + int i = 0; + ASN1_INTEGER *pSn = NULL; + ASN1_TIME *pTime = NULL; + + /* + * Get the Serial Number and format for display + */ + serialNumber[0] = '\0'; + pSn = X509_get_serialNumber(cert); + pTemp = serialNumber; + if ( pSn == NULL || pSn->length > ub_serial ) { + radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Serial Number"); + local_ok = 0; + } else { + for (i = 0; i < pSn->length; i++) { + sprintf(pTemp, "%02x", (int)pSn->data[i]); + pTemp += 2; + } + } + + /* + * Get the Expiration Date and format for display + */ + expiration[0] = '\0'; + pTime = X509_get_notAfter(cert); + if ( pTime == NULL || pTime->length > ub_time ) { + radlog(L_AUTH, "rlm_eap_tls: Malformed Certificate Expiration"); + local_ok = 0; + } else { + strncpy(expiration, (char*) pTime->data, pTime->length); + expiration[pTime->length] = '\0'; + } + + /* + * Get the Issuer + */ + issuer[0] = '\0'; + X509_NAME_oneline(X509_get_issuer_name(cert), issuer, + ub_name); + + /* + * Get the Subject + */ + subject[0] = '\0'; + X509_NAME_oneline(X509_get_subject_name(cert), subject, + ub_name); + + /* + * Log required attributes + */ + radlog(L_AUTH, "Certificate: Serial=%s; Expiration=%s; Issuer:%s; Subject:%s", + serialNumber, expiration, issuer, subject); + + return local_ok; +} /* * TODO: Check for the type of key exchange * like conf->dh_key @@ -256,12 +330,6 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); - if (!my_ok) { - radlog(L_ERR,"--> verify error:num=%d:%s\n",err, - X509_verify_cert_error_string(err)); - return my_ok; - } - /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. @@ -271,6 +339,15 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); + if (!my_ok) { + radlog(L_ERR,"--> verify error:num=%d:%s\n",err, + X509_verify_cert_error_string(err)); + if (conf->log_certificates) { + log_full_cert(my_ok, client_cert); + } + return my_ok; + } + /* * Get the Subject & Issuer */ @@ -346,6 +423,14 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) } } } /* check_cert_cn */ + + /* + * If the conf telss us to, log the certificate + */ + if (conf->log_certificates) { + my_ok = log_full_cert(my_ok, client_cert); + } + } /* depth == 0 */ if (debug_flag > 0) { diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h index 470dbcd..7846d1e 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h @@ -20,6 +20,8 @@ * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com> * Copyright 2003 Alan DeKok <aland@freeradius.org> * Copyright 2006 The FreeRADIUS server project + * Copyright 2010 The Boeing Company + * */ #ifndef _RLM_EAP_TLS_H #define _RLM_EAP_TLS_H @@ -59,6 +61,7 @@ typedef struct eap_tls_conf { char *check_cert_cn; char *cipher_list; char *check_cert_issuer; + int log_certificates; int session_cache_enable; int session_timeout; -- 1.7.0.4
Alan DeKok wrote:
Yes, there's interest. It's hard to easily see what the feature is without a patch. There are a lot of features that get discussed, but the patches do something else entirely.
Use "git" (http://git.freeradius.org). Or save a copy of the original file in the same directory, and do a "diff". Patches should be simple to create.
Mike Ross wrote on 8/16:
Here is the patch for this feature.
Are there any thoughts about adding the patch from the 8/16 email to the FreeRADIUS baseline? Thanks Mike Ross
Ross, Michael wrote:
Are there any thoughts about adding the patch from the 8/16 email to the FreeRADIUS baseline?
Sorry for not responding earlier. A variant of the patch has been added. See the v2.1.x branch on http://git.freeradius.org The patch creates client/server attributes from the certificate fields. These attributes can be used for anything: policies, *or* logging. This makes it slightly more complex to set up logging like it was in your patch. But IMHO, it's a more correct approach. The TLS module should do minimal logging, but it should enable you to do any logging you want. The code in git does that. Alan DeKok.
On Fri, 2010-08-27 at 21:51 +0200, Alan DeKok wrote:
Ross, Michael wrote:
Are there any thoughts about adding the patch from the 8/16 email to the FreeRADIUS baseline?
Sorry for not responding earlier. A variant of the patch has been added. See the v2.1.x branch on http://git.freeradius.org
The patch creates client/server attributes from the certificate fields. These attributes can be used for anything: policies, *or* logging.
This makes it slightly more complex to set up logging like it was in your patch. But IMHO, it's a more correct approach. The TLS module should do minimal logging, but it should enable you to do any logging you want. The code in git does that.
Awesome! So, it followed more my suggestions :) I think this solution will be more flexible for sure. David
Alan DeKok wrote:
Are there any thoughts about adding the patch from the 8/16 email to the FreeRADIUS baseline?
Sorry for not responding earlier. A variant of the patch has been added. See the v2.1.x branch on http://git.freeradius.org
The patch creates client/server attributes from the certificate fields. These attributes can be used for anything: policies, *or* logging.
This makes it slightly more complex to set up logging like it was in your patch. But IMHO, it's a more correct approach. The TLS module should do minimal logging, but it should enable you to do any logging you want. The code in git does that.
Thank you. I downloaded the updates, but wasn't able to figure out how to enable the logging. Is there an example that someone could point me to? Thanks Mike Ross
Ross, Michael wrote:
Thank you. I downloaded the updates, but wasn't able to figure out how to enable the logging. Is there an example that someone could point me to?
Read raddb/sites-enabled/default. Look for "TLS-Cert". These are attributes that you can use in *any* logging. See the "log" section of radiusd.conf for custom log messages. See the "linelog" module for logging miscellaneous text. Alan DeKok.
Alan DeKok wrote:
Read raddb/sites-enabled/default. Look for "TLS-Cert".
These are attributes that you can use in *any* logging. See the "log" section of radiusd.conf for custom log messages. See the "linelog" module for logging miscellaneous text.
Thanks. I've got it working now where it logs things correctly for successful authentications, but I'm not having any luck getting the logging to work when the authentication fails. The code that generates the attributes is being executed, but when it logs them they all show up as empty. Here is my post-auth configuration: post-auth { update reply { x509_CA_Serial += "%{TLS-Cert-Serial}" x509_CA_Expiration += "%{TLS-Cert-Expiration}" x509_CA_Issuer += "%{TLS-Cert-Issuer}" x509_CA_Subject += "%{TLS-Cert-Subject}" x509_Client_Serial += "%{TLS-Client-Cert-Serial}" x509_Client_Expiration += "%{TLS-Client-Cert-Expiration}" x509_Client_Issuer += "%{TLS-Client-Cert-Issuer}" x509_Client_Subject += "%{TLS-Client-Cert-Subject}" } post_auth_log Post-Auth-Type REJECT { update reply { Failure_Reason += "%{Module-Failure-Message}" x509_CA_Serial += "%{TLS-Cert-Serial}" x509_CA_Expiration += "%{TLS-Cert-Expiration}" x509_CA_Issuer += "%{TLS-Cert-Issuer}" x509_CA_Subject += "%{TLS-Cert-Subject}" x509_Client_Serial += "%{TLS-Client-Cert-Serial}" x509_Client_Expiration += "%{TLS-Client-Cert-Expiration}" x509_Client_Issuer += "%{TLS-Client-Cert-Issuer}" x509_Client_Subject += "%{TLS-Client-Cert-Subject}" } post_auth_log } } Here's an example log from a request where the common name check within cbtls_verify failed: Mon Sep 6 21:32:59 2010 Packet-Type = Access-Reject Failure_Reason = "TLS Alert write:fatal:certificate unknown" x509_CA_Serial = "" x509_CA_Expiration = "" x509_CA_Issuer = "" x509_CA_Subject = "" x509_Client_Serial = "" x509_Client_Expiration = "" x509_Client_Issuer = "" x509_Client_Subject = "" Note: This is all with the patch submitted in the separate email. Any suggestions or ideas? Thanks, Mike Ross Here is the radiusd -X output: FreeRADIUS Version 2.1.10, for host x86_64-unknown-linux-gnu, built on Sep 6 2010 at 21:00:35 Copyright (C) 1999-2009 The FreeRADIUS server project and contributors. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. You may redistribute copies of FreeRADIUS under the terms of the GNU General Public License v2. Starting - reading configuration files ... including configuration file /home/mross/Documents/FreeRadius/build/etc/raddb/radiusd.conf including files in directory /home/mross/Documents/FreeRadius/build/etc/raddb/modules/ including configuration file /home/mross/Documents/FreeRadius/build/etc/raddb/modules/detail including configuration file /home/mross/Documents/FreeRadius/build/etc/raddb/modules/expr including configuration file /home/mross/Documents/FreeRadius/build/etc/raddb/eap.conf including files in directory /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/ including configuration file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x including dictionary file /home/mross/Documents/FreeRadius/build/etc/raddb/dictionary main { prefix = "/home/mross/Documents/FreeRadius/build" localstatedir = "/home/mross/Documents/FreeRadius/build/var" logdir = "/home/mross/Documents/FreeRadius/build/var/log/radius" libdir = "/home/mross/Documents/FreeRadius/build/lib" radacctdir = "/home/mross/Documents/FreeRadius/build/var/log/radius/radacct" hostname_lookups = no max_request_time = 30 cleanup_delay = 5 max_requests = 1024 pidfile = "/home/mross/Documents/FreeRadius/build/var/run/radiusd/radiusd.pid" checkrad = "/home/mross/Documents/FreeRadius/build/sbin/checkrad" debug_level = 0 proxy_requests = no log { stripped_names = no auth = no auth_badpass = no auth_goodpass = no } security { max_attributes = 200 reject_delay = 1 status_server = yes } } radiusd: #### Loading Realms and Home Servers #### radiusd: #### Loading Clients #### radiusd: #### Instantiating modules #### instantiate { Module: Linked to module rlm_expr Module: Instantiating module "expr" from file /home/mross/Documents/FreeRadius/build/etc/raddb/modules/expr } radiusd: #### Loading Virtual Servers #### server 802.1x { # from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x modules { Module: Checking authenticate {...} for more modules to load Module: Linked to module rlm_eap Module: Instantiating module "eap" from file /home/mross/Documents/FreeRadius/build/etc/raddb/eap.conf eap { default_eap_type = "tls" timer_expire = 60 ignore_unknown_eap_types = no cisco_accounting_username_bug = no max_sessions = 4096 } Module: Linked to sub-module rlm_eap_tls Module: Instantiating eap-tls tls { rsa_key_exchange = no dh_key_exchange = yes rsa_key_length = 512 dh_key_length = 512 verify_depth = 0 CA_path = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/crl" pem_file_type = yes private_key_file = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/server.pem" certificate_file = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/server.pem" CA_file = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/ca.pem" private_key_password = "1234" dh_file = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/dh" random_file = "/home/mross/Documents/FreeRadius/build/etc/raddb/certs/random" fragment_size = 1024 include_length = yes check_crl = no check_cert_cn = "valid.radius.client.boeing.com" cipher_list = "DEFAULT" check_cert_issuer = "/O=Boeing/OU=Integration Labs/CN=Test Issuing CA" cache { enable = no lifetime = 24 max_entries = 255 } } Module: Checking authorize {...} for more modules to load Module: Linked to module rlm_detail Module: Instantiating module "auth_log" from file /home/mross/Documents/FreeRadius/build/etc/raddb/modules/detail detail auth_log { detailfile = "/home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log" header = "%t" detailperm = 384 dirperm = 493 locking = no log_packet_header = no } Module: Checking post-auth {...} for more modules to load Module: Instantiating module "post_auth_log" from file /home/mross/Documents/FreeRadius/build/etc/raddb/modules/detail detail post_auth_log { detailfile = "/home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Certs.log" header = "%t" detailperm = 384 dirperm = 493 locking = no log_packet_header = no } } # modules } # server server { # from file /home/mross/Documents/FreeRadius/build/etc/raddb/radiusd.conf modules { } # modules } # server radiusd: #### Opening IP addresses and Ports #### listen { type = "auth" ipaddr = * port = 0 client localhost { ipaddr = 127.0.0.1 require_message_authenticator = yes secret = "radius" } } Listening on authentication address * port 1812 as server 802.1x ... rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=0, length=122 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x0200000c017265766f6b6564 Message-Authenticator = 0x34b88010164dae08466e82105d6cc3b0 server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 0 length 12 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] EAP Identity [eap] processing type tls [tls] Requiring client certificate [tls] Initiate [tls] Start returned 1 ++[eap] returns handled } # server 802.1x Sending Access-Challenge of id 0 to 127.0.0.1 port 41736 EAP-Message = 0x010100060d20 Message-Authenticator = 0x00000000000000000000000000000000 State = 0x93c976a093c87bf74064bdd70ea2ce5a Finished request 7. Going to the next request Waking up in 3.6 seconds. rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=1, length=221 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x0201005d0d0016030100520100004e03014c85c07b6ef0254b1c5e67dcc7d434c8cf5dfbbb47e268059c980e3508a55d8b00002600390038003500160013000a00330032002f0005000400150012000900140011000800060003020100 State = 0x93c976a093c87bf74064bdd70ea2ce5a Message-Authenticator = 0x642240c2ca740d3cc52481800fc0e2c4 server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 1 length 93 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] Request found, released from the list [eap] EAP/tls [eap] processing type tls [tls] Authenticate [tls] processing EAP-TLS [tls] eaptls_verify returned 7 [tls] Done initial handshake [tls] (other): before/accept initialization [tls] TLS_accept: before/accept initialization [tls] <<< TLS 1.0 Handshake [length 0052], ClientHello [tls] TLS_accept: SSLv3 read client hello A [tls] >>> TLS 1.0 Handshake [length 002a], ServerHello [tls] TLS_accept: SSLv3 write server hello A [tls] >>> TLS 1.0 Handshake [length 0827], Certificate [tls] TLS_accept: SSLv3 write certificate A [tls] >>> TLS 1.0 Handshake [length 018d], ServerKeyExchange [tls] TLS_accept: SSLv3 write key exchange A [tls] >>> TLS 1.0 Handshake [length 00a1], CertificateRequest [tls] TLS_accept: SSLv3 write certificate request A [tls] TLS_accept: SSLv3 flush data [tls] TLS_accept: Need to read more data: SSLv3 read client certificate A In SSL Handshake Phase In SSL Accept mode [tls] eaptls_process returned 13 ++[eap] returns handled } # server 802.1x Sending Access-Challenge of id 1 to 127.0.0.1 port 41736 EAP-Message = 0x010204000dc000000a93160301002a0200002603014c85c07b5541e9efd0b1a03acfcd769fd872efe79184b008930685ea565e65fd0000390116030108270b0008230008200002f4308202f030820259a0030201020203008002300d06092a864886f70d01010505003046310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311830160603550403130f546573742049737375696e67204341301e170d3130303632383230353335365a170d3131303632383230353335365a304f310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c61 EAP-Message = 0x62733121301f060355040313187261646975732e7365727665722e626f65696e672e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100d3155c38f186b00cf87885b31ff5917dbbd3c358c9f5db8f91510ed05c87bf6725763d4b0b982e042b9466129ca1bdc6e39eb90af1e7303b84d0d3754921420c173b1e7c6335e2c7c322adf2d542329976097435456db12144be332b75c96c0944f7f8faf9e689c9158d4c51257dd5144efc70f21a676ac6d2663af4e4ff54070203010001a381e23081df301d0603551d0e04160414209b0bac45d59fb4b4a9212855ca080fc804126b300e0603551d0f0101ff0404030205a030 EAP-Message = 0x2a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e672e636f6d2f63726c306d0603551d2304663064801496c24ba1a2a1646c74e23852a2783042c7c1bde2a147a4453043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341820300800130130603551d25040c300a06082b06010505070301300d06092a864886f70d0101050500038181000e0fc64d4594eb457bba4091d7a272ad91082d26477bbc0152d637e8264db614373f83a4399f7d32deeb87db12109acd8a3cbb8c75792832c7d37f EAP-Message = 0x2e0668c457c2cffc12e5bdc83f709d9cc111ce75c50ac7b7f79ba6f07447e30a25de66aaec523dc2635821e5b5e9fc3a29cc678909fa30afe136be1b089c086a19a6fe3a690002903082028c308201f5a0030201020203008001300d06092a864886f70d01010505003043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341301e170d3130303632383230353034345a170d3132303231383230353034345a3046310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c61627331 EAP-Message = 0x1830160603550403130f5465 Message-Authenticator = 0x00000000000000000000000000000000 State = 0x93c976a092cb7bf74064bdd70ea2ce5a Finished request 8. Going to the next request Waking up in 3.6 seconds. rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=2, length=134 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x020200060d00 State = 0x93c976a092cb7bf74064bdd70ea2ce5a Message-Authenticator = 0xb053bb841c26283ad8f5c21c1e223043 server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 2 length 6 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] Request found, released from the list [eap] EAP/tls [eap] processing type tls [tls] Authenticate [tls] processing EAP-TLS [tls] Received TLS ACK [tls] ACK handshake fragment handler [tls] eaptls_verify returned 1 [tls] eaptls_process returned 13 ++[eap] returns handled } # server 802.1x Sending Access-Challenge of id 2 to 127.0.0.1 port 41736 EAP-Message = 0x010304000dc000000a9373742049737375696e6720434130819f300d06092a864886f70d010101050003818d0030818902818100af2b2753c03e4a4b4a4cc484300d9c7f9b2f5c500ce3af01036eba55ba2e943ca1685a7f93413c5509a3b46658b561010bed73c11d85f8436040783ba03285917c5c5cde64e978758f53665588c0feb14d5c51f5d2ef864aff87d829660c19f6d638680c5c242c808866dc61eff9ecef058cd036d5162d84d5d2b69ca3b303870203010001a3818a308187300c0603551d13040530030101ff300b0603551d0f040403020186302a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e EAP-Message = 0x672e636f6d2f63726c301d0603551d0e0416041496c24ba1a2a1646c74e23852a2783042c7c1bde2301f0603551d23041830168014ca3a1f841d11917e92af5a133b829d1c19622225300d06092a864886f70d01010505000381810034907042242eb4977e8e97b9d2bbd5bb05535ea42c56f65ede6e85a48e8d1b60f89d832f1a892c56fd390c20dd3039cfeedb3590d7c3895faf89ab5b00464a33f522379707a4b44ed35b6c8c232ef8ec12878eab669e0fb25cfc0b61b8df749e311710a61ea4881b33b71d0f4ae832eec77b45b01978e578e1620dcb0179cd340002933082028f308201f8a003020102020900a04aeb4bb318e1b9300d06092a86 EAP-Message = 0x4886f70d01010505003043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341301e170d3130303632383230343130355a170d3132303632373230343130355a3043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f7420434130819f300d06092a864886f70d010101050003818d0030818902818100d3e745a362bf42e0e655c26ec00210ea0d08cdb89b7770cdb6f614b24bc44d132ba21d513ad35cd024f32d3742 EAP-Message = 0xc62ac413603f5084a7e88c9399375f2063d07d2fa06dd0b2bd18c99da8c1b8208f762f68ec3e42c571a80eed00e8d7a04e09edd87cc62bfc231b391823cedca81dbab493babf896ee9ba35a6695ec640e2fef70203010001a3818a308187300c0603551d13040530030101ff300b0603551d0f040403020186302a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e672e636f6d2f63726c301d0603551d0e04160414ca3a1f841d11917e92af5a133b829d1c19622225301f0603551d23041830168014ca3a1f841d11917e92af5a133b829d1c19622225300d06092a864886f70d0101050500038181005ed06447c7 EAP-Message = 0xaa67bc0b7a9a4c1b077c4c11 Message-Authenticator = 0x00000000000000000000000000000000 State = 0x93c976a091ca7bf74064bdd70ea2ce5a Finished request 9. Going to the next request Waking up in 3.6 seconds. rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=3, length=134 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x020300060d00 State = 0x93c976a091ca7bf74064bdd70ea2ce5a Message-Authenticator = 0x46c759e73f4679f66cff50b898b33548 server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 3 length 6 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] Request found, released from the list [eap] EAP/tls [eap] processing type tls [tls] Authenticate [tls] processing EAP-TLS [tls] Received TLS ACK [tls] ACK handshake fragment handler [tls] eaptls_verify returned 1 [tls] eaptls_process returned 13 ++[eap] returns handled } # server 802.1x Sending Access-Challenge of id 3 to 127.0.0.1 port 41736 EAP-Message = 0x010402b10d8000000a930cdd42b0902d3f9f6b44d919f7adc94c6f6cb988de1bf4fa8f6f63e852e176876520913ce4d258357b377b5344d335d1cf720e8e603a3bc031900650a0ae8cb08414d7ccff4d2586ef18b564f3f270036cf5fcec77fa71174ffa7a48da15708cd4f39c2f932b6364c716b150f536cf160301018d0c0001890080a8e2983a7e702cf8c9bf9ec8338205d62e2de224ae42cbfd7aaceae1732c5adc6e086a6a7cc4609bb3f3d68699c7ae503c4db864389293897bfc8cc33cba8055236a75f53c93c359bea6f6540967fee5d90e19b3e4a9e4a7d1850147556aa78a2c04061bec997b1d9fcd07af439844feb9039a9bdf4cb1f099 EAP-Message = 0xfd4806c510941b00010200802aa2c61f6f37f94430c4b3dbb279108ebb03a230b8b429a801b61197621af5c029700708e859b0e4b0db7e649821112ca5444602a0d93bf7d0265990a1ab646c31e16fb0a78a01e3deca9f3d2ba42f855ce39a8fbc8c9b1b0c305b45fc696e1b994d7cfd3a77fa660292ccc6126b8b4c86aad5e88705b6933d272c3eb2964c290080ccfa270f4cf40ad6dae99668f61a3c44d80b3e37b8dc0e07780e0716275834ff978c0380e1bb2136ad6c24ac96df4b4e3e9bce267152e538f7cc367fa10eed98bbdf1380f9e74d7d850da981934a5f1e4977374aa5f0cced621a471c862cc80b5bf8c7509f3e9aea6920d819c0ce50 EAP-Message = 0x9083c655c3984e607cde1f0e497c2b695d16030100a10d000099050304010240009100453043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f7420434100483046310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311830160603550403130f546573742049737375696e672043410e000000 Message-Authenticator = 0x00000000000000000000000000000000 State = 0x93c976a090cd7bf74064bdd70ea2ce5a Finished request 10. Going to the next request Waking up in 3.6 seconds. rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=4, length=1546 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x020405800dc00000098d16030108370b0008330008300003043082030030820269a0030201020203008004300d06092a864886f70d01010505003046310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311830160603550403130f546573742049737375696e67204341301e170d3130303632383230353530385a170d3131303632383230353530385a3057310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c61627331293027060355040313207265766f6b65642e7261646975732e636c69656e742e626f65696e672e636f6d3081 EAP-Message = 0x9f300d06092a864886f70d010101050003818d0030818902818100c1723fe0a67647a9005d71c9e69e39dc05a73c48bf00a907e3cb2f0a68120aa1c6d36fca1a2012f273a32b11941ec18aaa0eb3dfe5d00598f704cef07aebfb04ead5e18a938b92b5731878a5ca35a79368deec65a2609df22c9e8bd65ed49189e4ba4256affe0370611b315bd8984af00966c906fafcc9d8e8a79cbdc4e4a01f0203010001a381ea3081e730090603551d1304023000300b0603551d0f0404030203a8302a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e672e636f6d2f63726c301d0603551d0e04160414738d69ee7829458c EAP-Message = 0x65f7106cd2397832bf35b3d0306d0603551d2304663064801496c24ba1a2a1646c74e23852a2783042c7c1bde2a147a4453043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341820300800130130603551d25040c300a06082b06010505070302300d06092a864886f70d0101050500038181009ef238103cbbe445355f2784e348e4a61d740a7a2fbb39a176331bc745f0c3c96bb0f39a8a582d742765edfc32e1548d5d33a36a02bce52b3d0e0f4afed6418327a3b7fbf9e60455c4ad76492717b5504120e1369c20141c979b EAP-Message = 0x16a17b2e922d4f35fce56ae2119828f2fa53a4371cabae11f31e1864ab56c4934b580767ff9c0002903082028c308201f5a0030201020203008001300d06092a864886f70d01010505003043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341301e170d3130303632383230353034345a170d3132303231383230353034345a3046310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311830160603550403130f546573742049737375696e6720434130819f300d06 EAP-Message = 0x092a864886f70d010101050003818d0030818902818100af2b2753c03e4a4b4a4cc484300d9c7f9b2f5c500ce3af01036eba55ba2e943ca1685a7f93413c5509a3b46658b561010bed73c11d85f8436040783ba03285917c5c5cde64e978758f53665588c0feb14d5c51f5d2ef864aff87d829660c19f6d638680c5c242c808866dc61eff9ecef058cd036d5162d84d5d2b69ca3b303870203010001a3818a308187300c0603551d13040530030101ff300b0603551d0f040403020186302a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e672e636f6d2f63726c301d0603551d0e0416041496c24ba1a2a1646c74 EAP-Message = 0xe23852a2783042c7c1bde2301f0603551d23041830168014ca3a1f841d11917e92af5a133b829d1c19622225300d06092a864886f70d01010505000381810034907042242eb4977e8e97b9d2bbd5bb05535ea42c56f65ede6e85a48e8d1b60f89d832f1a892c56fd390c20dd3039cfeedb3590d7c3895faf89ab5b00464a33f522379707a4b44ed35b6c8c232ef8ec State = 0x93c976a090cd7bf74064bdd70ea2ce5a Message-Authenticator = 0x9fdf113724143a85a8dd88c18a71bb19 server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 4 length 253 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] Request found, released from the list [eap] EAP/tls [eap] processing type tls [tls] Authenticate [tls] processing EAP-TLS TLS Length 2445 [tls] Received EAP-TLS First Fragment of the message [tls] eaptls_verify returned 9 [tls] eaptls_process returned 13 ++[eap] returns handled } # server 802.1x Sending Access-Challenge of id 4 to 127.0.0.1 port 41736 EAP-Message = 0x010500060d00 Message-Authenticator = 0x00000000000000000000000000000000 State = 0x93c976a097cc7bf74064bdd70ea2ce5a Finished request 11. Going to the next request Waking up in 3.5 seconds. rad_recv: Access-Request packet from host 127.0.0.1 port 41736, id=5, length=1189 User-Name = "revoked" NAS-IP-Address = 127.0.0.1 Calling-Station-Id = "02-00-00-00-00-01" Framed-MTU = 1400 NAS-Port-Type = Wireless-802.11 Connect-Info = "CONNECT 11Mbps 802.11b" EAP-Message = 0x0205041d0d0012878eab669e0fb25cfc0b61b8df749e311710a61ea4881b33b71d0f4ae832eec77b45b01978e578e1620dcb0179cd340002933082028f308201f8a003020102020900a04aeb4bb318e1b9300d06092a864886f70d01010505003043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c616273311530130603550403130c5465737420526f6f74204341301e170d3130303632383230343130355a170d3132303632373230343130355a3043310f300d060355040a1306426f65696e6731193017060355040b1310496e746567726174696f6e204c61627331153013060355040313 EAP-Message = 0x0c5465737420526f6f7420434130819f300d06092a864886f70d010101050003818d0030818902818100d3e745a362bf42e0e655c26ec00210ea0d08cdb89b7770cdb6f614b24bc44d132ba21d513ad35cd024f32d3742c62ac413603f5084a7e88c9399375f2063d07d2fa06dd0b2bd18c99da8c1b8208f762f68ec3e42c571a80eed00e8d7a04e09edd87cc62bfc231b391823cedca81dbab493babf896ee9ba35a6695ec640e2fef70203010001a3818a308187300c0603551d13040530030101ff300b0603551d0f040403020186302a0603551d1f04233021301fa01da01b8619687474703a2f2f7777772e626f65696e672e636f6d2f63726c30 EAP-Message = 0x1d0603551d0e04160414ca3a1f841d11917e92af5a133b829d1c19622225301f0603551d23041830168014ca3a1f841d11917e92af5a133b829d1c19622225300d06092a864886f70d0101050500038181005ed06447c7aa67bc0b7a9a4c1b077c4c110cdd42b0902d3f9f6b44d919f7adc94c6f6cb988de1bf4fa8f6f63e852e176876520913ce4d258357b377b5344d335d1cf720e8e603a3bc031900650a0ae8cb08414d7ccff4d2586ef18b564f3f270036cf5fcec77fa71174ffa7a48da15708cd4f39c2f932b6364c716b150f536cf16030100861000008200807358dd170ee10d6ae05ef6268d9bcbddcaea4600807449c1fd34ccc0209581c3 EAP-Message = 0x3c970de15d3183933176296ad5c12c08c86d006478bb7756125697ca4a657366e291867ba4c75d89ef8bc482be6fb168f2f15ead669e21e008a7d09fefef76dc5c7bb8c72e50a40b2f9e1d4cbfff97638818fcaee912881ee39914f7471a44bf16030100860f0000820080619ad4903355db107a056da0c4cc926dfb0e2c4b3d1c4493a769bd6fe106295dff843919d955edd22afb0b73015c7b7d6e8e782ba1c10fb0690659d76a84c7e8d1a9d51fdb00855d662e12b59d52d3b1353b2ce94add122dca8bd3de1e10468b7e9f97a17f6c56c8489df968a86392f496bc2028b3f6a68e7bbf1488176ee1fb1403010001011603010030b8cb62a16164bf EAP-Message = 0x18190bd8efa1c701add0b5bf12043b254b726c309b1819add1aac148c88372b442fee43eb26ceb668c State = 0x93c976a097cc7bf74064bdd70ea2ce5a Message-Authenticator = 0xde36629679b88d12fd0c9dc5ee83ef1c server 802.1x { # Executing section authorize from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authorize {...} [eap] EAP packet type response id 5 length 253 [eap] No EAP Start, assuming it's an on-going EAP conversation ++[eap] returns updated [auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Packets.log [auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[auth_log] returns ok Found Auth-Type = EAP # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group authenticate {...} [eap] Request found, released from the list [eap] EAP/tls [eap] processing type tls [tls] Authenticate [tls] processing EAP-TLS [tls] eaptls_verify returned 7 [tls] Done initial handshake [tls] <<< TLS 1.0 Handshake [length 0837], Certificate [tls] chain-depth=2, [tls] error=0 [tls] --> User-Name = revoked [tls] --> BUF-Name = ? [tls] --> subject = ùeÊ_ [tls] --> issuer = `W?ßÿ [tls] --> verify return:1 [tls] chain-depth=1, [tls] error=0 [tls] --> User-Name = revoked [tls] --> BUF-Name = Test Issuing CA [tls] --> subject = /O=Boeing/OU=Integration Labs/CN=Test Issuing CA [tls] --> issuer = /O=Boeing/OU=Integration Labs/CN=Test Root CA [tls] --> verify return:1 [tls] expand: valid.radius.client.boeing.com -> valid.radius.client.boeing.com [tls] checking certificate CN (revoked.radius.client.boeing.com) with xlat'ed value (valid.radius.client.boeing.com) rlm_eap_tls: Certificate CN (revoked.radius.client.boeing.com) does not match specified value (valid.radius.client.boeing.com)! [tls] chain-depth=0, [tls] error=0 [tls] --> User-Name = revoked [tls] --> BUF-Name = revoked.radius.client.boeing.com [tls] --> subject = /O=Boeing/OU=Integration Labs/CN=revoked.radius.client.boeing.com [tls] --> issuer = /O=Boeing/OU=Integration Labs/CN=Test Issuing CA [tls] --> verify return:0 [tls] >>> TLS 1.0 Alert [length 0002], fatal certificate_unknown TLS Alert write:fatal:certificate unknown TLS_accept: error in SSLv3 read client certificate B rlm_eap: SSL error error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned SSL: SSL_read failed in a system call (-1), TLS session fails. TLS receive handshake failed during operation [tls] eaptls_process returned 4 [eap] Handler failed in EAP/tls [eap] Failed in EAP select ++[eap] returns invalid Failed to authenticate the user. } # server 802.1x Using Post-Auth-Type Reject # Executing group from file /home/mross/Documents/FreeRadius/build/etc/raddb/sites-enabled/802.1x +- entering group REJECT {...} expand: %{Module-Failure-Message} -> TLS Alert write:fatal:certificate unknown expand: %{TLS-Cert-Serial} -> expand: %{TLS-Cert-Expiration} -> expand: %{TLS-Cert-Issuer} -> expand: %{TLS-Cert-Subject} -> expand: %{TLS-Client-Cert-Serial} -> expand: %{TLS-Client-Cert-Expiration} -> expand: %{TLS-Client-Cert-Issuer} -> expand: %{TLS-Client-Cert-Subject} -> ++[reply] returns noop [post_auth_log] expand: /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Certs.log -> /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Certs.log [post_auth_log] /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Certs.log expands to /home/mross/Documents/FreeRadius/build/var/log/radius/radacct/Certs.log [post_auth_log] expand: %t -> Mon Sep 6 21:32:59 2010 ++[post_auth_log] returns ok Delaying reject of request 12 for 1 seconds Going to the next request Waking up in 0.9 seconds. Sending delayed reject for request 12 Sending Access-Reject of id 5 to 127.0.0.1 port 41736 EAP-Message = 0x04050004 Message-Authenticator = 0x00000000000000000000000000000000 Waking up in 2.5 seconds.
Ross, Michael wrote:
Thanks. I've got it working now where it logs things correctly for successful authentications, but I'm not having any luck getting the logging to work when the authentication fails. The code that generates the attributes is being executed, but when it logs them they all show up as empty. Here is my post-auth configuration:
Hmm.. OK. That would require some more in-depth debugging.
Here's an example log from a request where the common name check within cbtls_verify failed:
The issue is that there is no difference that I can see between the accept && reject cases in the code.
Note: This is all with the patch submitted in the separate email.
With the access to un-initialized memory:
[tls] --> BUF-Name = ? [tls] --> subject = ùeÊ_ [tls] --> issuer = `W?ßÿ
My patch doesn't have that issue. :)
Any suggestions or ideas?
More detailed debugging to see what's going on... Alan DeKok.
Alan DeKok wrote:
Sorry for not responding earlier. A variant of the patch has been added. See the v2.1.x branch on http://git.freeradius.org
The patch creates client/server attributes from the certificate fields. These attributes can be used for anything: policies, *or* logging.
I've been playing around with this and the attached patch fixes a few issues. The issues were: - missing 'i' in Expiration - sprintf in serial number printing was using buf instead of p - array index for subject and issuer storage were reversed I also changed the lookup logic to only log the issueing CA and the client CA, except for when the session is going to fail due to a certificate error. The previous logic only recorded the root CA certificate, which in my opinion isn't as valuable as the issueing CA since most servers will be set up allowing a limited number of root CAs. A few root CAs could expand into a large number of issuing CAs. Thanks, Mike Ross
From ed6d8c770758e38208d522faf73a4c7e31216138 Mon Sep 17 00:00:00 2001 From: Mike Ross <michael.ross2@boeing.com> Date: Mon, 6 Sep 2010 21:51:10 -0700 Subject: [PATCH] Corrections to certificate logging in rlm_eap_tls.c
--- .../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c | 115 ++++++++++---------- 1 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 6e080d5..65dbdd7 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -228,7 +228,7 @@ static SSL_SESSION *cbtls_get_session(UNUSED SSL *s, */ static const char *cert_attr_names[5][2] = { { "TLS-Client-Cert-Serial", "TLS-Cert-Serial" }, - { "TLS-Client-Cert-Expiration", "TLS-Cert-Expiraton" }, + { "TLS-Client-Cert-Expiration", "TLS-Cert-Expiration" }, { "TLS-Client-Cert-Issuer", "TLS-Cert-Issuer" }, { "TLS-Client-Cert-Subject", "TLS-Cert-Subject" }, { "TLS-Client-Cert-Common-Name", "TLS-Cert-Common-Name" } @@ -282,7 +282,7 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) depth = X509_STORE_CTX_get_error_depth(ctx); lookup = depth; - if (lookup > 1) lookup = 1; + if (lookup > 1 && !my_ok) lookup = 1; /* * Retrieve the pointer to the SSL of the connection currently treated @@ -293,66 +293,69 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); - /* - * Get the Serial Number - */ - buf[0] = '\0'; - sn = X509_get_serialNumber(client_cert); - if (sn && (sn->length < (sizeof(buf) / 2))) { - char *p = buf; - int i; - - for (i = 0; i < sn->length; i++) { - sprintf(buf, "%02x", (unsigned int)sn->data[i]); - p += 2; + if ( lookup <= 1 ) { + /* + * Get the Serial Number + */ + buf[0] = '\0'; + sn = X509_get_serialNumber(client_cert); + if (sn && (sn->length < (sizeof(buf) / 2))) { + char *p = buf; + int i; + + for (i = 0; i < sn->length; i++) { + sprintf(p, "%02x", (unsigned int)sn->data[i]); + p += 2; + } + pairadd(&handler->certs, + pairmake(cert_attr_names[0][lookup], buf, T_OP_SET)); } - pairadd(&handler->certs, - pairmake(cert_attr_names[0][lookup], buf, T_OP_SET)); - } - /* - * Get the Expiration Date - */ - buf[0] = '\0'; - asn_time = X509_get_notAfter(client_cert); - if (asn_time && (asn_time->length < MAX_STRING_LEN)) { - memcpy(buf, (char*) asn_time->data, asn_time->length); - buf[asn_time->length] = '\0'; - pairadd(&handler->certs, - pairmake(cert_attr_names[1][lookup], buf, T_OP_SET)); - } + /* + * Get the Expiration Date + */ + buf[0] = '\0'; + asn_time = X509_get_notAfter(client_cert); + if (asn_time && (asn_time->length < MAX_STRING_LEN)) { + memcpy(buf, (char*) asn_time->data, asn_time->length); + buf[asn_time->length] = '\0'; + pairadd(&handler->certs, + pairmake(cert_attr_names[1][lookup], buf, T_OP_SET)); + } - /* - * Get the Subject & Issuer - */ - subject[0] = issuer[0] = '\0'; - X509_NAME_oneline(X509_get_subject_name(client_cert), subject, - sizeof(subject)); - subject[sizeof(subject) - 1] = '\0'; - if (subject[0] && (strlen(subject) < MAX_STRING_LEN)) { - pairadd(&handler->certs, - pairmake(cert_attr_names[2][lookup], subject, T_OP_SET)); - } + /* + * Get the Subject & Issuer + */ + subject[0] = issuer[0] = '\0'; + X509_NAME_oneline(X509_get_subject_name(client_cert), subject, + sizeof(subject)); + subject[sizeof(subject) - 1] = '\0'; + if (subject[0] && (strlen(subject) < MAX_STRING_LEN)) { + pairadd(&handler->certs, + pairmake(cert_attr_names[3][lookup], subject, T_OP_SET)); + } - X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, - sizeof(issuer)); - issuer[sizeof(issuer) - 1] = '\0'; - if (issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) { - pairadd(&handler->certs, - pairmake(cert_attr_names[3][lookup], issuer, T_OP_SET)); - } + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, + sizeof(issuer)); + issuer[sizeof(issuer) - 1] = '\0'; + if (issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) { + pairadd(&handler->certs, + pairmake(cert_attr_names[2][lookup], issuer, T_OP_SET)); + } - /* - * Get the Common Name - */ - X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), - NID_commonName, common_name, sizeof(common_name)); - common_name[sizeof(common_name) - 1] = '\0'; - if (common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { - pairadd(&handler->certs, - pairmake(cert_attr_names[4][lookup], common_name, T_OP_SET)); - } + /* + * Get the Common Name + */ + X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), + NID_commonName, common_name, sizeof(common_name)); + common_name[sizeof(common_name) - 1] = '\0'; + if (common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { + pairadd(&handler->certs, + pairmake(cert_attr_names[4][lookup], common_name, T_OP_SET)); + } + + } /* lookup <= 1 */ if (!my_ok) { const char *p = X509_verify_cert_error_string(err); -- 1.7.0.4
Ross, Michael wrote:
Alan DeKok wrote:
Sorry for not responding earlier. A variant of the patch has been added. See the v2.1.x branch on http://git.freeradius.org
The patch creates client/server attributes from the certificate fields. These attributes can be used for anything: policies, *or* logging.
I've been playing around with this and the attached patch fixes a few issues. The issues were:
- missing 'i' in Expiration - sprintf in serial number printing was using buf instead of p - array index for subject and issuer storage were reversed
OK. Fixed that, thanks.
I also changed the lookup logic to only log the issueing CA and the client CA, except for when the session is going to fail due to a certificate error. The previous logic only recorded the root CA certificate, which in my opinion isn't as valuable as the issueing CA since most servers will be set up allowing a limited number of root CAs. A few root CAs could expand into a large number of issuing CAs.
That's better, yes. My only issue with the patch is that there's an "if (lookup <= 1)" around the code which grabs the issuer. Then later, the issuer field is used. It would be better to move that check to just surround the code which creates the TLS-Cert-* attributes. I've done that. See the v2.1.x branch. If there are no further issues, we should be able to release 2.1.10 soon. This certificate patch is very, very, useful. Alan DeKok.
Alan DeKok wrote:
It would be better to move that check to just surround the code which creates the TLS-Cert-* attributes. I've done that. See the v2.1.x branch.
If there are no further issues, we should be able to release 2.1.10 soon. This certificate patch is very, very, useful.
Missed the check on Common Name (segmentation fault). Other than that the logging during successful requests works well. I'll continue looking into the logging of the failure cases. --- .../rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index 255c17a..cb4c846 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -364,7 +364,7 @@ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0'; - if (common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { + if ((lookup <= 1) && common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_CN][lookup], common_name, T_OP_SET)); } -- 1.7.0.4
Ross, Michael wrote:
Alan DeKok wrote:
It would be better to move that check to just surround the code which creates the TLS-Cert-* attributes. I've done that. See the v2.1.x branch.
If there are no further issues, we should be able to release 2.1.10 soon. This certificate patch is very, very, useful.
Missed the check on Common Name (segmentation fault). Other than that the logging during successful requests works well. I'll continue looking into the logging of the failure cases.
Fixed, thanks. Alan DeKok.
participants (3)
-
Alan DeKok -
David Bird -
Ross, Michael