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;
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));
269 * we would like to use the length of the SessionTicket
270 * but Cisco hates everyone and sends a zero padding payload
271 * so we have to use the length in the PAC-Opaque header
273 length = ntohs(opaque->hdr.length);
274 if (len - sizeof(opaque->hdr) < length) {
275 errmsg = "PAC has bad length in header";
279 if (length < PAC_A_ID_LENGTH + EVP_MAX_IV_LENGTH + EVP_GCM_TLS_TAG_LEN + 1) {
280 errmsg = "PAC file too short";
284 if (memcmp(opaque->aad, t->a_id, PAC_A_ID_LENGTH)) {
285 errmsg = "PAC has incorrect A_ID";
289 dlen = length - sizeof(opaque->aad) - sizeof(opaque->iv) - sizeof(opaque->tag);
290 plen = eap_fast_decrypt(opaque->data, dlen, opaque->aad, PAC_A_ID_LENGTH,
291 (uint8_t const *) opaque->tag, t->pac_opaque_key, opaque->iv,
292 (uint8_t *)&opaque_plaintext);
294 errmsg = "PAC failed to decrypt";
298 fast_da = dict_attrbyname("EAP-FAST-PAC-Opaque-TLV");
299 rad_assert(fast_da != NULL);
301 fast_vps = eap_fast_fast2vp((REQUEST *)tls_session, s, (uint8_t *)&opaque_plaintext, plen, fast_da, NULL);
302 if (!fast_vps) return 0;
304 for (VALUE_PAIR *vp = fr_cursor_init(&cursor, &fast_vps); vp; vp = fr_cursor_next(&cursor)) {
307 switch (vp->da->attr) {
308 case PAC_INFO_PAC_TYPE:
309 rad_assert(t->pac.type == 0);
310 t->pac.type = vp->vp_integer;
312 case PAC_INFO_PAC_LIFETIME:
313 rad_assert(t->pac.expires == 0);
314 t->pac.expires = vp->vp_integer;
315 t->pac.expired = (vp->vp_integer <= time(NULL));
317 case PAC_INFO_PAC_KEY:
318 rad_assert(t->pac.key == NULL);
319 rad_assert(vp->vp_length == PAC_KEY_LENGTH);
320 t->pac.key = talloc_size(t, PAC_KEY_LENGTH);
321 rad_assert(t->pac.key != NULL);
322 memcpy(t->pac.key, vp->vp_octets, PAC_KEY_LENGTH);
325 value = vp_aprints_value(tls_session, vp, '"');
326 RERROR("unknown TLV: %s", value);
328 errmsg = "unknown TLV";
334 errmsg = "PAC missing type TLV";
338 if (t->pac.type != PAC_TYPE_TUNNEL) {
339 errmsg = "PAC is of not of tunnel type";
343 if (!t->pac.expires) {
344 errmsg = "PAC missing lifetime TLV";
349 errmsg = "PAC missing key TLV";
353 if (!SSL_set_session_secret_cb(tls_session->ssl, _session_secret, tls_session)) {
354 RERROR("Failed setting SSL session secret callback");
363 * Do authentication, by letting EAP-TLS do most of the work.
365 static int mod_process(void *arg, eap_handler_t *handler)
368 fr_tls_status_t status;
369 rlm_eap_fast_t *inst = (rlm_eap_fast_t *) arg;
370 tls_session_t *tls_session = (tls_session_t *) handler->opaque;
371 eap_fast_tunnel_t *t = (eap_fast_tunnel_t *) tls_session->opaque;
372 REQUEST *request = handler->request;
374 RDEBUG2("Authenticate");
377 * We need FAST data associated with the session, so
378 * allocate it here, if it wasn't already alloacted.
380 if (!t) t = tls_session->opaque = eap_fast_alloc(tls_session, inst);
383 * Process TLS layer until done.
385 status = eaptls_process(handler);
386 if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
387 REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
389 RDEBUG2("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
394 * EAP-TLS handshake was successful, tell the
395 * client to keep talking.
397 * If this was EAP-TLS, we would just return
398 * an EAP-TLS-Success packet here.
401 tls_handshake_send(request, tls_session);
402 rad_assert(t != NULL);
406 * The TLS code is still working on the TLS
407 * exchange, and it's a valid TLS request.
414 * Handshake is done, proceed with decoding tunneled
421 * Anything else: fail.
428 * Session is established, proceed with decoding
431 RDEBUG2("Session established. Proceeding to decode tunneled attributes");
434 * Process the FAST portion of the request.
436 rcode = eap_fast_process(handler, tls_session);
439 case PW_CODE_ACCESS_REJECT:
441 eaptls_fail(handler, 0);
445 * Access-Challenge, continue tunneled conversation.
447 case PW_CODE_ACCESS_CHALLENGE:
449 tls_handshake_send(request, tls_session);
450 eaptls_request(handler->eap_ds, tls_session);
454 * Success: Automatically return MPPE keys.
456 case PW_CODE_ACCESS_ACCEPT:
457 RDEBUG("Note the missing PRF label warning below is harmless, ignore it");
459 RDEBUG2("Using saved attributes from the original Access-Accept");
460 rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL);
461 fr_pair_list_mcopy_by_num(handler->request->reply,
462 &handler->request->reply->vps,
463 &t->accept_vps, 0, 0, TAG_ANY);
464 } else if (t->use_tunneled_reply) {
465 RDEBUG2("No saved attributes in the original Access-Accept");
467 return eaptls_success(handler, 0);
470 * No response packet, MUST be proxying it.
471 * The main EAP module will take care of discovering
472 * that the request now has a "proxy" packet, and
473 * will proxy it, rather than returning an EAP packet.
475 case PW_CODE_STATUS_CLIENT:
477 rad_assert(handler->request->proxy != NULL);
486 * Something we don't understand: Reject it.
488 eaptls_fail(handler, 0);
492 static int eap_fast_tls_start(EAP_DS * eap_ds,tls_session_t *tls_session)
496 reply.code = FR_TLS_START;
497 reply.length = TLS_HEADER_LEN + 1 + tls_session->clean_in.used;/*flags*/
499 reply.flags = tls_session->peap_flag;
500 reply.flags = SET_START(reply.flags);
502 reply.data = tls_session->clean_in.data;
503 reply.dlen = tls_session->clean_in.used;
505 eaptls_compose(eap_ds, &reply);
512 * Send an initial eap-tls request to the peer, using the libeap functions.
514 static int mod_session_init(void *type_arg, eap_handler_t *handler)
517 tls_session_t *tls_session;
518 rlm_eap_fast_t *inst;
521 REQUEST *request = handler->request;
528 * EAP-TLS-Require-Client-Cert attribute will override
529 * the require_client_cert configuration option.
531 vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
533 client_cert = vp->vp_integer ? true : false;
535 client_cert = inst->req_client_cert;
537 handler->opaque = tls_session = eaptls_session(handler, inst->tls_conf, client_cert);
539 if (!tls_session) return 0;
542 * Push TLV of authority_identity into tls_record
543 * call eap_tls_compose() with args
545 * RFC 4851 section 4.1.1
546 * N.B. mandatory/reserved flags are not applicable here
548 eap_fast_tlv_append(tls_session, PAC_INFO_A_ID, false, PAC_A_ID_LENGTH, inst->a_id);
549 tls_session->peap_flag = EAP_FAST_VERSION;
550 tls_session->length_flag = false;
551 rcode = eap_fast_tls_start(handler->eap_ds, tls_session);
554 talloc_free(tls_session);
558 tls_session->record_init(&tls_session->clean_in);
560 if (!SSL_set_session_ticket_ext_cb(tls_session->ssl, _session_ticket, tls_session)) {
561 RERROR("Failed setting SSL session ticket callback");
565 handler->stage = PROCESS;
572 * The module name should be the only globally exported symbol.
573 * That is, everything else should be 'static'.
575 extern rlm_eap_module_t rlm_eap_fast;
576 rlm_eap_module_t rlm_eap_fast = {
578 .instantiate = mod_instantiate, /* Create new submodule instance */
579 .session_init = mod_session_init, /* Initialise a new EAP session */
580 .process = mod_process /* Process next round of EAP method */