The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
rlm_eap.c
Go to the documentation of this file.
1/*
2 * This program 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: b255ac960567069fdabe4abf304eeb4944588a47 $
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: b255ac960567069fdabe4abf304eeb4944588a47 $")
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/protocol/freeradius/freeradius.internal.h>
34#include "rlm_eap.h"
35
37
38/** Resume context for calling a submodule
39 *
40 */
41typedef struct {
42 char const *caller; //!< Original caller.
43 rlm_eap_t *inst; //!< Instance of the rlm_eap module.
44 eap_session_t *eap_session; //!< The eap_session we're continuing.
45 rlm_rcode_t rcode; //!< The result of the submodule.
47
48static int submodule_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
49 CONF_ITEM *ci, UNUSED conf_parser_t const *rule);
50static int eap_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
51 CONF_ITEM *ci, UNUSED conf_parser_t const *rule);
52
54 { L("nai"), REQUIRE_REALM_NAI },
55 { L("no"), REQUIRE_REALM_NO },
56 { L("yes"), REQUIRE_REALM_YES }
57};
59
60static const conf_parser_t module_config[] = {
61 { FR_CONF_OFFSET("require_identity_realm", rlm_eap_t, require_realm),
64 .dflt = "nai" },
65
66 { FR_CONF_OFFSET_IS_SET("default_eap_type", FR_TYPE_VOID, 0, rlm_eap_t, default_method), .func = eap_type_parse },
67
69
70 { FR_CONF_OFFSET("ignore_unknown_eap_types", rlm_eap_t, ignore_unknown_types), .dflt = "no" },
71
72 { FR_CONF_V3_DEPRECATED("timer_expire", rlm_eap_t, timer_limit), .dflt = "60" },
73 { FR_CONF_V3_DEPRECATED("cisco_accounting_username_bug", rlm_eap_t,
74 cisco_accounting_username_bug), .dflt = "no" },
75 { FR_CONF_V3_DEPRECATED("max_sessions", rlm_eap_t, max_sessions), .dflt = "2048" },
77};
78
80static fr_dict_t const *dict_radius;
81
84 { .out = &dict_freeradius, .proto = "freeradius" },
85 { .out = &dict_radius, .proto = "radius" },
87};
88
94
98
99
102 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
103 { .out = &attr_eap_type, .name = "EAP-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
104 { .out = &attr_eap_identity, .name = "EAP-Identity", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
105 { .out = &attr_stripped_user_domain, .name = "Stripped-User-Domain", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
106 { .out = &attr_module_failure_message, .name = "Module-Failure-Message", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
107
108 { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
109 { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius },
110 { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_radius },
111
113};
114
116static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request) CC_HINT(nonnull);
117
118/** Loads submodules based on type = foo pairs
119 *
120 * @param[in] ctx to allocate data in (instance of rlm_eap_t).
121 * @param[out] out Where to write child conf section to.
122 * @param[in] parent Base structure address.
123 * @param[in] ci #CONF_PAIR specifying the name of the type module.
124 * @param[in] rule unused.
125 * @return
126 * - 0 on success.
127 * - -1 on failure.
128 */
129static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent,
130 CONF_ITEM *ci, conf_parser_t const *rule)
131{ char const *name = cf_pair_value(cf_item_to_pair(ci));
132 char *our_name = NULL;
133 char *p;
134 eap_type_t method;
135
136 /*
137 * Search with underscores smashed to hyphens
138 * as that's what's used in the dictionary.
139 */
140 p = our_name = talloc_strdup(NULL, name);
141 while (*p) {
142 if (*p == '_') *p = '-';
143 p++;
144 }
145
146 method = eap_name2type(our_name);
147 talloc_free(our_name);
148
149 if (method == FR_EAP_METHOD_INVALID) {
150 cf_log_err(ci, "Unknown EAP type %s", name);
151 return -1;
152 }
153
154#if !defined(HAVE_OPENSSL_SSL_H) || !defined(HAVE_LIBSSL)
155 /*
156 * This allows the default configuration to be
157 * shipped with EAP-TLS, etc. enabled. If the
158 * system doesn't have OpenSSL, they will be
159 * ignored.
160 *
161 * If the system does have OpenSSL, then this
162 * code will not be used. The administrator will
163 * then have to delete the tls,
164 * etc. configurations from eap.conf in order to
165 * have EAP without the TLS types.
166 */
167 switch (method) {
176 {
178
181 module_instance_t, "rlm_eap"))));
182 WARN("Ignoring EAP method %s because we don't have OpenSSL support", name);
183 }
184 return 0;
185
186 default:
187 break;
188 }
189#endif
190 return module_rlm_submodule_parse(ctx, out, parent, ci, rule);
191}
192
193/** Convert EAP type strings to eap_type_t values
194 *
195 * @param[in] ctx unused.
196 * @param[out] out Where to write the #eap_type_t value we found.
197 * @param[in] parent Base structure address.
198 * @param[in] ci #CONF_PAIR specifying the name of the EAP method.
199 * @param[in] rule unused.
200 * @return
201 * - 0 on success.
202 * - -1 on failure.
203 */
204static int eap_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
205 CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
206{
207 char const *default_method_name = cf_pair_value(cf_item_to_pair(ci));
208 eap_type_t method;
209
210 /*
211 * Ensure that the default EAP type is loaded.
212 */
213 method = eap_name2type(default_method_name);
214 if (method == FR_EAP_METHOD_INVALID) {
215 cf_log_err(ci, "Unknown EAP type %s", default_method_name);
216 return -1;
217 }
218
219 *(eap_type_t *)out = method;
220
221 return 0;
222}
223
224/** Is this method allowed inside of a tunnel?
225 *
226 * A tunnelling method (TTLS, PEAP, FAST) cannot run inside another tunnelling method.
227 * A method which does not tunnel (MD5, MSCHAPv2, PSK, TLS) can run anywhere.
228 *
229 * @param[in] method to start. The submodule MUST be loaded.
230 * @param[in] request the method would run in.
231 * @return
232 * - true if the method can be used
233 * - false if the method would create a tunneled method inside of a tunneled method.
234 */
235static bool eap_type_nest_allowed(rlm_eap_method_t const *method, request_t const *request)
236{
237 return !method->submodule->tunnel || !eap_session_in_tunnel(request);
238}
239
240/** Find a method to offer when the default method is a nested tunnel.
241 *
242 * MS-CHAPv2 is preferred when the method is loaded. Otherwise the lowest numbered EAP type is used.
243 *
244 * @param[in] inst of rlm_eap.
245 * @param[in] request the method would run in.
246 * @return
247 * - #FR_EAP_METHOD_MSCHAPV2 if it's configured
248 * - The lowest numbered loaded EAP type is configured
249 * - #FR_EAP_METHOD_INVALID if there is no non-tunneled method available.
250 */
252{
253 unsigned int type;
254
255 /*
256 * Try EAP-MSCHAPv2 first. It's the default for essentially all tunneled methods.
257 */
258 if (inst->methods[FR_EAP_METHOD_MSCHAPV2].submodule &&
261 }
262
264 if (inst->methods[type].submodule && eap_type_nest_allowed(&inst->methods[type], request)) {
265 return (eap_type_t)type;
266 }
267 }
268
270}
271
272/** Process NAK data from EAP peer
273 *
274 */
276 eap_type_t last_type,
277 eap_type_data_t *nak)
278{
280 unsigned int i, s_i = 0;
281 fr_pair_t *vp = NULL;
283 eap_type_t sanitised[FR_EAP_METHOD_MAX];
284
285 /*
286 * The NAK data is the preferred EAP type(s) of
287 * the client.
288 *
289 * RFC 3748 says to list one or more proposed
290 * alternative types, one per octet, or to use
291 * 0 for no alternative.
292 */
293 if (!nak->data) {
294 REDEBUG("Peer sent empty (invalid) NAK. Can't select method to continue with");
295
297 }
298
299 /*
300 * Do a loop over the contents of the NAK, only moving entries
301 * which are valid to the sanitised array.
302 */
303 for (i = 0; (i < nak->length) && (i < FR_EAP_METHOD_MAX); i++) {
304
305 /*
306 * Type 0 is valid, and means there are no
307 * common choices.
308 */
309 if (nak->data[i] == 0) {
310 REDEBUG("Peer NAK'd indicating it is not willing to continue");
311
313 }
314
315 /*
316 * It is invalid to request identity,
317 * notification & nak in nak.
318 */
319 if (nak->data[i] < FR_EAP_METHOD_MD5) {
320 REDEBUG("Peer NAK'd asking for bad type %s (%d)", eap_type2name((eap_type_t)nak->data[i]), nak->data[i]);
321
323 }
324
325 if ((nak->data[i] >= FR_EAP_METHOD_MAX) ||
326 !inst->methods[nak->data[i]].submodule) {
327 RDEBUG2("Peer NAK'd asking for unsupported EAP type %s (%d), skipping...",
328 eap_type2name((eap_type_t)nak->data[i]),
329 nak->data[i]);
330
331 continue;
332 }
333
334 /*
335 * Forbid nesting a tunneled method inside of a tunneled method.
336 */
337 if (!eap_type_nest_allowed(&inst->methods[nak->data[i]], request)) {
338 RDEBUG2("Peer NAK'd asking for EAP type %s (%d), which cannot be nested inside "
339 "another tunnelling EAP type, skipping...",
340 eap_type2name((eap_type_t)nak->data[i]), nak->data[i]);
341 continue;
342 }
343
344 /*
345 * Prevent a firestorm if the client is confused.
346 *
347 * FIXME: Really we should keep a list of
348 * methods we've already sent back.
349 */
350 if (last_type == nak->data[i]) {
351 char const *type_str = eap_type2name((eap_type_t)nak->data[i]);
352
353 RDEBUG2("Peer NAK'd our request for %s (%d) with a request for %s (%d), skipping...",
354 type_str, nak->data[i], type_str, nak->data[i]);
355
356 RWARN("!!! We requested to use EAP type %s (%i)", type_str, nak->data[i]);
357 RWARN("!!! The supplicant rejected that, and requested to use the same EAP type.");
358 RWARN("!!! i.e. the supplicant said 'I don't like %s, please use %s instead.",
359 type_str, type_str);
360 RWARN("!!! The supplicant software is broken and does not work properly.");
361 RWARN("!!! Please upgrade it to software that works.");
362
363 continue;
364 }
365
366 sanitised[s_i++] = (eap_type_t)nak->data[i];
367 }
368
369 if (s_i == 0) {
370 REDEBUG("Peer presented no valid EAP types in its NAK response");
372 }
373
374 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_type);
375 if (vp) {
376 /*
377 * Loop over allowed methods and the contents
378 * of the NAK, attempting to find something
379 * we can continue with.
380 */
381 do {
382 /*
383 * Provide a way of the admin potentially
384 * disabling EAP negotiation.
385 */
386 if (vp->vp_uint32 == FR_EAP_METHOD_INVALID) continue;
387
388 for (i = 0; i < s_i; i++) {
389 /*
390 * Enforce per-user configuration of EAP
391 * types.
392 */
393 if (vp->vp_uint32 != sanitised[i]) continue;
394 RDEBUG2("Found mutually acceptable type %s (%u)", eap_type2name(sanitised[i]), sanitised[i]);
395 method = sanitised[i];
396 break;
397 }
398
399 if (method != FR_EAP_METHOD_INVALID) break; /* Found one1 */
400 } while ((vp = fr_pair_find_by_da(&request->control_pairs, vp, attr_eap_type)));
401 /*
402 * If there's no control pairs, respond with
403 * the first valid method in the NAK.
404 */
405 } else {
406 method = sanitised[0];
407 }
408
409 /*
410 * Couldn't find something to continue with,
411 * emit a very verbose message.
412 */
413 if (method == FR_EAP_METHOD_INVALID) {
414 fr_sbuff_t *proposed = NULL, *allowed = NULL;
415
416 FR_SBUFF_TALLOC_THREAD_LOCAL(&proposed, 256, 1024);
417 FR_SBUFF_TALLOC_THREAD_LOCAL(&allowed, 256, 1024);
418
419 for (i = 0; i < s_i; i++) {
420 (void) fr_sbuff_in_sprintf(proposed, "%s (%d), ", eap_type2name(sanitised[i]), sanitised[i]);
421 }
422 fr_sbuff_advance(proposed, -2);
423 fr_sbuff_terminate(proposed);
424
425 vp = NULL;
426 while ((vp = fr_pair_find_by_da(&request->control_pairs, vp, attr_eap_type))) {
427 (void) fr_sbuff_in_sprintf(allowed, "%pV (%u), ", &vp->data, vp->vp_uint32);
428 }
429 fr_sbuff_advance(allowed, -2); /* Negative advance past start should be disallowed */
430 fr_sbuff_terminate(allowed);
431
432 REDEBUG("No mutually acceptable EAP types found. Supplicant proposed: %s. We allow: %s",
433 fr_sbuff_start(proposed), fr_sbuff_start(allowed));
434 }
435
436 return method;
437}
438
439/** Cancel a call to a submodule
440 *
441 * @param[in] mctx module calling ctx.
442 * @param[in] request The current request.
443 * @param[in] action to perform.
444 */
446{
447 eap_session_t *eap_session;
448
449 RDEBUG2("Request cancelled - Destroying EAP-Session");
450
451 eap_session = talloc_get_type_abort(mctx->rctx, eap_session_t);
452
453 /*
454 * Normally the unlang_subrequest_signal() function forwards the CANCEL signal to the subrequest,
455 * when there's a subrequest frame in the parents stack. For EAP, this isn't the case. So we
456 * have to forward the signal manually.
457 *
458 * Note that we CANNOT detach the child. It was created as not detachable. If we did create it
459 * as detachable, then the admin could use the "detach" keyword, which is bad.
460 */
461 if (eap_session->subrequest) {
463 }
464
465 /*
466 * This is the only safe thing to do.
467 * We have no idea what state the submodule
468 * left its opaque data in.
469 */
470 eap_session_destroy(&eap_session);
471}
472
473/** Process the result of calling a submodule
474 *
475 * @param[out] p_result Result of calling the module, one of:
476 * - RLM_MODULE_INVALID if the request or EAP session state is invalid.
477 * - RLM_MODULE_OK if this round succeeded.
478 * - RLM_MODULE_HANDLED if we're done with this round.
479 * - RLM_MODULE_REJECT if the user should be rejected.
480 * @param[in] request The current request.
481 * @param[in] mctx module calling ctx.
482 * @param[in] eap_session the EAP session
483 * @param[in] submodule_result the input result from the submodule
484 */
486 request_t *request, eap_session_t *eap_session, unlang_result_t *submodule_result)
487{
488 rlm_rcode_t rcode;
489 fr_pair_t *next, *vp;
490
491 /*
492 * Hoist any instances of Module-Failure-Message from the subrequest
493 * so they can be used for logging failures.
494 */
495 vp = fr_pair_find_by_da(&eap_session->subrequest->request_pairs, NULL, attr_module_failure_message);
496 while (vp) {
497 next = fr_pair_find_by_da(&eap_session->subrequest->request_pairs, vp, attr_module_failure_message);
498 fr_pair_remove(&eap_session->subrequest->request_pairs, vp);
499 fr_pair_steal_append(request->request_ctx, &request->request_pairs, vp);
500 vp = next;
501 }
502
503 /*
504 * Cleanup the subrequest
505 */
506 TALLOC_FREE(eap_session->subrequest);
507
508 /*
509 * The submodule failed. Die.
510 */
511 switch (submodule_result->rcode) {
512 case RLM_MODULE_FAIL:
514 eap_fail(eap_session);
515 eap_session_destroy(&eap_session);
516
517 rcode = RLM_MODULE_INVALID;
518 goto finish;
519
520 /*
521 * Inconsistent result from submodule...
522 */
526 break;
527
528 default:
529 break;
530 }
531
532 /*
533 * We are done, wrap the EAP-request in RADIUS to send
534 * with all other required radius attributes
535 */
536 rcode = eap_compose(eap_session);
537
538 /*
539 * Add to the list only if it is EAP-Request, OR if
540 * it's LEAP, and a response.
541 */
542 if (((eap_session->this_round->request->code == FR_EAP_CODE_REQUEST) &&
543 (eap_session->this_round->request->type.num >= FR_EAP_METHOD_MD5)) ||
544
545 /*
546 * LEAP is a little different. At Stage 4,
547 * it sends an EAP-Success message, but we still
548 * need to keep the State attribute & session
549 * data structure around for the AP Challenge.
550 *
551 * At stage 6, LEAP sends an EAP-Response, which
552 * isn't put into the list.
553 */
554 ((eap_session->this_round->response->code == FR_EAP_CODE_RESPONSE) &&
555 (eap_session->this_round->response->type.num == FR_EAP_METHOD_LEAP) &&
556 (eap_session->this_round->request->code == FR_EAP_CODE_SUCCESS) &&
557 (eap_session->this_round->request->type.num == 0))) {
558 talloc_free(eap_session->prev_round);
559 eap_session->prev_round = eap_session->this_round;
560 eap_session->this_round = NULL;
561 } else {
562 RDEBUG2("Cleaning up EAP session");
563 eap_session_destroy(&eap_session);
564 }
565
566 /*
567 * Freeze the eap_session so we can continue
568 * the authentication session later.
569 */
570 eap_session_freeze(&eap_session);
571
572finish:
573 RETURN_UNLANG_RCODE(rcode);
574}
575
576/** Call mod_authenticate_result asynchronously from the unlang interpreter
577 *
578 * @param[out] p_result The result of the operation.
579 * @param[in] mctx module calling ctx.
580 * @param[in] request the current request.
581 * @return The result of this round of authentication.
582 */
585{
586 eap_session_t *eap_session = talloc_get_type_abort(mctx->rctx, eap_session_t);
587
588 return mod_authenticate_result(p_result, mctx, request, eap_session, &eap_session->submodule_result);
589}
590
591/** Basic tests to determine if an identity is a valid NAI
592 *
593 * In this version we mostly just care about realm.
594 *
595 * @param[in] identity to check.
596 * @return
597 * - The length of the string on success.
598 * - <= 0 a negative offset specifying where the format error occurred.
599 */
600static ssize_t eap_identity_is_nai_with_realm(char const *identity)
601{
602 char const *p = identity;
603 char const *end = identity + (talloc_strlen(identity));
604 char const *realm;
605
606 /*
607 * Get the last '@'
608 */
609 p = realm = memrchr(identity, '@', end - p);
610 if (!p) {
611 fr_strerror_printf("Identity is not valid. Missing realm separator '@'");
612 return identity - end;
613 }
614
615 p = memchr(p, '.', end - p);
616 if (!p) {
617 fr_strerror_printf("Identity is not valid. Realm is missing label separator '.'");
618 return identity - end;
619 }
620
621 if ((realm + 1) == p) {
622 fr_strerror_printf("Identity is not valid. "
623 "Realm is missing label between realm separator '@' and label separator '.'");
624 return identity - realm;
625 }
626 if ((p + 1) == end) {
627 fr_strerror_printf("Identity is not valid. "
628 "Realm is missing label between label separator '.' and the end of the "
629 "identity string");
630 return identity - end;
631 }
632
633 return end - identity;
634}
635
636/** Select the correct callback based on a response
637 *
638 * Based on the EAP response from the supplicant, and setup a call on the
639 * unlang stack to the appropriate submodule.
640 *
641 * Default to the configured EAP-Type for all Unsupported EAP-Types.
642 *
643 * @param[out] p_result the result of the operation.
644 * @param[in] mctx module calling ctx.
645 * @param[in] eap_session State data that persists over multiple rounds of EAP.
646 * @return
647 * - UNLANG_ACTION_CALCULATE_RESULT + p_result->rcode = RLM_MODULE_INVALID.
648 * Invalid request.
649 * - UNLANG_ACTION_PUSHED_CHILD Yield control back to the interpreter so it can
650 * call the submodule.
651 */
653{
655 eap_type_data_t *type = &eap_session->this_round->response->type;
656 request_t *request = eap_session->request;
657
658 rlm_eap_method_t const *method;
659
660 eap_type_t next = inst->default_method;
661 fr_pair_t *vp;
662
663 /*
664 * Session must have been thawed...
665 */
666 fr_assert(eap_session->request);
667
668 /*
669 * Don't trust anyone.
670 */
671 if ((type->num == 0) || (type->num >= FR_EAP_METHOD_MAX)) {
672 REDEBUG("Peer sent EAP type number %u, which is outside known range", type->num);
673
674 is_invalid:
676 }
677
678 RDEBUG2("Peer sent packet with EAP method %s (%u)", eap_type2name(type->num), type->num);
679
680 /*
681 * Figure out what to do.
682 */
683 switch (type->num) {
685 {
686 ssize_t slen;
687
688 /*
689 * Check if we allow this identity format
690 */
691 switch (inst->require_realm) {
693 slen = eap_identity_is_nai_with_realm(eap_session->identity);
694 if (slen <= 0) {
695 char *tmp_id;
696 bad_id:
697 /*
698 * Produce an escaped version and run that
699 * through the format check function to get
700 * the correct offset *sigh*...
701 */
702 MEM(tmp_id = fr_asprint(NULL,
703 eap_session->identity,
704 talloc_strlen(eap_session->identity),
705 '"'));
706 slen = eap_identity_is_nai_with_realm(tmp_id);
707
708 REMARKER(tmp_id, slen, "%s", fr_strerror());
709
710 talloc_free(tmp_id);
711 goto is_invalid;
712 }
713 break;
714
716 slen = eap_identity_is_nai_with_realm(eap_session->identity);
717 if (slen <= 0) {
718 fr_pair_t *stripped_user_domain;
719
720 /*
721 * If it's not an NAI with a realm, check
722 * to see if the user has set Stripped-User-domain.
723 */
724 stripped_user_domain = fr_pair_find_by_da_idx(&eap_session->request->request_pairs,
726 if (!stripped_user_domain) goto bad_id;
727 }
728 break;
729
730 case REQUIRE_REALM_NO:
731 break;
732 }
733 }
734 /*
735 * Allow per-user configuration of EAP types.
736 */
737 vp = fr_pair_find_by_da(&eap_session->request->control_pairs, NULL, attr_eap_type);
738 if (vp) {
739 RDEBUG2("Using method from control.EAP-Type");
740 if (vp->vp_uint32 >= FR_EAP_METHOD_MAX) {
741 REDEBUG2("control.EAP-Type %u is not a valid EAP type", vp->vp_uint32);
742 goto is_invalid;
743 }
744 next = vp->vp_uint32;
745 /*
746 * We have an array of the submodules which
747 * have a type_identity callback. Call
748 * each of these in turn to see if any of
749 * them recognise the identity.
750 */
751 } else if (inst->type_identity_submodule) {
752 size_t i;
753
754 for (i = 0; i < inst->type_identity_submodule_len; i++) {
755 rlm_eap_submodule_t const *submodule =
756 (rlm_eap_submodule_t const *)inst->type_identity_submodule[i]->exported;
757 eap_type_t ret;
758
759 ret = submodule->type_identity(inst->type_identity_submodule[i]->data,
760 eap_session->identity,
761 talloc_strlen(eap_session->identity));
762 if (ret != FR_EAP_METHOD_INVALID) {
763 next = ret;
764 break;
765 }
766 }
767 }
768 do_init:
769 /*
770 * Ensure it's valid.
771 */
772 if ((next < FR_EAP_METHOD_MD5) || (next >= FR_EAP_METHOD_MAX) || (!inst->methods[next].submodule)) {
773 REDEBUG2("Peer tried to start unsupported EAP type %s (%u)",
774 eap_type2name(next), next);
775 goto is_invalid;
776 }
777
778 /*
779 * The server chooses the method here, and a single eap instance often serves both
780 * the outer session and the tunnel. The default method of the instance is then a
781 * tunnelling method, which cannot start inside a tunnel. The peer sends a NAK to
782 * ask for the method that the peer wants, so offer a method which can nest.
783 */
784 if (!eap_type_nest_allowed(&inst->methods[next], request)) {
786
787 if (fallback == FR_EAP_METHOD_INVALID) {
788 REDEBUG("EAP type %s (%u) cannot be nested inside another tunnelling EAP type, "
789 "and no other EAP type is loaded", eap_type2name(next), next);
790 goto is_invalid;
791 }
792
793 RDEBUG2("EAP type %s (%u) cannot be nested inside another tunnelling EAP type, "
794 "offering %s (%u) instead", eap_type2name(next), next,
795 eap_type2name(fallback), fallback);
796 next = fallback;
797 }
798
799 eap_session->process = inst->methods[next].submodule->session_init;
800 eap_session->type = next;
801 break;
802
804 /*
805 * Delete old data, if necessary. If we called a method
806 * before, and it initialized itself, we need to free
807 * the memory it alloced.
808 */
809 TALLOC_FREE(eap_session->opaque);
810 fr_state_discard_child(eap_session->request, eap_session, 0);
811 next = eap_process_nak(mctx, eap_session->request, eap_session->type, type);
812 if (!next) RETURN_UNLANG_REJECT;
813
814 /*
815 * Initialise the state machine for the next submodule
816 */
817 goto do_init;
818
819 /*
820 * Only allow modules that are enabled to be called,
821 * treating any other requests as invalid.
822 *
823 * This may seem a bit harsh, but remember the server
824 * dictates which type of EAP method should be started,
825 * so this is the supplicant ignoring the normal EAP method
826 * negotiation mechanism, by not NAKing and just trying
827 * to start a new EAP method.
828 */
829 default:
830 if (!inst->methods[type->num].submodule) {
831 REDEBUG2("Peer asked for unsupported EAP type %s (%u)", eap_type2name(type->num), type->num);
832 goto is_invalid;
833 }
834 /*
835 * Perr started the EAP method without
836 * sending an Identity-Response.
837 *
838 * There's nothing that says it *HAS* to send an
839 * identity response before starting a method,
840 * so just jump to the initialisation function
841 * of the method and continue.
842 */
843 if (eap_session->rounds == 0) {
844 RDEBUG2("Peer started EAP type %s (%u) without sending an Identity", eap_type2name(type->num), type->num);
845 vp = fr_pair_find_by_da(&eap_session->request->control_pairs, NULL, attr_eap_type);
846 if (vp) {
847 RDEBUG2("Using method from control.EAP-Type");
848 if (vp->vp_uint32 >= FR_EAP_METHOD_MAX) {
849 REDEBUG2("control.EAP-Type %u is not a valid EAP type", vp->vp_uint32);
850 goto is_invalid;
851 }
852 next = vp->vp_uint32;
853 }
854 goto do_init;
855 }
856
857 /*
858 * FIXME - We should only update the type
859 * on completion of the final round.
860 */
861 eap_session->type = type->num;
862 break;
863 }
864
865 method = &inst->methods[eap_session->type];
866
867 /*
868 * We cannot nest tunneled methods.
869 */
870 if (!eap_type_nest_allowed(method, request)) {
871 REDEBUG("EAP type %s (%u) cannot be nested inside another tunnelling EAP type",
872 eap_type2name(eap_session->type), eap_session->type);
873 goto is_invalid;
874 }
875
876 /*
877 * Save the tunneled status in the EAP session, which can be found from the request data.
878 */
879 eap_session->tunnel = method->submodule->tunnel;
880
881 RDEBUG2("Calling submodule %s", method->submodule->common.name);
882
883 /*
884 * Allocate a new subrequest, which is NOT detachable.
885 */
887 method->submodule->namespace ?
888 *(method->submodule->namespace) :
889 request->proto_dict));
890
891 if (method->submodule->clone_parent_lists) {
892 if (fr_pair_list_copy(eap_session->subrequest->control_ctx,
893 &eap_session->subrequest->control_pairs, &request->control_pairs) < 0) {
894 list_copy_fail:
895 RERROR("Failed copying parent's attribute list");
896 fail:
897 TALLOC_FREE(eap_session->subrequest);
899 }
900
901 if (fr_pair_list_copy(eap_session->subrequest->request_ctx,
902 &eap_session->subrequest->request_pairs,
903 &request->request_pairs) < 0) goto list_copy_fail;
904 }
905
906 /*
907 * Push a resumption frame for the parent
908 * This will get executed when the child is
909 * done (after the subrequest frame in the
910 * parent gets popped).
911 */
913
914 /*
915 * This sets up a subrequest frame in the parent
916 * and a resumption frame in the child.
917 *
918 * This must be done before pushing frames onto
919 * the child's stack.
920 */
921 if (unlang_subrequest_child_push(&eap_session->submodule_result, eap_session->subrequest,
922 eap_session,
923 false, UNLANG_SUB_FRAME) < 0) {
924 child_fail:
925 unlang_interpet_frame_discard(request); /* Ensure the yield frame doesn't stick around */
926 goto fail;
927 }
928
929 /*
930 * Push the EAP submodule into the child's stack
931 */
932 if (unlang_module_push(NULL, /* rcode should bubble up and be set in eap_session->submodule_rcode */
933 eap_session->subrequest, method->submodule_inst, eap_session->process,
934 UNLANG_SUB_FRAME) < 0) {
935 goto child_fail;
936 }
937
938 if (eap_session->identity) {
939 fr_pair_t *identity;
940
941 request = eap_session->subrequest; /* Set request for pair_append_request macro */
942
943 MEM(pair_append_request(&identity, attr_eap_identity) >= 0);
944 fr_pair_value_bstrdup_buffer(identity, eap_session->identity, true);
945 }
946
947 /*
948 * Add the EAP-Type we're running to the subrequest
949 * This is useful for when policies are shared between
950 * virtual server sections for multiple EAP types.
951 */
952 {
953 fr_pair_t *type_vp;
954
955 MEM(pair_append_request(&type_vp, attr_eap_type) >= 0);
956 type_vp->vp_uint32 = eap_session->type;
957 }
958
960}
961
963{
964 fr_pair_t *vp;
965 uint8_t buffer[4];
966
968
970 if (!vp) {
971 static uint8_t const auth_vector[RADIUS_AUTH_VECTOR_LENGTH] = { 0x00 };
972
974 fr_pair_value_memdup(vp, auth_vector, sizeof(auth_vector), false);
975 }
977
979 buffer[1] = (vp->vp_length >= 2) ? vp->vp_octets[1] : 0;
980 buffer[2] = 0;
981 buffer[3] = 4;
982
984 fr_pair_value_memdup(vp, buffer, sizeof(buffer), false);
985
987}
988
990{
992 eap_session_t *eap_session;
993 eap_packet_raw_t *eap_packet;
995 fr_pair_t *vp;
996
997 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_message);
998 if (!vp) {
999 REDEBUG("You set 'Auth-Type = EAP' for a request that does not contain an EAP-Message attribute!");
1001 }
1002
1003 /*
1004 * Reconstruct the EAP packet from the EAP-Message
1005 * attribute. The relevant decoder should have already
1006 * concatenated the fragments into a single buffer.
1007 *
1008 * If it's not a valid EAP packet, we send back a canned
1009 * failure, and discard the entire EAP session.
1010 */
1011 eap_packet = eap_packet_from_vp(request, &request->request_pairs);
1012 if (!eap_packet) {
1013 RPERROR("Malformed EAP Message");
1014 fail:
1017 }
1018
1019 /*
1020 * Allocate a new eap_session, or if this request
1021 * is part of an ongoing authentication session,
1022 * retrieve the existing eap_session from the request
1023 * data.
1024 */
1025 eap_session = eap_session_continue(inst, &eap_packet, request);
1026 if (!eap_session) goto fail;
1027
1028 /*
1029 * Call an EAP submodule to process the request,
1030 * or with simple types like Identity and NAK,
1031 * process it ourselves.
1032 */
1033 if ((ua = eap_method_select(p_result, mctx, eap_session)) != UNLANG_ACTION_CALCULATE_RESULT) return ua;
1034 switch (p_result->rcode) {
1035 case RLM_MODULE_OK:
1036 case RLM_MODULE_UPDATED:
1037 eap_session_freeze(&eap_session);
1038 break;
1039
1040 /*
1041 * RFC 3748 Section 2
1042 * The conversation continues until the authenticator cannot
1043 * authenticate the peer (unacceptable Responses to one or more
1044 * Requests), in which case the authenticator implementation MUST
1045 * transmit an EAP Failure (Code 4).
1046 */
1047 default:
1048 eap_fail(eap_session);
1049 eap_session_destroy(&eap_session);
1050 break;
1051 }
1052
1053 return ua;
1054}
1055
1056/*
1057 * EAP authorization DEPENDS on other rlm authorizations,
1058 * to check for user existence & get their configured values.
1059 * It Handles EAP-START Messages, User-Name initialization.
1060 */
1062{
1064 rlm_rcode_t status;
1065
1066 if (!inst->auth_type) {
1067 WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup EAP authentication",
1068 mctx->mi->name, mctx->mi->name);
1070 }
1071
1072 /*
1073 * For EAP_START, send Access-Challenge with EAP Identity
1074 * request. even when we have to proxy this request
1075 *
1076 * RFC 2869, Section 2.3.1 notes that the "domain" of the
1077 * user, (i.e. where to proxy it) comes from the EAP-Identity,
1078 * so we CANNOT proxy the user, until we know its identity.
1079 *
1080 * We therefore send an EAP Identity request.
1081 */
1082 status = eap_start(request, inst->methods, inst->ignore_unknown_types);
1083 switch (status) {
1084 case RLM_MODULE_NOOP:
1085 case RLM_MODULE_HANDLED:
1086 RETURN_UNLANG_RCODE(status);
1087
1088 case RLM_MODULE_FAIL:
1089 case RLM_MODULE_INVALID:
1091 RETURN_UNLANG_RCODE(status);
1092
1093 default:
1094 break;
1095 }
1096
1098
1099 if (status == RLM_MODULE_OK) RETURN_UNLANG_OK;
1100
1102}
1103
1105{
1106 fr_pair_t *vp;
1107 eap_session_t *eap_session;
1108 fr_pair_t *username;
1109
1110 /*
1111 * If it's an Access-Accept, RFC 2869, Section 2.3.1
1112 * says that we MUST include a User-Name attribute in the
1113 * Access-Accept.
1114 */
1115 username = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
1116 if ((request->reply->code == FR_RADIUS_CODE_ACCESS_ACCEPT) && username) {
1117 /*
1118 * Doesn't exist, add it in.
1119 */
1120 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_user_name);
1121 if (!vp) {
1122 MEM(vp = fr_pair_copy(request->reply_ctx, username));
1123 fr_pair_append(&request->reply_pairs, vp);
1124 }
1125 }
1126
1127 /*
1128 * Only synthesize a failure message if something
1129 * previously rejected the request.
1130 */
1132
1133 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_message)) {
1134 RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
1136 }
1137
1138 if (fr_pair_find_by_da(&request->reply_pairs, NULL, attr_eap_message)) {
1139 RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
1141 }
1142
1143 /*
1144 * Retrieve pre-existing eap_session from request
1145 * data. This will have been added to the request
1146 * data by the state API.
1147 */
1148 eap_session = eap_session_thaw(request);
1149 if (!eap_session) {
1150 RDEBUG3("Failed to get eap_session, probably already removed, not inserting EAP-Failure");
1152 }
1153
1154 /*
1155 * If this reject is before eap has been called in authenticate
1156 * the eap_round will not have been populated.
1157 */
1158 if (!eap_session->this_round) {
1159 eap_packet_raw_t *eap_packet = eap_packet_from_vp(request, &request->request_pairs);
1160 eap_session->this_round = eap_round_build(eap_session, &eap_packet);
1161 }
1162
1163 /*
1164 * This should never happen, but we may be here
1165 * because there was an unexpected error in the
1166 * EAP module.
1167 */
1168 if (!fr_cond_assert(eap_session->this_round) || !fr_cond_assert(eap_session->this_round->request)) {
1169 eap_session_destroy(&eap_session); /* Free the EAP session, and dissociate it from the request */
1171 }
1172
1173 /*
1174 * Already set to failure, assume something else
1175 * added EAP-Message with a failure code, do nothing.
1176 */
1178
1179 /*
1180 * Was *NOT* an EAP-Failure, so we now need to turn it into one.
1181 */
1182 REDEBUG("Request rejected after last call to module \"%s\", transforming response into EAP-Failure",
1183 mctx->mi->name);
1184 eap_fail(eap_session); /* Compose an EAP failure */
1185 eap_session_destroy(&eap_session); /* Free the EAP session, and dissociate it from the request */
1186
1187 /*
1188 * Make sure there's a message authenticator attribute in the response
1189 * RADIUS protocol code will calculate the correct value later...
1190 */
1193
1195}
1196
1197static int mod_instantiate(module_inst_ctx_t const *mctx)
1198{
1199 rlm_eap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_eap_t);
1200 size_t i;
1201 size_t j, loaded, count = 0;
1202
1203 loaded = talloc_array_length(inst->type_submodules);
1204
1205 /*
1206 * Pre-allocate the method identity to be the number
1207 * of modules we're going to load.
1208 *
1209 * We'll shrink it later.
1210 */
1211 if (!inst->default_method_is_set) {
1212 MEM(inst->type_identity_submodule = talloc_array(inst, module_instance_t const *, loaded));
1213 }
1214
1215 for (i = 0; i < loaded; i++) {
1216 module_instance_t *submodule_inst = inst->type_submodules[i];
1217 rlm_eap_submodule_t const *submodule;
1218
1219 if (!submodule_inst) continue; /* Skipped as we don't have SSL support */
1220
1221 submodule = (rlm_eap_submodule_t const *)submodule_inst->module->exported;
1222
1223 /*
1224 * Add the methods the submodule provides
1225 */
1226 for (j = 0; j < MAX_PROVIDED_METHODS; j++) {
1227 eap_type_t method;
1228
1229 if (!submodule->provides[j]) break;
1230
1231 method = submodule->provides[j];
1232
1233 /*
1234 * If the user didn't specify a default method
1235 * take the first method provided by the first
1236 * submodule as the default.
1237 */
1238 if (!inst->default_method_is_set && (i == 0)) inst->default_method = method;
1239
1240 /*
1241 * Check for duplicates
1242 */
1243 if (inst->methods[method].submodule) {
1244 CONF_SECTION *conf = inst->methods[method].submodule_inst->conf;
1245
1246 cf_log_err(submodule_inst->conf,
1247 "Duplicate EAP-Type %s. Conflicting entry %s[%d]",
1248 eap_type2name(method),
1250
1251 return -1;
1252 }
1253
1254 inst->methods[method].submodule_inst = submodule_inst;
1255 inst->methods[method].submodule = submodule;
1256 }
1257
1258 /*
1259 * This module provides a method identity
1260 * callback. We need to call each of these
1261 * in turn if default_eap_type isn't set,
1262 * to figure out the default eap type.
1263 */
1264 if (!inst->default_method_is_set && submodule->type_identity) {
1265 inst->type_identity_submodule[inst->type_identity_submodule_len++] = submodule_inst;
1266 }
1267 count++;
1268 }
1269
1270 /*
1271 * Check if the default method specified is actually
1272 * allowed by the config.
1273 */
1274 if (inst->default_method_is_set && !inst->methods[inst->default_method].submodule) {
1275 cf_log_err_by_child(mctx->mi->conf, "default_eap_type", "EAP-Type \"%s\" is not enabled",
1276 eap_type2name(inst->default_method));
1277 return -1;
1278 }
1279
1280 if (count == 0) {
1281 cf_log_err(mctx->mi->conf, "No EAP method(s) configured, module cannot do anything");
1282 return -1;
1283 }
1284
1285 /*
1286 * Shrink the method identity array so it's the
1287 * correct length.
1288 */
1289 if (!inst->default_method_is_set) {
1290 if (inst->type_identity_submodule_len > 0) {
1291 MEM(inst->type_identity_submodule = talloc_realloc(inst, inst->type_identity_submodule,
1292 module_instance_t const *,
1293 inst->type_identity_submodule_len));
1294 } else {
1295 TALLOC_FREE(inst->type_identity_submodule);
1296 }
1297 }
1298
1299 inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->mi->name, -1);
1300 if (!inst->auth_type) {
1301 WARN("Failed to find 'authenticate %s {...}' section. EAP authentication will likely not work",
1302 mctx->mi->name);
1303 }
1304
1305 /*
1306 * Create our own random pool.
1307 */
1308 for (i = 0; i < 256; i++) inst->rand_pool.randrsl[i] = fr_rand();
1309 fr_isaac_init(&inst->rand_pool);
1310 inst->rand_pool.randcnt = 0;
1311
1312 return 0;
1313}
1314
1315static int mod_load(void)
1316{
1317 if (eap_base_init() < 0) {
1318 fr_perror("Failed initialising EAP base library");
1319 return -1;
1320 }
1321 return 0;
1322}
1323
1324static void mod_unload(void)
1325{
1326 eap_base_free();
1327}
1328
1329/*
1330 * The module name should be the only globally exported symbol.
1331 * That is, everything else should be 'static'.
1332 */
1334 .common = {
1335 .magic = MODULE_MAGIC_INIT,
1336 .name = "eap",
1337 .inst_size = sizeof(rlm_eap_t),
1339 .onload = mod_load,
1340 .unload = mod_unload,
1341 .instantiate = mod_instantiate,
1342 },
1343 .method_group = {
1344 .bindings = (module_method_binding_t[]){
1345 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate },
1346 { .section = SECTION_NAME("recv", "Access-Request"), .method = mod_authorize },
1347 { .section = SECTION_NAME("send", CF_IDENT_ANY), .method = mod_post_auth },
1349 }
1350 }
1351};
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:40
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:38
static int const char char buffer[256]
Definition acutest.h:641
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:245
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:722
cf_parse_t func
Override default parsing behaviour for the specified type with a custom parsing function.
Definition cf_parse.h:666
#define FR_CONF_V3_DEPRECATED(_name, _struct, _field)
conf_parser_t entry which raises an error if a deprecated v3 item is found
Definition cf_parse.h:446
#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:280
@ CONF_FLAG_MULTI
CONF_PAIR can have multiple copies.
Definition cf_parse.h:483
@ CONF_FLAG_NOT_EMPTY
CONF_PAIR is required to have a non zero length value.
Definition cf_parse.h:484
int cf_table_parse_uint8(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule)
#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:294
#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:238
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:649
Common header for all CONF_* types.
Definition cf_priv.h:54
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
void * cf_data_value(CONF_DATA const *cd)
Return the user assigned value of CONF_DATA.
Definition cf_util.c:1922
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:671
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:651
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1758
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define cf_lineno(_cf)
Definition cf_util.h:118
#define cf_log_err_by_child(_parent, _child, _fmt,...)
Log an error message against a specified child.
Definition cf_util.h:367
#define cf_data_find(_cf, _type, _name)
Definition cf_util.h:295
#define cf_parent(_cf)
Definition cf_util.h:115
#define cf_filename(_cf)
Definition cf_util.h:121
#define CF_IDENT_ANY
Definition cf_util.h:80
eap_round_t * eap_round_build(eap_session_t *eap_session, eap_packet_raw_t **eap_packet_p)
Definition compose.c:566
rlm_rcode_t eap_fail(eap_session_t *eap_session)
Definition compose.c:513
rlm_rcode_t eap_start(request_t *request, rlm_eap_method_t const methods[], bool ignore_unknown_types)
Definition compose.c:302
rlm_rcode_t eap_compose(eap_session_t *eap_session)
Definition compose.c:157
eap_type_data_t type
Definition compose.h:38
eap_packet_t * response
Packet we received from the peer.
Definition compose.h:48
eap_code_t code
Definition compose.h:35
eap_packet_t * request
Packet we will send to the peer.
Definition compose.h:49
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define MEM(x)
Definition debug.h:38
@ 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:316
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:329
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:335
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3778
Specifies an attribute which must be present for the module to function.
Definition dict.h:315
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:328
#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_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_MSCHAPV2
Definition types.h:71
@ 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_PSK
Definition types.h:93
@ FR_EAP_METHOD_MAX
Definition types.h:102
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_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
Structure to represent packet format of eap on wire
Definition types.h:121
EAP-Type specific data.
Definition types.h:109
talloc_free(hp)
void unlang_interpet_frame_discard(request_t *request)
Discard the bottom most frame on the request's stack.
Definition interpret.c:2746
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1789
#define UNLANG_SUB_FRAME
Definition interpret.h:37
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
void fr_isaac_init(fr_randctx *ctx)
Definition isaac.c:85
int eap_base_init(void)
Initialise the lib eap base library.
Definition base.c:419
void eap_base_free(void)
De-init the lib eap base library.
Definition base.c:442
eap_packet_raw_t * eap_packet_from_vp(TALLOC_CTX *ctx, fr_pair_list_t *vps)
Definition base.c:277
eap_session_t * eap_session_thaw(request_t *request)
Thaw an eap_session_t so it can be continued.
Definition session.c:259
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:361
void eap_session_discard(request_t *request)
discard and 'destroy' an EAP session and disassociate it from the current request
Definition session.c:164
bool eap_session_in_tunnel(request_t const *request)
Check whether a request runs inside the tunnel of another EAP method.
Definition session.c:223
void eap_session_freeze(eap_session_t **eap_session)
Freeze an eap_session_t so that it can continue later.
Definition session.c:204
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:56
bool tunnel
copied from #fr_rlm_eap_submodule_t
Definition session.h:72
void * opaque
Opaque data used by EAP methods.
Definition session.h:63
request_t * subrequest
Current subrequest being executed.
Definition session.h:46
eap_type_t type
EAP method number.
Definition session.h:50
module_method_t process
Callback that should be used to process the next round.
Definition session.h:65
unlang_result_t submodule_result
Result of last submodule call.
Definition session.h:47
request_t * request
Current request.
Definition session.h:52
eap_round_t * this_round
The EAP response we're processing, and the EAP request we're building.
Definition session.h:60
eap_round_t * prev_round
Previous response/request pair.
Definition session.h:58
int rounds
How many roundtrips have occurred this session.
Definition session.h:67
Tracks the progress of a single session of any EAP method.
Definition session.h:41
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define RERROR(fmt,...)
Definition log.h:315
#define RPERROR(fmt,...)
Definition log.h:319
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:515
#define REDEBUG2(fmt,...)
Definition log.h:389
@ 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
unsigned char uint8_t
void * memrchr(void const *s, int c, size_t n)
GNU libc extension on some platforms.
Definition missing.c:82
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:954
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:405
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:2303
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2897
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:707
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:1297
int fr_pair_delete_by_da(fr_pair_list_t *list, fr_dict_attr_t const *da)
Delete matching pairs from the specified list.
Definition pair.c:1641
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:754
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:562
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:500
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:2846
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:438
static const conf_parser_t config[]
Definition base.c:162
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define WARN(fmt,...)
static rs_t * conf
Definition radsniff.c:52
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:128
#define RETURN_UNLANG_UPDATED
Definition rcode.h:76
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define RETURN_UNLANG_HANDLED
Definition rcode.h:71
#define RETURN_UNLANG_INVALID
Definition rcode.h:72
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:67
#define RETURN_UNLANG_FAIL
Definition rcode.h:69
#define RETURN_UNLANG_REJECT
Definition rcode.h:68
#define RETURN_UNLANG_OK
Definition rcode.h:70
#define RETURN_UNLANG_NOOP
Definition rcode.h:75
fr_dict_autoload_t rlm_eap_dict[]
Definition rlm_eap.c:83
static int mod_load(void)
Definition rlm_eap.c:1315
static fr_table_num_sorted_t const require_identity_realm_table[]
Definition rlm_eap.c:53
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:600
static fr_dict_attr_t const * attr_module_failure_message
Definition rlm_eap.c:93
static bool eap_type_nest_allowed(rlm_eap_method_t const *method, request_t const *request)
Is this method allowed inside of a tunnel?
Definition rlm_eap.c:235
char const * caller
Original caller.
Definition rlm_eap.c:42
static fr_dict_attr_t const * attr_eap_identity
Definition rlm_eap.c:91
static unlang_action_t mod_post_auth(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:1104
fr_dict_attr_autoload_t rlm_eap_dict_attr[]
Definition rlm_eap.c:101
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:95
static unlang_action_t mod_authenticate_result_async(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Call mod_authenticate_result asynchronously from the unlang interpreter.
Definition rlm_eap.c:583
static fr_dict_attr_t const * attr_eap_type
Definition rlm_eap.c:90
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:275
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:445
static fr_dict_t const * dict_freeradius
Definition rlm_eap.c:79
eap_session_t * eap_session
The eap_session we're continuing.
Definition rlm_eap.c:44
rlm_eap_t * inst
Instance of the rlm_eap module.
Definition rlm_eap.c:43
module_rlm_t rlm_eap
Definition rlm_eap.c:1333
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:204
static fr_dict_t const * dict_radius
Definition rlm_eap.c:80
static fr_dict_attr_t const * attr_auth_type
Definition rlm_eap.c:89
rlm_rcode_t rcode
The result of the submodule.
Definition rlm_eap.c:45
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:1061
static void mod_unload(void)
Definition rlm_eap.c:1324
static fr_dict_attr_t const * attr_stripped_user_domain
Definition rlm_eap.c:92
static unlang_action_t eap_method_select(unlang_result_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:652
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_eap.c:989
static fr_dict_attr_t const * attr_user_name
Definition rlm_eap.c:97
static size_t require_identity_realm_table_len
Definition rlm_eap.c:58
static eap_type_t eap_type_nest_fallback(rlm_eap_t const *inst, request_t const *request)
Find a method to offer when the default method is a nested tunnel.
Definition rlm_eap.c:251
static const conf_parser_t module_config[]
Definition rlm_eap.c:60
static unlang_action_t mod_authenticate_result(unlang_result_t *p_result, UNUSED module_ctx_t const *mctx, request_t *request, eap_session_t *eap_session, unlang_result_t *submodule_result)
Process the result of calling a submodule.
Definition rlm_eap.c:485
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_eap.c:1197
static fr_dict_attr_t const * attr_message_authenticator
Definition rlm_eap.c:96
Resume context for calling a submodule.
Definition rlm_eap.c:41
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
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition sbuff.c:1680
#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:39
char const * name
Instance name e.g. user_database.
Definition module.h:361
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:355
size_t inst_size
Size of the module's instance data.
Definition module.h:213
void * data
Module's instance data.
Definition module.h:297
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:153
module_t * exported
Public module structure.
Definition module.h:302
Module instance data.
Definition module.h:291
Named methods exported by a module.
Definition module.h:175
#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
#define pair_append_reply(_attr, _da)
Allocate and append a fr_pair_t to reply list.
Definition pair.h:47
void fr_state_discard_child(request_t *parent, void const *unique_ptr, int unique_int)
Remove state from a child.
Definition state.c:1037
#define FR_SIGNAL_ALLOW(_signals)
Signal mask which blocks every signal except the ones in _signals.
Definition signal.h:58
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
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:431
int unlang_module_push(unlang_result_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:50
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
eap_failure
FAILURE state - State machine exit point after sending EAP-Failure.
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:71
#define MAX_PROVIDED_METHODS
Definition submodule.h:28
rlm_eap_submodule_t const * submodule
Submodule's exported interface.
Definition submodule.h:72
bool tunnel
A tunneled method (PEAP, TTLS, FAST, TEAP) Tunnelled methods cannot be nested.
Definition submodule.h:63
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:70
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:316
int unlang_subrequest_child_push(unlang_result_t *p_result, request_t *child, void const *unique_session_ptr, bool free_child, bool top_frame)
Push a pre-existing child back onto the stack as a subrequest.
Definition subrequest.c:440
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:117
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
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:864
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
static fr_sbuff_err_t char ** out
Definition value.h:1061
int nonnull(2, 5))