2 * rlm_eap_fast.c contains the interfaces that are called from eap
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2016 Alan DeKok <aland@freeradius.org>
21 * Copyright 2016 The FreeRADIUS server project
25 USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
29 #include "eap_fast_crypto.h"
32 #include <freeradius-devel/md5.h>
35 * An instance of EAP-FAST
37 typedef struct rlm_eap_fast_t {
38 char const *tls_conf_name; //!< Name of shared TLS config.
39 fr_tls_server_conf_t *tls_conf;
41 char const *default_method_name;
44 char const *virtual_server; //!< Virtual server to use for processing
45 //!< inner EAP method.
46 bool req_client_cert; //!< Whether we require a client cert
47 //!< in the outer tunnel.
49 int stage; //!< Processing stage.
51 uint32_t pac_lifetime; //!< seconds to add to current time to describe PAC lifetime
52 char const *authority_identity; //!< The identity we present in the EAP-TLS
53 uint8_t a_id[PAC_A_ID_LENGTH]; //!< The identity we present in the EAP-TLS
54 char const *pac_opaque_key; //!< The key used to encrypt PAC-Opaque
55 bool use_tunneled_reply; //!< Use the reply attributes from the tunneled session in
56 //!< the non-tunneled reply to the client.
58 bool copy_request_to_tunnel; //!< Use SOME of the request attributes from outside of the
62 static CONF_PARSER module_config[] = {
63 { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_fast_t, tls_conf_name), NULL },
65 { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_fast_t, default_method_name), "mschapv2" },
67 { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_NOT_EMPTY, rlm_eap_fast_t, virtual_server) , NULL},
69 { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, req_client_cert), "no" },
71 { "pac_lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_fast_t, pac_lifetime), "604800" },
72 { "authority_identity", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_fast_t, authority_identity), NULL },
73 { "pac_opaque_key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_fast_t, pac_opaque_key), NULL },
74 { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, copy_request_to_tunnel), "no" },
76 { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_fast_t, use_tunneled_reply), "no" },
78 CONF_PARSER_TERMINATOR
84 static int mod_instantiate(CONF_SECTION *cs, void **instance)
88 *instance = inst = talloc_zero(cs, rlm_eap_fast_t);
92 * Parse the configuration attributes.
94 if (cf_section_parse(cs, inst, module_config) < 0) {
98 if (!cf_section_sub_find_name2(main_config.config, "server", inst->virtual_server)) {
99 ERROR("rlm_eap_fast.virtual_server: Unknown virtual server '%s'", inst->virtual_server);
103 inst->default_method = eap_name2type(inst->default_method_name);
104 if (!inst->default_method) {
105 ERROR("rlm_eap_fast.default_provisioning_eap_type: "
106 "Unknown EAP type %s",
107 inst->default_method_name);
112 * Read tls configuration, either from group given by 'tls'
113 * option, or from the eap-tls configuration.
115 inst->tls_conf = eaptls_conf_parse(cs, "tls");
117 if (!inst->tls_conf) {
118 ERROR("rlm_eap_fast.tls: Failed initializing SSL context");
122 if (talloc_array_length(inst->pac_opaque_key) - 1 != 32) {
123 ERROR("rlm_eap_fast.pac_opaque_key: Must be 32 bytes long");
127 // FIXME TLSv1.2 uses a different PRF and SSL_export_keying_material("key expansion") is forbidden
128 if (!inst->tls_conf->disable_tlsv1_2) {
129 ERROR("rlm_eap_fast.disable_tlsv1_2: require disable_tlsv1_2=yes");
133 if (!inst->pac_lifetime) {
134 ERROR("rlm_eap_fast.pac_lifetime: must be non-zero");
138 rad_assert(PAC_A_ID_LENGTH == MD5_DIGEST_LENGTH);
141 fr_md5_update(&ctx, inst->authority_identity, talloc_array_length(inst->authority_identity) - 1);
142 fr_md5_final(inst->a_id, &ctx);
147 /** Allocate the FAST per-session data
150 static eap_fast_tunnel_t *eap_fast_alloc(TALLOC_CTX *ctx, rlm_eap_fast_t *inst)
152 eap_fast_tunnel_t *t = talloc_zero(ctx, eap_fast_tunnel_t);
154 t->mode = EAP_FAST_UNKNOWN;
155 t->stage = TLS_SESSION_HANDSHAKE;
157 t->default_method = inst->default_method;
158 t->copy_request_to_tunnel = inst->copy_request_to_tunnel;
159 t->use_tunneled_reply = inst->use_tunneled_reply;
161 t->pac_lifetime = inst->pac_lifetime;
162 t->authority_identity = inst->authority_identity;
163 t->a_id = inst->a_id;
164 t->pac_opaque_key = (const uint8_t *)inst->pac_opaque_key;
166 t->virtual_server = inst->virtual_server;
171 static void eap_fast_session_ticket(tls_session_t *tls_session, uint8_t *client_random,
172 uint8_t *server_random, uint8_t *secret, int *secret_len)
174 eap_fast_tunnel_t *t = (eap_fast_tunnel_t *) tls_session->opaque;
175 uint8_t seed[2 * SSL3_RANDOM_SIZE];
177 rad_assert(t->pac.key);
179 memcpy(seed, server_random, SSL3_RANDOM_SIZE);
180 memcpy(&seed[SSL3_RANDOM_SIZE], client_random, SSL3_RANDOM_SIZE);
182 T_PRF(t->pac.key, PAC_KEY_LENGTH, "PAC to master secret label hash",
183 seed, sizeof(seed), secret, SSL_MAX_MASTER_KEY_LENGTH);
184 *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
187 // hostap:src/crypto/tls_openssl.c:tls_sess_sec_cb()
188 static int _session_secret(SSL *s, void *secret, int *secret_len,
189 UNUSED STACK_OF(SSL_CIPHER) *peer_ciphers,
190 UNUSED SSL_CIPHER **cipher, void *arg)
192 // FIXME enforce non-anon cipher
194 REQUEST *request = (REQUEST *)SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
195 tls_session_t *tls_session = arg;
196 eap_fast_tunnel_t *t;
198 if (!tls_session) return 0;
200 t = (eap_fast_tunnel_t *) tls_session->opaque;
202 if (!t->pac.key) return 0;
204 RDEBUG("processing PAC-Opaque");
206 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
207 eap_fast_session_ticket(tls_session, s->s3->client_random, s->s3->server_random, secret, secret_len);
209 uint8_t const client_random[SSL3_RANDOM_SIZE];
210 uint8_t const server_random[SSL3_RANDOM_SIZE];
212 SSL_get_client_random(s, client_random, sizeof(client_random));
213 SSL_get_server_random(s, server_random, sizeof(server_random));
215 eap_fast_session_ticket(tls_session, client_random, server_random, secret, secret_len);
218 memset(t->pac.key, 0, PAC_KEY_LENGTH);
219 talloc_free(t->pac.key);
226 * hints from hostap:src/crypto/tls_openssl.c:tls_session_ticket_ext_cb()
228 * N.B. we actually always tell OpenSSL we have digested the ticket so that
229 * it does not cause a fail loop and enables us to update the PAC easily
232 static int _session_ticket(SSL *s, uint8_t const *data, int len, void *arg)
234 tls_session_t *tls_session = arg;
235 REQUEST *request = (REQUEST *)SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
236 eap_fast_tunnel_t *t;
237 VALUE_PAIR *fast_vps = NULL;
239 DICT_ATTR const *fast_da;
243 eap_fast_attr_pac_opaque_t const *opaque = (eap_fast_attr_pac_opaque_t const *) data;
244 eap_fast_attr_pac_opaque_t opaque_plaintext;
246 if (!tls_session) return 0;
248 t = (eap_fast_tunnel_t *) tls_session->opaque;
250 RDEBUG("PAC provided via ClientHello SessionTicket extension");
252 if ((ntohs(opaque->hdr.type) & EAP_FAST_TLV_TYPE) != PAC_INFO_PAC_OPAQUE) {
253 errmsg = "PAC is not of type Opaque";
255 RERROR("%s, sending alert to client", errmsg);
257 if (tls_session_handshake_alert(request, tls_session, SSL3_AL_FATAL, SSL_AD_BAD_CERTIFICATE)) {
258 RERROR("too many alerts");
262 if (t->pac.key) talloc_free(t->pac.key);
264 memset(&t->pac, 0, sizeof(t->pac));
265 if (fast_vps) fr_pair_list_free(&fast_vps);
270 * we would like to use the length of the SessionTicket
271 * but Cisco hates everyone and sends a zero padding payload
272 * so we have to use the length in the PAC-Opaque header
274 length = ntohs(opaque->hdr.length);
275 if (len - sizeof(opaque->hdr) < length) {
276 errmsg = "PAC has bad length in header";
280 if (length < PAC_A_ID_LENGTH + EVP_MAX_IV_LENGTH + EVP_GCM_TLS_TAG_LEN + 1) {
281 errmsg = "PAC file too short";
285 if (memcmp(opaque->aad, t->a_id, PAC_A_ID_LENGTH)) {
286 errmsg = "PAC has incorrect A_ID";
290 dlen = length - sizeof(opaque->aad) - sizeof(opaque->iv) - sizeof(opaque->tag);
291 plen = eap_fast_decrypt(opaque->data, dlen, opaque->aad, PAC_A_ID_LENGTH,
292 (uint8_t const *) opaque->tag, t->pac_opaque_key, opaque->iv,
293 (uint8_t *)&opaque_plaintext);
295 errmsg = "PAC failed to decrypt";
299 fast_da = dict_attrbyname("FreeRADIUS-EAP-FAST-PAC-Opaque-TLV");
300 rad_assert(fast_da != NULL);
302 fast_vps = eap_fast_fast2vp((REQUEST *)tls_session, s, (uint8_t *)&opaque_plaintext, plen, fast_da, NULL);
303 if (!fast_vps) return 0;
305 for (VALUE_PAIR *vp = fr_cursor_init(&cursor, &fast_vps); vp; vp = fr_cursor_next(&cursor)) {
308 switch ((vp->da->attr >> fr_attr_shift[3]) & fr_attr_mask[3]) {
309 case PAC_INFO_PAC_TYPE:
310 rad_assert(t->pac.type == 0);
311 t->pac.type = vp->vp_integer;
313 case PAC_INFO_PAC_LIFETIME:
314 rad_assert(t->pac.expires == 0);
315 t->pac.expires = vp->vp_integer;
316 t->pac.expired = (vp->vp_integer <= time(NULL));
318 case PAC_INFO_PAC_KEY:
319 rad_assert(t->pac.key == NULL);
320 rad_assert(vp->vp_length == PAC_KEY_LENGTH);
321 t->pac.key = talloc_size(t, PAC_KEY_LENGTH);
322 rad_assert(t->pac.key != NULL);
323 memcpy(t->pac.key, vp->vp_octets, PAC_KEY_LENGTH);
326 value = vp_aprints(tls_session, vp, '"');
327 RERROR("unknown TLV: %s", value);
329 errmsg = "unknown TLV";
334 fr_pair_list_free(&fast_vps);
337 errmsg = "PAC missing type TLV";
341 if (t->pac.type != PAC_TYPE_TUNNEL) {
342 errmsg = "PAC is of not of tunnel type";
346 if (!t->pac.expires) {
347 errmsg = "PAC missing lifetime TLV";
352 errmsg = "PAC missing key TLV";
356 if (!SSL_set_session_secret_cb(tls_session->ssl, _session_secret, tls_session)) {
357 RERROR("Failed setting SSL session secret callback");
366 * Do authentication, by letting EAP-TLS do most of the work.
368 static int mod_process(void *arg, eap_handler_t *handler)
371 fr_tls_status_t status;
372 rlm_eap_fast_t *inst = (rlm_eap_fast_t *) arg;
373 tls_session_t *tls_session = (tls_session_t *) handler->opaque;
374 eap_fast_tunnel_t *t = (eap_fast_tunnel_t *) tls_session->opaque;
375 REQUEST *request = handler->request;
377 RDEBUG2("Authenticate");
380 * We need FAST data associated with the session, so
381 * allocate it here, if it wasn't already alloacted.
383 if (!t) t = tls_session->opaque = eap_fast_alloc(tls_session, inst);
386 * Process TLS layer until done.
388 status = eaptls_process(handler);
389 if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
390 REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
392 RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
397 * EAP-TLS handshake was successful, tell the
398 * client to keep talking.
400 * If this was EAP-TLS, we would just return
401 * an EAP-TLS-Success packet here.
404 tls_handshake_send(request, tls_session);
405 rad_assert(t != NULL);
409 * The TLS code is still working on the TLS
410 * exchange, and it's a valid TLS request.
417 * Handshake is done, proceed with decoding tunneled
424 * Anything else: fail.
431 * Session is established, proceed with decoding
434 RDEBUG2("Session established. Proceeding to decode tunneled attributes");
437 * Process the FAST portion of the request.
439 rcode = eap_fast_process(handler, tls_session);
442 case PW_CODE_ACCESS_REJECT:
444 eaptls_fail(handler, EAP_FAST_VERSION);
448 * Access-Challenge, continue tunneled conversation.
450 case PW_CODE_ACCESS_CHALLENGE:
452 tls_handshake_send(request, tls_session);
453 eaptls_request(handler->eap_ds, tls_session);
457 * Success: Automatically return MPPE keys.
459 case PW_CODE_ACCESS_ACCEPT:
460 RDEBUG("Note the missing PRF label warning below is harmless, ignore it");
462 RDEBUG2("Using saved attributes from the original Access-Accept");
463 rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL);
464 fr_pair_list_mcopy_by_num(handler->request->reply,
465 &handler->request->reply->vps,
466 &t->accept_vps, 0, 0, TAG_ANY);
467 } else if (t->use_tunneled_reply) {
468 RDEBUG2("No saved attributes in the original Access-Accept");
470 return eaptls_success(handler, EAP_FAST_VERSION);
473 * No response packet, MUST be proxying it.
474 * The main EAP module will take care of discovering
475 * that the request now has a "proxy" packet, and
476 * will proxy it, rather than returning an EAP packet.
478 case PW_CODE_STATUS_CLIENT:
480 rad_assert(handler->request->proxy != NULL);
489 * Something we don't understand: Reject it.
491 eaptls_fail(handler, EAP_FAST_VERSION);
495 static int eap_fast_tls_start(EAP_DS * eap_ds,tls_session_t *tls_session)
499 reply.code = FR_TLS_START;
500 reply.length = TLS_HEADER_LEN + 1 + tls_session->clean_in.used;/*flags*/
502 reply.flags = tls_session->peap_flag;
503 reply.flags = SET_START(reply.flags);
505 reply.data = tls_session->clean_in.data;
506 reply.dlen = tls_session->clean_in.used;
508 eaptls_compose(eap_ds, &reply);
515 * Send an initial eap-tls request to the peer, using the libeap functions.
517 static int mod_session_init(void *type_arg, eap_handler_t *handler)
520 tls_session_t *tls_session;
521 rlm_eap_fast_t *inst;
524 REQUEST *request = handler->request;
531 * EAP-TLS-Require-Client-Cert attribute will override
532 * the require_client_cert configuration option.
534 vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
536 client_cert = vp->vp_integer ? true : false;
538 client_cert = inst->req_client_cert;
540 handler->opaque = tls_session = eaptls_session(handler, inst->tls_conf, client_cert);
542 if (!tls_session) return 0;
545 * Push TLV of authority_identity into tls_record
546 * call eap_tls_compose() with args
548 * RFC 4851 section 4.1.1
549 * N.B. mandatory/reserved flags are not applicable here
551 eap_fast_tlv_append(tls_session, PAC_INFO_A_ID, false, PAC_A_ID_LENGTH, inst->a_id);
552 tls_session->peap_flag = EAP_FAST_VERSION;
553 tls_session->length_flag = false;
554 rcode = eap_fast_tls_start(handler->eap_ds, tls_session);
557 talloc_free(tls_session);
561 tls_session->record_init(&tls_session->clean_in);
563 if (!SSL_set_session_ticket_ext_cb(tls_session->ssl, _session_ticket, tls_session)) {
564 RERROR("Failed setting SSL session ticket callback");
568 handler->stage = PROCESS;
575 * The module name should be the only globally exported symbol.
576 * That is, everything else should be 'static'.
578 extern rlm_eap_module_t rlm_eap_fast;
579 rlm_eap_module_t rlm_eap_fast = {
581 .instantiate = mod_instantiate, /* Create new submodule instance */
582 .session_init = mod_session_init, /* Initialise a new EAP session */
583 .process = mod_process /* Process next round of EAP method */