The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
base.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
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: bd2be0bbb223bb43ed266af63f4607f6da588b2a $
19 * @file src/process/eap_psk/base.c
20 * @brief The EAP-PSK (RFC 4764) state machine
21 *
22 * Runs the four-message EAP-PSK exchange on a request in the eap-psk
23 * namespace, yielding to the virtual server's policy sections between
24 * protocol steps:
25 *
26 * - send Identity-Request - before the first message; may override
27 * Server-Identity (ID_S).
28 * - recv Identity-Response - after the second message; must supply
29 * control.Password.PSK for the asserted
30 * Identity (ID_P).
31 * - send Result-Indication - before the third message.
32 * - recv Result-Acknowledgement- after the fourth message.
33 * - send Success / send Failure- before the final EAP result.
34 *
35 * Messages which fail validation are not processed, and the reply
36 * Packet-Type is set to Do-Not-Respond. RFC 4764 Section 4.1 calls for
37 * such messages to be silently discarded; a true silent discard cannot
38 * be expressed through the EAP module, which fails the session for any
39 * unhandled round, so an invalid message ends the session with an
40 * EAP-Failure, as with every other EAP method. The Do-Not-Respond
41 * reply code preserves the distinction from a policy-driven Failure
42 * for logging.
43 *
44 * @copyright 2026 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
45 */
46#include <freeradius-devel/eap/base.h>
47#include <freeradius-devel/eap/types.h>
48#include <freeradius-devel/server/process_types.h>
49#include <freeradius-devel/server/virtual_servers.h>
50#include <freeradius-devel/server/pair.h>
51#include <freeradius-devel/unlang/module.h>
52#include <freeradius-devel/util/debug.h>
53#include <freeradius-devel/protocol/eap/psk/freeradius.h>
54
55#include <openssl/rand.h>
56
57#include "crypto.h"
58
59static fr_dict_t const *dict_eap_psk;
61
64 { .out = &dict_eap_psk, .base_dir = "eap/psk", .proto = "eap-psk" },
65 { .out = &dict_freeradius, .proto = "freeradius" },
67};
68
75
78 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_eap_psk },
79 { .out = &attr_identity, .name = "Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_psk },
80 { .out = &attr_server_identity, .name = "Server-Identity", .type = FR_TYPE_STRING, .dict = &dict_eap_psk },
81 { .out = &attr_rand_server, .name = "RAND-Server", .type = FR_TYPE_OCTETS, .dict = &dict_eap_psk },
82 { .out = &attr_rand_peer, .name = "RAND-Peer", .type = FR_TYPE_OCTETS, .dict = &dict_eap_psk },
83 { .out = &attr_psk_password, .name = "Password.PSK", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
85};
86
95
96typedef struct {
97 char const *identity; //!< Default ID_S sent in the first message.
100
102 { FR_CONF_OFFSET("identity", process_eap_psk_t, identity), .dflt = "FreeRADIUS" },
103
105};
106
107/*
108 * Yield to a policy section if the virtual server defines one,
109 * otherwise call the resume function directly. Either way the
110 * resume function sees the session as mctx->rctx.
111 */
112#define YIELD_OR_RESUME(_section, _resume) \
113 do { \
114 module_ctx_t our_mctx; \
115 if (inst->sections._section) { \
116 return unlang_module_yield_to_section(&session->section_result, request, \
117 inst->sections._section, RLM_MODULE_NOOP, \
118 _resume, NULL, 0, session); \
119 } \
120 session->section_result.rcode = RLM_MODULE_NOOP; \
121 our_mctx = *mctx; \
122 our_mctx.rctx = session; \
123 return _resume(p_result, &our_mctx, request); \
124 } while (0)
125
126/*
127 * Write the EAP-PSK message fields into the outgoing eap_round->request.
128 */
129static int eap_psk_compose(eap_round_t *eap_round, uint8_t flags,
130 uint8_t const rand_s[static EAP_PSK_RAND_LEN],
131 uint8_t const *body, size_t body_len)
132{
133 uint8_t *p;
134
135 eap_round->request->code = FR_EAP_CODE_REQUEST;
136 eap_round->request->type.num = FR_EAP_METHOD_PSK;
137 eap_round->request->type.length = 1 + EAP_PSK_RAND_LEN + body_len;
138
139 MEM(eap_round->request->type.data = talloc_array(eap_round->request, uint8_t,
140 eap_round->request->type.length));
141
142 p = eap_round->request->type.data;
143 *p++ = flags;
144
145 memcpy(p, rand_s, EAP_PSK_RAND_LEN);
146 p += EAP_PSK_RAND_LEN;
147 if (body_len > 0) memcpy(p, body, body_len);
148
149 return 0;
150}
151
152/*
153 * Reconstruct the 22-byte EAX Header H for an EAP-PSK packet. H is the
154 * first 22 bytes of the EAP packet on the wire. The 4-byte EAP header
155 * (Code, Id, Length), the 1-byte EAP Type, the 1-byte EAP-PSK Flags,
156 * and the 16-byte RAND_S.
157 */
158static void eap_psk_header(uint8_t header[static EAP_PSK_HEADER_LEN],
159 uint8_t code, uint8_t id, size_t type_length,
160 uint8_t flags, uint8_t const rand_s[static EAP_PSK_RAND_LEN])
161{
162 /*
163 * The EAP Length is the 4-byte header + 1-byte Type + the
164 * type-specific data. See eap_wireformat().
165 */
166 uint16_t eap_len = (uint16_t) (4 + 1 + type_length);
167
168 header[0] = code;
169 header[1] = id;
170 header[2] = (uint8_t) (eap_len >> 8);
171 header[3] = (uint8_t) (eap_len & 0xff);
172 header[4] = FR_EAP_METHOD_PSK;
173 header[5] = flags;
174
175 memcpy(header + 6, rand_s, EAP_PSK_RAND_LEN);
176}
177
178/*
179 * Reject an invalid message. The message is not processed, and the
180 * reply Packet-Type is set to Do-Not-Respond, which rlm_eap_psk
181 * translates into session failure (see the file header for why a
182 * true RFC 4764 Section 4.1 silent discard is not possible here).
183 *
184 * If policy has already set reply.Packet-Type := ::Failure, report
185 * the explicit failure code instead.
186 */
188{
189 fr_pair_t *vp;
190
191 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
192 if (vp && (vp->vp_uint32 == FR_PACKET_TYPE_VALUE_FAILURE)) {
193 RDEBUG2("Policy overrides the silent discard, failing explicitly");
194 session->state = EAP_PSK_STATE_FAILED;
195 request->reply->code = FR_PACKET_TYPE_VALUE_FAILURE;
197 }
198
199 RDEBUG2("Silently discarding invalid message");
200 request->reply->code = FR_PACKET_TYPE_VALUE_DO_NOT_RESPOND;
202}
203
204/*
205 * Fail the authentication explicitly: run 'send Failure' if the
206 * virtual server defines one, then report Failure to the submodule.
207 */
209 request_t *request)
210{
211 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
212
213 session->state = EAP_PSK_STATE_FAILED;
214 request->reply->code = FR_PACKET_TYPE_VALUE_FAILURE;
216}
217
220{
222}
223
224/** Compose the first message once 'send Identity-Request' has run
225 *
226 * Policy may override the ID_S sent to the peer by setting
227 * reply.Server-Identity.
228 */
230 request_t *request)
231{
233 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
234 eap_session_t *eap_session = eap_session_get(request->parent);
235 fr_pair_t *vp;
236 char const *id_s;
237
238 /*
239 * If policy didn't provide a Server-Identity, seed the reply
240 * pair with the configured default, then always read the
241 * reply pair. Same pattern as network_name / KDF-Input in
242 * EAP-AKA'.
243 */
244 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_server_identity);
245 if (!vp) {
247 fr_pair_value_strdup(vp, inst->identity, false);
248 }
249 id_s = vp->vp_strvalue;
250
251 if (id_s[0] == '\0') {
252 REDEBUG("Server-Identity (ID_S) must not be empty");
254 }
255
256 MEM(session->id_s = talloc_strdup(session, id_s));
257
258 RDEBUG2("Sending first message as \"%s\"", session->id_s);
259
260 if (eap_psk_compose(eap_session->this_round, EAP_PSK_FLAGS_FIRST, session->rand_s,
261 (uint8_t const *) session->id_s, strlen(session->id_s)) < 0) RETURN_UNLANG_FAIL;
262
264 request->reply->code = FR_PACKET_TYPE_VALUE_SUCCESS;
265
267}
268
269/** Start the conversation: generate RAND_S and run 'send Identity-Request'
270 *
271 */
273 request_t *request)
274{
276 eap_session_t *eap_session = eap_session_get(request->parent);
277 eap_psk_session_t *session;
278 fr_pair_t *vp;
279
280 MEM(session = talloc_zero(eap_session, eap_psk_session_t));
281 eap_session->opaque = session;
282
283 /*
284 * Get a fresh random server nonce. RFC 4764 requires a
285 * cryptographic-quality RNG here, so we use OpenSSL.
286 */
287 if (RAND_bytes(session->rand_s, sizeof(session->rand_s)) != 1) {
288 REDEBUG("Failed generating RAND_S");
290 }
291
293 fr_pair_value_memdup(vp, session->rand_s, sizeof(session->rand_s), false);
294
295 YIELD_OR_RESUME(send_identity_request, resume_send_identity_request);
296}
297
298/** Compose the third message once 'send Result-Indication' has run
299 *
300 */
302 request_t *request)
303{
304 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
305 fr_pair_t *vp;
306
307 vp = fr_pair_find_by_da(&request->reply_pairs, NULL, attr_packet_type);
308 if (vp && (vp->vp_uint32 == FR_PACKET_TYPE_VALUE_FAILURE)) {
310
311 RDEBUG2("Policy rejected the peer");
312 return eap_psk_failure(p_result, mctx, request, inst, session);
313 }
314
316 request->reply->code = FR_PACKET_TYPE_VALUE_SUCCESS;
317
319}
320
321/** Verify MAC_P and build the third message once 'recv Identity-Response' has run
322 *
323 * The policy section is responsible for populating control.Password.PSK
324 * for the asserted Identity (ID_P).
325 */
327 request_t *request)
328{
330 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
331 eap_session_t *eap_session = eap_session_get(request->parent);
332 eap_round_t *eap_round = eap_session->this_round;
333 fr_pair_t *known_good;
334
336 uint8_t expected[EAP_PSK_MAC_LEN], mac_s[EAP_PSK_MAC_LEN];
339 uint8_t plain, cipher;
340 uint8_t *p;
341 uint32_t nonce = 0;
342
343 switch (session->section_result.rcode) {
346 RDEBUG2("Policy rejected the peer");
347 return eap_psk_failure(p_result, mctx, request, inst, session);
348
349 case RLM_MODULE_FAIL:
351
352 default:
353 break;
354 }
355
356 /*
357 * No PSK for the asserted identity. Discard by default so
358 * that identity probing is not possible; policy may override
359 * with reply.Packet-Type := ::Failure.
360 */
361 known_good = fr_pair_find_by_da_nested(&request->control_pairs, NULL, attr_psk_password);
362 if (!known_good) {
363 RDEBUG2("No control.Password.PSK for Identity \"%pV\"",
364 fr_box_strvalue_len((char const *) session->id_p, session->id_p_len));
365 return eap_psk_discard(p_result, request, session);
366 }
367
368 /*
369 * RFC 4764 Section 1.2 - the PSK is exactly 16 octets. Anything
370 * else is a misconfiguration, and must not be silently padded
371 * or truncated.
372 */
373 if (known_good->vp_length != EAP_PSK_PSK_LEN) {
374 REDEBUG("Password.PSK must be exactly %d octets, got %zu octets",
375 EAP_PSK_PSK_LEN, known_good->vp_length);
376 return eap_psk_failure(p_result, mctx, request, inst, session);
377 }
378
379 /*
380 * Key setup, then verify MAC_P before doing anything else.
381 */
382 if (eap_psk_derive_ak_kdk(ak, kdk, known_good->vp_octets) < 0) {
383 REDEBUG("Key setup failed");
385 }
386
387 if (eap_psk_mac_p(expected, ak,
388 session->id_p, session->id_p_len,
389 (uint8_t const *) session->id_s, strlen(session->id_s),
390 session->rand_s, session->rand_p) < 0) {
391 REDEBUG("MAC_P computation failed");
393 }
394
395 if (fr_digest_cmp(expected, session->mac_p, EAP_PSK_MAC_LEN) != 0) {
396 RDEBUG2("MAC_P is incorrect: the peer used the wrong key");
397 return eap_psk_discard(p_result, request, session);
398 }
399
400 RDEBUG2("Peer authenticated (MAC_P valid)");
401
402 /*
403 * Derive the session keys and compute MAC_S for the peer.
404 */
405 if (eap_psk_derive_keys(session->tek, session->msk, session->emsk,
406 kdk, session->rand_p) < 0) {
407 REDEBUG("Session-key derivation failed");
409 }
410
411 if (eap_psk_mac_s(mac_s, ak,
412 (uint8_t const *) session->id_s, strlen(session->id_s),
413 session->rand_p) < 0) {
414 REDEBUG("MAC_S computation failed");
416 }
417
418 /*
419 * Build the third message body: MAC_S || PCHANNEL.
420 *
421 * The PCHANNEL protects the 22-byte header H, which depends on
422 * the EAP Id and Length of THIS reply. Pin the Id so that the
423 * value we authenticate matches what goes on the wire.
424 */
425 eap_round->request->id = eap_round->response->id + 1;
426 eap_round->set_request_id = true;
427
428 eap_psk_header(header, FR_EAP_CODE_REQUEST, eap_round->request->id,
430 EAP_PSK_FLAGS_THIRD, session->rand_s);
431
432 /*
433 * The single protected byte carries R = DONE_SUCCESS, E = 0.
434 */
436
437 p = body;
438 memcpy(p, mac_s, EAP_PSK_MAC_LEN);
439 p += EAP_PSK_MAC_LEN;
440
441 /*
442 * PCHANNEL = Nonce(4, big-endian) || Tag(16) || EncryptedPayload(1)
443 */
444 *p++ = (uint8_t) (nonce >> 24);
445 *p++ = (uint8_t) (nonce >> 16);
446 *p++ = (uint8_t) (nonce >> 8);
447 *p++ = (uint8_t) (nonce);
448
449 if (eap_psk_pchannel_encrypt(&cipher, p, session->tek, nonce,
450 header, sizeof(header), &plain, 1) < 0) {
451 REDEBUG("Protected-channel encryption failed");
453 }
454 p += EAP_PSK_TAG_LEN;
455 *p++ = cipher;
456
457 if (eap_psk_compose(eap_round, EAP_PSK_FLAGS_THIRD, session->rand_s,
459
460 YIELD_OR_RESUME(send_result_indication, resume_send_result_indication);
461}
462
463/** Parse the second message and run 'recv Identity-Response'
464 *
465 */
467 request_t *request)
468{
470 eap_session_t *eap_session = eap_session_get(request->parent);
471 eap_psk_session_t *session = talloc_get_type_abort(eap_session->opaque, eap_psk_session_t);
472 eap_round_t *eap_round = eap_session->this_round;
473 uint8_t const *in = eap_round->response->type.data;
474 size_t in_len = eap_round->response->type.length;
475
476 uint8_t const *rand_s_echo, *rand_p, *mac_p, *id_p;
477 size_t id_p_len;
478 fr_pair_t *vp;
479
480 /*
481 * Flags(1) || RAND_S(16) || RAND_P(16) || MAC_P(16) || ID_P(*)
482 */
483 if (in_len < (size_t) (1 + EAP_PSK_RAND_LEN + EAP_PSK_RAND_LEN + EAP_PSK_MAC_LEN)) {
484 RDEBUG2("Second message is too short");
485 return eap_psk_discard(p_result, request, session);
486 }
487
488 if ((in[0] & EAP_PSK_T_MASK) != EAP_PSK_FLAGS_SECOND) {
489 RDEBUG2("Second message has the wrong T flag");
490 return eap_psk_discard(p_result, request, session);
491 }
492
493 rand_s_echo = in + 1;
494 rand_p = rand_s_echo + EAP_PSK_RAND_LEN;
495 mac_p = rand_p + EAP_PSK_RAND_LEN;
496 id_p = mac_p + EAP_PSK_MAC_LEN;
497 id_p_len = in_len - (1 + EAP_PSK_RAND_LEN + EAP_PSK_RAND_LEN + EAP_PSK_MAC_LEN);
498
499 /*
500 * The peer must echo the RAND_S we sent. Constant-time compare.
501 */
502 if (fr_digest_cmp(rand_s_echo, session->rand_s, EAP_PSK_RAND_LEN) != 0) {
503 RDEBUG2("Second message did not echo RAND_S");
504 return eap_psk_discard(p_result, request, session);
505 }
506
507 if (id_p_len > EAP_PSK_MAX_ID_P_LEN) {
508 RDEBUG2("ID_P is longer than the %u bytes allowed by RFC 4764", EAP_PSK_MAX_ID_P_LEN);
509 return eap_psk_discard(p_result, request, session);
510 }
511
512 memcpy(session->rand_p, rand_p, sizeof(session->rand_p));
513 memcpy(session->mac_p, mac_p, sizeof(session->mac_p));
514 talloc_free(session->id_p);
515 MEM(session->id_p = talloc_memdup(session, id_p, id_p_len));
516 session->id_p_len = id_p_len;
517
518 /*
519 * Expose the exchange to policy.
520 */
522 fr_pair_value_bstrndup(vp, (char const *) id_p, id_p_len, true);
523
525 fr_pair_value_strdup(vp, session->id_s, false);
526
528 fr_pair_value_memdup(vp, session->rand_s, sizeof(session->rand_s), false);
529
531 fr_pair_value_memdup(vp, session->rand_p, sizeof(session->rand_p), false);
532
533 YIELD_OR_RESUME(recv_identity_response, resume_recv_identity_response);
534}
535
536/** Finish successfully once 'send Success' has run
537 *
538 */
540 request_t *request)
541{
542 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
543
544 session->state = EAP_PSK_STATE_DONE;
545 request->reply->code = FR_PACKET_TYPE_VALUE_SUCCESS;
546
548}
549
550/** Verify the protected result once 'recv Result-Acknowledgement' has run
551 *
552 */
554 request_t *request)
555{
557 eap_psk_session_t *session = talloc_get_type_abort(mctx->rctx, eap_psk_session_t);
558 eap_session_t *eap_session = eap_session_get(request->parent);
559 eap_round_t *eap_round = eap_session->this_round;
560 uint8_t const *in = eap_round->response->type.data;
561 size_t in_len = eap_round->response->type.length;
562
563 uint8_t const *pchannel, *tag, *cipher;
565 uint8_t plain;
566 uint32_t nonce = 1;
567 int r;
568
569 switch (session->section_result.rcode) {
572 RDEBUG2("Policy rejected the peer");
573 return eap_psk_failure(p_result, mctx, request, inst, session);
574
575 case RLM_MODULE_FAIL:
577
578 default:
579 break;
580 }
581
582 pchannel = in + 1 + EAP_PSK_RAND_LEN;
583 tag = pchannel + EAP_PSK_NONCE_LEN;
584 cipher = tag + EAP_PSK_TAG_LEN;
585
586 /*
587 * Reconstruct the header H over the RECEIVED packet, then verify
588 * the tag and decrypt the single result byte.
589 */
590 eap_psk_header(header, FR_EAP_CODE_RESPONSE, eap_round->response->id,
591 in_len, in[0], session->rand_s);
592
593 if (eap_psk_pchannel_decrypt(&plain, session->tek, nonce,
594 header, sizeof(header), cipher, 1, tag) < 0) {
595 RDEBUG2("Protected-channel verification failed: mutual authentication failed");
596 return eap_psk_discard(p_result, request, session);
597 }
598
599 r = EAP_PSK_R(plain);
600 if (r != EAP_PSK_R_DONE_SUCCESS) {
601 RDEBUG2("Peer reported result %d (not success)", r);
602 return eap_psk_failure(p_result, mctx, request, inst, session);
603 }
604
605 RDEBUG2("Mutual authentication succeeded");
606
607 /*
608 * The rlm_eap_psk submodule delivers the keying material
609 * (session->msk) to the outer protocol once the session
610 * reaches EAP_PSK_STATE_DONE.
611 */
612 eap_round->request->code = FR_EAP_CODE_SUCCESS;
613 eap_round->request->type.length = 0;
614
616}
617
618/** Parse the fourth message and run 'recv Result-Acknowledgement'
619 *
620 */
622 request_t *request)
623{
625 eap_session_t *eap_session = eap_session_get(request->parent);
626 eap_psk_session_t *session = talloc_get_type_abort(eap_session->opaque, eap_psk_session_t);
627 eap_round_t *eap_round = eap_session->this_round;
628 uint8_t const *in = eap_round->response->type.data;
629 size_t in_len = eap_round->response->type.length;
630 uint8_t const *pchannel;
631
632 /*
633 * Flags(1) || RAND_S(16) || PCHANNEL(21)
634 */
635 if (in_len < (size_t) (1 + EAP_PSK_RAND_LEN + EAP_PSK_PCHANNEL_LEN)) {
636 RDEBUG2("Fourth message is too short");
637 return eap_psk_discard(p_result, request, session);
638 }
639
640 if ((in[0] & EAP_PSK_T_MASK) != EAP_PSK_FLAGS_FOURTH) {
641 RDEBUG2("Fourth message has the wrong T flag");
642 return eap_psk_discard(p_result, request, session);
643 }
644
645 if (fr_digest_cmp(in + 1, session->rand_s, EAP_PSK_RAND_LEN) != 0) {
646 RDEBUG2("Fourth message did not echo RAND_S");
647 return eap_psk_discard(p_result, request, session);
648 }
649
650 /*
651 * The peer's PCHANNEL nonce for the fourth message is 1.
652 * RFC 4764 Section 3.3.
653 */
654 pchannel = in + 1 + EAP_PSK_RAND_LEN;
655 if ((pchannel[0] != 0) || (pchannel[1] != 0) || (pchannel[2] != 0) || (pchannel[3] != 1)) {
656 RDEBUG2("Fourth message has an unexpected Nonce");
657 return eap_psk_discard(p_result, request, session);
658 }
659
660 YIELD_OR_RESUME(recv_result_acknowledgement, resume_recv_result_acknowledgement);
661}
662
663/** Dispatch to the state matching the packet type set by rlm_eap_psk
664 *
665 */
666static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
667{
668 eap_session_t *eap_session;
669 eap_psk_session_t *session;
670
672
673 request->component = "eap-psk";
674 request->module = NULL;
675 fr_assert(request->proto_dict == dict_eap_psk);
676
677 if (!request->parent || !(eap_session = eap_session_get(request->parent))) {
678 REDEBUG("EAP-PSK requests must be run from within the EAP module");
680 }
681
682 switch (request->packet->code) {
683 case FR_PACKET_TYPE_VALUE_IDENTITY_REQUEST:
684 if (eap_session->opaque) goto bad_state;
685 return state_identity_request(p_result, mctx, request);
686
687 case FR_PACKET_TYPE_VALUE_IDENTITY_RESPONSE:
688 session = talloc_get_type_abort(eap_session->opaque, eap_psk_session_t);
689 if (session->state != EAP_PSK_STATE_IDENTITY_REQUEST_SENT) goto bad_state;
690 return state_identity_response(p_result, mctx, request);
691
692 case FR_PACKET_TYPE_VALUE_RESULT_ACKNOWLEDGEMENT:
693 session = talloc_get_type_abort(eap_session->opaque, eap_psk_session_t);
694 if (session->state != EAP_PSK_STATE_RESULT_INDICATION_SENT) goto bad_state;
695 return state_result_acknowledgement(p_result, mctx, request);
696
697 default:
698 bad_state:
699 REDEBUG("Invalid packet code %u for the current session state", request->packet->code);
701 }
702}
703
705 {
706 .section = SECTION_NAME("send", "Identity-Request"),
707 .actions = &mod_actions_authorize,
708 .offset = offsetof(process_eap_psk_t, sections.send_identity_request)
709 },
710 {
711 .section = SECTION_NAME("recv", "Identity-Response"),
713 .offset = offsetof(process_eap_psk_t, sections.recv_identity_response)
714 },
715 {
716 .section = SECTION_NAME("send", "Result-Indication"),
718 .offset = offsetof(process_eap_psk_t, sections.send_result_indication)
719 },
720 {
721 .section = SECTION_NAME("recv", "Result-Acknowledgement"),
723 .offset = offsetof(process_eap_psk_t, sections.recv_result_acknowledgement)
724 },
725 {
726 .section = SECTION_NAME("send", "Success"),
728 .offset = offsetof(process_eap_psk_t, sections.send_success)
729 },
730 {
731 .section = SECTION_NAME("send", "Failure"),
733 .offset = offsetof(process_eap_psk_t, sections.send_failure)
734 },
736};
737
740 .common = {
741 .magic = MODULE_MAGIC_INIT,
742 .name = "eap_psk",
743 .inst_size = sizeof(process_eap_psk_t),
744 .inst_type = "process_eap_psk_t",
746 },
747 .process = mod_process,
748 .compile_list = compile_list,
749 .dict = &dict_eap_psk,
750 .packet_type = &attr_packet_type
751};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
eap_type_data_t type
Definition compose.h:38
bool set_request_id
Whether the EAP-Method already set the next request ID.
Definition compose.h:50
eap_packet_t * response
Packet we received from the peer.
Definition compose.h:48
eap_code_t code
Definition compose.h:35
uint8_t id
Definition compose.h:36
eap_packet_t * request
Packet we will send to the peer.
Definition compose.h:49
Contains a pair of request and response packets.
Definition compose.h:47
Constants, session state, and crypto declarations for EAP-PSK (RFC 4764)
#define EAP_PSK_T_MASK
Definition crypto.h:49
#define EAP_PSK_MAX_ID_P_LEN
Definition crypto.h:83
uint8_t emsk[EAP_PSK_EMSK_LEN]
Definition crypto.h:112
@ EAP_PSK_STATE_FAILED
Definition crypto.h:90
@ EAP_PSK_STATE_RESULT_INDICATION_SENT
Definition crypto.h:88
@ EAP_PSK_STATE_DONE
Definition crypto.h:89
@ EAP_PSK_STATE_IDENTITY_REQUEST_SENT
Definition crypto.h:87
#define EAP_PSK_PCHANNEL_LEN
Definition crypto.h:71
#define EAP_PSK_TAG_LEN
Definition crypto.h:43
#define EAP_PSK_FLAGS_THIRD
Definition crypto.h:52
uint8_t rand_s[EAP_PSK_RAND_LEN]
Definition crypto.h:106
uint8_t tek[EAP_PSK_TEK_LEN]
Definition crypto.h:110
#define EAP_PSK_R(_b)
Definition crypto.h:64
uint8_t mac_p[EAP_PSK_MAC_LEN]
As received, verified once the PSK is known.
Definition crypto.h:108
#define EAP_PSK_PAYLOAD(_r)
Definition crypto.h:65
#define EAP_PSK_FLAGS_SECOND
Definition crypto.h:51
#define EAP_PSK_MAC_LEN
Definition crypto.h:35
eap_psk_state_t state
Definition crypto.h:99
char * id_s
ID_S actually sent in the first message.
Definition crypto.h:102
#define EAP_PSK_PSK_LEN
Definition crypto.h:36
#define EAP_PSK_HEADER_LEN
Definition crypto.h:78
#define EAP_PSK_AK_LEN
Definition crypto.h:37
uint8_t msk[EAP_PSK_MSK_LEN]
Definition crypto.h:111
#define EAP_PSK_R_DONE_SUCCESS
Definition crypto.h:61
#define EAP_PSK_FLAGS_FIRST
Definition crypto.h:50
#define EAP_PSK_FLAGS_FOURTH
Definition crypto.h:53
uint8_t rand_p[EAP_PSK_RAND_LEN]
Definition crypto.h:107
uint8_t * id_p
ID_P the peer asserted in the second message.
Definition crypto.h:103
#define EAP_PSK_NONCE_LEN
Definition crypto.h:42
#define EAP_PSK_RAND_LEN
Definition crypto.h:34
unlang_result_t section_result
Result of the last policy section.
Definition crypto.h:100
size_t id_p_len
Definition crypto.h:104
#define EAP_PSK_KDK_LEN
Definition crypto.h:38
#define MEM(x)
Definition debug.h:36
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
@ FR_EAP_CODE_RESPONSE
Definition types.h:38
@ FR_EAP_CODE_REQUEST
Definition types.h:37
@ FR_EAP_CODE_SUCCESS
Definition types.h:39
eap_type_t num
Definition types.h:110
size_t length
Definition types.h:111
uint8_t * data
Definition types.h:112
@ FR_EAP_METHOD_PSK
Definition types.h:93
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
static fr_dict_t const * dict_freeradius
Definition base.c:37
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
static eap_session_t * eap_session_get(request_t *request)
Definition session.h:85
void * opaque
Opaque data used by EAP methods.
Definition session.h:63
eap_round_t * this_round
The EAP response we're processing, and the EAP request we're building.
Definition session.h:60
Tracks the progress of a single session of any EAP method.
Definition session.h:41
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_OCTETS
Raw octets.
unsigned int uint32_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:504
unlang_mod_actions_t const mod_actions_authorize
Definition mod_action.c:45
unlang_mod_action_t actions[RLM_MODULE_NUMCODES]
Definition mod_action.h:69
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:2962
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, 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:784
int fr_pair_value_strdup(fr_pair_t *vp, char const *src, bool tainted)
Copy data into an "string" data type.
Definition pair.c:2663
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
int fr_pair_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:2812
static unlang_action_t mod_process(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:168
static const virtual_server_compile_t compile_list[]
Definition base.c:192
static conf_parser_t submodule_config[]
Definition base.c:37
fr_process_module_t process_eap_psk
Definition base.c:739
static unlang_action_t resume_send_identity_request(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Compose the first message once 'send Identity-Request' has run.
Definition base.c:229
static int eap_psk_compose(eap_round_t *eap_round, uint8_t flags, uint8_t const rand_s[static EAP_PSK_RAND_LEN], uint8_t const *body, size_t body_len)
Definition base.c:129
static void eap_psk_header(uint8_t header[static EAP_PSK_HEADER_LEN], uint8_t code, uint8_t id, size_t type_length, uint8_t flags, uint8_t const rand_s[static EAP_PSK_RAND_LEN])
Definition base.c:158
static fr_dict_attr_t const * attr_rand_server
Definition base.c:72
static unlang_action_t resume_recv_identity_response(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Verify MAC_P and build the third message once 'recv Identity-Response' has run.
Definition base.c:326
CONF_SECTION * send_identity_request
Definition base.c:88
static fr_dict_t const * dict_eap_psk
Definition base.c:59
char const * identity
Default ID_S sent in the first message.
Definition base.c:97
#define YIELD_OR_RESUME(_section, _resume)
Definition base.c:112
static unlang_action_t state_identity_request(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Start the conversation: generate RAND_S and run 'send Identity-Request'.
Definition base.c:272
process_eap_psk_sections_t sections
Definition base.c:98
CONF_SECTION * recv_identity_response
Definition base.c:89
static unlang_action_t resume_send_success(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Finish successfully once 'send Success' has run.
Definition base.c:539
static fr_dict_attr_t const * attr_server_identity
Definition base.c:71
CONF_SECTION * send_result_indication
Definition base.c:90
CONF_SECTION * send_failure
Definition base.c:93
static unlang_action_t state_result_acknowledgement(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Parse the fourth message and run 'recv Result-Acknowledgement'.
Definition base.c:621
fr_dict_attr_autoload_t process_eap_psk_dict_attr[]
Definition base.c:77
static unlang_action_t eap_psk_failure(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request, process_eap_psk_t const *inst, eap_psk_session_t *session)
Definition base.c:218
static unlang_action_t resume_recv_result_acknowledgement(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Verify the protected result once 'recv Result-Acknowledgement' has run.
Definition base.c:553
fr_dict_autoload_t process_eap_psk_dict[]
Definition base.c:63
static fr_dict_attr_t const * attr_identity
Definition base.c:70
static fr_dict_attr_t const * attr_rand_peer
Definition base.c:73
CONF_SECTION * recv_result_acknowledgement
Definition base.c:91
static unlang_action_t resume_send_result_indication(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Compose the third message once 'send Result-Indication' has run.
Definition base.c:301
static unlang_action_t eap_psk_discard(unlang_result_t *p_result, request_t *request, eap_psk_session_t *session)
Definition base.c:187
static fr_dict_attr_t const * attr_psk_password
Definition base.c:74
static unlang_action_t state_identity_response(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Parse the second message and run 'recv Identity-Response'.
Definition base.c:466
CONF_SECTION * send_success
Definition base.c:92
static unlang_action_t resume_send_failure(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition base.c:208
int eap_psk_derive_keys(uint8_t tek[static EAP_PSK_TEK_LEN], uint8_t msk[static EAP_PSK_MSK_LEN], uint8_t emsk[static EAP_PSK_EMSK_LEN], uint8_t const kdk[static EAP_PSK_KDK_LEN], uint8_t const rand_p[static EAP_PSK_RAND_LEN])
Session-key derivation: expand RAND_P under KDK into nine output blocks.
Definition crypto.c:194
int eap_psk_mac_s(uint8_t mac_s[static EAP_PSK_MAC_LEN], uint8_t const ak[static EAP_PSK_AK_LEN], uint8_t const *id_s, size_t id_s_len, uint8_t const rand_p[static EAP_PSK_RAND_LEN])
Compute MAC_S = CMAC-AES-128(AK, ID_S || RAND_P)
Definition crypto.c:311
int eap_psk_pchannel_encrypt(uint8_t *cipher, uint8_t tag[static EAP_PSK_TAG_LEN], uint8_t const tek[static EAP_PSK_TEK_LEN], uint32_t nonce, uint8_t const *header, size_t header_len, uint8_t const *plain, size_t plain_len)
EAX encrypt for the protected channel (RFC 4764 Section 3.3)
Definition crypto.c:395
int eap_psk_mac_p(uint8_t mac_p[static EAP_PSK_MAC_LEN], uint8_t const ak[static EAP_PSK_AK_LEN], uint8_t const *id_p, size_t id_p_len, uint8_t const *id_s, size_t id_s_len, uint8_t const rand_s[static EAP_PSK_RAND_LEN], uint8_t const rand_p[static EAP_PSK_RAND_LEN])
Compute MAC_P = CMAC-AES-128(AK, ID_P || ID_S || RAND_S || RAND_P)
Definition crypto.c:283
int eap_psk_derive_ak_kdk(uint8_t ak[static EAP_PSK_AK_LEN], uint8_t kdk[static EAP_PSK_KDK_LEN], uint8_t const psk[static EAP_PSK_PSK_LEN])
Key setup: derive AK (counter 1) and KDK (counter 2) from the PSK.
Definition crypto.c:164
int eap_psk_pchannel_decrypt(uint8_t *plain, uint8_t const tek[static EAP_PSK_TEK_LEN], uint32_t nonce, uint8_t const *header, size_t header_len, uint8_t const *cipher, size_t cipher_len, uint8_t const tag[static EAP_PSK_TAG_LEN])
EAX decrypt-and-verify for the protected channel (RFC 4764 Section 3.3)
Definition crypto.c:439
static const conf_parser_t config[]
Definition base.c:162
module_t common
Common fields for all loadable modules.
Common public symbol definition for all process modules.
#define fr_assert(_expr)
Definition rad_assert.h:37
#define pair_update_request(_attr, _da)
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RETURN_UNLANG_HANDLED
Definition rcode.h:65
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:52
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
#define pair_append_reply(_attr, _da)
Allocate and append a fr_pair_t to reply list.
Definition pair.h:47
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
#define fr_box_strvalue_len(_val, _len)
Definition value.h:309
section_name_t const * section
Identifier for the section.
#define COMPILE_TERMINATOR
Processing sections which are allowed in this virtual server.