The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
state_machine.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 * @file src/lib/eap_aka_sim/state_machine.c
19 * @brief Implement a common state machine for EAP-SIM, EAP-AKA, EAP-AKA'.
20 *
21 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 * @copyright 2019 The FreeRADIUS server project
25 * @copyright 2019 Network RADIUS (legal@networkradius.com)
26 */
27RCSID("$Id: 5b65e81cd890c2b49388ed7df1be5458a33fcc91 $")
28#include <freeradius-devel/eap/base.h>
29#include <freeradius-devel/server/pair.h>
30#include <freeradius-devel/unlang/interpret.h>
31#include <freeradius-devel/unlang/module.h>
32#include <freeradius-devel/util/misc.h>
33#include <freeradius-devel/util/rand.h>
34
35#include "base.h"
36#include "state_machine.h"
37#include "attrs.h"
38
39#ifndef EAP_TLS_MPPE_KEY_LEN
40# define EAP_TLS_MPPE_KEY_LEN 32
41#endif
42
43#define STATE(_x) static inline unlang_action_t state_ ## _x(unlang_result_t *p_result, \
44 module_ctx_t const *mctx, \
45 request_t *request)
46
47#define STATE_GUARD(_x) static unlang_action_t guard_ ## _x(unlang_result_t *p_result, \
48 module_ctx_t const *mctx, \
49 request_t *request)
50
51#define RESUME(_x) static inline unlang_action_t resume_ ## _x(unlang_result_t *p_result, \
52 module_ctx_t const *mctx, \
53 request_t *request)
54
55#define STATE_NO_RESULT(_x) \
56 static inline unlang_action_t state_ ## _x(UNUSED unlang_result_t *p_result, \
57 module_ctx_t const *mctx, \
58 request_t *request)
59
60#define STATE_GUARD_NO_RESULT(_x) \
61 static unlang_action_t guard_ ## _x(UNUSED unlang_result_t *p_result, \
62 module_ctx_t const *mctx, \
63 request_t *request)
64
65#define STATE_TRANSITION(_x) guard_ ## _x(p_result, mctx, request)
66
67#define CALL_SECTION(_x) unlang_module_yield_to_section(&eap_aka_sim_session->section_result, \
68 request, \
69 inst->actions._x, \
70 RLM_MODULE_NOOP, \
71 resume_ ## _x, \
72 mod_signal, \
73 FR_SIGNAL_ALLOW(FR_SIGNAL_CANCEL), \
74 talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t))
75
76/*
77 * Declare all state and guard functions to
78 * avoid ordering issues.
79 */
94STATE_GUARD(common_challenge);
99STATE_GUARD(common_identity);
101
103 { L("INIT"), NULL },
104
105 { L("EAP-IDENTITY"), (void *)state_init },
106 { L("SIM-START"), (void *)state_sim_start },
107 { L("AKA-IDENTITY"), (void *)state_aka_identity },
108
109 { L("SIM-CHALLENGE"), (void *)state_sim_challenge },
110 { L("AKA-CHALLENGE"), (void *)state_aka_challenge },
111
112 { L("SUCCESS-NOTIFICATION"), (void *)state_common_success_notification },
113 { L("FAILURE-NOTIFICATION"), (void *)state_common_failure_notification },
114
115 { L("REAUTHENTICATION"), (void *)state_common_reauthentication },
116
117 { L("EAP-SUCCESS"), (void *)state_eap_success },
118 { L("EAP-FAILURE"), (void *)state_eap_failure }
119};
121
122/** Cancel a call to a submodule
123 *
124 * @param[in] mctx UNUSED.
125 * @param[in] request The current request.
126 * @param[in] action to perform.
127 */
128static void mod_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
129{
130 RDEBUG2("Request cancelled - Destroying session");
131
132 /*
133 * Remove data from the request to
134 * avoid double free.
135 */
137
138 talloc_free(mctx->rctx);
139}
140
141/** Warn the user that the rcode they provided is being ignored in this section
142 *
143 */
144#define SECTION_RCODE_IGNORED \
145do { \
146 switch (eap_aka_sim_session->section_result.rcode) { \
147 case RLM_MODULE_USER_SECTION_REJECT: \
148 RWDEBUG("Ignoring rcode (%s)", \
149 fr_table_str_by_value(rcode_table, eap_aka_sim_session->section_result.rcode, "<invalid>")); \
150 break; \
151 default: \
152 break; \
153 } \
154} while(0)
155
156/** Trigger a state transition to FAILURE-NOTIFICATION if the section returned a failure code
157 *
158 */
159#define SECTION_RCODE_PROCESS \
160do { \
161 if (after_authentication(eap_aka_sim_session)) { \
162 switch (eap_aka_sim_session->section_result.rcode) { \
163 case RLM_MODULE_REJECT: \
164 case RLM_MODULE_DISALLOW: \
165 eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_TEMPORARILY_DENIED; \
166 return STATE_TRANSITION(common_failure_notification); \
167 case RLM_MODULE_NOTFOUND: \
168 eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED; \
169 return STATE_TRANSITION(common_failure_notification); \
170 case RLM_MODULE_INVALID: \
171 case RLM_MODULE_FAIL: \
172 eap_aka_sim_session->failure_type = FR_NOTIFICATION_VALUE_GENERAL_FAILURE_AFTER_AUTHENTICATION;\
173 return STATE_TRANSITION(common_failure_notification); \
174 default: \
175 break; \
176 } \
177 } else { \
178 switch (eap_aka_sim_session->section_result.rcode) { \
179 case RLM_MODULE_USER_SECTION_REJECT: \
180 REDEBUG("Section rcode (%s) indicates we should reject the user", \
181 fr_table_str_by_value(rcode_table, eap_aka_sim_session->section_result.rcode, "<INVALID>")); \
182 return STATE_TRANSITION(common_failure_notification); \
183 default: \
184 break; \
185 } \
186 } \
187} while(0)
188
189/** Print debugging information, and write new state to eap_aka_sim_session->state
190 *
191 */
192static inline CC_HINT(always_inline) void state_set(request_t *request,
194 module_method_t new_state)
195{
197
198 if (new_state != old_state) {
199 RDEBUG2("Changed state %s -> %s",
200 fr_table_str_by_value(aka_sim_state_table, (void *)old_state, "<unknown>"),
201 fr_table_str_by_value(aka_sim_state_table, (void *)new_state, "<unknown>"));
202 } else {
203 RDEBUG2("Reentering state %s",
204 fr_table_str_by_value(aka_sim_state_table, (void *)old_state, "<unknown>"));
205 }
206
207 eap_aka_sim_session->state = new_state;
208}
209#define STATE_SET(_new_state) state_set(request, talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t), state_ ## _new_state)
210
211/** Determine if we're after authentication
212 *
213 */
218
219/** Print out the error the client returned
220 *
221 */
222static inline CC_HINT(always_inline) void client_error_debug(request_t *request)
223{
224 fr_pair_t *vp;
225
227 if (!vp) {
228 REDEBUG("Peer has not supplied a AT_ERROR_CODE");
229 } else {
230 REDEBUG("Peer rejected request with error: %i (%pV)", vp->vp_uint16, &vp->data);
231 }
232}
233
234/** Sync up what identity we're requesting with attributes in the reply
235 *
236 */
238{
239 bool set_by_user = false;
240
241 /*
242 * Check if the user included any of the
243 * ID req attributes. If they did, use
244 * them to inform what we do next, and
245 * then delete them so they don't screw
246 * up any of the other code.
247 */
248 fr_pair_list_foreach(&request->reply_pairs, vp) {
251 found:
252 set_by_user = true;
253 RDEBUG2("Previous section added reply.%pP, will request additional identity", vp);
254 fr_pair_delete(&request->reply_pairs, vp);
255 }
258 goto found;
259 }
260 else if (vp->da == attr_eap_aka_sim_any_id_req) {
262 goto found;
263 }
264 }
265
266 return set_by_user;
267}
268
269/** Based on the hint byte in the identity, add &Identity-Type and &Method-Hint attributes
270 *
271 */
273 request_t *request, char const *identity)
274{
277
278 /*
279 * Process the identity that we received.
280 */
281 if (fr_aka_sim_id_type(&type, &method,
282 identity, talloc_strlen(identity)) < 0) {
283 RPWDEBUG2("Failed parsing identity, continuing anyway");
284 }
285
286 /*
287 * Map the output from the generic ID parser
288 * function to specific EAP-AKA internal
289 * attributes in the subrequest.
290 */
292 fr_pair_t *vp = NULL;
293
295 switch (type) {
297 vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_PERMANENT;
298 break;
299
301 vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_PSEUDONYM;
302 break;
303
305 vp->vp_uint32 = FR_IDENTITY_TYPE_VALUE_FASTAUTH;
306 break;
307
308 default:
309 fr_assert(0);
310 }
311 }
312
313 /*
314 * Map the output from the generic ID parser
315 * function to specific EAP-AKA internal
316 * attributes in the subrequest.
317 */
318 if (method != AKA_SIM_METHOD_HINT_UNKNOWN) {
319 fr_pair_t *vp = NULL;
320
322 switch (method) {
324 vp->vp_uint32 = FR_METHOD_HINT_VALUE_AKA_PRIME;
325 break;
326
328 vp->vp_uint32 = FR_METHOD_HINT_VALUE_AKA;
329 break;
330
332 vp->vp_uint32 = FR_METHOD_HINT_VALUE_SIM;
333 break;
334
335 default:
336 fr_assert(0);
337 }
338 }
339
340 if (type_p) *type_p = type;
341 if (method_p) *method_p = method;
342}
343
344/** Add an Identity Request attribute to the reply
345 *
346 * Verify the progression of identity requests is valid.
347 *
348 * @param[in] request The current request.
349 * @param[in] eap_aka_sim_session The current eap_aka_sim_session.
350 * @return
351 * - 0 on success.
352 * - -1 on failure (progression of identities was not valid).
353 */
355{
356 fr_pair_t *vp;
357
358 switch (eap_aka_sim_session->id_req) {
359 /*
360 * This is allowed for EAP-SIM.
361 *
362 * The EAP-SIM-Start packet gets used both for "version negotiation" and
363 * for request identities, so for EAP-SIM it's perfectly acceptable
364 * to send four Sim-Start packets:
365 *
366 * - No ID
367 * - Any ID
368 * - Fullauth ID
369 * - Permanent ID
370 *
371 * We need to represent the starting state as separate from no id req,
372 * so that we can catch potential loops in the identity statate machine
373 */
376 id_out_of_order:
377 REDEBUG("Cannot send %s, already sent %s",
379 eap_aka_sim_session->id_req, "<INVALID>"),
381 eap_aka_sim_session->last_id_req, "<INVALID>"));
382 return -1;
383 }
384 break;
385
390 break;
391
392 default:
393 goto id_out_of_order;
394 }
396 vp->vp_bool = true;
397 break;
398
402 case AKA_SIM_NO_ID_REQ: /* Not sent anything before */
403 case AKA_SIM_ANY_ID_REQ: /* Last request was for any ID, but the re-auth ID was bad */
404 break;
405
406 default:
407 goto id_out_of_order;
408 }
410 vp->vp_bool = true;
411 break;
412
416 case AKA_SIM_NO_ID_REQ: /* Not sent anything before */
417 case AKA_SIM_ANY_ID_REQ: /* Last request was for any ID, but the re-auth ID was bad */
418 case AKA_SIM_FULLAUTH_ID_REQ: /* ...didn't understand the pseudonym either */
419 break;
420
421 default:
422 goto id_out_of_order;
423 }
425 vp->vp_bool = true;
426 break;
427
428 default:
429 fr_assert(0);
430 }
431
432 return 0;
433}
434
435/** Copy the incoming identity to the permanent identity attribute
436 *
437 * If the incoming ID really looks like a permanent ID, and we were
438 * told it was a permanent ID, then (optionally) trim the first byte
439 * to form the real permanent ID.
440 *
441 * Otherwise copy the entire incoming Identity to the
442 * &session-state.Permanent-Identity attribute.
443 *
444 * @param[in] request The current request.
445 * @param[in] in current identity.
446 * @param[in] eap_type The current eap_type.
447 * @param[in] strip_hint Whether to strip the hint byte off the permanent identity
448 */
450{
451 fr_aka_sim_id_type_t our_type;
452 fr_aka_sim_method_hint_t our_method, expected_method;
453 fr_pair_t *vp;
454
455 if (in->vp_length == 0) {
456 RDEBUG2("Not processing zero length identity");
457 return -1;
458 }
459
460 /*
461 * Not requested to strip hint, don't do anything
462 * fancy, just copy Identity -> Permanent-Identity.
463 */
464 if (!strip_hint) {
466 fr_pair_value_bstrndup(vp, in->vp_strvalue, in->vp_length, true);
467 return 0;
468 }
469
470 switch (eap_type) {
472 expected_method = AKA_SIM_METHOD_HINT_SIM;
473 break;
474
476 expected_method = AKA_SIM_METHOD_HINT_AKA;
477 break;
478
480 expected_method = AKA_SIM_METHOD_HINT_AKA_PRIME;
481 break;
482
483 default:
484 return -1;
485 }
486
487 /*
488 * First, lets see if this looks like an identity
489 * we can process.
490 *
491 * For now we allow all permanent identities no
492 * matter what EAP method.
493 *
494 * This is because we could be starting a different
495 * EAP method to the one the identity hinted,
496 * but we still want to strip the first byte.
497 */
498 if ((fr_aka_sim_id_type(&our_type, &our_method, in->vp_strvalue, in->vp_length) < 0) ||
499 (our_type != AKA_SIM_ID_TYPE_PERMANENT)) {
501 fr_pair_value_bstrndup(vp, in->vp_strvalue, in->vp_length, true);
502
503 RDEBUG2("%s has incorrect hint byte, expected '%c', got '%c'. "
504 "'hint' byte not stripped",
507 fr_aka_sim_hint_byte(our_type, our_method));
508 RINDENT();
509 RDEBUG2("session-state.%pP", vp);
510 REXDENT();
511 } else {
512 /*
513 * To get here the identity must be >= 1 and must have
514 * had the expected hint byte.
515 *
516 * Strip off the hint byte, and then add the permanent
517 * identity to the output list.
518 */
520 fr_pair_value_bstrndup(vp, in->vp_strvalue + 1, in->vp_length - 1, true);
521
522 RDEBUG2("Stripping 'hint' byte from %s", attr_eap_aka_sim_permanent_identity->name);
523 RINDENT();
524 RDEBUG2("session-state.%pP", vp);
525 REXDENT();
526 }
527
528 return 0;
529}
530
531/** Check &control.checkcode matches &reply.checkcode
532 *
533 * @param[in] request The current request.
534 * @return
535 * - 1 if the check was skipped.
536 * - 0 if the check was successful.
537 * - -1 if the check failed.
538 */
540{
541 fr_pair_t *peer_checkcode, *our_checkcode;
542 /*
543 * Checkcode validation
544 *
545 * The actual cryptographic calculations are
546 * done by the calling module, we just check
547 * the result.
548 */
549 our_checkcode = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_aka_sim_checkcode);
550 if (our_checkcode) {
551 /*
552 * If the peer doesn't include a checkcode then that
553 * means they don't support it, and we can't validate
554 * their view of the identity packets.
555 */
556 peer_checkcode = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_checkcode);
557 if (peer_checkcode) {
558 /*
559 * Compare the octets. fr_pair_cmp() cannot be used here, as it returns
560 * "false" for any pair which has the default operator, and so it never
561 * compares the data.
562 *
563 * A checkcode which covers no identity packets is empty.
564 */
565 if ((peer_checkcode->vp_length == our_checkcode->vp_length) &&
566 ((our_checkcode->vp_length == 0) ||
567 (fr_digest_cmp(peer_checkcode->vp_octets, our_checkcode->vp_octets,
568 our_checkcode->vp_length) == 0))) {
569 RDEBUG2("Received AT_CHECKCODE matches calculated AT_CHECKCODE");
570 return 0;
571 } else {
572 REDEBUG("Received AT_CHECKCODE does not match calculated AT_CHECKCODE");
573 RHEXDUMP_INLINE2(peer_checkcode->vp_octets, peer_checkcode->vp_length, "Received");
574 RHEXDUMP_INLINE2(our_checkcode->vp_octets, our_checkcode->vp_length, "Expected");
575 return -1;
576 }
577 /*
578 * Only print something if we calculated a checkcode
579 */
580 } else {
581 RDEBUG2("Peer didn't include AT_CHECKCODE, skipping checkcode validation");
582 }
583 }
584 return 1;
585}
586
587/** Check &control.mac matches &reply.mac
588 *
589 * @param[in] request The current request.
590 * @return
591 * - 0 if the check was successful.
592 * - -1 if the check failed.
593 */
595{
596 fr_pair_t *peer_mac, *our_mac;
597 /*
598 * mac validation
599 *
600 * The actual cryptographic calculations are
601 * done by the calling module, we just check
602 * the result.
603 */
604 our_mac = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_aka_sim_mac);
605 if (!our_mac) {
606 REDEBUG("Missing control.%s", attr_eap_aka_sim_mac->name);
607 return -1;
608
609 }
610
611 /*
612 * If the peer doesn't include a mac then that
613 * means they don't support it, and we can't validate
614 * their view of the identity packets.
615 */
616 peer_mac = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_mac);
617 if (!peer_mac) {
618 REDEBUG("Peer didn't include AT_MAC");
619 return -1;
620 }
621
622 /*
623 * Compare the octets, and not the pairs. fr_pair_cmp() returns "false" for any
624 * pair which has the default operator, so the result is the same whatever the
625 * peer sent. The comparison takes the same time whatever the octets are, so that
626 * it does not tell the peer how much of the AT_MAC was right.
627 */
628 if (!our_mac->vp_length || (peer_mac->vp_length != our_mac->vp_length) ||
629 (fr_digest_cmp(peer_mac->vp_octets, our_mac->vp_octets, our_mac->vp_length) != 0)) {
630 REDEBUG("Received AT_MAC does not match calculated AT_MAC");
631 RHEXDUMP_INLINE2(peer_mac->vp_octets, peer_mac->vp_length, "Received");
632 RHEXDUMP_INLINE2(our_mac->vp_octets, our_mac->vp_length, "Expected");
633 return -1;
634 }
635
636 RDEBUG2("Received AT_MAC matches calculated AT_MAC");
637 return 0;
638}
639
640/** Set the crypto identity from a received identity
641 *
642 */
644 uint8_t const *identity, size_t len)
645{
646 RDEBUG3("Setting cryptographic identity to \"%pV\"", fr_box_strvalue_len((char const *)identity, len));
647
650 MEM(eap_aka_sim_session->keys.identity = talloc_memdup(eap_aka_sim_session, identity, len));
651
652}
653
654/** Resume after 'store session { ... }'
655 *
656 */
657RESUME(store_session)
658{
659 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
660
662 /*
663 * Store failed. Don't send fastauth id
664 */
667 break;
668
669 default:
670 break;
671 }
672
674
675 return eap_aka_sim_session->next(p_result, mctx, request);
676}
677
678/** Resume after 'store pseudonym { ... }'
679 *
680 * Stores session data if required.
681 */
682RESUME(store_pseudonym)
683{
684 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
685 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
686 fr_pair_t *vp;
687 fr_pair_t *new;
688
690 /*
691 * Store failed. Don't send pseudonym
692 */
695 break;
696
697 default:
698 break;
699 }
700
701 eap_aka_sim_session->section_result.rcode = RLM_MODULE_NOOP; /* Needed because we may call resume functions directly */
702
704
705 /*
706 * Generate fast-reauth data if we
707 * find a next_reauth_id pair in the
708 * reply list.
709 */
711 if (vp) {
712 /*
713 * Generate a random fastauth string
714 */
715 if (vp->vp_length == 0) {
716 char *identity;
717
719 RWDEBUG("Found empty Next-Reauth-Id, and told not to generate one. "
720 "Skipping store session { ... } section");
721
722 goto done;
723 }
724
725 MEM(identity = talloc_array(vp, char, inst->ephemeral_id_length + 2));
726 switch (eap_aka_sim_session->type) {
728 identity[0] = (char)ID_TAG_SIM_FASTAUTH;
729 break;
730
732 identity[0] = (char)ID_TAG_AKA_FASTAUTH;
733 break;
734
736 identity[0] = (char)ID_TAG_AKA_PRIME_FASTAUTH;
737 break;
738
739 default:
740 break;
741 }
742 fr_rand_str((uint8_t *)identity + 1, inst->ephemeral_id_length, 'a');
743 identity[talloc_strlen(identity)] = '\0';
744
745 fr_value_box_bstrdup_buffer_shallow(NULL, &vp->data, NULL, identity, false);
746 }
748 fr_pair_value_memdup(new, (uint8_t const *)vp->vp_strvalue, vp->vp_length, vp->vp_tainted);
749
751 vp->vp_strvalue, vp->vp_length));
752
753 switch (eap_aka_sim_session->type) {
754 /*
755 * AKA and SIM use the original MK for session resumption.
756 */
760
764 false);
765 break;
766 /*
767 * AKA' KDF 1 generates an additional key k_re
768 * which is used for reauthentication instead
769 * of the MK.
770 */
773
777 false); /* truncates */
778 break;
779
780 default:
781 fr_assert(0);
782 break;
783 }
784
785 /*
786 * If the counter already exists in session
787 * state increment by 1, otherwise, add the
788 * attribute and set to zero.
789 */
791 if (vp) {
792 vp->vp_uint16++;
793 /*
794 * Will get incremented by 1 in
795 * reauthentication_send, so when
796 * used, it'll be 1 (as per the standard).
797 */
798 } else {
800 vp->vp_uint16 = 0;
801 }
802
803 return CALL_SECTION(store_session);
804 }
805
806 /*
807 * We didn't store any fast-reauth data
808 */
809done:
810 return eap_aka_sim_session->next(p_result, mctx, request);
811}
812
813/** Implements a set of states for storing pseudonym and fastauth identities
814 *
815 * At the end of challenge or reauthentication rounds, the user may have specified
816 * a pseudonym and fastauth identity to return to the supplicant.
817 *
818 * Call the appropriate sections to persist those values.
819 *
820 * @param[out] p_result Result of calling the module.
821 * @param[in] mctx Module calling ctx.
822 * @param[in] request the current request.
823 * @param[in] eap_aka_sim_session the EAP session
824 * @param[in] next function to call after storing sessions and pseudonyms.
825 */
829 module_method_t next)
830{
831 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
832 fr_pair_t *vp;
833 fr_pair_t *new;
834
836
838
839 eap_aka_sim_session->section_result.rcode = RLM_MODULE_NOOP; /* Reset the result code, as we may call resume functions directly */
840
842 if (vp) {
843 /*
844 * Generate a random pseudonym string
845 */
846 if (vp->vp_length == 0) {
847 char *identity;
848
850 RWDEBUG("Found empty Pseudonym-Id, and told not to generate one. "
851 "Skipping store pseudonym { ... } section");
852
853 return resume_store_pseudonym(p_result, mctx, request);
854 }
855
856 MEM(identity = talloc_array(vp, char, inst->ephemeral_id_length + 2));
857 fr_rand_str((uint8_t *)identity + 1, inst->ephemeral_id_length, 'a');
858 switch (eap_aka_sim_session->type) {
860 identity[0] = (char)ID_TAG_SIM_PSEUDONYM;
861 break;
862
864 identity[0] = (char)ID_TAG_AKA_PSEUDONYM;
865 break;
866
868 identity[0] = (char)ID_TAG_AKA_PRIME_PSEUDONYM;
869 break;
870
871 default:
872 break;
873 }
874 identity[talloc_strlen(identity)] = '\0';
875 fr_value_box_bstrdup_buffer_shallow(NULL, &vp->data, NULL, identity, false);
876 }
879
881 vp->vp_strvalue, vp->vp_length));
882 return CALL_SECTION(store_pseudonym);
883 }
884
885 return resume_store_pseudonym(p_result, mctx, request);
886}
887
888/** Resume after 'clear session { ... }'
889 *
890 */
891RESUME(clear_session)
892{
893 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
894
896
897 return eap_aka_sim_session->next(p_result, mctx, request);
898}
899
900/** Resume after 'clear pseudonym { ... }'
901 *
902 */
903RESUME(clear_pseudonym)
904{
905 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
906 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
907
909
910 /*
911 * Clear session (if one was stored)
912 */
914 fr_pair_t *vp;
915
917
919 fr_value_box_memdup(vp, &vp->data, NULL,
923
924 return CALL_SECTION(clear_session);
925 }
926
927 return eap_aka_sim_session->next(p_result, mctx, request);
928}
929
930/** Implements a set of states for clearing out pseudonym and fastauth identities
931 *
932 * If either a Challenge round or Reauthentication round fail, we need to clear
933 * any identities that were provided during those rounds, as the supplicant
934 * will have discarded them.
935 *
936 * @param[out] p_result Result of calling the module.
937 * @param[in] mctx module calling ctx.
938 * @param[in] request the current request.
939 * @param[in] eap_aka_sim_session the current EAP session
940 * @param[in] next function to call after clearing sessions and pseudonyms.
941 */
945 module_method_t next)
946{
947 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
948
950
952
953 eap_aka_sim_session->section_result.rcode = RLM_MODULE_NOOP; /* Reset the result code, as we may call resume functions directly */
954
955 /*
956 * Clear out pseudonyms (if we sent one)
957 */
959 fr_pair_t *vp;
960
964
965 return CALL_SECTION(clear_pseudonym);
966 }
967
968 return resume_clear_pseudonym(p_result, mctx, request);
969}
970
971/** Export EAP-SIM/AKA['] attributes
972 *
973 * Makes any internal data available as attributes in the response.
974 * This allows test frameworks and the encoder to access any data they need without
975 * needing to look at the eap_aka_session_t.
976 */
978{
980
981 /*
982 * Set the subtype to identity request
983 */
985 subtype_vp->vp_uint8 = subtype;
986
988 fr_pair_list_foreach(&request->reply_pairs, vp) {
990 RWDEBUG("Silently discarding reply.%pP: Encrypted attributes not "
991 "allowed in this round", vp);
992 fr_pair_delete(&request->reply_pairs, vp);
993 }
994 }
995 }
996}
997
998static void CC_HINT(nonnull(1,2))
1000 uint8_t const *hmac_extra_request, size_t hmac_extra_request_len,
1001 uint8_t const *hmac_extra_response, size_t hmac_extra_response_len)
1002{
1003 fr_pair_t *vp;
1004
1005 /*
1006 * Export keying material necessary for
1007 * the encoder to encrypt and sign
1008 * packets.
1009 */
1010 if (hmac_extra_request && hmac_extra_request_len) {
1012 MEM(fr_pair_value_memdup(vp, hmac_extra_request, hmac_extra_request_len, true) == 0);
1013 }
1014
1015 if (hmac_extra_response && hmac_extra_response_len) {
1017 MEM(fr_pair_value_memdup(vp, hmac_extra_response, hmac_extra_response_len, true) == 0);
1018 }
1019
1024 true) == 0);
1025
1030 true) == 0);
1031}
1032
1033/** Called after 'store session { ... }' and 'store pseudonym { ... }'
1034 *
1035 */
1037 module_ctx_t const *mctx, request_t *request)
1038{
1040
1041 /*
1042 * Return reply attributes - AT_IV is handled automatically by the encoder
1043 */
1044 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_AKA_SIM_REAUTHENTICATION);
1045
1046 /*
1047 * 9.5. EAP-Request/SIM/Re-authentication
1048 *
1049 * AT_MAC MUST be included. No message-specific data is included in the
1050 * MAC calculation. See Section 10.14.
1051 *
1052 * 9.6. EAP-Response/SIM/Re-authentication
1053 *
1054 * The AT_MAC attribute MUST be included. For
1055 * EAP-Response/SIM/Re-authentication, the MAC code is calculated over
1056 * the following data: EAP packet| NONCE_S
1057 *
1058 * 9.7. EAP-Request/AKA-Reauthentication
1059 *
1060 * The AT_MAC attribute MUST be included. No message-specific data is
1061 * included in the MAC calculation, see Section 10.15.
1062 *
1063 * 9.8. EAP-Response/AKA-Reauthentication
1064 *
1065 * The AT_MAC attribute MUST be included. For
1066 * EAP-Response/AKA-Reauthentication, the MAC code is calculated over
1067 * the following data: EAP packet| NONCE_S.
1068 */
1070 NULL, 0,
1071 eap_aka_sim_session->keys.reauth.nonce_s,
1072 sizeof(eap_aka_sim_session->keys.reauth.nonce_s));
1073
1075}
1076
1077/** Called after 'store session { ... }' and 'store pseudonym { ... }'
1078 *
1079 */
1081{
1083
1084 /*
1085 * Encode the packet - AT_IV is handled automatically
1086 * by the encoder.
1087 */
1088 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_AKA_CHALLENGE);
1089
1090 /*
1091 * 9.3. EAP-Request/AKA-Challenge
1092 *
1093 * AT_MAC MUST be included. In EAP-Request/AKA-Challenge, there is no
1094 * message-specific data covered by the MAC, see Section 10.15.
1095 *
1096 * 9.4. EAP-Response/AKA-Challenge
1097 *
1098 * The AT_MAC attribute MUST be included. In
1099 * EAP-Response/AKA-Challenge, there is no message-specific data covered
1100 * by the MAC, see Section 10.15.
1101 */
1103
1105}
1106
1107/** Called after 'store session { ... }' and 'store pseudonym { ... }'
1108 *
1109 */
1111{
1114 uint8_t *p = sres_cat;
1115
1116 /*
1117 * Encode the packet - AT_IV is handled automatically
1118 * by the encoder.
1119 */
1120 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_SIM_CHALLENGE);
1121
1122 memcpy(p, eap_aka_sim_session->keys.gsm.vector[0].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
1124 memcpy(p, eap_aka_sim_session->keys.gsm.vector[1].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
1126 memcpy(p, eap_aka_sim_session->keys.gsm.vector[2].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
1127
1128 /*
1129 * 9.3. EAP-Request/SIM/Challenge
1130 *
1131 * The AT_MAC attribute MUST be included. For
1132 * EAP-Request/SIM/Challenge, the MAC code is calculated over the
1133 * following data: EAP packet| NONCE_MT
1134 *
1135 * 9.4. EAP-Response/SIM/Challenge
1136 *
1137 * The AT_MAC attribute MUST be included. For EAP-
1138 * Response/SIM/Challenge, the MAC code is calculated over the following
1139 * data: EAP packet| n*SRES
1140 */
1142 eap_aka_sim_session->keys.gsm.nonce_mt, sizeof(eap_aka_sim_session->keys.gsm.nonce_mt),
1143 sres_cat, sizeof(sres_cat));
1144
1146}
1147
1148/** Helper function to check for the presence and length of AT_SELECTED_VERSION and copy its value into the keys structure
1149 *
1150 * Also checks the version matches one of the ones we advertised in our version list,
1151 * which is a bit redundant seeing as there's only one version of EAP-SIM.
1152 */
1154{
1155 fr_pair_t *selected_version_vp;
1156
1157 /*
1158 * Check that we got an AT_SELECTED_VERSION
1159 */
1160 selected_version_vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_selected_version);
1161 if (!selected_version_vp) {
1162 REDEBUG("EAP-Response/SIM/Start does not contain AT_SELECTED_VERSION");
1163 return -1;
1164 }
1165
1166 /*
1167 * See if the selected version was in our list
1168 */
1169 {
1170 uint8_t selected_version[2];
1171 uint8_t *p, *end;
1172 bool found = false;
1173
1174 p = eap_aka_sim_session->keys.gsm.version_list;
1175 end = p + eap_aka_sim_session->keys.gsm.version_list_len;
1176
1177 selected_version[0] = (selected_version_vp->vp_uint16 & 0xff00) >> 8;
1178 selected_version[1] = (selected_version_vp->vp_uint16 & 0x00ff);
1179
1180 while ((p + 2) <= end) {
1181 if ((p[0] == selected_version[0]) && (p[1] == selected_version[1])) {
1182 found = true;
1183
1184 /*
1185 * Update our keying material
1186 */
1187 eap_aka_sim_session->keys.gsm.version_select[0] = selected_version[0];
1188 eap_aka_sim_session->keys.gsm.version_select[1] = selected_version[1];
1189 break;
1190 }
1191 p += 2;
1192 }
1193
1194 if (!found) {
1195 REDEBUG("AT_SELECTED_VERSION (%u) does not match a value in our version list",
1196 selected_version_vp->vp_uint16);
1197 return -1;
1198 }
1199 }
1200
1201 return 0;
1202}
1203
1204/** Helper function to check for the presence and length of AT_NONCE_MT and copy its value into the keys structure
1205 *
1206 * Does not actually perform cryptographic validation of AT_NONCE_MT, this is done later.
1207 */
1209{
1210 fr_pair_t *nonce_mt_vp;
1211
1212 /*
1213 * Copy nonce_mt to the keying material
1214 */
1215 nonce_mt_vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_nonce_mt);
1216 if (!nonce_mt_vp) {
1217 REDEBUG("EAP-Response/SIM/Start does not contain AT_NONCE_MT");
1218 return -1;
1219 }
1220
1221 if (nonce_mt_vp->vp_length != sizeof(eap_aka_sim_session->keys.gsm.nonce_mt)) {
1222 REDEBUG("AT_NONCE_MT must be exactly %zu bytes, not %zu bytes",
1223 sizeof(eap_aka_sim_session->keys.gsm.nonce_mt), nonce_mt_vp->vp_length);
1224 return -1;
1225 }
1226 memcpy(eap_aka_sim_session->keys.gsm.nonce_mt, nonce_mt_vp->vp_octets,
1227 sizeof(eap_aka_sim_session->keys.gsm.nonce_mt));
1228
1229 return 0;
1230}
1231
1232/** FAILURE state - State machine exit point after sending EAP-Failure
1233 *
1234 * Should never actually be called. Is just a placeholder function to represent the FAILURE
1235 * termination state. Could equally be a NULL pointer, but then on a logic error
1236 * we'd get a SEGV instead of a more friendly assert/failure rcode.
1237 */
1239{
1240 if (!fr_cond_assert(request && mctx && mctx->rctx)) RETURN_UNLANG_FAIL; /* unused args */
1241
1242 fr_assert(0); /* Should never actually be called */
1243
1245}
1246
1247/** Resume after 'send EAP-Failure { ... }'
1248 *
1249 */
1250RESUME(send_eap_failure)
1251{
1252 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1253
1254 if (!fr_cond_assert(mctx && mctx->rctx)) RETURN_UNLANG_FAIL; /* unused args */
1255
1257
1258 RDEBUG2("Sending EAP-Failure");
1259
1261}
1262
1263/** Enter EAP-FAILURE state
1264 *
1265 */
1267{
1268 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1269 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1270
1271 /*
1272 * Free anything we were going to send out...
1273 */
1274 fr_pair_list_free(&request->reply_pairs);
1275
1276 /*
1277 * If we're failing, then any identities
1278 * we sent are now invalid.
1279 */
1281 return session_and_pseudonym_clear(p_result, mctx,
1282 request, eap_aka_sim_session, guard_eap_failure);
1283 /* come back when we're done */
1284 }
1285
1287
1288 return CALL_SECTION(send_eap_failure);
1289}
1290
1291/** Resume after 'recv Failure-Notification-Ack { ... }'
1292 *
1293 * - Enter the EAP-FAILURE state.
1294 */
1295RESUME(recv_common_failure_notification_ack)
1296{
1297 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1298
1300
1301 /*
1302 * Case 2 where we're allowed to send an EAP-Failure
1303 */
1305}
1306
1307/** FAILURE-NOTIFICATION state - Continue the state machine after receiving a response to our EAP-Request/(AKA|SIM)-Notification
1308 *
1309 * - Continue based on received AT_SUBTYPE value:
1310 * - EAP-Response/SIM-Client-Error - Call 'recv Failure-Notification-Ack { ... }'
1311 * - Anything else, enter the FAILURE-NOTIFICATION state.
1312 */
1314{
1315 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1318
1320 if (!subtype_vp) goto fail;
1321
1322 switch (subtype_vp->vp_uint8) {
1323 case FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION:
1324 RDEBUG2("Failure-Notification ACKed, sending EAP-Failure");
1325 return CALL_SECTION(recv_common_failure_notification_ack);
1326
1327 default:
1328 fail:
1329 RWDEBUG("Failure-Notification not ACKed correctly, sending EAP-Failure anyway");
1331 }
1332}
1333
1334/** Resume after 'send Failure-Notification { ... }'
1335 *
1336 * Ignores return code from send Failure-Notification { ... } section.
1337 */
1338RESUME(send_common_failure_notification)
1339{
1340 fr_pair_t *vp, *notification_vp;
1341 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx,
1343
1344 if (!fr_cond_assert(mctx)) RETURN_UNLANG_FAIL; /* quiet unused warning */
1345
1347
1348 /*
1349 * Allow the user to specify specific failure notification
1350 * types. We assume the user knows what they're doing and
1351 * only toggle success and phase bits.
1352 *
1353 * This allows custom notification schemes to be implemented.
1354 *
1355 * If this is prior to authentication, valid values are:
1356 * - FR_NOTIFICATION_VALUE_GENERAL_FAILURE
1357 *
1358 * If this is after authentication, valid values are:
1359 * - FR_NOTIFICATION_VALUE_GENERAL_FAILURE_AFTER_AUTHENTICATION
1360 * - FR_NOTIFICATION_VALUE_TEMPORARILY_DENIED - User has been
1361 * temporarily denied access to the requested service.
1362 * - FR_NOTIFICATION_VALUE_NOT_SUBSCRIBED
1363 * User has not subscribed to the requested service.
1364 */
1365 notification_vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_eap_aka_sim_notification);
1366
1367 /*
1368 * Keep Notification, but remove everything else...
1369 */
1370 if (notification_vp) fr_pair_remove(&request->reply_pairs, notification_vp);
1371 fr_pair_list_free(&request->reply_pairs);
1372 if (notification_vp) fr_pair_append(&request->reply_pairs, notification_vp);
1373
1374 /*
1375 * Change the failure notification depending where
1376 * we are in the eap_aka_state machine.
1377 */
1379 if (!notification_vp) {
1380 MEM(pair_append_reply(&notification_vp, attr_eap_aka_sim_notification) >= 0);
1381 notification_vp->vp_uint16 = eap_aka_sim_session->failure_type; /* Default will be zero */
1382 }
1383
1384 notification_vp->vp_uint16 &= ~0x4000; /* Unset phase bit */
1385
1386 /*
1387 * Include the counter attribute if we're failing
1388 * after a reauthentication success.
1389 *
1390 * RFC 4187 Section #9.10
1391 *
1392 * If EAP-Request/AKA-Notification is used on
1393 * a fast re-authentication exchange, and if
1394 * the P bit in AT_NOTIFICATION is set to zero,
1395 * then AT_COUNTER is used for replay protection.
1396 * In this case, the AT_ENCR_DATA and AT_IV
1397 * attributes MUST be included, and the
1398 * encapsulated plaintext attributes MUST include
1399 * the AT_COUNTER attribute. The counter value
1400 * included in AT_COUNTER MUST be the same
1401 * as in the EAP-Request/AKA-Reauthentication
1402 * packet on the same fast re-authentication
1403 * exchange.
1404 *
1405 * If the counter is used it should never be zero,
1406 * as it's incremented on first reauthentication
1407 * request.
1408 */
1410 MEM(pair_update_reply(&notification_vp, attr_eap_aka_sim_counter) >= 0);
1411 notification_vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
1412 }
1413
1414 /*
1415 * If we're after the challenge phase
1416 * then we need to include a MAC to
1417 * protect notifications.
1418 */
1420 fr_pair_value_memdup(vp, NULL, 0, false);
1421 } else {
1422 /*
1423 * Only valid code is general failure
1424 */
1425 if (!notification_vp) {
1426 MEM(pair_append_reply(&notification_vp, attr_eap_aka_sim_notification) >= 0);
1427 notification_vp->vp_uint16 = FR_NOTIFICATION_VALUE_GENERAL_FAILURE;
1428 /*
1429 * User supplied failure code
1430 */
1431 } else {
1432 notification_vp->vp_uint16 |= 0x4000; /* Set phase bit */
1433 }
1434 }
1435 notification_vp->vp_uint16 &= ~0x8000; /* In both cases success bit should be low */
1436
1437 /*
1438 * Send a response
1439 */
1440 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION);
1441
1443}
1444
1445/** Enter the FAILURE-NOTIFICATION state
1446 *
1447 */
1449{
1450 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1451 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1452
1453 /*
1454 * If we're failing, then any identities
1455 * we sent are now invalid.
1456 */
1459 guard_common_failure_notification); /* come back when we're done */
1460 }
1461
1462 /*
1463 * We've already sent a failure notification
1464 * Now we just fail as it means something
1465 * went wrong processing the ACK or we got
1466 * garbage from the supplicant.
1467 */
1468 if (eap_aka_sim_session->state == state_common_failure_notification) {
1470 }
1471
1472 /*
1473 * Otherwise just transition as normal...
1474 */
1476
1477 return CALL_SECTION(send_common_failure_notification);
1478}
1479
1480/** SUCCESS state - State machine exit point after sending EAP-Success
1481 *
1482 * Should never actually be called. Is just a placeholder function to represent the SUCCESS
1483 * termination state. Could equally be a NULL pointer, but then on a logic error
1484 * we'd get a SEGV instead of a more friendly assert/failure rcode.
1485 */
1487{
1488 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1489
1491
1492 fr_assert(0); /* Should never actually be called */
1493
1495}
1496
1497/** Resume after 'send EAP-Success { ... }'
1498 *
1499 * Add MPPE keys to the request being sent to the supplicant
1500 *
1501 * The only work to be done is the add the appropriate SEND/RECV
1502 * attributes derived from the MSK.
1503 */
1504RESUME(send_eap_success)
1505{
1506 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1507 uint8_t *p;
1508
1509 RDEBUG2("Sending EAP-Success");
1510
1511 /*
1512 * If this is true we're entering this state
1513 * after sending a AKA-Success-Notification
1514 *
1515 * Is seems like a really bad idea to allow the
1516 * user to send a protected success to the
1517 * supplicant and then force a failure using
1518 * the send EAP-Success { ... } section.
1519 */
1523 RWDEBUG("Ignoring rcode (%s) from send EAP-Success { ... } "
1524 "as we already sent a Success-Notification",
1526 RWDEBUG("If you need to force a failure, return an error code from "
1527 "send Success-Notification { ... }");
1528 break;
1529
1530 default:
1531 break;
1532 }
1533
1534 /*
1535 * But... if we're not working with protected
1536 * success indication, this is the only
1537 * opportunity the user has to force a failure at
1538 * the end of authentication.
1539 */
1540 } else {
1542 }
1543
1544 RDEBUG2("Adding attributes for MSK");
1549
1551}
1552
1553/** Enter EAP-SUCCESS state
1554 *
1555 */
1557{
1558 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1559 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1560
1562
1563 return CALL_SECTION(send_eap_success);
1564}
1565
1566/** Resume after 'recv Success-Notification-Ack { ... }'
1567 *
1568 * - Enter the EAP-SUCCESS state.
1569 */
1570RESUME(recv_common_success_notification_ack)
1571{
1572 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1573
1575
1576 /*
1577 * RFC 4187 says we ignore the contents of the
1578 * next packet after we send our success notification
1579 * and always send a success.
1580 */
1582}
1583
1584/** SUCCESS-NOTIFICATION state - Continue the state machine after receiving a response to our EAP-Request/(AKA|SIM)-Notification
1585 *
1586 * - Call 'recv Success-Notification-Ack { ... }'
1587 */
1589{
1590 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1591 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx,
1593 /*
1594 * Because the server uses the AT_NOTIFICATION code "Success" (32768) to
1595 * indicate that the EAP exchange has completed successfully, the EAP
1596 * exchange cannot fail when the server processes the EAP-AKA response
1597 * to this notification. Hence, the server MUST ignore the contents of
1598 * the EAP-AKA response it receives to the EAP-Request/AKA-Notification
1599 * with this code. Regardless of the contents of the EAP-AKA response,
1600 * the server MUST send EAP-Success as the next packet.
1601 */
1602 return CALL_SECTION(recv_common_success_notification_ack);
1603}
1604
1605/** Resume after 'send Success-Notification { ... }'
1606 *
1607 */
1608RESUME(send_common_success_notification)
1609{
1610 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1611 fr_pair_t *vp;
1612
1614
1616
1617 /*
1618 * If we're in this state success bit is
1619 * high phase bit is low.
1620 */
1622 vp->vp_uint16 = FR_NOTIFICATION_VALUE_SUCCESS;
1623
1624 /*
1625 * RFC 4187 section #9.10
1626 *
1627 * If EAP-Request/AKA-Notification is used on
1628 * a fast re-authentication exchange, and if
1629 * the P bit in AT_NOTIFICATION is set to zero,
1630 * then AT_COUNTER is used for replay protection.
1631 * In this case, the AT_ENCR_DATA and AT_IV
1632 * attributes MUST be included, and the
1633 * encapsulated plaintext attributes MUST include
1634 * the AT_COUNTER attribute. The counter value
1635 * included in AT_COUNTER MUST be the same
1636 * as in the EAP-Request/AKA-Reauthentication
1637 * packet on the same fast re-authentication
1638 * exchange.
1639 *
1640 * If the counter is used it should never be zero,
1641 * as it's incremented on first reauthentication
1642 * request.
1643 */
1644 if (eap_aka_sim_session->keys.reauth.counter > 0) {
1646 vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
1647 }
1648
1649 /*
1650 * Need to include an AT_MAC attribute so that
1651 * it will get calculated.
1652 */
1654 fr_pair_value_memdup(vp, NULL, 0, false);
1655
1656 /*
1657 * Return reply attributes
1658 */
1659 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_AKA_SIM_NOTIFICATION);
1660
1662}
1663
1664/** Enter the SUCCESS-NOTIFICATION state
1665 *
1666 */
1668{
1669 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1670 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx,
1673
1674 return CALL_SECTION(send_common_success_notification);
1675}
1676
1677/** Resume after 'recv Client-Error { ... }'
1678 *
1679 * - Enter the EAP-FAILURE state.
1680 */
1681RESUME(recv_common_client_error)
1682{
1683 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1684
1686
1688}
1689
1690/** Resume after 'recv Reauthentication-Response { ... }'
1691 *
1692 * - If 'recv Reauthentication-Response { ... }' returned a failure
1693 * rcode, enter the FAILURE-NOTIFICATION state.
1694 * - ...or call the EAP-Request/Reauthentication-Response function to act on the
1695 * contents of the response.
1696 */
1697RESUME(recv_common_reauthentication_response)
1698{
1699 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1700
1702
1703 /*
1704 * Validate mac
1705 */
1706 if (mac_validate(request) < 0) {
1707 failure:
1709 }
1710
1711 /*
1712 * Validate the checkcode
1713 */
1714 if (checkcode_validate(request) < 0) goto failure;
1715
1716 /*
1717 * Check to see if the supplicant sent
1718 * AT_COUNTER_TOO_SMALL, if they did then we
1719 * clear out reauth information and enter the
1720 * challenge state.
1721 */
1723 RWDEBUG("Peer sent AT_COUNTER_TOO_SMALL (indicating our AT_COUNTER value (%u) wasn't fresh)",
1724 eap_aka_sim_session->keys.reauth.counter);
1725
1728
1730 }
1731
1732 /*
1733 * If the peer wants a Success notification, and
1734 * we included AT_RESULT_IND then send a success
1735 * notification, otherwise send a normal EAP-Success.
1736 *
1737 * RFC 4187 Section #6.2. Result Indications
1738 */
1740 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
1741 RDEBUG("We wanted to use protected result indications, but peer does not");
1743 } else {
1745 }
1746 } else if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
1747 RDEBUG("Peer wanted to use protected result indications, but we do not");
1748 }
1749
1751
1753}
1754
1755/** REAUTHENTICATION state - Continue the state machine after receiving a response to our EAP-Request/SIM-Start
1756 *
1757 * - Continue based on received AT_SUBTYPE value:
1758 * - EAP-Response/(SIM|AKA)-Reauthentication - call 'recv Reauthentication-Response { ... }'
1759 * - EAP-Response/(SIM|AKA)-Client-Error - call 'recv Client-Error { ... }' and after that
1760 * send a EAP-Request/(SIM|AKA)-Notification indicating a General Failure.
1761 * - Anything else, enter the FAILURE-NOTIFICATION state.
1762 */
1764{
1765 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
1766 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1767 fr_pair_t *subtype_vp = NULL;
1768
1771 REDEBUG("Missing AT_SUBTYPE");
1772 goto fail;
1773 }
1774
1775 /*
1776 * These aren't allowed in Reauthentication responses as they don't apply:
1777 *
1778 * EAP_AKA_AUTHENTICATION_REJECT - We didn't provide an AUTN value
1779 * EAP_AKA_SYNCHRONIZATION_FAILURE - We didn't use new vectors.
1780 */
1781 switch (subtype_vp->vp_uint8) {
1782 case FR_SUBTYPE_VALUE_AKA_SIM_REAUTHENTICATION:
1783 /*
1784 * AT_COUNTER_TOO_SMALL is handled
1785 * in common_reauthentication_response_process.
1786 */
1787 return CALL_SECTION(recv_common_reauthentication_response);
1788
1789 /*
1790 * Case 1 where we're allowed to send an EAP-Failure
1791 */
1792 case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
1794
1796
1797 return CALL_SECTION(recv_common_client_error);
1798 /*
1799 * RFC 4187 says we *MUST* notify, not just
1800 * send an EAP-Failure in this case.
1801 */
1802 default:
1803 REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
1804 fail:
1806
1808 }
1809}
1810
1811
1812/** Send a EAP-Request/(AKA|SIM)-Reauthenticate message to the supplicant
1813 *
1814 */
1816 module_ctx_t const *mctx,
1819{
1820 fr_pair_t *vp;
1821 fr_pair_t *kdf_id;
1822
1823 /*
1824 * Allow override of KDF Identity
1825 *
1826 * Because certain handset manufacturers don't
1827 * implement RFC 4187 correctly and use the
1828 * wrong identity as input the the PRF/KDF.
1829 *
1830 * Not seen any doing this for re-authentication
1831 * but you never know...
1832 */
1833 kdf_id = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_aka_sim_kdf_identity);
1834 if (kdf_id) {
1836 (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
1838 }
1839
1840 RDEBUG2("Generating new session keys");
1841
1842 switch (eap_aka_sim_session->type) {
1843 /*
1844 * The GSM and UMTS KDF_0 mutate their keys using
1845 * and identical algorithm.
1846 */
1847 case FR_EAP_METHOD_SIM:
1848 case FR_EAP_METHOD_AKA:
1850 &eap_aka_sim_session->keys) != 0) {
1851 request_new_id:
1853 /*
1854 * Got here processing EAP-Identity-Response
1855 * If this is the *true* reauth ID, then
1856 * there's no point in setting AKA_SIM_ANY_ID_REQ.
1857 */
1859 case AKA_SIM_NO_ID_REQ:
1860 case AKA_SIM_ANY_ID_REQ:
1861 RDEBUG2("Composing EAP-Request/Reauthentication failed. Clearing reply attributes and "
1862 "requesting additional Identity");
1863 fr_pair_list_free(&request->reply_pairs);
1865 return STATE_TRANSITION(common_identity);
1866
1869 REDEBUG("Last requested fullauth or permanent ID, "
1870 "but received, or were told we received (by policy), "
1871 "a fastauth ID. Cannot continue");
1873 }
1874 }
1875 if (fr_aka_sim_crypto_kdf_0_reauth(&eap_aka_sim_session->keys) < 0) goto request_new_id;
1876 break;
1877
1881 &eap_aka_sim_session->keys) != 0) {
1882 goto request_new_id;
1883 }
1884 if (fr_aka_sim_crypto_umts_kdf_1_reauth(&eap_aka_sim_session->keys) < 0) goto request_new_id;
1885 } else {
1886 fr_assert(0);
1887 }
1888 break;
1889
1890 default:
1891 fr_assert(0);
1892 break;
1893 }
1894
1896
1897 /*
1898 * Indicate we'd like to use protected success messages
1899 * with AT_RESULT_IND
1900 *
1901 * Use our default, but allow user override too.
1902 */
1904 if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
1905
1906 /*
1907 * RFC 5448 says AT_BIDDING is only sent in the challenge
1908 * not in reauthentication, so don't add that here.
1909 */
1910
1911 /*
1912 * Add AT_NONCE_S
1913 */
1916 sizeof(eap_aka_sim_session->keys.reauth.nonce_s), false);
1917
1918 /*
1919 * Add AT_COUNTER
1920 */
1922 vp->vp_uint16 = eap_aka_sim_session->keys.reauth.counter;
1923
1924 /*
1925 * need to include an empty AT_MAC attribute so that
1926 * the mac will get calculated.
1927 */
1929 fr_pair_value_memdup(vp, NULL, 0, false);
1930
1931 /*
1932 * We've sent the challenge so the peer should now be able
1933 * to accept encrypted attributes.
1934 */
1936
1939}
1940
1941/** Resume after 'send Reauthentication-Request { ... }'
1942 *
1943 */
1944RESUME(send_common_reauthentication_request)
1945{
1946 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
1947
1949 /*
1950 * Failed getting the values we need for resumption
1951 * Request a different identity.
1952 */
1953 default:
1955 /*
1956 * Got here processing EAP-Identity-Response
1957 * If this is the *true* reauth ID, then
1958 * there's no point in setting AKA_SIM_ANY_ID_REQ.
1959 */
1961 case AKA_SIM_NO_ID_REQ:
1962 case AKA_SIM_ANY_ID_REQ:
1963 RDEBUG2("Previous section returned (%s), clearing reply attributes and "
1964 "requesting additional identity",
1966 fr_pair_list_free(&request->reply_pairs);
1968
1969 return STATE_TRANSITION(common_identity);
1970
1973 default:
1974 break;
1975 }
1976 REDEBUG("Last requested Full-Auth-Id or Permanent-Identity, "
1977 "but received a Fast-Auth-Id. Cannot continue");
1978 failure:
1980
1981 /*
1982 * Policy rejected the user
1983 */
1984 case RLM_MODULE_REJECT:
1986 goto failure;
1987
1988 /*
1989 * Everything looks ok, send the EAP-Request/reauthentication message
1990 * After storing any new pseudonyms or session information.
1991 */
1992 case RLM_MODULE_NOOP:
1993 case RLM_MODULE_OK:
1994 case RLM_MODULE_UPDATED:
1996 }
1997}
1998
1999/** Resume after 'load pseudonym { ... }'
2000 *
2001 */
2002RESUME(load_pseudonym)
2003{
2004 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2005 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2006
2008
2009 /*
2010 * Control attributes required could have been specified
2011 * in another section.
2012 */
2013 if (!inst->actions.load_pseudonym) {
2014 next_state:
2015 return eap_aka_sim_session->next(p_result, mctx, request);
2016 }
2017
2019 /*
2020 * Failed resolving the pseudonym
2021 * request a different identity.
2022 */
2023 default:
2026 case AKA_SIM_NO_ID_REQ:
2027 case AKA_SIM_ANY_ID_REQ:
2029 RDEBUG2("Previous section returned (%s), clearing reply attributes and "
2030 "requesting additional identity",
2032 fr_pair_list_free(&request->reply_pairs);
2034 return STATE_TRANSITION(common_identity);
2035
2037 REDEBUG("Last requested a Permanent-Identity, but received a Pseudonym. Cannot continue");
2038 failure:
2040 }
2041 break;
2042
2043 /*
2044 * Policy rejected the user
2045 */
2046 case RLM_MODULE_REJECT:
2048 goto failure;
2049
2050 /*
2051 * Everything OK
2052 */
2053 case RLM_MODULE_OK:
2054 case RLM_MODULE_UPDATED:
2055 goto next_state;
2056 }
2057
2058 goto failure;
2059}
2060
2061/** Resume after 'load session { ... }'
2062 *
2063 */
2064RESUME(load_session)
2065{
2066 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2067 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2068
2070
2071 /*
2072 * Control attributes required could have been specified
2073 * in another section.
2074 */
2075 if (!inst->actions.load_session) goto reauthenticate;
2076
2078 /*
2079 * Failed getting the values we need for resumption
2080 * Request a different identity.
2081 */
2082 default:
2084 /*
2085 * Got here processing EAP-Identity-Response
2086 * If this is the *true* reauth ID, then
2087 * there's no point in setting AKA_SIM_ANY_ID_REQ.
2088 */
2090 case AKA_SIM_NO_ID_REQ:
2091 case AKA_SIM_ANY_ID_REQ:
2092 RDEBUG2("Previous section returned (%s), clearing reply attributes and "
2093 "requesting additional identity",
2095 fr_pair_list_free(&request->reply_pairs);
2097 return STATE_TRANSITION(common_identity);
2098
2101 REDEBUG("Last requested Full-Auth-Id or Permanent-Identity, "
2102 "but received a Fast-Auth-Id. Cannot continue");
2104 }
2105 break;
2106
2107 /*
2108 * Policy rejected the user
2109 */
2110 case RLM_MODULE_REJECT:
2112 reject:
2114
2115 /*
2116 * Everything OK
2117 */
2118 case RLM_MODULE_OK:
2119 case RLM_MODULE_UPDATED:
2120 reauthenticate:
2121 return CALL_SECTION(send_common_reauthentication_request);
2122 }
2123
2124 goto reject;
2125}
2126
2127/** Enter the REAUTHENTICATION state
2128 *
2129 */
2131{
2132 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2133 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2134 fr_pair_t *vp = NULL;
2135
2137
2138 /*
2139 * Add the current identity as session_id
2140 * to make it easier to load/store things from
2141 * the cache module.
2142 */
2145
2146 return CALL_SECTION(load_session);
2147}
2148
2149/** Resume after 'recv Synchronization-Failure { ... }'
2150 *
2151 * - If 'recv Synchronization-Failure { ... }' returned a failure
2152 * rcode, enter the FAILURE-NOTIFICATION state.
2153 * - ...or if no 'recv Synchronization-Failure { ... }' section was
2154 * defined, then enter the FAILURE-NOTIFICATION state.
2155 * - ...or if the user didn't provide a new SQN value in &control.SQN
2156 * then enter the FAILURE-NOTIFICATION state.
2157 * - ...or enter the AKA-CHALLENGE state.
2158 */
2159RESUME(recv_aka_synchronization_failure)
2160{
2161 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2162 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2163 fr_pair_t *vp;
2164
2166
2167 /*
2168 * If there's no section to handle this, then no resynchronisation
2169 * can't have occurred and we just send a reject.
2170 *
2171 * Similarly, if we've already received one synchronisation failure
2172 * then it's highly likely whatever user configured action was
2173 * configured was unsuccessful, and we should just give up.
2174 */
2175 if (!inst->actions.recv_aka_synchronization_failure || eap_aka_sim_session->prev_recv_sync_failure) {
2176 failure:
2178 }
2179
2180 /*
2181 * We couldn't generate an SQN and the user didn't provide one,
2182 * so we need to fail.
2183 */
2184 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_sim_sqn);
2185 if (!vp) {
2186 REDEBUG("No control.SQN value provided after resynchronisation, cannot continue");
2187 goto failure;
2188 }
2189
2190 /*
2191 * RFC 4187 Section #6.3.1
2192 *
2193 * "if the peer detects that the
2194 * sequence number in AUTN is not correct, the peer responds with
2195 * EAP-Response/AKA-Synchronization-Failure (Section 9.6), and the
2196 * server proceeds with a new EAP-Request/AKA-Challenge."
2197 */
2199}
2200
2201/** Resume after 'recv Authentication-Reject { ... }'
2202 *
2203 * - Enter the FAILURE-NOTIFICATION state.
2204 */
2205RESUME(recv_aka_authentication_reject)
2206{
2207 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2208
2210
2211 /*
2212 * Case 2 where we're allowed to send an EAP-Failure
2213 */
2215}
2216
2217/** Resume after 'recv Challenge-Response { ... }'
2218 *
2219 * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
2220 * - ...or call a function to process the contents of the AKA-Challenge message.
2221 *
2222 * Verify that MAC, and RES match what we expect.
2223 */
2224RESUME(recv_aka_challenge_response)
2225{
2226 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2227 fr_pair_t *vp = NULL;
2228
2230
2231 /*
2232 * Validate mac
2233 */
2234 if (mac_validate(request) < 0) {
2235 failure:
2237 }
2238
2239 /*
2240 * Validate the checkcode
2241 */
2242 if (checkcode_validate(request) < 0) goto failure;
2243
2244 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_res);
2245 if (!vp) {
2246 REDEBUG("AT_RES missing from challenge response");
2247 goto failure;
2248 }
2249
2250 if (vp->vp_length != eap_aka_sim_session->keys.umts.vector.xres_len) {
2251 REDEBUG("Received RES' length (%zu) does not match calculated XRES' length (%zu)",
2252 vp->vp_length, eap_aka_sim_session->keys.umts.vector.xres_len);
2253 goto failure;
2254 }
2255
2256 if (fr_digest_cmp(vp->vp_octets, eap_aka_sim_session->keys.umts.vector.xres, vp->vp_length)) {
2257 REDEBUG("Received RES does not match calculated XRES");
2258 RHEXDUMP_INLINE2(vp->vp_octets, vp->vp_length, "RES :");
2259 RHEXDUMP_INLINE2(eap_aka_sim_session->keys.umts.vector.xres,
2260 eap_aka_sim_session->keys.umts.vector.xres_len, "XRES :");
2261 goto failure;
2262 }
2263
2264 RDEBUG2("Received RES matches calculated XRES");
2265
2267
2268 /*
2269 * If the peer wants a Success notification, and
2270 * we included AT_RESULT_IND then send a success
2271 * notification, otherwise send a normal EAP-Success.
2272 *
2273 * RFC 4187 Section #6.2. Result Indications
2274 */
2276 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
2277 RDEBUG("We wanted to use protected result indications, but peer does not");
2279 } else {
2281 }
2282 } else if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
2283 RDEBUG("Peer wanted to use protected result indications, but we do not");
2284 }
2285
2287}
2288
2289/** AKA-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Challenge
2290 *
2291 * - Continue based on received AT_SUBTYPE value:
2292 * - EAP-Response/AKA-Challenge - call 'recv Challenge-Response { ... }'.
2293 * - EAP-Response/AKA-Authentication-Reject - call 'recv Authentication-Reject { ... }' and after that
2294 * send a EAP-Request/SIM-Notification indicating a General Failure.
2295 * - EAP-Response/AKA-Synchronization-Failure - call 'recv Synchronization-Failure { ... }'.
2296 * - EAP-Response/AKA-Client-Error - call 'recv Client-Error { ... }' and after that
2297 * send a EAP-Request/AKA-Notification indicating a General Failure.
2298 * - Anything else, enter the FAILURE-NOTIFICATION state.
2299 */
2301{
2302 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2303 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2304 fr_pair_t *subtype_vp = NULL;
2306
2308 if (!subtype_vp) {
2309 REDEBUG("Missing AT_SUBTYPE");
2310 goto fail;
2311 }
2312
2313 switch (subtype_vp->vp_uint8) {
2314 case FR_SUBTYPE_VALUE_AKA_CHALLENGE:
2315 return CALL_SECTION(recv_aka_challenge_response);
2316
2317 /*
2318 * Case 2 where we're allowed to send an EAP-Failure
2319 */
2320 case FR_SUBTYPE_VALUE_AKA_AUTHENTICATION_REJECT:
2322 return CALL_SECTION(recv_aka_authentication_reject);
2323
2324 case FR_SUBTYPE_VALUE_AKA_SYNCHRONIZATION_FAILURE:
2325 {
2326 uint64_t new_sqn;
2327
2329
2330 vp = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_auts);
2331 if (!vp) {
2332 REDEBUG("EAP-Response/AKA-Synchronisation-Failure missing AT_AUTS");
2333 failure:
2335 }
2336
2337 switch (fr_aka_sim_umts_resync_from_attrs(&new_sqn,
2339 /*
2340 * Add everything back that we'll need in the
2341 * next challenge round.
2342 */
2343 case 0:
2345 vp->vp_uint64 = new_sqn;
2346
2349 sizeof(eap_aka_sim_session->keys.auc.ki), false);
2350
2353 sizeof(eap_aka_sim_session->keys.auc.opc), false);
2354 break;
2355
2356 case 1: /* Don't have Ki or OPc so something else will need to deal with this */
2357 break;
2358
2359 default:
2360 case -1:
2361 goto failure;
2362 }
2363
2364 return CALL_SECTION(recv_aka_synchronization_failure);
2365 }
2366
2367 /*
2368 * Case 1 where we're allowed to send an EAP-Failure
2369 */
2370 case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
2372
2374
2375 return CALL_SECTION(recv_common_client_error);
2376
2377 /*
2378 * RFC 4187 says we *MUST* notify, not just
2379 * send an EAP-Failure in this case.
2380 */
2381 default:
2382 REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
2383 fail:
2385 goto failure;
2386 }
2387}
2388
2389/** Resume after 'send Challenge-Request { ... }'
2390 *
2391 */
2392RESUME(send_aka_challenge_request)
2393{
2394 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2395 fr_pair_t *vp;
2397
2398 fr_pair_t *kdf_id;
2399
2401
2402 /*
2403 * Allow override of KDF Identity
2404 *
2405 * Because certain handset manufacturers don't
2406 * implement RFC 4187 correctly and use the
2407 * wrong identity as input the the PRF/KDF.
2408 */
2409 kdf_id = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_aka_sim_kdf_identity);
2410 if (kdf_id) {
2412 (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
2414 }
2415
2416 RDEBUG2("Acquiring UMTS vector(s)");
2417
2419 /*
2420 * Copy the network name the user specified for
2421 * key derivation purposes.
2422 */
2424 if (vp) {
2427 (uint8_t const *)vp->vp_strvalue,
2428 vp->vp_length);
2429 eap_aka_sim_session->keys.network_len = vp->vp_length;
2430 } else {
2431 REDEBUG("No network name available, can't set AT_KDF_INPUT");
2432 failure:
2434 }
2435
2436 /*
2437 * We don't allow the user to specify
2438 * the KDF currently.
2439 */
2441 vp->vp_uint16 = eap_aka_sim_session->kdf;
2442 }
2443
2444 /*
2445 * Get vectors from attribute or generate
2446 * them using COMP128-* or Milenage.
2447 */
2449 &eap_aka_sim_session->keys, &src) != 0) {
2450 REDEBUG("Failed retrieving UMTS vectors");
2451 goto failure;
2452 }
2453
2454 /*
2455 * Don't leave the AMF hanging around
2456 */
2458
2459 /*
2460 * All set, calculate keys!
2461 */
2462 switch (eap_aka_sim_session->type) {
2463 default:
2464 case FR_EAP_METHOD_SIM:
2465 fr_assert(0); /* EAP-SIM has its own Challenge state */
2466 break;
2467
2468 case FR_EAP_METHOD_AKA:
2470 break;
2471
2474 fr_pair_t *ck_prime_vp;
2475 fr_pair_t *ik_prime_vp;
2476
2477 /*
2478 * When control.CK-Prime / control.IK-Prime are
2479 * supplied (e.g. by a 3GPP HSS over SWx which performs
2480 * the RFC 5448 / TS 33.402 transform itself), read them
2481 * from those attributes and skip the local Annex A
2482 * derivation in fr_aka_sim_crypto_umts_kdf_1().
2483 *
2484 * The helper returns 1 when neither attribute is set
2485 * (normal local-derivation path) and -1 when the inputs
2486 * are present but invalid (mismatched pair or wrong
2487 * length). Only the -1 case is a real error.
2488 */
2489 ck_prime_vp = fr_pair_find_by_da(&request->control_pairs, NULL,
2491 ik_prime_vp = fr_pair_find_by_da(&request->control_pairs, NULL,
2493
2495 ck_prime_vp, ik_prime_vp,
2496 &eap_aka_sim_session->keys) < 0) {
2497 goto failure;
2498 }
2500 } else {
2501 fr_assert(0);
2502 }
2503 }
2505
2506 /*
2507 * Indicate we'd like to use protected success messages
2508 * with AT_RESULT_IND
2509 *
2510 * Use our default, but allow user override too.
2511 */
2513 if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
2514
2515 /*
2516 * These attributes are only allowed with
2517 * EAP-AKA', protect users from themselves.
2518 */
2522 }
2523
2524 /*
2525 * Okay, we got the challenge! Put it into an attribute.
2526 */
2529
2530 /*
2531 * Send the AUTN value to the client, so it can authenticate
2532 * whoever has knowledge of the Ki.
2533 */
2536
2537 /*
2538 * need to include an AT_MAC attribute so that it will get
2539 * calculated.
2540 */
2542 fr_pair_value_memdup(vp, NULL, 0, false);
2543
2544 /*
2545 * We've sent the challenge so the peer should now be able
2546 * to accept encrypted attributes.
2547 */
2549
2551}
2552
2553/** Enter the AKA-CHALLENGE state
2554 *
2555 */
2557{
2558 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2559 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2560 fr_pair_t *vp;
2561
2562 /*
2563 * If we've sent either of these identities it
2564 * means we've come here form a Reauthentication-Request
2565 * that failed.
2566 */
2568 return session_and_pseudonym_clear(p_result, mctx, request,
2569 eap_aka_sim_session, guard_aka_challenge);
2570 /* come back when we're done */
2571 }
2572
2574
2575 /*
2576 * Set some default attributes, giving the user a
2577 * chance to modify them.
2578 */
2579 switch (eap_aka_sim_session->type) {
2581 {
2582 uint8_t amf_buff[2] = { 0x80, 0x00 }; /* Set the AMF separation bit high */
2583
2584 /*
2585 * Toggle the AMF high bit to indicate we're doing AKA'
2586 */
2588 fr_pair_value_memdup(vp, amf_buff, sizeof(amf_buff), false);
2589
2590 /*
2591 * Use the default network name we have configured
2592 * and send it to the peer.
2593 */
2594 if (inst->network_name &&
2595 !fr_pair_find_by_da(&request->reply_pairs, NULL, attr_eap_aka_sim_kdf_input)) {
2598 }
2599 }
2600 break;
2601
2602 default:
2603 break;
2604
2605 }
2606
2607 /*
2608 * Set the defaults for protected result indicator
2609 */
2613 vp->vp_bool = true;
2614 }
2615
2616 return CALL_SECTION(send_aka_challenge_request);
2617}
2618
2619/** Resume after 'recv Challenge-Response { ... }'
2620 *
2621 * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
2622 * - ...or call a function to process the contents of the SIM-Challenge message.
2623 *
2624 * Verify that MAC, and RES match what we expect.
2625 */
2626RESUME(recv_sim_challenge_response)
2627{
2628 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2630 uint8_t *p = sres_cat;
2631
2633
2634 memcpy(p, eap_aka_sim_session->keys.gsm.vector[0].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
2636 memcpy(p, eap_aka_sim_session->keys.gsm.vector[1].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
2638 memcpy(p, eap_aka_sim_session->keys.gsm.vector[2].sres, AKA_SIM_VECTOR_GSM_SRES_SIZE);
2639
2640 /*
2641 * Validate mac
2642 */
2644
2646
2647 /*
2648 * If the peer wants a Success notification, and
2649 * we included AT_RESULT_IND then send a success
2650 * notification, otherwise send a normal EAP-Success.
2651 */
2653 if (!fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
2654 RDEBUG("We wanted to use protected result indications, but peer does not");
2656 } else {
2658 }
2659 } else if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_result_ind)) {
2660 RDEBUG("Peer wanted to use protected result indications, but we do not");
2661 }
2662
2664}
2665
2666/** SIM-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Challenge
2667 *
2668 * - Continue based on received AT_SUBTYPE value:
2669 * - EAP-Response/SIM-Challenge - call 'recv Challenge-Response { ... }'.
2670 * - EAP-Response/SIM-Client-Error - call 'recv Client-Error { ... }' and after that
2671 * send a EAP-Request/SIM-Notification indicating a General Failure.
2672 * - Anything else, enter the FAILURE-NOTIFICATION state.
2673 */
2675{
2676 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2677 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2678 fr_pair_t *subtype_vp = NULL;
2679
2681 if (!subtype_vp) {
2682 REDEBUG("Missing AT_SUBTYPE");
2683 goto fail;
2684 }
2685
2686 switch (subtype_vp->vp_uint8) {
2687 case FR_SUBTYPE_VALUE_SIM_CHALLENGE:
2688 return CALL_SECTION(recv_sim_challenge_response);
2689
2690 /*
2691 * Case 1 where we're allowed to send an EAP-Failure
2692 */
2693 case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
2695
2697
2698 return CALL_SECTION(recv_common_client_error);
2699
2700 /*
2701 * RFC 4186 says we *MUST* notify, not just
2702 * send an EAP-Failure in this case.
2703 */
2704 default:
2705 REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
2706 fail:
2708
2710 }
2711}
2712
2713/** Resume after 'send Challenge-Request { ... }'
2714 *
2715 */
2716RESUME(send_sim_challenge_request)
2717{
2718 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2719
2720 fr_pair_t *vp;
2722
2723 fr_pair_t *kdf_id;
2724
2726
2727 /*
2728 * Allow override of KDF Identity
2729 *
2730 * Because certain handset manufacturers don't
2731 * implement RFC 4187 correctly and use the
2732 * wrong identity as input the the PRF/KDF.
2733 */
2734 kdf_id = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_aka_sim_kdf_identity);
2735 if (kdf_id) {
2737 (uint8_t const *)kdf_id->vp_strvalue, kdf_id->vp_length);
2739 }
2740
2741 RDEBUG2("Acquiring GSM vector(s)");
2742 if ((fr_aka_sim_vector_gsm_from_attrs(request, &request->control_pairs, 0,
2743 &eap_aka_sim_session->keys, &src) != 0) ||
2745 &eap_aka_sim_session->keys, &src) != 0) ||
2747 &eap_aka_sim_session->keys, &src) != 0)) {
2748 REDEBUG("Failed retrieving SIM vectors");
2750 }
2751
2753
2755
2756 /*
2757 * Indicate we'd like to use protected success messages
2758 * with AT_RESULT_IND
2759 *
2760 * Use our default, but allow user override too.
2761 */
2763 if (vp) eap_aka_sim_session->send_result_ind = vp->vp_bool;
2764
2765 /*
2766 * Okay, we got the challenges! Put them into attributes.
2767 */
2770
2773
2776
2777 /*
2778 * need to include an AT_MAC attribute so that it will get
2779 * calculated.
2780 */
2782 fr_pair_value_memdup(vp, NULL, 0, false);
2783
2784 /*
2785 * We've sent the challenge so the peer should now be able
2786 * to accept encrypted attributes.
2787 */
2789
2791}
2792
2793/** Enter the SIM-CHALLENGE state
2794 *
2795 */
2797{
2798 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2799 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2800 fr_pair_t *vp;
2801
2802 /*
2803 * If we've sent either of these identities it
2804 * means we've come here form a Reauthentication-Request
2805 * that failed.
2806 */
2808 return session_and_pseudonym_clear(p_result, mctx, request,
2809 eap_aka_sim_session, guard_sim_challenge);
2810 /* come back when we're done */
2811 }
2812
2814
2815 /*
2816 * Set the defaults for protected result indicator
2817 */
2821 vp->vp_bool = true;
2822 }
2823
2824 return CALL_SECTION(send_sim_challenge_request);
2825}
2826
2827/** Enter the SIM-CHALLENGE or AKA-CHALLENGE state
2828 *
2829 * Called by functions which are common to both the EAP-SIM and EAP-AKA state machines
2830 * to enter the correct challenge state.
2831 */
2832STATE_GUARD(common_challenge)
2833{
2834 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2835
2836 switch (eap_aka_sim_session->type) {
2837 case FR_EAP_METHOD_SIM:
2839
2840 case FR_EAP_METHOD_AKA:
2843
2844 default:
2845 break;
2846 }
2847
2848 fr_assert(0);
2850}
2851
2852/** Resume after 'recv Identity-Response { ... }' or 'recv AKA-Identity { ... }'
2853 *
2854 * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
2855 * - ...or call a function to process the contents of the AKA-Identity message, mainly the AT_IDENTITY value.
2856 * - If the message does not contain AT_IDENTITY, then enter the FAILURE-NOTIFICATION state.
2857 * - If the user requested another identity, re-enter the AKA-Identity state.
2858 * - ...or continue based on the value of &Identity-Type which was added by #aka_identity,
2859 * and possibly modified by the user.
2860 * - Fastauth - Enter the REAUTHENTICATION state.
2861 * - Pseudonym - Call 'load pseudonym { ... }'
2862 * - Permanent - Enter the CHALLENGE state.
2863 */
2864RESUME(recv_aka_identity_response)
2865{
2866 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2867 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2868
2869 bool user_set_id_req;
2870 fr_pair_t *identity_type;
2871
2873
2874 /*
2875 * See if the user wants us to request another
2876 * identity.
2877 *
2878 * If they set one themselves don't override
2879 * what they set.
2880 */
2882 if ((eap_aka_sim_session->section_result.rcode == RLM_MODULE_NOTFOUND) || user_set_id_req) {
2883 if (!user_set_id_req) {
2885 case AKA_SIM_ANY_ID_REQ:
2887 break;
2888
2891 break;
2892
2893 case AKA_SIM_NO_ID_REQ: /* We always request an ID in AKA-Identity unlike SIM-Start */
2894 case AKA_SIM_INIT_ID_REQ: /* Should not happen */
2895 fr_assert(0);
2897
2899 REDEBUG("Peer sent no usable identities");
2901
2902 }
2903 RDEBUG2("Previous section returned (%s), requesting next most permissive identity (%s)",
2906 eap_aka_sim_session->id_req, "<INVALID>"));
2907 }
2909 }
2910
2911 /*
2912 * If the identity looks like a fast re-auth id
2913 * run fast re-auth, otherwise do fullauth.
2914 */
2915 identity_type = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_identity_type);
2916 if (identity_type) switch (identity_type->vp_uint32) {
2917 case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
2919
2920 /*
2921 * It's a pseudonym, which now needs resolving.
2922 * The resume function here calls aka_challenge_enter
2923 * if pseudonym resolution went ok.
2924 */
2925 case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
2926 eap_aka_sim_session->next = guard_aka_challenge;
2927 return CALL_SECTION(load_pseudonym);
2928
2929 default:
2930 break;
2931 }
2932
2934}
2935
2936/** AKA-IDENTITY state - Continue the state machine after receiving a response to our EAP-Request/AKA-Identity
2937 *
2938 * - Continue based on received AT_SUBTYPE value:
2939 * - EAP-Response/AKA-Identity - call either 'recv Identity-Response { ... }' or if
2940 * provided 'recv AKA-Identity-Response { ... }'. The idea here is that the
2941 * EAP-Identity-Response is really the first round in identity negotiation and
2942 * there's no real value distinguishing between the first round and subsequent
2943 * rounds, but if users do want to run different logic, then give them a way of
2944 * doing that.
2945 * - EAP-Response/AKA-Client-Error - call 'recv Client-Error { ... }' and after that
2946 * send a EAP-Request/SIM-Notification indicating a General Failure.
2947 * - Anything else, enter the FAILURE-NOTIFICATION state.
2948 */
2950{
2951 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
2952 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
2953 fr_pair_t *subtype_vp = NULL;
2954
2956 if (!subtype_vp) {
2957 REDEBUG("Missing AT_SUBTYPE");
2958 goto fail;
2959 }
2960
2961 switch (subtype_vp->vp_uint8) {
2962 /*
2963 * This is the subtype we expect
2964 */
2965 case FR_SUBTYPE_VALUE_AKA_IDENTITY:
2966 {
2967 fr_pair_t *id;
2969
2970 id = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_identity);
2971 if (!id) {
2972 /*
2973 * 9.2. EAP-Response/Identity
2974 *
2975 * The peer sends EAP-Response/Identity in response to a valid
2976 * EAP-Request/Identity from the server.
2977 * The peer MUST include the AT_IDENTITY attribute. The usage of
2978 * AT_IDENTITY is defined in Section 4.1.
2979 */
2980 REDEBUG("EAP-Response/Identity does not contain AT_IDENTITY");
2982 }
2983
2984 /*
2985 * Add ID hint attributes to the request to help
2986 * the user make policy decisions.
2987 */
2988 identity_hint_pairs_add(&type, NULL, request, id->vp_strvalue);
2993 }
2994
2995 /*
2996 * Update cryptographic identity
2997 */
2999 (uint8_t const *)id->vp_strvalue, id->vp_length);
3000
3002 request,
3003 inst->actions.recv_aka_identity_response ?
3004 inst->actions.recv_aka_identity_response:
3005 inst->actions.recv_common_identity_response,
3007 resume_recv_aka_identity_response,
3008 mod_signal,
3011 }
3012
3013 /*
3014 * Case 1 where we're allowed to send an EAP-Failure
3015 *
3016 * This can happen in the case of a conservative
3017 * peer, where it refuses to provide the permanent
3018 * identity.
3019 */
3020 case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
3022
3023 return CALL_SECTION(recv_common_client_error);
3024
3025 default:
3026 /*
3027 * RFC 4187 says we *MUST* notify, not just
3028 * send an EAP-Failure in this case.
3029 */
3030 REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
3031 fail:
3033 }
3034}
3035
3036/** Resume after 'send Identity-Request { ... }'
3037 *
3038 * There are three types of user identities that can be implemented
3039 * - Permanent identities such as 0123456789098765@myoperator.com
3040 * Permanent identities can be identified by the leading zero followed by
3041 * by 15 digits (the IMSI number).
3042 * - Ephemeral identities (pseudonyms). These are identities assigned for
3043 * identity privacy so the user can't be tracked. These can identities
3044 * can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
3045 * document section 14, where a set of up to 16 encryption keys are used
3046 * to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
3047 * randomised and stored in a datastore.
3048 * - A fast resumption ID which resolves to data used for fast resumption.
3049 *
3050 * In order to perform full authentication the original IMSI is required for
3051 * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
3052 * or can't perform fast resumption, we need to request the full identity from
3053 * the supplicant.
3054 *
3055 */
3056RESUME(send_aka_identity_request)
3057{
3058 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3059
3061
3062 /*
3063 * Update eap_aka_sim_session->id_req in case the the
3064 * user set attributes in `send Identity-Request { ... }`
3065 * Also removes all existing id_req attributes
3066 * from the reply.
3067 */
3069
3070 /*
3071 * Select the right type of identity request attribute
3072 *
3073 * Implement checks on identity request order described
3074 * by RFC4187 section #4.1.5.
3075 *
3076 * The internal state machine should always handle this
3077 * correctly, but the user may have other ideas...
3078 */
3081 }
3082 eap_aka_sim_session->last_id_req = eap_aka_sim_session->id_req; /* Record what we last requested */
3083
3084 /*
3085 * Encode the packet
3086 */
3087 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_AKA_IDENTITY);
3088
3090}
3091
3092/** Enter the AKA-IDENTITY state
3093 *
3094 */
3096{
3097 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3098 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3099
3101
3102 /*
3103 * If we have an send_aka_identity_request section
3104 * then run that, otherwise just run the normal
3105 * identity request section.
3106 */
3108 request,
3109 inst->actions.send_aka_identity_request ?
3110 inst->actions.send_aka_identity_request:
3113 resume_send_aka_identity_request,
3114 mod_signal,
3117}
3118
3119/** Resume after 'recv Identity-Response { ... }' or 'recv SIM-Start { ... }'
3120 *
3121 * - If the previous section returned a failure rcode, enter the FAILURE-NOTIFICATION state.
3122 * - ...or call a function to process the contents of the SIM-Start message, mainly the AT_IDENTITY value.
3123 * - If the message does not contain AT_IDENTITY, then enter the FAILURE-NOTIFICATION state.
3124 * - If the user requested another identity, re-enter the SIM-START state.
3125 * - ...or continue based on the value of &Identity-Type which was added by #sim_start,
3126 * and possibly modified by the user.
3127 * - Fastauth
3128 * - If AT_NONCE_MT or AT_SELECTED_VERSION are present, enter the FAILURE-NOTIFICATION state.
3129 * - ...or enter the REAUTHENTICATION state.
3130 * - Pseudonym - Verify selected version and AT_NONCE_MT, then call 'load pseudonym { ... }'
3131 * - Permanent - Verify selected version and AT_NONCE_MT, then enter the CHALLENGE state.
3132 */
3133RESUME(recv_sim_start_response)
3134{
3135 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3136 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3137 bool user_set_id_req;
3138 fr_pair_t *identity_type;
3139
3141
3142 /*
3143 * See if the user wants us to request another
3144 * identity.
3145 *
3146 * If they set one themselves don't override
3147 * what they set.
3148 */
3150 if ((eap_aka_sim_session->section_result.rcode == RLM_MODULE_NOTFOUND) || user_set_id_req) {
3151 if (!user_set_id_req) {
3153 case AKA_SIM_NO_ID_REQ: /* Should not happen */
3155 break;
3156
3157 case AKA_SIM_ANY_ID_REQ:
3159 break;
3160
3163 break;
3164
3165 case AKA_SIM_INIT_ID_REQ: /* Should not happen */
3166 fr_assert(0);
3168
3170 REDEBUG("Peer sent no usable identities");
3171 failure:
3173 }
3174 RDEBUG2("Previous section returned (%s), requesting next most permissive identity (%s)",
3177 eap_aka_sim_session->id_req, "<INVALID>"));
3178 }
3180 }
3181
3182 /*
3183 * If the identity looks like a fast re-auth id
3184 * run fast re-auth, otherwise do fullauth.
3185 */
3186 identity_type = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_identity_type);
3187 if (identity_type) switch (identity_type->vp_uint32) {
3188 case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
3189 /*
3190 * RFC 4186 Section #9.2
3191 *
3192 * The AT_NONCE_MT attribute MUST NOT be included if the AT_IDENTITY
3193 * with a fast re-authentication identity is present for fast
3194 * re-authentication
3195 */
3196 if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_nonce_mt)) {
3197 REDEBUG("AT_NONCE_MT is not allowed in EAP-Response/SIM-Reauthentication messages");
3199 }
3200
3201 /*
3202 * RFC 4186 Section #9.2
3203 *
3204 * The AT_SELECTED_VERSION attribute MUST NOT be included if the
3205 * AT_IDENTITY attribute with a fast re-authentication identity is
3206 * present for fast re-authentication.
3207 */
3208 if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_selected_version)) {
3209 REDEBUG("AT_SELECTED_VERSION is not allowed in EAP-Response/SIM-Reauthentication messages");
3211 }
3212
3214
3215 /*
3216 * It's a pseudonym, which now needs resolving.
3217 * The resume function here calls aka_challenge_enter
3218 * if pseudonym resolution went ok.
3219 */
3220 case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
3223
3224 eap_aka_sim_session->next = guard_sim_challenge;
3225 return CALL_SECTION(load_pseudonym);
3226
3227 /*
3228 * If it's a permanent ID, copy it over to
3229 * the session state list for use in the
3230 * store pseudonym/store session sections
3231 * later.
3232 */
3233 case FR_IDENTITY_TYPE_VALUE_PERMANENT:
3236
3238 default:
3239 break;
3240 }
3241
3243}
3244
3245/** SIM-START state - Continue the state machine after receiving a response to our EAP-Request/SIM-Start
3246 *
3247 * - Continue based on received AT_SUBTYPE value:
3248 * - EAP-Response/SIM-Start - call either 'recv Identity-Response { ... }' or if
3249 * provided 'recv SIM-Start-Response { ... }'. The idea here is that the
3250 * EAP-Identity-Response is really the first round in identity negotiation and
3251 * there's no real value distinguishing between the first round and subsequent
3252 * rounds, but if users do want to run different logic, then give them a way of
3253 * doing that.
3254 * - EAP-Response/SIM-Client-Error - call 'recv Client-Error { ... }' and after that
3255 * send a EAP-Request/SIM-Notification indicating a General Failure.
3256 * - Anything else, enter the FAILURE-NOTIFICATION state.
3257 */
3259{
3260 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3261 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3262 fr_pair_t *subtype_vp = NULL;
3263
3265 if (!subtype_vp) {
3266 REDEBUG("Missing AT_SUBTYPE");
3267 goto fail;
3268 }
3269 switch (subtype_vp->vp_uint8) {
3270 case FR_SUBTYPE_VALUE_SIM_START:
3271 {
3272 eap_session_t *eap_session = eap_session_get(request->parent);
3273 fr_pair_t *id;
3275
3276 id = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_identity);
3277 if (!id && (eap_aka_sim_session->id_req != AKA_SIM_NO_ID_REQ)) {
3278 /*
3279 * RFC 4186 Section #9.2
3280 *
3281 * The peer sends EAP-Response/SIM/Start in response to a valid
3282 * EAP-Request/SIM/Start from the server.
3283 *
3284 * If and only if the server's EAP-Request/SIM/Start includes one of the
3285 * identity-requesting attributes, then the peer MUST include the
3286 * AT_IDENTITY attribute. The usage of AT_IDENTITY is defined in
3287 * Section 4.2.
3288 * The peer MUST include the AT_IDENTITY attribute. The usage of
3289 * AT_IDENTITY is defined in Section 4.1.
3290 */
3291 REDEBUG("EAP-Response/SIM/Start does not contain AT_IDENTITY");
3293 }
3294
3295 /*
3296 * Add ID hint attributes to the request to help
3297 * the user make policy decisions.
3298 */
3299 if (id) {
3300 identity_hint_pairs_add(&type, NULL, request, id->vp_strvalue);
3305 }
3306
3307 /*
3308 * Update cryptographic identity
3309 *
3310 * We only do this if we received a new identity.
3311 */
3313 (uint8_t const *)id->vp_strvalue, id->vp_length);
3314 /*
3315 * If there's no additional identity provided, just
3316 * use eap_session->identity again...
3317 */
3318 } else {
3319 /*
3320 * Copy the EAP-Identity into our Identity
3321 * attribute to make policies easier.
3322 */
3324 fr_pair_value_bstrdup_buffer(id, eap_session->identity, true);
3325
3326 /*
3327 * Add ID hint attributes to the request to help
3328 * the user make policy decisions.
3329 */
3330 identity_hint_pairs_add(&type, NULL, request, eap_session->identity);
3331
3336 }
3337 }
3338
3340 request,
3341 inst->actions.recv_sim_start_response?
3342 inst->actions.recv_sim_start_response:
3343 inst->actions.recv_common_identity_response,
3345 resume_recv_sim_start_response,
3346 mod_signal,
3349 }
3350
3351 /*
3352 * Case 1 where we're allowed to send an EAP-Failure
3353 *
3354 * This can happen in the case of a conservative
3355 * peer, where it refuses to provide the permanent
3356 * identity.
3357 */
3358 case FR_SUBTYPE_VALUE_AKA_SIM_CLIENT_ERROR:
3360
3361 return CALL_SECTION(recv_common_client_error);
3362
3363 default:
3364 /*
3365 * RFC 4187 says we *MUST* notify, not just
3366 * send an EAP-Failure in this case.
3367 */
3368 REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
3369 fail:
3371 }
3372}
3373
3374/** Resume after 'send Start { ... }'
3375 *
3376 * Send a EAP-Request/SIM-Start message to the supplicant
3377 *
3378 * There are three types of user identities that can be implemented
3379 * - Permanent identities such as 0123456789098765@myoperator.com
3380 * Permanent identities can be identified by the leading zero followed by
3381 * by 15 digits (the IMSI number).
3382 * - Ephemeral identities (pseudonyms). These are identities assigned for
3383 * identity privacy so the user can't be tracked. These can identities
3384 * can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
3385 * document section 14, where a set of up to 16 encryption keys are used
3386 * to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
3387 * randomised and stored in a datastore.
3388 * - A fast resumption ID which resolves to data used for fast resumption.
3389 *
3390 * In order to perform full authentication the original IMSI is required for
3391 * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
3392 * or can't perform fast resumption, we need to request the full identity from
3393 * the supplicant.
3394 */
3395RESUME(send_sim_start)
3396{
3397 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3398 fr_pair_t *vp;
3399 uint8_t *p, *end;
3400
3402
3403 p = eap_aka_sim_session->keys.gsm.version_list;
3404 end = p + sizeof(eap_aka_sim_session->keys.gsm.version_list);
3405 eap_aka_sim_session->keys.gsm.version_list_len = 0;
3406
3407 /*
3408 * If the user provided no versions, then
3409 * just add the default (1).
3410 */
3411 if (!(fr_pair_find_by_da(&request->reply_pairs, NULL, attr_eap_aka_sim_version_list))) {
3413 vp->vp_uint16 = EAP_SIM_VERSION;
3414 }
3415
3416 /*
3417 * Iterate over the the versions adding them
3418 * to the version list we use for keying.
3419 */
3420 for (vp = fr_pair_list_head(&request->reply_pairs);
3421 vp;
3422 vp = fr_pair_list_next(&request->reply_pairs, vp)) {
3423 if (vp->da != attr_eap_aka_sim_version_list) continue;
3424
3425 if ((end - p) < 2) break;
3426
3427 /*
3428 * Store as big endian
3429 */
3430 *p++ = (vp->vp_uint16 & 0xff00) >> 8;
3431 *p++ = (vp->vp_uint16 & 0x00ff);
3432 eap_aka_sim_session->keys.gsm.version_list_len += sizeof(uint16_t);
3433 }
3434
3435 /*
3436 * Update eap_aka_sim_session->id_req in case the the
3437 * user set attributes in `send Identity-Request { ... }`
3438 * Also removes all existing id_req attributes
3439 * from the reply.
3440 */
3442
3443 /*
3444 * Select the right type of identity request attribute
3445 *
3446 * Implement checks on identity request order described
3447 * by RFC4186 section #4.2.5.
3448 *
3449 * The internal state machine should always handle this
3450 * correctly, but the user may have other ideas...
3451 */
3454 }
3455 eap_aka_sim_session->last_id_req = eap_aka_sim_session->id_req; /* Record what we last requested */
3456
3457 common_reply(request, eap_aka_sim_session, FR_SUBTYPE_VALUE_SIM_START);
3458
3460}
3461
3462/** Enter the SIM-START state
3463 *
3464 */
3466{
3467 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3468 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3469
3471
3473 request,
3474 inst->actions.send_sim_start_request ?
3475 inst->actions.send_sim_start_request:
3478 resume_send_sim_start,
3479 mod_signal,
3482}
3483
3484/** Enter the SIM-START or AKA-IDENTITY state
3485 *
3486 * Called by functions which are common to both the EAP-SIM and EAP-AKA state machines
3487 * to enter the correct Identity-Request state.
3488 */
3489STATE_GUARD(common_identity)
3490{
3491 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3492
3493 switch (eap_aka_sim_session->type) {
3494 case FR_EAP_METHOD_SIM:
3496
3497 case FR_EAP_METHOD_AKA:
3500
3501 default:
3502 break;
3503 }
3504
3505 fr_assert(0);
3507}
3508
3509/** Resume after 'recv Identity-Response { ... }'
3510 *
3511 * - Perform the majority of eap_aka_sim_session_t initialisation.
3512 * - If 'recv Identity-Response { ... }' returned a failure rcode, enter the FAILURE-NOTIFICATION state.
3513 * - ...or continue based on the identity hint byte in the AT_IDENTITY value or EAP-Identity-Response value:
3514 * - If identity is a pseudonym, call load pseudonym { ... }.
3515 * - If identity is a fastauth identity, enter the REAUTHENTICATE state.
3516 * - If identity is a permanent identity, enter the CHALLENGE state.
3517 */
3518RESUME(recv_common_identity_response)
3519{
3520 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3521 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3522 fr_pair_t *eap_type, *method, *identity_type;
3523 fr_aka_sim_method_hint_t running, hinted;
3524
3526
3527 /*
3528 * Ignore attempts to change the EAP-Type
3529 * This must be done before we enter
3530 * the submodule.
3531 */
3532 eap_type = fr_pair_find_by_da(&request->control_pairs, NULL, attr_eap_type);
3533 if (eap_type) RWDEBUG("Ignoring control.EAP-Type, this must be set *before* the EAP module is called");
3534
3535 method = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_method_hint);
3536
3537 /*
3538 * Set default configuration, we may allow these
3539 * to be toggled by attributes later.
3540 */
3542 eap_aka_sim_session->id_req = AKA_SIM_NO_ID_REQ; /* Set the default */
3543
3544 /*
3545 * Unless AKA-Prime is explicitly disabled,
3546 * use it... It has stronger keying, and
3547 * binds authentication to the network.
3548 */
3549 switch (eap_aka_sim_session->type) {
3550 case FR_EAP_METHOD_SIM:
3551 RDEBUG2("New EAP-SIM session");
3552
3553 running = AKA_SIM_METHOD_HINT_SIM;
3554
3556 eap_aka_sim_session->mac_md = EVP_sha1();
3557 break;
3558
3559 case FR_EAP_METHOD_AKA:
3560 RDEBUG2("New EAP-AKA session");
3561
3562 running = AKA_SIM_METHOD_HINT_AKA;
3563
3565 eap_aka_sim_session->mac_md = EVP_sha1();
3566 break;
3567
3569 RDEBUG2("New EAP-AKA' session");
3570
3572
3575 eap_aka_sim_session->mac_md = EVP_sha256();
3576 break;
3577
3578 default:
3579 fr_assert(0);
3581 }
3582
3583 /*
3584 * Warn the user if the selected identity
3585 * does not match what's hinted.
3586 */
3587 if (method) {
3588 switch (method->vp_uint32) {
3589 case FR_METHOD_HINT_VALUE_AKA_PRIME:
3591 break;
3592
3593 case FR_METHOD_HINT_VALUE_AKA:
3594 hinted = AKA_SIM_METHOD_HINT_AKA;
3595 break;
3596
3597 case FR_METHOD_HINT_VALUE_SIM:
3598 hinted = AKA_SIM_METHOD_HINT_SIM;
3599 break;
3600
3601 default:
3602 hinted = running;
3603 break;
3604 }
3605
3606 if (hinted != running) {
3607 RWDEBUG("EAP-Identity hints that EAP-%s should be started, but we're attempting EAP-%s",
3609 fr_table_str_by_value(fr_aka_sim_id_method_table, running, "<INVALID>"));
3610 }
3611 }
3612
3613 /*
3614 * Unless the user has told us otherwise We always
3615 * start by requesting any ID initially as we can
3616 * always negotiate down.
3617 */
3621 RDEBUG2("Previous section returned (%s), requesting additional identity (%s)",
3624 eap_aka_sim_session->id_req, "<INVALID>"));
3625 } else if (inst->request_identity != AKA_SIM_NO_ID_REQ) {
3627 RDEBUG2("Requesting additional identity (%s)",
3629 eap_aka_sim_session->id_req, "<INVALID>"));
3630 }
3631 }
3632
3633 /*
3634 * For EAP-SIM we _always_ start with a SIM-Start
3635 * for "version negotiation" even if we don't need
3636 * another identity.
3637 */
3639
3640 /*
3641 * User may want us to always request an identity
3642 * initially. The RFCs says this is also the
3643 * better way to operate, as the supplicant
3644 * can 'decorate' the identity in the identity
3645 * response.
3646 *
3647 * For EAP-AKA/EAP-AKA' unless we've been configured
3648 * to always request the identity or it was set
3649 * dynamically, we can save a round of EAP and just
3650 * jump straight into the challenge.
3651 */
3652 if (eap_aka_sim_session->id_req != AKA_SIM_NO_ID_REQ) return STATE_TRANSITION(common_identity);
3653
3654 /*
3655 * If the identity looks like a fast re-auth id
3656 * run fast re-auth, otherwise do a fullauth.
3657 */
3658 identity_type = fr_pair_find_by_da(&request->request_pairs, NULL, attr_eap_aka_sim_identity_type);
3659 if (identity_type) switch (identity_type->vp_uint32) {
3660 case FR_IDENTITY_TYPE_VALUE_FASTAUTH:
3662
3663 /*
3664 * It's a pseudonym, which now needs resolving.
3665 * The resume function here calls aka_challenge_enter
3666 * if pseudonym resolution went ok.
3667 */
3668 case FR_IDENTITY_TYPE_VALUE_PSEUDONYM:
3669 eap_aka_sim_session->next = guard_common_challenge;
3670 return CALL_SECTION(load_pseudonym);
3671
3672 case FR_IDENTITY_TYPE_VALUE_PERMANENT:
3673 default:
3674 break;
3675 }
3676
3677 return STATE_TRANSITION(common_challenge);
3678}
3679
3680/** Enter the EAP-IDENTITY state
3681 *
3682 * - Process the incoming EAP-Identity-Response
3683 * - Start EAP-SIM/EAP-AKA/EAP-AKA' state machine optionally calling 'recv Identity-Response { ... }'
3684 */
3685STATE(init)
3686{
3687 eap_session_t *eap_session = eap_session_get(request->parent);
3688 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3689 eap_aka_sim_session_t *eap_aka_sim_session = talloc_get_type_abort(mctx->rctx, eap_aka_sim_session_t);
3690 fr_pair_t *vp;
3692
3693 /*
3694 * Verify we received an EAP-Response/Identity
3695 * message before the supplicant started sending
3696 * EAP-SIM/AKA/AKA' packets.
3697 */
3698 if (!eap_session->identity) {
3699 REDEBUG("All SIM or AKA exchanges must begin with a EAP-Response/Identity message");
3701 }
3702
3703 /*
3704 * Copy the EAP-Identity into our Identity
3705 * attribute to make policies easier.
3706 */
3709
3710 /*
3711 * Add ID hint attributes to the request to help
3712 * the user make policy decisions.
3713 */
3714 identity_hint_pairs_add(&type, NULL, request, eap_session->identity);
3719
3720 /*
3721 * Set the initial crypto identity from
3722 * the EAP-Identity-Response
3723 */
3725 (uint8_t const *)eap_session->identity,
3726 talloc_strlen(eap_session->identity));
3727
3728 return CALL_SECTION(recv_common_identity_response);
3729}
3730
3731/** Zero out the eap_aka_sim_session when we free it to clear knowledge of secret keys
3732 *
3733 * @param[in] eap_aka_sim_session to free.
3734 * @return 0
3735 */
3737{
3738 memset(eap_aka_sim_session, 0, sizeof(*eap_aka_sim_session));
3739 return 0;
3740}
3741
3742/** Resumes the state machine when receiving a new response packet
3743 *
3744 */
3746{
3747 eap_aka_sim_process_conf_t *inst = talloc_get_type_abort(mctx->mi->data, eap_aka_sim_process_conf_t);
3750 0);
3751 module_ctx_t our_mctx = *mctx;
3752
3753 /*
3754 * A new EAP-SIM/AKA/AKA' session!
3755 */
3756 if (!eap_aka_sim_session) {
3757
3758 /*
3759 * Must be allocated in the NULL ctx as this will
3760 * need to persist over multiple rounds of EAP.
3761 */
3762 MEM(eap_aka_sim_session = talloc_zero(NULL, eap_aka_sim_session_t));
3763 talloc_set_destructor(eap_aka_sim_session, _eap_aka_sim_session_free);
3764
3765 /*
3766 * Add new session data to the request
3767 * We only ever need to do this once as it's restored
3768 * during the next round of EAP automatically.
3769 *
3770 * It will also be freed automatically if the request
3771 * is freed and persistable data hasn't been moved
3772 * into the parent.
3773 */
3775 eap_aka_sim_session, true, true, true) < 0)) {
3776 RPEDEBUG("Failed creating new EAP-SIM/AKA/AKA' session");
3778 }
3780
3781 our_mctx.rctx = eap_aka_sim_session;
3782
3783 return state_init(p_result, &our_mctx, request);
3784 }
3785
3786 /*
3787 * This function is called without a resume ctx as it's the
3788 * entry point for each new round of EAP-AKA.
3789 */
3790 our_mctx.rctx = eap_aka_sim_session;
3791
3793
3794 return eap_aka_sim_session->state(p_result, &our_mctx, request);
3795}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
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 FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
#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_dict_attr_t const * fr_dict_attr_common_parent(fr_dict_attr_t const *a, fr_dict_attr_t const *b, bool is_ancestor)
Find a common ancestor that two TLV type attributes share.
Definition dict_util.c:2365
eap_type
Definition types.h:44
@ FR_EAP_METHOD_SIM
Definition types.h:63
@ FR_EAP_METHOD_AKA
Definition types.h:68
@ FR_EAP_METHOD_AKA_PRIME
Definition types.h:96
enum eap_type eap_type_t
uint8_t * identity
Identity from AT_IDENTITY.
Definition base.h:152
int fr_aka_sim_crypto_umts_kdf_0(fr_aka_sim_keys_t *keys)
Key Derivation Function as described in RFC4187 (EAP-AKA) section 7.
Definition crypto.c:587
int fr_aka_sim_crypto_umts_kdf_1_reauth(fr_aka_sim_keys_t *keys)
Key Derivation Function (Fast-Reauthentication) as described in RFC 5448 (EAP-AKA') section 3....
Definition crypto.c:1168
#define AKA_SIM_VECTOR_UMTS_RAND_SIZE
Definition base.h:73
#define AKA_SIM_VECTOR_GSM_RAND_SIZE
Length of RAND in GSM triplet.
Definition base.h:65
uint8_t * network
Network name (EAP-AKA-Prime only).
Definition base.h:155
size_t mk_len
Definition base.h:212
void fr_aka_sim_crypto_keys_log(request_t *request, fr_aka_sim_keys_t *keys)
Dump the current state of all keys associated with the EAP SIM session.
Definition crypto.c:1255
#define AKA_SIM_VECTOR_GSM_SRES_SIZE
Length of SRES in GSM triplet.
Definition base.h:66
size_t network_len
Length of the network name (EAP-AKA-Prime only).
Definition base.h:156
int fr_aka_sim_crypto_kdf_0_reauth(fr_aka_sim_keys_t *keys)
Key Derivation Function (Fast-Reauthentication) as described in RFC4186/7 (EAP-SIM/AKA) section 7.
Definition crypto.c:1019
int fr_aka_sim_vector_gsm_umts_kdf_0_reauth_from_attrs(request_t *request, fr_pair_list_t *vps, fr_aka_sim_keys_t *keys)
Populate a fr_aka_sim_keys_t structure from attributes in the session-state list.
Definition vector.c:889
#define AKA_PRIME_MK_REAUTH_SIZE
The portion of the MK used for re-auth.
Definition base.h:57
void fr_aka_sim_vector_umts_reauth_clear(fr_aka_sim_keys_t *keys)
Clear reauth data if reauthentication failed.
Definition vector.c:982
int fr_aka_sim_crypto_gsm_kdf_0(fr_aka_sim_keys_t *keys)
Key Derivation Function as described in RFC4186 (EAP-SIM) section 7.
Definition crypto.c:485
size_t identity_len
Length of the identity.
Definition base.h:153
fr_aka_sim_vector_src_t
Where to get EAP-SIM vectors from.
Definition base.h:99
@ AKA_SIM_VECTOR_SRC_AUTO
Discover where to get Triplets from automatically.
Definition base.h:100
#define AKA_SIM_MK_SIZE
Definition base.h:54
uint8_t k_aut[32]
Derived authentication key.
Definition base.h:228
size_t k_aut_len
Length of k_aut. 16 for AKA/SIM, 32 for AKA'.
Definition base.h:229
int fr_aka_sim_crypto_umts_kdf_1(fr_aka_sim_keys_t *keys)
Key Derivation Function as described in RFC 5448 (EAP-AKA') section 3.3.
Definition crypto.c:869
int fr_aka_sim_vector_gsm_from_attrs(request_t *request, fr_pair_list_t *vps, int idx, fr_aka_sim_keys_t *keys, fr_aka_sim_vector_src_t *src)
Retrieve GSM triplets from sets of attributes.
Definition vector.c:328
uint8_t k_encr[16]
Derived encryption key.
Definition base.h:230
int fr_aka_sim_vector_umts_ck_ik_prime_from_attrs(request_t *request, fr_pair_t *ck_prime_vp, fr_pair_t *ik_prime_vp, fr_aka_sim_keys_t *keys)
Validate and copy pre-derived CK'/IK' for EAP-AKA' from caller-supplied pairs.
Definition vector.c:842
#define AKA_SIM_VECTOR_UMTS_AUTN_SIZE
Definition base.h:69
int fr_aka_sim_vector_umts_kdf_1_reauth_from_attrs(request_t *request, fr_pair_list_t *vps, fr_aka_sim_keys_t *keys)
Populate a fr_aka_sim_keys_t structure from attributes in the session-state list.
Definition vector.c:938
uint8_t mk[AKA_SIM_MK_MAX_SIZE]
Master key from session attributes.
Definition base.h:211
uint8_t msk[64]
Derived master session key.
Definition base.h:231
int fr_aka_sim_umts_resync_from_attrs(uint64_t *new_sqn, request_t *request, fr_pair_t *auts_vp, fr_aka_sim_keys_t *keys)
Perform milenage AUTS validation and resynchronisation.
Definition vector.c:1000
int fr_aka_sim_vector_umts_from_attrs(request_t *request, fr_pair_list_t *vps, fr_aka_sim_keys_t *keys, fr_aka_sim_vector_src_t *src)
Retrieve UMTS quintuplets from sets of attributes.
Definition vector.c:760
#define EAP_SIM_VERSION
Definition base.h:76
talloc_free(hp)
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:140
HIDDEN fr_dict_attr_t const * attr_ms_mppe_send_key
Definition base.c:99
HIDDEN fr_dict_attr_t const * attr_ms_mppe_recv_key
Definition base.c:100
HIDDEN fr_dict_attr_t const * attr_eap_type
Definition base.c:90
void eap_add_reply(request_t *request, fr_dict_attr_t const *da, uint8_t const *value, int len)
Definition base.c:345
char * identity
NAI (User-Name) from EAP-Identity.
Definition session.h:56
static eap_session_t * eap_session_get(request_t *request)
Definition session.h:86
eap_type_t type
EAP method number.
Definition session.h:50
Tracks the progress of a single session of any EAP method.
Definition session.h:41
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_kdf
Definition base.c:83
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_counter
Definition base.c:68
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_nonce_s
Definition base.c:92
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_permanent_id_req
Definition base.c:94
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_encr_data
Definition base.c:70
HIDDEN fr_dict_attr_t const * attr_session_data
Definition base.c:104
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_next_reauth_id
Definition base.c:90
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_ck_prime
Definition base.c:66
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_counter_too_small
Definition base.c:69
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_permanent_identity
Definition base.c:95
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_nonce_mt
Definition base.c:91
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_auts
Definition base.c:62
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_kdf_identity
Definition base.c:84
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_k_encr
Definition base.c:80
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_version_list
Definition base.c:102
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_checkcode
Definition base.c:64
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_res
Definition base.c:97
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_k_aut
Definition base.c:79
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_identity
Definition base.c:74
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_client_error_code
Definition base.c:67
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_autn
Definition base.c:61
HIDDEN fr_dict_attr_t const * attr_sim_opc
Definition base.c:116
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_rand
Definition base.c:96
HIDDEN fr_dict_attr_t const * attr_session_id
Definition base.c:105
HIDDEN fr_dict_attr_t const * attr_sim_ki
Definition base.c:114
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_result_ind
Definition base.c:98
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_next_pseudonym
Definition base.c:89
HIDDEN fr_dict_attr_t const * attr_sim_sqn
Definition base.c:117
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_any_id_req
Definition base.c:60
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_mac
Definition base.c:86
HIDDEN fr_dict_attr_t const * attr_sim_amf
Definition base.c:113
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_method_hint
Definition base.c:87
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_selected_version
Definition base.c:99
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_fullauth_id_req
Definition base.c:71
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_subtype
Definition base.c:101
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_ik_prime
Definition base.c:77
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_hmac_extra_request
Definition base.c:72
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_notification
Definition base.c:93
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_identity_type
Definition base.c:75
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_hmac_extra_response
Definition base.c:73
HIDDEN fr_value_box_t const * enum_kdf_prime_with_ck_prime_ik_prime
Definition base.c:193
HIDDEN fr_dict_attr_t const * attr_eap_aka_sim_kdf_input
Definition base.c:85
fr_table_num_sorted_t const fr_aka_sim_id_method_table[]
Definition id.c:43
char fr_aka_sim_hint_byte(fr_aka_sim_id_type_t type, fr_aka_sim_method_hint_t method)
Return the expected identity hint for a given type/method combination.
Definition id.c:375
fr_table_num_sorted_t const fr_aka_sim_id_request_table[]
Definition id.c:33
int fr_aka_sim_id_type(fr_aka_sim_id_type_t *type, fr_aka_sim_method_hint_t *hint, char const *id, size_t id_len)
Determine what type of ID was provided in the initial identity response.
Definition id.c:167
fr_aka_sim_method_hint_t
SIM/AKA method hints.
Definition id.h:39
@ AKA_SIM_METHOD_HINT_AKA
The identity hints the supplicant wants to use EAP-AKA.
Definition id.h:43
@ AKA_SIM_METHOD_HINT_SIM
The identity hints the supplicant wants to use EAP-SIM.
Definition id.h:41
@ AKA_SIM_METHOD_HINT_AKA_PRIME
Definition id.h:45
@ AKA_SIM_METHOD_HINT_UNKNOWN
We don't know what method the identity hints at.
Definition id.h:40
@ AKA_SIM_INIT_ID_REQ
We've requested no ID. This is used for last_id_req.
Definition id.h:78
@ AKA_SIM_NO_ID_REQ
We're not requesting any ID.
Definition id.h:79
@ AKA_SIM_ANY_ID_REQ
Request IMSI, Pseudonym or Fast-reauth.
Definition id.h:80
@ AKA_SIM_FULLAUTH_ID_REQ
Request IMSI or Pseudonym.
Definition id.h:81
@ AKA_SIM_PERMANENT_ID_REQ
Request IMSI.
Definition id.h:82
@ ID_TAG_AKA_PRIME_FASTAUTH
Fastuath, continue EAP-AKA-Prime.
Definition id.h:72
@ ID_TAG_AKA_FASTAUTH
Fastauth, continue EAP-AKA.
Definition id.h:68
@ ID_TAG_SIM_PSEUDONYM
Pseudonym, continue EAP-SIM.
Definition id.h:63
@ ID_TAG_AKA_PRIME_PSEUDONYM
Pseudonym, continue EAP-AKA-Prime.
Definition id.h:71
@ ID_TAG_AKA_PSEUDONYM
Pseudonym, continue EAP-AKA.
Definition id.h:67
@ ID_TAG_SIM_FASTAUTH
Fastauth, continue EAP-SIM.
Definition id.h:64
fr_aka_sim_id_type_t
SIM/AKA identity type hints.
Definition id.h:53
@ AKA_SIM_ID_TYPE_UNKNOWN
We don't know what type of identity this is.
Definition id.h:54
@ AKA_SIM_ID_TYPE_PSEUDONYM
This is a custom pseudonym.
Definition id.h:56
@ AKA_SIM_ID_TYPE_PERMANENT
This is a permanent identity (the IMSI of the SIM).
Definition id.h:55
@ AKA_SIM_ID_TYPE_FASTAUTH
This is a fastauth (session-resumption) id.
Definition id.h:57
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition log.h:460
#define RWDEBUG(fmt,...)
Definition log.h:378
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:352
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RHEXDUMP_INLINE2(_data, _len, _fmt,...)
Definition log.h:704
#define RPEDEBUG(fmt,...)
Definition log.h:393
#define RPWDEBUG2(fmt,...)
Definition log.h:384
#define RINDENT()
Indent R* messages by one level.
Definition log.h:447
unsigned short uint16_t
unsigned char uint8_t
int fr_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL data.
Definition misc.c:516
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
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
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:2906
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition pair.c:791
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:716
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:1306
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:1650
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2756
int fr_pair_delete(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list and free.
Definition pair.c:1787
int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src)
Copy the value from one pair to another.
Definition pair.c:2524
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
static bool done
Definition radclient.c:80
void fr_rand_str(uint8_t *out, size_t len, char class)
Generate a random string.
Definition rand.c:186
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ 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_NOTFOUND
User not found.
Definition rcode.h:53
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
#define RLM_MODULE_USER_SECTION_REJECT
Rcodes that translate to a user configurable section failing overall.
Definition rcode.h:85
#define RETURN_UNLANG_HANDLED
Definition rcode.h:71
#define RETURN_UNLANG_REJECT
Definition rcode.h:68
#define RETURN_UNLANG_OK
Definition rcode.h:70
void * request_data_reference(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request without removing it.
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
#define request_data_add(_request, _unique_ptr, _unique_int, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
void * data
Module's instance data.
Definition module.h:297
unlang_action_t(* module_method_t)(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Module section callback.
Definition module.h:70
#define pair_append_control(_attr, _da)
Allocate and append a fr_pair_t to the control list.
Definition pair.h:57
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition pair.h:129
#define pair_append_session_state(_attr, _da)
Allocate and append a fr_pair_t to session-state list.
Definition pair.h:67
#define pair_append_request(_attr, _da)
Allocate and append a fr_pair_t to the request list.
Definition pair.h:37
#define pair_delete_request(_pair_or_da)
Delete a fr_pair_t in the request list.
Definition pair.h:172
#define pair_update_session_state(_attr, _da)
Return or allocate a fr_pair_t in the session_state list.
Definition pair.h:151
#define pair_delete_reply(_pair_or_da)
Delete a fr_pair_t in the reply list.
Definition pair.h:181
#define pair_append_reply(_attr, _da)
Allocate and append a fr_pair_t to reply list.
Definition pair.h:47
#define pair_delete_control(_pair_or_da)
Delete a fr_pair_t in the control list.
Definition pair.h:190
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition pair.h:140
#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
unlang_action_t unlang_module_yield_to_section(unlang_result_t *p_result, request_t *request, CONF_SECTION *subcs, rlm_rcode_t default_rcode, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Definition module.c:236
static bool identity_req_set_by_user(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session)
Sync up what identity we're requesting with attributes in the reply.
aka_challenge
AKA-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Ch...
#define STATE_NO_RESULT(_x)
static int checkcode_validate(request_t *request)
Check &control.checkcode matches &reply.checkcode.
eap_aka_sim_session_t * eap_aka_sim_session
static int sim_start_selected_version_check(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session)
Helper function to check for the presence and length of AT_SELECTED_VERSION and copy its value into t...
common_success_notification
static int identity_req_pairs_add(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session)
Add an Identity Request attribute to the reply.
static void mod_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Cancel a call to a submodule.
static unlang_action_t common_reauthentication_request_compose(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request, eap_aka_sim_session_t *eap_aka_sim_session)
Send a EAP-Request/(AKA|SIM)-Reauthenticate message to the supplicant.
static int sim_start_nonce_mt_check(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session)
Helper function to check for the presence and length of AT_NONCE_MT and copy its value into the keys ...
static void state_set(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, module_method_t new_state)
Print debugging information, and write new state to eap_aka_sim_session->state.
common_failure_notification
FAILURE-NOTIFICATION state - Continue the state machine after receiving a response to our EAP-Request...
static int mac_validate(request_t *request)
Check &control.mac matches &reply.mac.
static void common_crypto_export(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, uint8_t const *hmac_extra_request, size_t hmac_extra_request_len, uint8_t const *hmac_extra_response, size_t hmac_extra_response_len)
static int identity_to_permanent_identity(request_t *request, fr_pair_t *in, eap_type_t eap_type, bool strip_hint)
Copy the incoming identity to the permanent identity attribute.
static size_t aka_sim_state_table_len
static unlang_action_t session_and_pseudonym_store(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, module_method_t next)
Implements a set of states for storing pseudonym and fastauth identities.
#define STATE_GUARD(_x)
unlang_action_t eap_aka_sim_state_machine_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Resumes the state machine when receiving a new response packet.
fr_pair_t * subtype_vp
#define CALL_SECTION(_x)
aka_identity
AKA-IDENTITY state - Continue the state machine after receiving a response to our EAP-Request/AKA-Ide...
static void identity_hint_pairs_add(fr_aka_sim_id_type_t *type_p, fr_aka_sim_method_hint_t *method_p, request_t *request, char const *identity)
Based on the hint byte in the identity, add &Identity-Type and &Method-Hint attributes.
#define STATE_SET(_new_state)
static unlang_action_t common_reauthentication_request_send(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Called after 'store session { ... }' and 'store pseudonym { ... }'.
static void client_error_debug(request_t *request)
Print out the error the client returned.
static bool after_authentication(eap_aka_sim_session_t *eap_aka_sim_session)
Determine if we're after authentication.
init
Enter the EAP-IDENTITY state.
static void common_reply(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, uint8_t subtype)
Export EAP-SIM/AKA['] attributes.
eap_aka_sim_process_conf_t * inst
fr_pair_value_bstrdup_buffer(vp, eap_session->identity, true)
#define RESUME(_x)
#define EAP_TLS_MPPE_KEY_LEN
static fr_table_ptr_ordered_t const aka_sim_state_table[]
#define SECTION_RCODE_IGNORED
Warn the user that the rcode they provided is being ignored in this section.
static unlang_action_t aka_challenge_request_send(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Called after 'store session { ... }' and 'store pseudonym { ... }'.
static unlang_action_t session_and_pseudonym_clear(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, module_method_t next)
Implements a set of states for clearing out pseudonym and fastauth identities.
static unlang_action_t sim_challenge_request_send(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Called after 'store session { ... }' and 'store pseudonym { ... }'.
#define STATE_GUARD_NO_RESULT(_x)
eap_success
SUCCESS state - State machine exit point after sending EAP-Success.
static int _eap_aka_sim_session_free(eap_aka_sim_session_t *eap_aka_sim_session)
Zero out the eap_aka_sim_session when we free it to clear knowledge of secret keys.
common_reauthentication
REAUTHENTICATION state - Continue the state machine after receiving a response to our EAP-Request/SIM...
static void crypto_identity_set(request_t *request, eap_aka_sim_session_t *eap_aka_sim_session, uint8_t const *identity, size_t len)
Set the crypto identity from a received identity.
#define SECTION_RCODE_PROCESS
Trigger a state transition to FAILURE-NOTIFICATION if the section returned a failure code.
#define STATE(_x)
sim_start
SIM-START state - Continue the state machine after receiving a response to our EAP-Request/SIM-Start.
#define STATE_TRANSITION(_x)
RETURN_UNLANG_FAIL
fr_aka_sim_id_type_t type
fr_pair_t * vp
sim_challenge
SIM-CHALLENGE state - Continue the state machine after receiving a response to our EAP-Request/SIM-Ch...
eap_failure
FAILURE state - State machine exit point after sending EAP-Failure.
Declarations for EAP-AKA.
bool protected_success
Send a success notification as well as and EAP-Success packet.
EVP_MD const * mac_md
HMAC-MD we use to generate the MAC.
eap_aka_sim_actions_t actions
Pre-compiled virtual server sections.
fr_aka_sim_id_req_type_t id_req
The type of identity we're requesting.
CONF_SECTION * load_session
Load cached authentication vectors.
bool challenge_success
Whether we received the correct challenge response.
size_t ephemeral_id_length
The length of any identities we're generating.
eap_type_t type
The preferred EAP-Type of this instance of the EAP-SIM/AKA/AKA' state machine.
fr_aka_sim_keys_t keys
Various EAP-AKA/AKA'/SIMkeys.
fr_aka_sim_id_req_type_t last_id_req
The last identity request we sent.
module_method_t state
The process function to run when we receive the next round of EAP-SIM/AKA/AKA'.
module_method_t next
Resumption function to call after executing common code.
unlang_result_t section_result
The result of the last unlang section we evaluted.
uint16_t failure_type
One of the following values:
char const * network_name
Network ID as described by RFC 5448.
bool allow_encrypted
Whether we can send encrypted attributes at this phase of the attempt.
bool send_result_ind
Say that we would like to use protected result indications (AKA-Notification-Success).
char * pseudonym_sent
Pseudonym value we sent.
bool strip_permanent_identity_hint
Control whether the hint byte is stripped when populating Permanent-Identity.
bool reauthentication_success
Whether we got a valid reauthentication response.
char * fastauth_sent
Fastauth value we sent.
bool prev_recv_sync_failure
We only allow one sync failure per session for sanity.
eap_type_t type
Either FR_TYPE_AKA, or FR_TYPE_AKA_PRIME.
uint16_t kdf
The key derivation function used to derive session keys.
fr_aka_sim_id_req_type_t request_identity
Whether we always request the identity of the subscriber.
CONF_SECTION * load_pseudonym
Resolve a pseudonym to a permanent ID.
CONF_SECTION * send_common_identity_request
Called when we're about to request a different identity.
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:804
An element in an arbitrarily ordered array of name to ptr mappings.
Definition table.h:73
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:693
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
Master include file to access all functions and structures in the library.
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition pair_inline.c:69
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
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition pair.h:285
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
int fr_value_box_bstrdup_buffer(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Copy a nul terminated talloced buffer to a fr_value_box_t.
Definition value.c:5077
int fr_value_box_bstrdup_buffer_shallow(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, bool tainted)
Assign a talloced buffer containing a nul terminated string to a box, but don't copy it.
Definition value.c:5122
int fr_value_box_memdup(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_dict_attr_t const *enumv, uint8_t const *src, size_t len, bool tainted)
Copy a buffer to a fr_value_box_t.
Definition value.c:5255
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
#define fr_box_strvalue_len(_val, _len)
Definition value.h:340
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
int nonnull(2, 5))