The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_eap.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 1b14d305a60f4523ba374b51deb7ee0bcbbecf27 $
19 * @file rlm_eap.c
20 * @brief Implements the EAP framework.
21 *
22 * @copyright 2000-2003,2006 The FreeRADIUS server project
23 * @copyright 2001 hereUare Communications, Inc. (raghud@hereuare.com)
24 * @copyright 2003 Alan DeKok (aland@freeradius.org)
25 */
26RCSID("$Id: 1b14d305a60f4523ba374b51deb7ee0bcbbecf27 $")
27
28#define LOG_PREFIX mctx->mi->name
29
30#include <freeradius-devel/server/base.h>
31#include <freeradius-devel/server/module_rlm.h>
32#include <freeradius-devel/server/dl_module.h>
33#include <freeradius-devel/server/dl_module.h>
34#include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
35#include <freeradius-devel/unlang/interpret.h>
36#include <freeradius-devel/unlang/module.h>
37#include "rlm_eap.h"
38
40
41/** Resume context for calling a submodule
42 *
43 */
44typedef struct {
45 char const *caller; //!< Original caller.
46 rlm_eap_t *inst; //!< Instance of the rlm_eap module.
47 eap_session_t *eap_session; //!< The eap_session we're continuing.
48 rlm_rcode_t rcode; //!< The result of the submodule.
50
51static int submodule_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
52 CONF_ITEM *ci, UNUSED conf_parser_t const *rule);
53static int eap_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
54 CONF_ITEM *ci, UNUSED conf_parser_t const *rule);
55
57 { L("nai"), REQUIRE_REALM_NAI },
58 { L("no"), REQUIRE_REALM_NO },
59 { L("yes"), REQUIRE_REALM_YES }
60};
62
63static const conf_parser_t module_config[] = {
64 { FR_CONF_OFFSET("require_identity_realm", rlm_eap_t, require_realm),
67 .dflt = "nai" },
68
69 { FR_CONF_OFFSET_IS_SET("default_eap_type", FR_TYPE_VOID, 0, rlm_eap_t, default_method), .func = eap_type_parse },
70
72
73 { FR_CONF_OFFSET("ignore_unknown_eap_types", rlm_eap_t, ignore_unknown_types), .dflt = "no" },
74
75 { FR_CONF_DEPRECATED("timer_expire", rlm_eap_t, timer_limit), .dflt = "60" },
76 { FR_CONF_DEPRECATED("cisco_accounting_username_bug", rlm_eap_t,
77 cisco_accounting_username_bug), .dflt = "no" },
78 { FR_CONF_DEPRECATED("max_sessions", rlm_eap_t, max_sessions), .dflt = "2048" },
80};
81
83static fr_dict_t const *dict_radius;
84
87 { .out = &dict_freeradius, .proto = "freeradius" },
88 { .out = &dict_radius, .proto = "radius" },
89 { NULL }
90};
91
97
102
103
106 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
107 { .out = &attr_eap_type, .name = "EAP-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
108 { .out = &attr_eap_identity, .name = "EAP-Identity", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
109 { .out = &attr_stripped_user_domain, .name = "Stripped-User-Domain", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
110 { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
111
112 { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
113 { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
114 { .out = &attr_state, .name = "State", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
115 { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_radius },
116
117 { NULL }
118};
119
120static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) CC_HINT(nonnull);
121static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) CC_HINT(nonnull);
122
123/** Loads submodules based on type = foo pairs
124 *
125 * @param[in] ctx to allocate data in (instance of rlm_eap_t).
126 * @param[out] out Where to write child conf section to.
127 * @param[in] parent Base structure address.
128 * @param[in] ci #CONF_PAIR specifying the name of the type module.
129 * @param[in] rule unused.
130 * @return
131 * - 0 on success.
132 * - -1 on failure.
133 */
134static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent,
135 CONF_ITEM *ci, conf_parser_t const *rule)
136{ char const *name = cf_pair_value(cf_item_to_pair(ci));
137 char *our_name = NULL;
138 char *p;
139 eap_type_t method;
140
141 /*
142 * Search with underscores smashed to hyphens
143 * as that's what's used in the dictionary.
144 */
145 p = our_name = talloc_strdup(NULL, name);
146 while (*p) {
147 if (*p == '_') *p = '-';
148 p++;
149 }
150
151 method = eap_name2type(our_name);
152 talloc_free(our_name);
153
154 if (method == FR_EAP_METHOD_INVALID) {
155 cf_log_err(ci, "Unknown EAP type %s", name);
156 return -1;
157 }
158
159#if !defined(HAVE_OPENSSL_SSL_H) || !defined(HAVE_LIBSSL)
160 /*
161 * This allows the default configuration to be
162 * shipped with EAP-TLS, etc. enabled. If the
163 * system doesn't have OpenSSL, they will be
164 * ignored.
165 *
166 * If the system does have OpenSSL, then this
167 * code will not be used. The administrator will
168 * then have to delete the tls,
169 * etc. configurations from eap.conf in order to
170 * have EAP without the TLS types.
171 */
172 switch (method) {
180 {
182
185 module_instance_t, "rlm_eap"))));
186 WARN("Ignoring EAP method %s because we don't have OpenSSL support", name);
187 }
188 return 0;
189
190 default:
191 break;
192 }
193#endif
194 return module_rlm_submodule_parse(ctx, out, parent, ci, rule);
195}
196
197/** Convert EAP type strings to eap_type_t values
198 *
199 * @param[in] ctx unused.
200 * @param[out] out Where to write the #eap_type_t value we found.
201 * @param[in] parent Base structure address.
202 * @param[in] ci #CONF_PAIR specifying the name of the EAP method.
203 * @param[in] rule unused.
204 * @return
205 * - 0 on success.
206 * - -1 on failure.
207 */
208static int eap_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
209 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
210{
211 char const *default_method_name = cf_pair_value(cf_item_to_pair(ci));
212 eap_type_t method;
213
214 /*
215 * Ensure that the default EAP type is loaded.
216 */
217 method = eap_name2type(default_method_name);
218 if (method == FR_EAP_METHOD_INVALID) {
219 cf_log_err(ci, "Unknown EAP type %s", default_method_name);
220 return -1;
221 }
222
223 *(eap_type_t *)out = method;
224
225 return 0;
226}
227
228/** Process NAK data from EAP peer
229 *
230 */
232 eap_type_t last_type,
233 eap_type_data_t *nak)
234{
236 unsigned int i, s_i = 0;
237 fr_pair_t *vp = NULL;
239 eap_type_t sanitised[nak->length];
240
241 /*
242 * The NAK data is the preferred EAP type(s) of
243 * the client.
244 *
245 * RFC 3748 says to list one or more proposed
246 * alternative types, one per octet, or to use
247 * 0 for no alternative.
248 */
249 if (!nak->data) {
250 REDEBUG("Peer sent empty (invalid) NAK. Can't select method to continue with");
251
253 }
254
255 /*
256 * Do a loop over the contents of the NAK, only moving entries
257 * which are valid to the sanitised array.
258 */
259 for (i = 0; i < nak->length; i++) {
260 /*
261 * Type 0 is valid, and means there are no
262 * common choices.
263 */
264 if (nak->data[i] == 0) {
265 REDEBUG("Peer NAK'd indicating it is not willing to continue");
266
268 }
269
270 /*
271 * It is invalid to request identity,
272 * notification & nak in nak.
273 */
274 if (nak->data[i] < FR_EAP_METHOD_MD5) {
275 REDEBUG("Peer NAK'd asking for bad type %s (%d)", eap_type2name(nak->data[i]), nak->data[i]);
276
278 }
279
280 if ((nak->data[i] >= FR_EAP_METHOD_MAX) ||
281 !inst->methods[nak->data[i]].submodule) {
282 RDEBUG2("Peer NAK'd asking for unsupported EAP type %s (%d), skipping...",
283 eap_type2name(nak->data[i]),
284 nak->data[i]);
285
286 continue;
287 }
288
289 /*
290 * Prevent a firestorm if the client is confused.
291 *
292 * FIXME: Really we should keep a list of
293 * methods we've already sent back.
294 */
295 if (last_type == nak->data[i]) {
296 char const *type_str = eap_type2name(nak->data[i]);
297
298 RDEBUG2("Peer NAK'd our request for %s (%d) with a request for %s (%d), skipping...",
299 type_str, nak->data[i], type_str, nak->data[i]);
300
301 RWARN("!!! We requested to use EAP type %s (%i)", type_str, nak->data[i]);
302 RWARN("!!! The supplicant rejected that, and requested to use the same EAP type.");
303 RWARN("!!! i.e. the supplicant said 'I don't like %s, please use %s instead.",
304 type_str, type_str);
305 RWARN("!!! The supplicant software is broken and does not work properly.");
306 RWARN("!!! Please upgrade it to software that works.");
307
308 continue;
309 }
310
311 sanitised[s_i++] = nak->data[i];
312 }
313
314 if (s_i == 0) {
315 REDEBUG("Peer presented no valid EAP types in its NAK response");
317 }
318
319 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_type);
320 if (vp) {
321 /*
322 * Loop over allowed methods and the contents
323 * of the NAK, attempting to find something
324 * we can continue with.
325 */
326 do {
327 /*
328 * Provide a way of the admin potentially
329 * disabling EAP negotiation.
330 */
331 if (vp->vp_uint32 == FR_EAP_METHOD_INVALID) continue;
332
333 for (i = 0; i < s_i; i++) {
334 /*
335 * Enforce per-user configuration of EAP
336 * types.
337 */
338 if (vp->vp_uint32 != sanitised[i]) continue;
339 RDEBUG2("Found mutually acceptable type %s (%d)", eap_type2name(sanitised[i]), sanitised[i]);
340 method = sanitised[i];
341 break;
342 }
343
344 if (method != FR_EAP_METHOD_INVALID) break; /* Found one1 */
345 } while ((vp = fr_pair_find_by_da(&request->control_pairs, vp, attr_eap_type)));
346 /*
347 * If there's no control pairs, respond with
348 * the first valid method in the NAK.
349 */
350 } else {
351 method = sanitised[0];
352 }
353
354 /*
355 * Couldn't find something to continue with,
356 * emit a very verbose message.
357 */
358 if (method == FR_EAP_METHOD_INVALID) {
359 fr_sbuff_t *proposed = NULL, *allowed = NULL;
360
361 FR_SBUFF_TALLOC_THREAD_LOCAL(&proposed, 256, 1024);
362 FR_SBUFF_TALLOC_THREAD_LOCAL(&allowed, 256, 1024);
363
364 for (i = 0; i < s_i; i++) {
365 (void) fr_sbuff_in_sprintf(proposed, "%s (%d), ", eap_type2name(sanitised[i]), sanitised[i]);
366 }
367 fr_sbuff_advance(proposed, -2);
368 fr_sbuff_terminate(proposed);
369
370 vp = NULL;
371 while ((vp = fr_pair_find_by_da(&request->control_pairs, vp, attr_eap_type))) {
372 (void) fr_sbuff_in_sprintf(allowed, "%s (%d), ", eap_type2name(vp->vp_uint32), vp->vp_uint32);
373 }
374 fr_sbuff_advance(allowed, -2); /* Negative advance past start should be disallowed */
375 fr_sbuff_terminate(allowed);
376
377 REDEBUG("No mutually acceptable EAP types found. Supplicant proposed: %s. We allow: %s",
378 fr_sbuff_start(proposed), fr_sbuff_start(allowed));
379 }
380
381 return method;
382}
383
384/** Cancel a call to a submodule
385 *
386 * @param[in] mctx module calling ctx.
387 * @param[in] request The current request.
388 * @param[in] action to perform.
389 */
390static void mod_authenticate_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
391{
392 eap_session_t *eap_session;
393
394 RDEBUG2("Request cancelled - Destroying EAP-Session");
395
396 eap_session = talloc_get_type_abort(mctx->rctx, eap_session_t);
397
398 TALLOC_FREE(eap_session->subrequest);
399
400 /*
401 * This is the only safe thing to do.
402 * We have no idea what state the submodule
403 * left its opaque data in.
404 */
405 eap_session_destroy(&eap_session);
406}
407
408/** Process the result of calling a submodule
409 *
410 * @param[out] p_result Result of calling the module, one of:
411 * - RLM_MODULE_INVALID if the request or EAP session state is invalid.
412 * - RLM_MODULE_OK if this round succeeded.
413 * - RLM_MODULE_HANDLED if we're done with this round.
414 * - RLM_MODULE_REJECT if the user should be rejected.
415 * @param[in] request The current request.
416 * @param[in] mctx module calling ctx.
417 * @param[in] eap_session the EAP session
418 * @param[in] result the input result from the submodule
419 */
421 request_t *request, eap_session_t *eap_session, rlm_rcode_t result)
422{
423 rlm_rcode_t rcode;
424 fr_pair_t *next, *vp;
425
426 /*
427 * Hoist any instances of Module-Failure-Message from the subrequest
428 * so they can be used for logging failures.
429 */
430 vp = fr_pair_find_by_da(&eap_session->subrequest->request_pairs, NULL, attr_module_failure_message);
431 while (vp) {
432 next = fr_pair_find_by_da(&eap_session->subrequest->request_pairs, vp, attr_module_failure_message);
433 fr_pair_remove(&eap_session->subrequest->request_pairs, vp);
434 fr_pair_steal_append(request->request_ctx, &request->request_pairs, vp);
435 vp = next;
436 }
437
438 /*
439 * Cleanup the subrequest
440 */
441 TALLOC_FREE(eap_session->subrequest);
442
443 /*
444 * The submodule failed. Die.
445 */
446 switch (result) {
447 case RLM_MODULE_FAIL:
449 eap_fail(eap_session);
450 eap_session_destroy(&eap_session);
451
452 rcode = RLM_MODULE_INVALID;
453 goto finish;
454
455 /*
456 * Inconsistent result from submodule...
457 */
461 break;
462
463 default:
464 break;
465 }
466
467 /*
468 * We are done, wrap the EAP-request in RADIUS to send
469 * with all other required radius attributes
470 */
471 rcode = eap_compose(eap_session);
472
473 /*
474 * Add to the list only if it is EAP-Request, OR if
475 * it's LEAP, and a response.
476 */
477 if (((eap_session->this_round->request->code == FR_EAP_CODE_REQUEST) &&
478 (eap_session->this_round->request->type.num >= FR_EAP_METHOD_MD5)) ||
479
480 /*
481 * LEAP is a little different. At Stage 4,
482 * it sends an EAP-Success message, but we still
483 * need to keep the State attribute & session
484 * data structure around for the AP Challenge.
485 *
486 * At stage 6, LEAP sends an EAP-Response, which
487 * isn't put into the list.
488 */
489 ((eap_session->this_round->response->code == FR_EAP_CODE_RESPONSE) &&
490 (eap_session->this_round->response->type.num == FR_EAP_METHOD_LEAP) &&
491 (eap_session->this_round->request->code == FR_EAP_CODE_SUCCESS) &&
492 (eap_session->this_round->request->type.num == 0))) {
493 talloc_free(eap_session->prev_round);
494 eap_session->prev_round = eap_session->this_round;
495 eap_session->this_round = NULL;
496 } else {
497 RDEBUG2("Cleaning up EAP session");
498 eap_session_destroy(&eap_session);
499 }
500
501 /*
502 * Freeze the eap_session so we can continue
503 * the authentication session later.
504 */
505 eap_session_freeze(&eap_session);
506
507finish:
508 RETURN_MODULE_RCODE(rcode);
509}
510
511/** Call mod_authenticate_result asynchronously from the unlang interpreter
512 *
513 * @param[out] p_result The result of the operation.
514 * @param[in] mctx module calling ctx.
515 * @param[in] request the current request.
516 * @return The result of this round of authentication.
517 */
519 request_t *request)
520{
521 eap_session_t *eap_session = talloc_get_type_abort(mctx->rctx, eap_session_t);
522
523 return mod_authenticate_result(p_result, mctx, request, eap_session, eap_session->submodule_rcode);
524}
525
526/** Basic tests to determine if an identity is a valid NAI
527 *
528 * In this version we mostly just care about realm.
529 *
530 * @param[in] identity to check.
531 * @return
532 * - The length of the string on success.
533 * - <= 0 a negative offset specifying where the format error occurred.
534 */
535static ssize_t eap_identity_is_nai_with_realm(char const *identity)
536{
537 char const *p = identity;
538 char const *end = identity + (talloc_array_length(identity) - 1);
539 char const *realm;
540
541 /*
542 * Get the last '@'
543 */
544 p = realm = memrchr(identity, '@', end - p);
545 if (!p) {
546 fr_strerror_printf("Identity is not valid. Missing realm separator '@'");
547 return identity - end;
548 }
549
550 p = memchr(p, '.', end - p);
551 if (!p) {
552 fr_strerror_printf("Identity is not valid. Realm is missing label separator '.'");
553 return identity - end;
554 }
555
556 if ((realm - 1) == p) {
557 fr_strerror_printf("Identity is not valid. "
558 "Realm is missing label between realm separator '@' and label separator '.'");
559 return identity - realm;
560 }
561 if ((p + 1) == end) {
562 fr_strerror_printf("Identity is not valid. "
563 "Realm is missing label between label separator '.' and the end of the "
564 "identity string");
565 return identity - end;
566 }
567
568 return end - identity;
569}
570
571/** Select the correct callback based on a response
572 *
573 * Based on the EAP response from the supplicant, and setup a call on the
574 * unlang stack to the appropriate submodule.
575 *
576 * Default to the configured EAP-Type for all Unsupported EAP-Types.
577 *
578 * @param[out] p_result the result of the operation.
579 * @param[in] mctx module calling ctx.
580 * @param[in] eap_session State data that persists over multiple rounds of EAP.
581 * @return
582 * - UNLANG_ACTION_CALCULATE_RESULT + *p_result = RLM_MODULE_INVALID.
583 * Invalid request.
584 * - UNLANG_ACTION_PUSHED_CHILD Yield control back to the interpreter so it can
585 * call the submodule.
586 */
587static unlang_action_t eap_method_select(rlm_rcode_t *p_result, module_ctx_t const *mctx, eap_session_t *eap_session)
588{
590 eap_type_data_t *type = &eap_session->this_round->response->type;
591 request_t *request = eap_session->request;
592
593 rlm_eap_method_t const *method;
594
595 eap_type_t next = inst->default_method;
596 fr_pair_t *vp;
597
598 /*
599 * Session must have been thawed...
600 */
601 fr_assert(eap_session->request);
602
603 /*
604 * Don't trust anyone.
605 */
606 if ((type->num == 0) || (type->num >= FR_EAP_METHOD_MAX)) {
607 REDEBUG("Peer sent EAP type number %d, which is outside known range", type->num);
608
609 is_invalid:
611 }
612
613 /*
614 * Multiple levels of TLS nesting are invalid. But if
615 * the parent has a home_server defined, then this
616 * request is being processed through a virtual
617 * server... so that's OK.
618 *
619 * i.e. we're inside an EAP tunnel, which means we have a
620 * parent. If the outer session exists, and doesn't have
621 * a home server, then it's multiple layers of tunneling.
622 */
623 if (type->num == FR_EAP_METHOD_TLS && eap_session->request->parent &&
624 eap_session->request->parent->parent) {
625 RERROR("Multiple levels of TLS nesting are invalid");
626 goto is_invalid;
627 }
628
629 RDEBUG2("Peer sent packet with EAP method %s (%d)", eap_type2name(type->num), type->num);
630
631 /*
632 * Figure out what to do.
633 */
634 switch (type->num) {
636 {
637 ssize_t slen;
638
639 /*
640 * Check if we allow this identity format
641 */
642 switch (inst->require_realm) {
644 slen = eap_identity_is_nai_with_realm(eap_session->identity);
645 if (slen <= 0) {
646 char *tmp_id;
647 bad_id:
648 /*
649 * Produce an escaped version and run that
650 * through the format check function to get
651 * the correct offset *sigh*...
652 */
653 MEM(tmp_id = fr_asprint(NULL,
654 eap_session->identity,
655 talloc_array_length(eap_session->identity) - 1,
656 '"'));
657 slen = eap_identity_is_nai_with_realm(tmp_id);
658
659 REMARKER(tmp_id, slen, "%s", fr_strerror());
660
661 talloc_free(tmp_id);
662 goto is_invalid;
663 }
664 break;
665
667 slen = eap_identity_is_nai_with_realm(eap_session->identity);
668 if (slen <= 0) {
669 fr_pair_t *stripped_user_domain;
670
671 /*
672 * If it's not an NAI with a realm, check
673 * to see if the user has set Stripped-User-domain.
674 */
675 stripped_user_domain = fr_pair_find_by_da_idx(&eap_session->request->request_pairs,
677 if (!stripped_user_domain) goto bad_id;
678 }
679 break;
680
681 case REQUIRE_REALM_NO:
682 break;
683 }
684 }
685 /*
686 * Allow per-user configuration of EAP types.
687 */
688 vp = fr_pair_find_by_da(&eap_session->request->control_pairs, NULL, attr_eap_type);
689 if (vp) {
690 RDEBUG2("Using method from control.EAP-Type");
691 next = vp->vp_uint32;
692 /*
693 * We have an array of the submodules which
694 * have a type_identity callback. Call
695 * each of these in turn to see if any of
696 * them recognise the identity.
697 */
698 } else if (inst->type_identity_submodule) {
699 size_t i;
700
701 for (i = 0; i < inst->type_identity_submodule_len; i++) {
702 rlm_eap_submodule_t const *submodule =
703 (rlm_eap_submodule_t const *)inst->type_identity_submodule[i]->exported;
704 eap_type_t ret;
705
706 ret = submodule->type_identity(inst->type_identity_submodule[i]->data,
707 eap_session->identity,
708 talloc_array_length(eap_session->identity) - 1);
709 if (ret != FR_EAP_METHOD_INVALID) {
710 next = ret;
711 break;
712 }
713 }
714 }
715 do_init:
716 /*
717 * Ensure it's valid.
718 */
719 if ((next < FR_EAP_METHOD_MD5) || (next >= FR_EAP_METHOD_MAX) || (!inst->methods[next].submodule)) {
720 REDEBUG2("Peer tried to start unsupported EAP type %s (%d)",
721 eap_type2name(next), next);
722 goto is_invalid;
723 }
724
725 eap_session->process = inst->methods[next].submodule->session_init;
726 eap_session->type = next;
727 break;
728
730 /*
731 * Delete old data, if necessary. If we called a method
732 * before, and it initialized itself, we need to free
733 * the memory it alloced.
734 */
735 TALLOC_FREE(eap_session->opaque);
736 fr_state_discard_child(eap_session->request, eap_session, 0);
737 next = eap_process_nak(mctx, eap_session->request, eap_session->type, type);
738 if (!next) RETURN_MODULE_REJECT;
739
740 /*
741 * Initialise the state machine for the next submodule
742 */
743 goto do_init;
744
745 /*
746 * Only allow modules that are enabled to be called,
747 * treating any other requests as invalid.
748 *
749 * This may seem a bit harsh, but remember the server
750 * dictates which type of EAP method should be started,
751 * so this is the supplicant ignoring the normal EAP method
752 * negotiation mechanism, by not NAKing and just trying
753 * to start a new EAP method.
754 */
755 default:
756 if (!inst->methods[type->num].submodule) {
757 REDEBUG2("Peer asked for unsupported EAP type %s (%d)", eap_type2name(type->num), type->num);
758 goto is_invalid;
759 }
760 /*
761 * Perr started the EAP method without
762 * sending an Identity-Response.
763 *
764 * There's nothing that says it *HAS* to send an
765 * identity response before starting a method,
766 * so just jump to the initialisation function
767 * of the method and continue.
768 */
769 if (eap_session->rounds == 0) {
770 RDEBUG2("Peer started EAP type %s (%d) without sending an Identity", eap_type2name(type->num), type->num);
771 vp = fr_pair_find_by_da(&eap_session->request->control_pairs, NULL, attr_eap_type);
772 if (vp) {
773 RDEBUG2("Using method from control.EAP-Type");
774 next = vp->vp_uint32;
775 }
776 goto do_init;
777 }
778
779 /*
780 * FIXME - We should only update the type
781 * on completion of the final round.
782 */
783 eap_session->type = type->num;
784 break;
785 }
786
787 method = &inst->methods[eap_session->type];
788
789 RDEBUG2("Calling submodule %s", method->submodule->common.name);
790
791 /*
792 * Allocate a new subrequest
793 */
794 MEM(eap_session->subrequest = unlang_subrequest_alloc(request,
795 method->submodule->namespace ?
796 *(method->submodule->namespace) :
797 request->proto_dict));
798
799 if (method->submodule->clone_parent_lists) {
800 if (fr_pair_list_copy(eap_session->subrequest->control_ctx,
801 &eap_session->subrequest->control_pairs, &request->control_pairs) < 0) {
802 list_copy_fail:
803 RERROR("Failed copying parent's attribute list");
804 fail:
805 TALLOC_FREE(eap_session->subrequest);
807 }
808
809 if (fr_pair_list_copy(eap_session->subrequest->request_ctx,
810 &eap_session->subrequest->request_pairs,
811 &request->request_pairs) < 0) goto list_copy_fail;
812 }
813
814 /*
815 * Push a resumption frame for the parent
816 * This will get executed when the child is
817 * done (after the subrequest frame in the
818 * parent gets popped).
819 */
821
822 /*
823 * This sets up a subrequest frame in the parent
824 * and a resumption frame in the child.
825 *
826 * This must be done before pushing frames onto
827 * the child's stack.
828 */
829 if (unlang_subrequest_child_push(&eap_session->submodule_rcode, eap_session->subrequest,
830 &(unlang_subrequest_session_t){ .enable = true, .unique_ptr = eap_session },
831 false, UNLANG_SUB_FRAME) < 0) {
832 child_fail:
833 unlang_interpet_frame_discard(request); /* Ensure the yield frame doesn't stick around */
834 goto fail;
835 }
836
837 /*
838 * Push the EAP submodule into the child's stack
839 */
840 if (unlang_module_push(NULL, /* rcode should bubble up and be set in eap_session->submodule_rcode */
841 eap_session->subrequest, method->submodule_inst, eap_session->process,
842 UNLANG_SUB_FRAME) < 0) {
843 goto child_fail;
844 }
845
846 if (eap_session->identity) {
847 fr_pair_t *identity;
848
849 request = eap_session->subrequest; /* Set request for pair_append_request macro */
850
851 MEM(pair_append_request(&identity, attr_eap_identity) >= 0);
852 fr_pair_value_bstrdup_buffer(identity, eap_session->identity, true);
853 }
854
855 /*
856 * Add the EAP-Type we're running to the subrequest
857 * This is useful for when policies are shared between
858 * virtual server sections for multiple EAP types.
859 */
860 {
861 fr_pair_t *type_vp;
862
863 MEM(pair_append_request(&type_vp, attr_eap_type) >= 0);
864 type_vp->vp_uint32 = eap_session->type;
865 }
866
868}
869
870static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
871{
873 eap_session_t *eap_session;
874 eap_packet_raw_t *eap_packet;
876
877 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_message)) {
878 REDEBUG("You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!");
880 }
881
882 /*
883 * Reconstruct the EAP packet from the EAP-Message
884 * attribute. The relevant decoder should have already
885 * concatenated the fragments into a single buffer.
886 */
887 eap_packet = eap_packet_from_vp(request, &request->request_pairs);
888 if (!eap_packet) {
889 RPERROR("Malformed EAP Message");
891 }
892
893 /*
894 * Allocate a new eap_session, or if this request
895 * is part of an ongoing authentication session,
896 * retrieve the existing eap_session from the request
897 * data.
898 */
899 eap_session = eap_session_continue(inst, &eap_packet, request);
900 if (!eap_session) RETURN_MODULE_INVALID; /* Don't emit error here, it will mask the real issue */
901
902 /*
903 * Call an EAP submodule to process the request,
904 * or with simple types like Identity and NAK,
905 * process it ourselves.
906 */
907 if ((ua = eap_method_select(p_result, mctx, eap_session)) != UNLANG_ACTION_CALCULATE_RESULT) return ua;
908 switch (*p_result) {
909 case RLM_MODULE_OK:
911 eap_session_freeze(&eap_session);
912 break;
913
914 /*
915 * RFC 3748 Section 2
916 * The conversation continues until the authenticator cannot
917 * authenticate the peer (unacceptable Responses to one or more
918 * Requests), in which case the authenticator implementation MUST
919 * transmit an EAP Failure (Code 4).
920 */
921 default:
922 eap_fail(eap_session);
923 eap_session_destroy(&eap_session);
924 break;
925 }
926
927 return ua;
928}
929
930/*
931 * EAP authorization DEPENDS on other rlm authorizations,
932 * to check for user existence & get their configured values.
933 * It Handles EAP-START Messages, User-Name initialization.
934 */
935static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
936{
938 int status;
939
940 if (!inst->auth_type) {
941 WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup EAP authentication",
942 mctx->mi->name, mctx->mi->name);
944 }
945
946 /*
947 * For EAP_START, send Access-Challenge with EAP Identity
948 * request. even when we have to proxy this request
949 *
950 * RFC 2869, Section 2.3.1 notes that the "domain" of the
951 * user, (i.e. where to proxy it) comes from the EAP-Identity,
952 * so we CANNOT proxy the user, until we know its identity.
953 *
954 * We therefore send an EAP Identity request.
955 */
956 status = eap_start(request, inst->methods, inst->ignore_unknown_types);
957 switch (status) {
958 case RLM_MODULE_NOOP:
959 case RLM_MODULE_FAIL:
961 return status;
962
963 default:
964 break;
965 }
966
968
969 if (status == RLM_MODULE_OK) RETURN_MODULE_OK;
970
972}
973
974static unlang_action_t mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
975{
976 fr_pair_t *vp;
977 eap_session_t *eap_session;
979
980 /*
981 * If it's an Access-Accept, RFC 2869, Section 2.3.1
982 * says that we MUST include a User-Name attribute in the
983 * Access-Accept.
984 */
985 username = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
986 if ((request->reply->code == FR_RADIUS_CODE_ACCESS_ACCEPT) && username) {
987 /*
988 * Doesn't exist, add it in.
989 */
990 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_user_name);
991 if (!vp) {
992 vp = fr_pair_copy(request->reply_ctx, username);
993 fr_pair_append(&request->reply_pairs, vp);
994 }
995 }
996
997 /*
998 * Only synthesize a failure message if something
999 * previously rejected the request.
1000 */
1001 if (request->reply->code != FR_RADIUS_CODE_ACCESS_REJECT) RETURN_MODULE_NOOP;
1002
1003 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_message)) {
1004 RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
1006 }
1007
1008 if (fr_pair_find_by_da(&request->reply_pairs, NULL, attr_eap_message)) {
1009 RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
1011 }
1012
1013 /*
1014 * Retrieve pre-existing eap_session from request
1015 * data. This will have been added to the request
1016 * data by the state API.
1017 */
1018 eap_session = eap_session_thaw(request);
1019 if (!eap_session) {
1020 RDEBUG3("Failed to get eap_session, probably already removed, not inserting EAP-Failure");
1022 }
1023
1024 /*
1025 * If this reject is before eap has been called in authenticate
1026 * the eap_round will not have been populated.
1027 */
1028 if (!eap_session->this_round) {
1029 eap_packet_raw_t *eap_packet = eap_packet_from_vp(request, &request->request_pairs);
1030 eap_session->this_round = eap_round_build(eap_session, &eap_packet);
1031 }
1032
1033 /*
1034 * This should never happen, but we may be here
1035 * because there was an unexpected error in the
1036 * EAP module.
1037 */
1038 if (!fr_cond_assert(eap_session->this_round) || !fr_cond_assert(eap_session->this_round->request)) {
1039 eap_session_destroy(&eap_session); /* Free the EAP session, and dissociate it from the request */
1041 }
1042
1043 /*
1044 * Already set to failure, assume something else
1045 * added EAP-Message with a failure code, do nothing.
1046 */
1048
1049 /*
1050 * Was *NOT* an EAP-Failure, so we now need to turn it into one.
1051 */
1052 REDEBUG("Request rejected after last call to module \"%s\", transforming response into EAP-Failure",
1053 mctx->mi->name);
1054 eap_fail(eap_session); /* Compose an EAP failure */
1055 eap_session_destroy(&eap_session); /* Free the EAP session, and dissociate it from the request */
1056
1057 /*
1058 * Make sure there's a message authenticator attribute in the response
1059 * RADIUS protocol code will calculate the correct value later...
1060 */
1063
1065}
1066
1067static int mod_instantiate(module_inst_ctx_t const *mctx)
1068{
1069 rlm_eap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_eap_t);
1070 size_t i;
1071 size_t j, loaded, count = 0;
1072
1073 loaded = talloc_array_length(inst->type_submodules);
1074
1075 /*
1076 * Pre-allocate the method identity to be the number
1077 * of modules we're going to load.
1078 *
1079 * We'll shrink it later.
1080 */
1081 if (!inst->default_method_is_set) {
1082 MEM(inst->type_identity_submodule = talloc_array(inst, module_instance_t const *, loaded));
1083 }
1084
1085 for (i = 0; i < loaded; i++) {
1086 module_instance_t *submodule_inst = inst->type_submodules[i];
1087 rlm_eap_submodule_t const *submodule;
1088
1089 if (!submodule_inst) continue; /* Skipped as we don't have SSL support */
1090
1091 submodule = (rlm_eap_submodule_t const *)submodule_inst->module->exported;
1092
1093 /*
1094 * Add the methods the submodule provides
1095 */
1096 for (j = 0; j < MAX_PROVIDED_METHODS; j++) {
1097 eap_type_t method;
1098
1099 if (!submodule->provides[j]) break;
1100
1101 method = submodule->provides[j];
1102
1103 /*
1104 * If the user didn't specify a default method
1105 * take the first method provided by the first
1106 * submodule as the default.
1107 */
1108 if (!inst->default_method_is_set && (i == 0)) inst->default_method = method;
1109
1110 /*
1111 * Check for duplicates
1112 */
1113 if (inst->methods[method].submodule) {
1114 CONF_SECTION *conf = inst->methods[method].submodule_inst->conf;
1115
1116 cf_log_err(submodule_inst->conf,
1117 "Duplicate EAP-Type %s. Conflicting entry %s[%u]",
1118 eap_type2name(method),
1120
1121 return -1;
1122 }
1123
1124 inst->methods[method].submodule_inst = submodule_inst;
1125 inst->methods[method].submodule = submodule;
1126 }
1127
1128 /*
1129 * This module provides a method identity
1130 * callback. We need to call each of these
1131 * in turn if default_eap_type isn't set,
1132 * to figure out the default eap type.
1133 */
1134 if (!inst->default_method_is_set && submodule->type_identity) {
1135 inst->type_identity_submodule[inst->type_identity_submodule_len++] = submodule_inst;
1136 }
1137 count++;
1138 }
1139
1140 /*
1141 * Check if the default method specified is actually
1142 * allowed by the config.
1143 */
1144 if (inst->default_method_is_set && !inst->methods[inst->default_method].submodule) {
1145 cf_log_err_by_child(mctx->mi->conf, "default_eap_type", "EAP-Type \"%s\" is not enabled",
1146 eap_type2name(inst->default_method));
1147 return -1;
1148 }
1149
1150 if (count == 0) {
1151 cf_log_err(mctx->mi->conf, "No EAP method(s) configured, module cannot do anything");
1152 return -1;
1153 }
1154
1155 /*
1156 * Shrink the method identity array so it's the
1157 * correct length.
1158 */
1159 if (!inst->default_method_is_set) {
1160 if (inst->type_identity_submodule_len > 0) {
1161 MEM(inst->type_identity_submodule = talloc_realloc(inst, inst->type_identity_submodule,
1162 module_instance_t const *,
1163 inst->type_identity_submodule_len));
1164 } else {
1165 TALLOC_FREE(inst->type_identity_submodule);
1166 }
1167 }
1168
1169 inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->mi->name, -1);
1170 if (!inst->auth_type) {
1171 WARN("Failed to find 'authenticate %s {...}' section. EAP authentication will likely not work",
1172 mctx->mi->name);
1173 }
1174
1175 /*
1176 * Create our own random pool.
1177 */
1178 for (i = 0; i < 256; i++) inst->rand_pool.randrsl[i] = fr_rand();
1179 fr_isaac_init(&inst->rand_pool, 1);
1180 inst->rand_pool.randcnt = 0;
1181
1182 return 0;
1183}
1184
1185static int mod_load(void)
1186{
1187 if (eap_base_init() < 0) {
1188 fr_perror("Failed initialising EAP base library");
1189 return -1;
1190 }
1191 return 0;
1192}
1193
1194static void mod_unload(void)
1195{
1196 eap_base_free();
1197}
1198
1199/*
1200 * The module name should be the only globally exported symbol.
1201 * That is, everything else should be 'static'.
1202 */
1204 .common = {
1205 .magic = MODULE_MAGIC_INIT,
1206 .name = "eap",
1207 .inst_size = sizeof(rlm_eap_t),
1209 .onload = mod_load,
1210 .unload = mod_unload,
1212 },
1213 .method_group = {
1214 .bindings = (module_method_binding_t[]){
1215 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate },
1216 { .section = SECTION_NAME("recv", "Access-Request"), .method = mod_authorize },
1217 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_post_auth },
1219 }
1220 }
1221};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic function for parsing conf pair values as int.
Definition cf_parse.c:1592
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:658
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:612
#define FR_CONF_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a matching CONF_PAIR is found
Definition cf_parse.h:414
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:284
#define FR_CONF_OFFSET_IS_SET(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct,...
Definition cf_parse.h:298
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:448
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:449
#define FR_CONF_OFFSET_TYPE_FLAGS(_name, _type, _flags, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:241
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:595
Common header for all CONF_* types.
Definition cf_priv.h:49
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
void * cf_data_value(CONF_DATA const *cd)
Return the user assigned value of CONF_DATA.
Definition cf_util.c:1762
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1593
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_lineno(_cf)
Definition cf_util.h:104
#define cf_log_err_by_child(_parent, _child, _fmt,...)
Log an error message against a specified child.
Definition cf_util.h:316
#define cf_data_find(_cf, _type, _name)
Definition cf_util.h:244
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_filename(_cf)
Definition cf_util.h:107
#define CF_IDENT_ANY
Definition cf_util.h:78
eap_round_t * eap_round_build(eap_session_t *eap_session, eap_packet_raw_t **eap_packet_p)
Definition compose.c:548
rlm_rcode_t eap_fail(eap_session_t *eap_session)
Definition compose.c:494
rlm_rcode_t eap_start(request_t *request, rlm_eap_method_t const methods[], bool ignore_unknown_types)
Definition compose.c:291
rlm_rcode_t eap_compose(eap_session_t *eap_session)
Definition compose.c:153
eap_type_data_t type
Definition compose.h:39
eap_packet_t * response
Packet we received from the peer.
Definition compose.h:49
eap_code_t code
Definition compose.h:36
eap_packet_t * request
Packet we will send to the peer.
Definition compose.h:50
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define MEM(x)
Definition debug.h:36
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
Definition defs.h:34
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
Definition defs.h:35
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:272
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:285
fr_dict_enum_value_t * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3397
Specifies an attribute which must be present for the module to function.
Definition dict.h:271
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:284
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
eap_type_t eap_name2type(char const *name)
Return an EAP-Type for a particular name.
Definition types.c:38
char const * eap_type2name(eap_type_t method)
Return an EAP-name for a particular type.
Definition types.c:54
@ FR_EAP_CODE_FAILURE
Definition types.h:40
@ FR_EAP_CODE_RESPONSE
Definition types.h:38
@ FR_EAP_CODE_REQUEST
Definition types.h:37
@ FR_EAP_CODE_SUCCESS
Definition types.h:39
eap_type_t num
Definition types.h:110
size_t length
Definition types.h:111
enum eap_type eap_type_t
uint8_t * data
Definition types.h:112
@ FR_EAP_METHOD_SIM
Definition types.h:63
@ FR_EAP_METHOD_LEAP
Definition types.h:62
@ FR_EAP_METHOD_NAK
Definition types.h:48
@ FR_EAP_METHOD_AKA
Definition types.h:68
@ FR_EAP_METHOD_PWD
Definition types.h:98
@ FR_EAP_METHOD_AKA_PRIME
Definition types.h:96
@ FR_EAP_METHOD_TTLS
Definition types.h:66
@ FR_EAP_METHOD_MD5
Definition types.h:49
@ FR_EAP_METHOD_IDENTITY
Definition types.h:46
@ FR_EAP_METHOD_INVALID
Definition types.h:45
@ FR_EAP_METHOD_TLS
Definition types.h:58
@ FR_EAP_METHOD_PEAP
Definition types.h:70
@ FR_EAP_METHOD_MAX
Definition types.h:102
Structure to represent packet format of eap on wire
Definition types.h:121
EAP-Type specific data.
Definition types.h:109
void unlang_interpet_frame_discard(request_t *request)
Discard the bottom most frame on the request's stack.
Definition interpret.c:1795
#define UNLANG_SUB_FRAME
Definition interpret.h:36
void fr_isaac_init(fr_randctx *ctx, int flag)
Definition isaac.c:85
int eap_base_init(void)
Initialise the lib eap base library.
Definition base.c:455
void eap_base_free(void)
De-init the lib eap base library.
Definition base.c:478
eap_packet_raw_t * eap_packet_from_vp(TALLOC_CTX *ctx, fr_pair_list_t *vps)
Definition base.c:291
eap_session_t * eap_session_thaw(request_t *request)
Thaw an eap_session_t so it can be continued.
Definition session.c:205
eap_session_t * eap_session_continue(void const *instance, eap_packet_raw_t **eap_packet_p, request_t *request)
Ingest an eap_packet into a thawed or newly allocated session.
Definition session.c:307
void eap_session_freeze(eap_session_t **eap_session)
Freeze an eap_session_t so that it can continue later.
Definition session.c:173
void eap_session_destroy(eap_session_t **eap_session)
'destroy' an EAP session and disassociate it from the current request
Definition session.c:148
char * identity
NAI (User-Name) from EAP-Identity.
Definition session.h:55
void * opaque
Opaque data used by EAP methods.
Definition session.h:62
request_t * subrequest
Current subrequest being executed.
Definition session.h:45
eap_type_t type
EAP method number.
Definition session.h:49
module_method_t process
Callback that should be used to process the next round.
Definition session.h:64
request_t * request
Current request.
Definition session.h:51
rlm_rcode_t submodule_rcode
Result of last submodule call.
Definition session.h:46
eap_round_t * this_round
The EAP response we're processing, and the EAP request we're building.
Definition session.h:59
eap_round_t * prev_round
Previous response/request pair.
Definition session.h:57
int rounds
How many roundtrips have occurred this session.
Definition session.h:66
Tracks the progress of a single session of any EAP method.
Definition session.h:40
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define RPERROR(fmt,...)
Definition log.h:302
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:498
#define REDEBUG2(fmt,...)
Definition log.h:372
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_OCTETS
Raw octets.
long int ssize_t
void * memrchr(void const *s, int c, size_t n)
GNU libc extension on some platforms.
Definition missing.c:83
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
#define MODULE_INST_CTX(_mi)
Wrapper to create a module_inst_ctx_t as a compound literal.
Definition module_ctx.h:158
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
int module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
Generic conf_parser_t func for loading drivers.
Definition module_rlm.c:936
bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_da, fr_dict_enum_value_t const *enumv)
Set the next section type if it's not already set.
Definition module_rlm.c:413
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
#define RADIUS_AUTH_VECTOR_LENGTH
Definition net.h:89
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition pair.c:2321
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:695
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1347
fr_pair_t * fr_pair_find_by_da_idx(fr_pair_list_t const *list, fr_dict_attr_t const *da, unsigned int idx)
Find a pair with a matching da at a given index.
Definition pair.c:743
int fr_pair_steal_append(TALLOC_CTX *list_ctx, fr_pair_list_t *list, fr_pair_t *vp)
Change a vp's talloc ctx and insert it into a new list.
Definition pair.c:548
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:491
int fr_pair_value_mem_alloc(fr_pair_t *vp, uint8_t **out, size_t size, bool tainted)
Pre-allocate a memory buffer for a "octets" type value pair.
Definition pair.c:2886
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition print.c:428
static const conf_parser_t config[]
Definition base.c:183
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define WARN(fmt,...)
Definition radclient.h:47
static rs_t * conf
Definition radsniff.c:53
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
#define RETURN_MODULE_REJECT
Definition rcode.h:55
#define RETURN_MODULE_NOOP
Definition rcode.h:62
#define RETURN_MODULE_RCODE(_rcode)
Definition rcode.h:64
#define RETURN_MODULE_INVALID
Definition rcode.h:59
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
#define RETURN_MODULE_UPDATED
Definition rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:41
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:44
fr_dict_autoload_t rlm_eap_dict[]
Definition rlm_eap.c:86
static int mod_load(void)
Definition rlm_eap.c:1185
static unlang_action_t eap_method_select(rlm_rcode_t *p_result, module_ctx_t const *mctx, eap_session_t *eap_session)
Select the correct callback based on a response.
Definition rlm_eap.c:587
static fr_table_num_sorted_t const require_identity_realm_table[]
Definition rlm_eap.c:56
static ssize_t eap_identity_is_nai_with_realm(char const *identity)
Basic tests to determine if an identity is a valid NAI.
Definition rlm_eap.c:535
static fr_dict_attr_t const * attr_module_failure_message
Definition rlm_eap.c:96
char const * caller
Original caller.
Definition rlm_eap.c:45
static fr_dict_attr_t const * attr_state
Definition rlm_eap.c:100
static fr_dict_attr_t const * attr_eap_identity
Definition rlm_eap.c:94
fr_dict_attr_autoload_t rlm_eap_dict_attr[]
Definition rlm_eap.c:105
static int submodule_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
static fr_dict_attr_t const * attr_eap_message
Definition rlm_eap.c:98
static fr_dict_attr_t const * attr_eap_type
Definition rlm_eap.c:93
static eap_type_t eap_process_nak(module_ctx_t const *mctx, request_t *request, eap_type_t last_type, eap_type_data_t *nak)
Process NAK data from EAP peer.
Definition rlm_eap.c:231
static void mod_authenticate_cancel(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Cancel a call to a submodule.
Definition rlm_eap.c:390
static fr_dict_t const * dict_freeradius
Definition rlm_eap.c:82
eap_session_t * eap_session
The eap_session we're continuing.
Definition rlm_eap.c:47
rlm_eap_t * inst
Instance of the rlm_eap module.
Definition rlm_eap.c:46
module_rlm_t rlm_eap
Definition rlm_eap.c:1203
static int eap_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
Convert EAP type strings to eap_type_t values.
Definition rlm_eap.c:208
static fr_dict_t const * dict_radius
Definition rlm_eap.c:83
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:870
static fr_dict_attr_t const * attr_auth_type
Definition rlm_eap.c:92
rlm_rcode_t rcode
The result of the submodule.
Definition rlm_eap.c:48
static void mod_unload(void)
Definition rlm_eap.c:1194
static fr_dict_attr_t const * attr_stripped_user_domain
Definition rlm_eap.c:95
static unlang_action_t mod_authenticate_result(rlm_rcode_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request, eap_session_t *eap_session, rlm_rcode_t result)
Process the result of calling a submodule.
Definition rlm_eap.c:420
static unlang_action_t mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:935
static fr_dict_attr_t const * attr_user_name
Definition rlm_eap.c:101
static size_t require_identity_realm_table_len
Definition rlm_eap.c:61
static const conf_parser_t module_config[]
Definition rlm_eap.c:63
static unlang_action_t mod_authenticate_result_async(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Call mod_authenticate_result asynchronously from the unlang interpreter.
Definition rlm_eap.c:518
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_eap.c:1067
static fr_dict_attr_t const * attr_message_authenticator
Definition rlm_eap.c:99
static unlang_action_t mod_post_auth(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:974
Resume context for calling a submodule.
Definition rlm_eap.c:44
Implements the EAP framework.
@ REQUIRE_REALM_NAI
Require the EAP-Identity contains an NAI domain.
Definition rlm_eap.h:41
@ REQUIRE_REALM_NO
Don't require that the identity is qualified.
Definition rlm_eap.h:40
@ REQUIRE_REALM_YES
Require the EAP-Identity string contain an NAI realm or that Stripped-User-Domain is present in the r...
Definition rlm_eap.h:38
Instance data for rlm_eap.
Definition rlm_eap.h:47
static char const * name
static int instantiate(module_inst_ctx_t const *mctx)
Definition rlm_rest.c:1313
username
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1597
#define fr_sbuff_start(_sbuff_or_marker)
#define fr_sbuff_advance(_sbuff_or_marker, _len)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name
Instance name e.g. user_database.
Definition module.h:336
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:330
size_t inst_size
Size of the module's instance data.
Definition module.h:204
void * data
Module's instance data.
Definition module.h:272
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
module_t * exported
Public module structure.
Definition module.h:277
Module instance data.
Definition module.h:266
Named methods exported by a module.
Definition module.h:173
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition pair.h:129
#define pair_append_request(_attr, _da)
Allocate and append a fr_pair_t to the request list.
Definition pair.h:37
void fr_state_discard_child(request_t *parent, void const *unique_ptr, int unique_int)
Remove state from a child.
Definition state.c:906
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
return count
Definition module.c:155
int unlang_module_push(rlm_rcode_t *p_result, request_t *request, module_instance_t *mi, module_method_t method, bool top_frame)
Push a module or submodule onto the stack for evaluation.
Definition module.c:51
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:418
eap_aka_sim_process_conf_t * inst
fr_pair_value_bstrdup_buffer(vp, eap_session->identity, true)
fr_aka_sim_id_type_t type
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
module_t common
Common fields provided by all modules.
Definition submodule.h:50
module_instance_t * submodule_inst
Submodule's instance data.
Definition submodule.h:68
#define MAX_PROVIDED_METHODS
Definition submodule.h:28
rlm_eap_submodule_t const * submodule
Submodule's exported interface.
Definition submodule.h:69
bool clone_parent_lists
< Namespace children should be allocated in.
Definition submodule.h:59
eap_type_t provides[MAX_PROVIDED_METHODS]
Allow the module to register itself for more than one EAP-Method.
Definition submodule.h:52
eap_type_identity_t type_identity
Do we recognise this identity?
Definition submodule.h:55
Private structure to hold handles and interfaces for an EAP method.
Definition submodule.h:67
Interface exported by EAP submodules.
Definition submodule.h:49
request_t * unlang_subrequest_alloc(request_t *parent, fr_dict_t const *namespace)
Allocate a subrequest to run through a virtual server at some point in the future.
Definition subrequest.c:287
int unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child, unlang_subrequest_session_t const *session, bool free_child, bool top_frame)
Push a pre-existing child back onto the stack as a subrequest.
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define talloc_get_type_abort_const
Definition talloc.h:282
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition pair_inline.c:93
static fr_slen_t parent
Definition pair.h:845
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1012