The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
rlm_dpsk.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025 Network RADIUS SARL (legal@networkradius.com)
3 *
4 * This software may not be redistributed in any form without the prior
5 * written consent of Network RADIUS.
6 *
7 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
8 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
9 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
10 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
11 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
12 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
15 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
16 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
17 * SUCH DAMAGE.
18 */
19
20/**
21 * $Id: de7c072a74d0abc1c5d0d4b0dbd96327426bbdd7 $
22 * @file rlm_dpsk.c
23 * @brief Dynamic PSK for WiFi
24 *
25 * @copyright 2025 Network RADIUS SAS (legal@networkradius.com)
26 */
27RCSID("$Id: de7c072a74d0abc1c5d0d4b0dbd96327426bbdd7 $")
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/server/tmpl_dcursor.h>
32#include <freeradius-devel/unlang/xlat_func.h>
33#include <freeradius-devel/util/base16.h>
34
35#include <openssl/ssl.h>
36#include <openssl/evp.h>
37#include <openssl/hmac.h>
38
39
40/*
41 Header: 02030075
42
43 descriptor 02
44 information 010a
45 length 0010
46 replay counter 000000000000001
47 snonce c3bb319516614aacfb44e933bf1671131fb1856e5b2721952d414ce3f5aa312b
48 IV 0000000000000000000000000000000
49 rsc 0000000000000000
50 reserved 0000000000000000
51 mic 35cddcedad0dfb6a12a2eca55c17c323
52 data length 0016
53 data 30140100000fac040100000fac040100000fac028c00
54
55 30
56 14 length of data
57 01 ...
58*/
59
60typedef struct eapol_key_frame_t {
61 uint8_t descriptor; // message number 2
63 uint16_t length; // always 0010, for 16 octers
64 uint8_t replay_counter[8]; // usually "1"
65 uint8_t nonce[32]; // random token
66 uint8_t iv[16]; // zeroes
67 uint8_t rsc[8]; // zeros
68 uint8_t reserved[8]; // zeroes
69 uint8_t mic[16]; // calculated data
70 uint16_t data_len; // various other things we don't need.
71} CC_HINT(__packed__) eapol_key_frame_t;
72
73typedef struct eapol_attr_t {
74 uint8_t header[4]; // 02030075
76} CC_HINT(__packed__) eapol_attr_t;
77
78
79typedef struct rlm_dpsk_s rlm_dpsk_t;
80
81typedef struct {
83 uint8_t mac[6];
84 uint8_t pmk[32];
85
87 size_t ssid_len;
88
89 char *identity;
91
92 char *psk;
93 size_t psk_len;
95
98
99typedef struct {
101
102 pthread_mutex_t mutex; //!< Held for every operation on cache, head, and the entries.
105
114
116
119 { .out = &dict_freeradius, .proto = "freeradius" },
121};
122
124
127 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
128
130};
131
132static const conf_parser_t module_config[] = {
133 { FR_CONF_OFFSET("cache_size", rlm_dpsk_t, cache_size) },
134 { FR_CONF_OFFSET("cache_lifetime", rlm_dpsk_t, cache_lifetime) },
135
137};
138
143
162
163#ifdef WITH_TLS
164static const call_env_method_t dpsk_autz_method_env = {
166 .inst_type = "dpsk_autz_call_env_t",
167 .env = (call_env_parser_t[]) {
169 dpsk_autz_call_env_t, anonce_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-Anonce",
170 .pair.dflt_quote = T_BARE_WORD },
172 dpsk_autz_call_env_t, key_msg_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
173 .pair.dflt_quote = T_BARE_WORD },
175 }
176};
177
178static const call_env_method_t dpsk_auth_method_env = {
180 .inst_type = "dpsk_auth_call_env_t",
181 .env = (call_env_parser_t[]) {
184 dpsk_auth_call_env_t, ssid, ssid_tmpl), .pair.dflt = "Called-Station-SSID",
185 .pair.dflt_quote = T_BARE_WORD },
188 dpsk_auth_call_env_t, anonce, anonce_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-Anonce",
189 .pair.dflt_quote = T_BARE_WORD },
192 dpsk_auth_call_env_t, key_msg, key_msg_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
193 .pair.dflt_quote = T_BARE_WORD },
195 dpsk_auth_call_env_t, username), .pair.dflt = "User-Name", .pair.dflt_quote = T_BARE_WORD },
197 dpsk_auth_call_env_t, calledstation), .pair.dflt = "Called-Station-MAC", .pair.dflt_quote = T_BARE_WORD },
199 dpsk_auth_call_env_t, masterkey, masterkey_tmpl), .pair.dflt = "control.Pairwise-Master-Key", .pair.dflt_quote = T_BARE_WORD },
201 dpsk_auth_call_env_t, psk, psk_tmpl), .pair.dflt = "control.Pre-Shared-Key", .pair.dflt_quote = T_BARE_WORD },
203 dpsk_auth_call_env_t, psk_identity), .pair.dflt = "control.PSK-Identity", .pair.dflt_quote = T_BARE_WORD },
205 dpsk_auth_call_env_t, psk_dest_tmpl), .pair.dflt = "reply.Pre-Shared-Key", .pair.dflt_quote = T_BARE_WORD },
207 dpsk_auth_call_env_t, psk_identity_dest_tmpl), .pair.dflt = "reply.PSK-Identity", .pair.dflt_quote = T_BARE_WORD },
210 }
211};
212
213/*
214 * mod_authorize() - authorize user if we can authenticate
215 * it later. Add Auth-Type attribute if present in module
216 * configuration (usually Auth-Type must be "DPSK")
217 */
218static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
219{
221 dpsk_autz_call_env_t *env = talloc_get_type_abort(mctx->env_data, dpsk_autz_call_env_t);
223 fr_dcursor_t cursor;
224 fr_pair_t *vp;
225
226 vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, env->anonce_tmpl);
229
230 vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, env->key_msg_tmpl);
233
234 if (!inst->auth_type) {
235 WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup DPSK authentication.",
236 mctx->mi->name, mctx->mi->name);
238 }
239
241
243}
244
245static void dpsk_cache_key_set(rlm_dpsk_cache_t *key, fr_value_box_t const *ssid, uint8_t const *mac)
246{
247 memcpy(key->mac, mac, sizeof(key->mac));
248 memcpy(&key->ssid, &ssid->vb_octets, sizeof(key->ssid)); /* const issues */
249 key->ssid_len = ssid->vb_length;
250}
251
252/** Find an unexpired cache entry, and copy its PMK to buffer
253 *
254 * The caller must hold inst->mutable->mutex.
255 */
256static rlm_dpsk_cache_t *dpsk_cache_find(request_t *request, rlm_dpsk_t const *inst, uint8_t *buffer, size_t buflen,
257 fr_value_box_t *ssid, uint8_t const *mac)
258{
259 rlm_dpsk_cache_t *entry, my_entry;
260
261 dpsk_cache_key_set(&my_entry, ssid, mac);
262
263 fr_rb_find((void **)&entry, &inst->mutable->cache, &my_entry);
264 if (entry) {
265 if fr_time_gt(entry->expires, fr_time()) {
266 RDEBUG3("Cache entry found");
267 memcpy(buffer, entry->pmk, buflen);
268 return entry;
269 }
270
271 RDEBUG3("Cache entry has expired");
272 fr_rb_delete(&inst->mutable->cache, entry);
273 }
274
275 return NULL;
276}
277
278
279static int generate_pmk(request_t *request, uint8_t *buffer, size_t buflen, fr_value_box_t *ssid, char const *psk, size_t psk_len)
280{
281 fr_assert(buflen == 32);
282
283 if (PKCS5_PBKDF2_HMAC_SHA1((const char *) psk, (int)psk_len, (const unsigned char *) ssid->vb_strvalue,
284 (int)ssid->vb_length, 4096, (int)buflen, buffer) == 0) {
285 RERROR("Failed calling OpenSSL to calculate the PMK");
286 return -1;
287 }
288
289 return 0;
290}
291
292/*
293 * Verify the DPSK information.
294 */
296{
297 rlm_dpsk_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
298 dpsk_auth_call_env_t *env = talloc_get_type_abort(mctx->env_data, dpsk_auth_call_env_t);
299 int lineno = 0;
300 int stage = 0;
302 size_t psk_len = 0;
303 unsigned int digest_len, mic_len;
304 eapol_attr_t const *eapol;
305 eapol_attr_t *zeroed;
306 FILE *fp = NULL;
307 char const *filename = (env->filename.type == FR_TYPE_STRING) ? env->filename.vb_strvalue : NULL;
308 char const *psk_identity = NULL, *psk = NULL;
309 uint8_t *p;
310 uint8_t const *snonce, *ap_mac;
311 uint8_t const *min_mac, *max_mac;
312 uint8_t const *min_nonce, *max_nonce;
313 uint8_t pmk[32];
314 uint8_t s_mac[6], message[sizeof("Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128];
315 uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE];
316 char token_identity[256];
317 char token_psk[256];
318 char token_mac[256];
319 char buffer[1024];
320 fr_sbuff_t sbuff;
322 size_t len;
323 fr_sbuff_err_t sberr;
324 fr_sbuff_term_t const terms = FR_SBUFF_TERMS(L("\n"),L("\r"),L(","));
325 fr_sbuff_term_t const quoted_terms = FR_SBUFF_TERMS(L("\""));
326 bool quoted = false;
327
328 /*
329 * Search for the information in a bunch of attributes.
330 */
331 if (env->anonce.vb_length != 32) {
332 RWARN("%s has incorrect length (%zu, not 32)", env->anonce_tmpl->name, env->anonce.vb_length);
334 }
335
336 if (env->key_msg.vb_length < sizeof(*eapol)) {
337 RWARN("%s has incorrect length (%zu < %zu)", env->key_msg_tmpl->name, env->key_msg.vb_length, sizeof(*eapol));
339 }
340
341 if (env->key_msg.vb_length > sizeof(frame)) {
342 RWARN("%s has incorrect length (%zu > %zu)", env->key_msg_tmpl->name, env->key_msg.vb_length, sizeof(frame));
344 }
345
346 /*
347 * At this point, the request has the relevant DPSK
348 * attributes. The module now should return FAIL for
349 * missing / invalid attributes, or REJECT for
350 * authentication failure.
351 *
352 * If the entry is found in a VP or a cache, the module
353 * returns OK. This means that the caller should not
354 * save &control:Pre-Shared-Key somewhere.
355 *
356 * If the module found a matching entry in the file, it
357 * returns UPDATED to indicate that the caller should
358 * update the database with the PSK which was found.
359 */
360
361#if defined(__clang_analyzer__) || defined(__COVERITY__)
362 /*
363 * Neither the clang analyser nor Coverity sees fr_base16_decode()
364 * fill s_mac through the dbuff, and both report the memcmp()
365 * below as a read of uninitialised memory.
366 */
367 memset(s_mac, 0, 6);
368#endif
369 /*
370 * Get supplicant MAC address from the User-Name
371 */
372 if (fr_base16_decode(NULL, &FR_DBUFF_TMP(s_mac, sizeof(s_mac)),
373 &FR_SBUFF_IN(env->username.vb_strvalue, env->username.vb_length), false) != 6) {
374 RERROR("User-Name is not a recognizable hex MAC address");
376 }
377
378 if (env->calledstation.vb_length != 6) {
379 RERROR("Called-Station-MAC is not a recognizable MAC address");
381 }
382
383 ap_mac = env->calledstation.vb_octets;
384
385 /*
386 * Sort the MACs
387 */
388 if (memcmp(s_mac, ap_mac, 6) <= 0) {
389 min_mac = s_mac;
390 max_mac = ap_mac;
391 } else {
392 min_mac = ap_mac;
393 max_mac = s_mac;
394 }
395
396 eapol = (eapol_attr_t const *) env->key_msg.vb_octets;
397
398 /*
399 * Get supplicant nonce and AP nonce.
400 *
401 * Then sort the nonces.
402 */
403 snonce = env->key_msg.vb_octets + 17;
404 if (memcmp(snonce, env->anonce.vb_octets, 32) <= 0) {
405 min_nonce = snonce;
406 max_nonce = env->anonce.vb_octets;
407 } else {
408 min_nonce = env->anonce.vb_octets;
409 max_nonce = snonce;
410 }
411
412 /*
413 * Create the base message which we will hash.
414 */
415 memcpy(message, "Pairwise key expansion", sizeof("Pairwise key expansion")); /* including trailing NUL */
416 p = &message[sizeof("Pairwise key expansion")];
417
418 memcpy(p, min_mac, 6);
419 memcpy(p + 6, max_mac, 6);
420 p += 12;
421
422 memcpy(p, min_nonce, 32);
423 memcpy(p + 32, max_nonce, 32);
424 p += 64;
425 *p = '\0';
426 fr_assert(sizeof(message) == (p + 1 - message));
427
428 /*
429 * If we're caching, then check the cache first, before
430 * trying the file. This check allows us to avoid the
431 * PMK calculation in many situations, as that can be
432 * expensive.
433 */
434 if (inst->cache_size) {
435 rlm_dpsk_cache_t *entry;
436
437 pthread_mutex_lock(&inst->mutable->mutex);
438 entry = dpsk_cache_find(request, inst, pmk, sizeof(pmk), &env->ssid, s_mac);
439 if (entry) {
440 /*
441 * Another thread can free the entry once the mutex is released.
442 */
443 MEM(psk_identity = talloc_bstrndup(env, entry->identity, entry->identity_len));
444 MEM(psk = talloc_bstrndup(env, entry->psk, entry->psk_len));
445 psk_len = entry->psk_len;
446 pthread_mutex_unlock(&inst->mutable->mutex);
447 goto make_digest;
448 }
449 pthread_mutex_unlock(&inst->mutable->mutex);
450 }
451
452 /*
453 * No cache, or no cache entry. Look for an external PMK
454 * taken from a database.
455 */
456stage1:
457 stage = 1;
458
459 if (env->masterkey.type == FR_TYPE_OCTETS) {
460 if (env->masterkey.vb_length != sizeof(pmk)) {
461 RWARN("%s has incorrect length (%zu != %zu) - ignoring it", env->masterkey_tmpl->name,
462 env->masterkey.vb_length, sizeof(pmk));
463 } else {
464 RDEBUG2("Using %s", env->masterkey_tmpl->name);
465 memcpy(pmk, env->masterkey.vb_octets, sizeof(pmk));
466 goto make_digest;
467 }
468 }
469
470 /*
471 * No external PMK. Try an external PSK.
472 */
473 if (env->psk.type == FR_TYPE_STRING) {
474 RDEBUG3("Trying %s", env->psk_tmpl->name);
475 if (generate_pmk(request, pmk, sizeof(pmk), &env->ssid, env->psk.vb_strvalue, env->psk.vb_length) < 0) {
476 fr_assert(!fp);
478 }
479
480 if (env->psk_identity.type == FR_TYPE_STRING) {
481 psk_identity = env->psk_identity.vb_strvalue;
482 } else {
483 psk_identity = env->username.vb_strvalue;
484 }
485
486 psk = env->psk.vb_strvalue;
487 psk_len = env->psk.vb_length;
488
489 goto make_digest;
490 }
491
492 /*
493 * No external PSK was found. If there's no file, then
494 * we can't do anything else.
495 */
496stage2:
497 stage = 2;
498
499 if (!filename) {
500 RERROR("No %s was found, and no 'filename' was configured", env->psk_tmpl->name);
502 }
503
504#if defined(__clang_analyzer__) || defined(__COVERITY__)
505 /*
506 * Neither the clang analyser nor Coverity sees the MAC
507 * copied into token_mac through the sbuff, and both report
508 * the memcmp() against s_mac as a read of uninitialised
509 * memory.
510 */
511 memset(token_mac, 0, sizeof(token_mac));
512#endif
513
514 RDEBUG3("Looking for PSK in file %s", filename);
515
516 fp = fopen(filename, "r");
517 if (!fp) {
518 REDEBUG("Failed opening %s - %s", filename, fr_syserror(errno));
520 }
521
522 fr_sbuff_init_file(&sbuff, &fctx, buffer, sizeof(buffer), fp, SIZE_MAX);
523
524stage2a:
525 lineno++;
526 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
527 quoted = fr_sbuff_next_if_char(&sbuff, '"');
528 sberr = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(token_identity, sizeof(token_identity)), &sbuff,
529 SIZE_MAX, quoted ? &quoted_terms : &terms, NULL);
530 if (sberr < 0) {
531 RERROR("%s[%d] Failed reading identity: %s", filename, lineno, fr_sbuff_err_to_str(sberr));
532 goto fail_file;
533 }
534 if (len == 0) {
535 RDEBUG("Failed to find matching PSK or MAC in %s", filename);
536 fail_file:
537 fclose(fp);
539 }
540 if (quoted) {
541 fr_sbuff_next_if_char(&sbuff, '"');
542 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
543 }
544
545 if (!fr_sbuff_next_if_char(&sbuff, ',')) {
546 RDEBUG("%s[%d] Failed to find ',' after identity", filename, lineno);
547 goto fail_file;
548 }
549
550 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
551 quoted = fr_sbuff_next_if_char(&sbuff, '"');
552 sberr = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(token_psk, sizeof(token_psk)), &sbuff,
553 SIZE_MAX, quoted ? &quoted_terms : &terms, NULL);
554 if (sberr < 0) {
555 RERROR("%s[%d] Failed reading PSK: %s", filename, lineno, fr_sbuff_err_to_str(sberr));
556 goto fail_file;
557 }
558 if (len == 0) {
559 RDEBUG("%s[%d] Failed parsing PSK", filename, lineno);
560 goto fail_file;
561 }
562 if (quoted) {
563 fr_sbuff_next_if_char(&sbuff, '"');
564 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
565 }
566
567 /*
568 * The MAC is optional. If there is a MAC, we
569 * loop over the file until we find a matching
570 * one.
571 */
572 if (fr_sbuff_next_if_char(&sbuff, ',')) {
573
574 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
575 quoted = fr_sbuff_next_if_char(&sbuff, '"');
576 sberr = fr_sbuff_out_bstrncpy_until(&len, &FR_SBUFF_OUT(token_mac, sizeof(token_mac)), &sbuff,
577 SIZE_MAX, quoted ? &quoted_terms : &terms, NULL);
578 if (sberr < 0) {
579 RERROR("%s[%d] Failed reading MAC: %s", filename, lineno, fr_sbuff_err_to_str(sberr));
580 goto fail_file;
581 }
582 if (len == 0) {
583 RERROR("%s[%d] Failed parsing MAC", filename, lineno);
584 goto fail_file;
585 }
586
587 /*
588 * See if the MAC matches. If not, skip
589 * this entry. That's a basic negative cache.
590 */
591 if ((len != 12) ||
592 (fr_base16_decode(NULL, &FR_DBUFF_TMP((uint8_t *)token_mac, 6),
593 &FR_SBUFF_IN(token_mac, 12), false) != 6)) {
594 RERROR("%s[%d] Failed parsing MAC", filename, lineno);
595 goto fail_file;
596 }
597 if (quoted) fr_sbuff_next_if_char(&sbuff, '"');
598
599 /*
600 * The MAC doesn't match, don't even bother trying to generate the PMK.
601 */
602 if (memcmp(s_mac, token_mac, 6) != 0) {
603 goto stage2a;
604 }
605
606 RDEBUG3("Found matching MAC");
607 stage = 3;
608 }
609
610 /*
611 * Generate the PMK using the SSID, this MAC, and the PSK we just read.
612 */
613 psk = token_psk;
614 psk_len = strlen(token_psk);
615 psk_identity = token_identity;
616
617 RDEBUG3("%s[%d] Trying PSK %s", filename, lineno, token_psk);
618 if (generate_pmk(request, pmk, sizeof(pmk), &env->ssid, psk, psk_len) < 0) {
619 goto fail_file;
620 }
621
622 /*
623 * HMAC = HMAC_SHA1(pmk, message);
624 *
625 * We need the first 16 octets of this.
626 */
627make_digest:
628 digest_len = sizeof(digest);
629#ifdef __COVERITY__
630 /*
631 * Coverity doesn't see that HMAC will populate digest
632 */
633 memset(digest, 0, digest_len);
634#endif
635 HMAC(EVP_sha1(), pmk, sizeof(pmk), message, sizeof(message), digest, &digest_len);
636
637 RHEXDUMP3(message, sizeof(message), "message:");
638 RHEXDUMP3(pmk, sizeof(pmk), "pmk :");
639 RHEXDUMP3(digest, 16, "kck :");
640
641 /*
642 * Create the frame with the middle field zero, and hash it with the KCK digest we calculated from the key expansion.
643 */
644 memcpy(frame, env->key_msg.vb_octets, env->key_msg.vb_length);
645 zeroed = (eapol_attr_t *) &frame[0];
646 memset(&zeroed->frame.mic[0], 0, 16);
647
648 RHEXDUMP3(frame, env->key_msg.vb_length, "zeroed:");
649
650 mic_len = sizeof(mic);
651#ifdef __COVERITY__
652 /*
653 * Coverity doesn't see that HMAC will populate mic
654 */
655 memset(mic, 0, mic_len);
656#endif
657 HMAC(EVP_sha1(), digest, 16, frame, env->key_msg.vb_length, mic, &mic_len);
658
659 /*
660 * The MICs don't match.
661 */
662 if (memcmp(&eapol->frame.mic[0], mic, 16) != 0) {
663 RDEBUG3("Stage %d", stage);
664 RHEXDUMP3(mic, 16, "calculated mic:");
665 RHEXDUMP3(eapol->frame.mic, 16, "packet mic :");
666
667 psk_identity = NULL;
668 psk = NULL;
669 psk_len = 0;
670
671 /*
672 * Found a cached entry, but it didn't match. Go
673 * check external PMK / PSK.
674 */
675 if (stage == 0) {
676 rlm_dpsk_cache_t *entry, my_entry;
677
678 dpsk_cache_key_set(&my_entry, &env->ssid, s_mac);
679
680 /*
681 * Another thread may have replaced the entry, so only delete
682 * it if it still holds the PMK which failed.
683 */
684 pthread_mutex_lock(&inst->mutable->mutex);
685 fr_rb_find((void **)&entry, &inst->mutable->cache, &my_entry);
686 if (entry && (memcmp(entry->pmk, pmk, sizeof(entry->pmk)) == 0)) {
687 fr_rb_delete(&inst->mutable->cache, entry);
688 }
689 pthread_mutex_unlock(&inst->mutable->mutex);
690 goto stage1;
691 }
692
693 /*
694 * Found an external PMK or PSK, but it didn't
695 * match. Go check the file.
696 */
697 if (stage == 1) {
698 if (env->psk.type == FR_TYPE_STRING) RWARN("%s did not match", env->psk_tmpl->name);
699
700 if (filename) {
701 RDEBUG("Checking file %s for PSK and MAC", filename);
702 goto stage2;
703 }
704
705 RWARN("No 'filename' was configured.");
707 }
708
709 /*
710 * The file is open, so we keep reading it until
711 * we find a matching entry.
712 */
713 fr_assert(fp);
714
715 if (stage == 2) goto stage2a;
716
717 fclose(fp);
718
719 /*
720 * We found a PSK associated with this MAC in the
721 * file. But it didn't match, so we're done.
722 */
723 fr_assert(stage == 3);
724
725 RWARN("Found matching MAC in %s, but the PSK does not match", filename);
727 }
728
729 /*
730 * We found a matching PSK. If we read it from the file,
731 * then close the file, and ensure that we return
732 * UPDATED. This tells the caller to write the entry
733 * into the database, so that we don't need to scan the
734 * file again.
735 */
736 if (fp) {
737 rcode = RLM_MODULE_UPDATED;
738 fr_assert(psk == token_psk);
739 fr_assert(psk_identity == token_identity);
740 fclose(fp);
741 }
742
743 /*
744 * Extend the lifetime of the cache entry, or add the
745 * cache entry if necessary. We only add / update the
746 * cache entry if the PSK was not found in a VP.
747 *
748 * If the caller gave us only a PMK, then don't cache anything.
749 */
750 if (inst->cache_size && psk && psk_identity) {
751 rlm_dpsk_cache_t *entry, my_entry;
752 tmpl_t psk_rhs;
753 map_t psk_map = {
754 .lhs = env->psk_dest_tmpl,
755 .op = T_OP_SET,
756 .rhs = &psk_rhs
757 };
758
759 dpsk_cache_key_set(&my_entry, &env->ssid, s_mac);
760
761 pthread_mutex_lock(&inst->mutable->mutex);
762 fr_rb_find((void **)&entry, &inst->mutable->cache, &my_entry);
763 if (stage == 0) {
764 /*
765 * The PSK came from the cache, so there's nothing new
766 * to save. Another thread may have evicted or replaced
767 * the entry since we read it, so only refresh the entry
768 * if it still holds the PMK which matched.
769 */
770 if (entry && (memcmp(entry->pmk, pmk, sizeof(entry->pmk)) != 0)) entry = NULL;
771
772 } else if (!entry) {
773 /*
774 * Maybe there are oo many entries in the
775 * cache. If so, delete the oldest one.
776 */
777 if (fr_rb_num_elements(&inst->mutable->cache) > inst->cache_size) {
778 fr_rb_delete(&inst->mutable->cache, fr_dlist_head(&inst->mutable->head));
779 }
780
781 MEM(entry = talloc_zero(&inst->mutable->cache, rlm_dpsk_cache_t));
782
783 memcpy(entry->mac, s_mac, sizeof(entry->mac));
784 memcpy(entry->pmk, pmk, sizeof(entry->pmk));
785
786 /*
787 * Save the SSID, PSK, and PSK identity in the cache entry.
788 */
789 MEM(entry->ssid = talloc_memdup(entry, env->ssid.vb_octets, env->ssid.vb_length));
790 entry->ssid_len = env->ssid.vb_length;
791
792 MEM(entry->psk = talloc_strdup(entry, psk));
793 entry->psk_len = psk_len;
794
795 entry->identity_len = strlen(psk_identity);
796 MEM(entry->identity = talloc_strdup(entry, psk_identity));
797
798 /*
799 * Cache it.
800 */
801 if (fr_rb_insert(&inst->mutable->cache, entry) != 0) {
802 TALLOC_FREE(entry);
803 pthread_mutex_unlock(&inst->mutable->mutex);
804 goto update_attributes;
805 }
806 RDEBUG3("Cache entry saved");
807 }
808
809 if (entry) {
810 entry->expires = fr_time_add(fr_time(), inst->cache_lifetime);
811 if (fr_dlist_entry_in_list(&entry->dlist)) fr_dlist_remove(&inst->mutable->head, entry);
812 fr_dlist_insert_tail(&inst->mutable->head, entry);
813 }
814 pthread_mutex_unlock(&inst->mutable->mutex);
815
816 /*
817 * Add the PSK to the reply items.
818 */
820 fr_value_box_bstrndup_shallow(&psk_map.rhs->data.literal, NULL, psk, psk_len, true);
821 if (map_to_request(request, &psk_map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
822 }
823
824update_attributes:
825 /*
826 * We found a cache entry, or an external PSK. Don't
827 * create new attributes.
828 */
829 if (rcode == RLM_MODULE_OK) RETURN_UNLANG_OK;
830
831 fr_assert(psk != NULL);
832 fr_assert(psk_identity != NULL);
833
834 {
835 tmpl_t rhs;
836 map_t map = {
837 .lhs = env->psk_dest_tmpl,
838 .op = T_OP_SET,
839 .rhs = &rhs
840 };
841
842 RDEBUG2("Creating %s and %s", env->psk_dest_tmpl->name, env->psk_identity_dest_tmpl->name);
844 fr_value_box_bstrndup_shallow(&map.rhs->data.literal, NULL, psk, psk_len, true);
845 if (map_to_request(request, &map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
846
847 map.lhs = env->psk_identity_dest_tmpl;
848 fr_value_box_bstrndup_shallow(&map.rhs->data.literal, NULL, psk_identity, strlen(psk_identity), true);
849 if (map_to_request(request, &map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
850 }
851
853}
854
855static xlat_arg_parser_t const dpsk_pmk_xlat_arg[] = {
856 { .required = true, .concat = true, .type = FR_TYPE_STRING },
857 { .required = true, .concat = true, .type = FR_TYPE_STRING },
859};
860
861/** xlat to generate the PMK from SSID and Pre-Shared-Key
862 *
863 * Example:
864 @verbatim
865 %dpsk.pmk(Calling-Station-SSID, Pre-Shared-Key)
866 @endverbatim
867 *
868 * @ingroup xlat_functions
869 */
870static xlat_action_t dpsk_pmk_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
871 request_t *request, fr_value_box_list_t *in)
872{
873 fr_value_box_t *ssid, *psk, *vb;
874 uint8_t buffer[32];
875
876 XLAT_ARGS(in, &ssid, &psk);
877
878 if (PKCS5_PBKDF2_HMAC_SHA1(psk->vb_strvalue, (int)psk->vb_length,
879 (const unsigned char *) ssid->vb_strvalue, (int)ssid->vb_length,
880 4096, sizeof(buffer), buffer) == 0) {
881 RERROR("Failed calling OpenSSL to calculate the PMK");
882 return XLAT_ACTION_FAIL;
883 }
884
885 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
886 fr_value_box_memdup(vb, vb, NULL, buffer, sizeof(buffer), true);
887
889
890 return XLAT_ACTION_DONE;
891}
892
893static int mod_load(void)
894{
895 xlat_t *xlat;
896 if (unlikely((xlat = xlat_func_register(NULL, "dpsk.pmk", dpsk_pmk_xlat, FR_TYPE_OCTETS)) == NULL)) return -1;
897 xlat_func_args_set(xlat, dpsk_pmk_xlat_arg);
898
899 return 0;
900}
901
902static void mod_unload(void)
903{
904 xlat_func_unregister("dpsk.pmk");
905}
906
907static fr_cmp_ret_t cache_entry_cmp(void const *one, void const *two)
908{
909 rlm_dpsk_cache_t const *a = (rlm_dpsk_cache_t const *) one;
910 rlm_dpsk_cache_t const *b = (rlm_dpsk_cache_t const *) two;
911 int rcode;
912
913 rcode = memcmp(a->mac, b->mac, sizeof(a->mac));
914 if (rcode != 0) return CMP(rcode, 0);
915
916 if (a->ssid_len < b->ssid_len) return CMP_LT;
917 if (a->ssid_len > b->ssid_len) return CMP_GT;
918
919 return CMP(memcmp(a->ssid, b->ssid, a->ssid_len), 0);
920}
921
922/** Unlink and free a cache entry
923 *
924 * Only called from fr_rb_delete(), whose caller holds inst->mutable->mutex.
925 */
926static void cache_entry_free(void *data)
927{
929
931
932 talloc_free(entry);
933}
934
935static int mod_detach(const module_detach_ctx_t *mctx)
936{
937 rlm_dpsk_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
938
939 if (!inst->cache_size) return 0;
940
941 pthread_mutex_destroy(&inst->mutable->mutex);
942
943 talloc_free(inst->mutable);
944
945 return 0;
946}
947#endif
948
949static int mod_instantiate(module_inst_ctx_t const *mctx)
950{
951#ifdef WITH_TLS
952 rlm_dpsk_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
953
954 inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->mi->name, -1);
955 if (!inst->auth_type) {
956 WARN("Failed to find 'authenticate %s {...}' section. DPSK will likely not work",
957 mctx->mi->name);
958 }
959
960 /*
961 * We can still use a cache if we're getting PSKs from a
962 * database. The PMK calculation can take time, so
963 * caching the PMK still saves us time.
964 */
965 if (!inst->cache_size) return 0;
966
967 FR_INTEGER_BOUND_CHECK("cache_size", inst->cache_size, <=, ((uint32_t) 1) << 16);
968
969 FR_TIME_DELTA_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, <=, fr_time_delta_from_sec(7 * 86400));
970 FR_TIME_DELTA_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, >=, fr_time_delta_from_sec(3600));
971
972 inst->mutable = talloc_zero(NULL, rlm_dpsk_mutable_t);
973
975
976 fr_dlist_init(&inst->mutable->head, rlm_dpsk_cache_t, dlist);
977
978 if (pthread_mutex_init(&inst->mutable->mutex, NULL) < 0) {
979 cf_log_err(mctx->mi->conf, "Failed creating mutex");
980 return -1;
981 }
982
983 return 0;
984#else
985 cf_log_err(mctx->mi->conf, "rlm_dpsk requires OpenSSL");
986 return 0;
987#endif
988}
989
990/*
991 * The module name should be the only globally exported symbol.
992 * That is, everything else should be 'static'.
993 *
994 * If the module needs to temporarily modify it's instantiation
995 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
996 * The server will then take care of ensuring that the module
997 * is single-threaded.
998 */
1001 .common = {
1002 .magic = MODULE_MAGIC_INIT,
1003 .name = "dpsk",
1004 .inst_size = sizeof(rlm_dpsk_t),
1005 .instantiate = mod_instantiate,
1007#ifdef WITH_TLS
1008 .detach = mod_detach,
1009 .onload = mod_load,
1010 .unload = mod_unload,
1011#endif
1012 },
1013#ifdef WITH_TLS
1014 .method_group = {
1015 .bindings = (module_method_binding_t[]){
1016 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_authorize, .method_env = &dpsk_autz_method_env },
1017 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &dpsk_auth_method_env },
1018
1020 }
1021 }
1022#endif
1023};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
static int const char char buffer[256]
Definition acutest.h:641
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:109
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 CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:130
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
fr_cmp_ret_t
Result of an ordering comparison.
Definition build.h:113
@ CMP_GT
a > b
Definition build.h:117
@ CMP_LT
a < b
Definition build.h:115
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
Definition call_env.h:365
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl MUST contain an attribute reference.
Definition call_env.h:86
@ CALL_ENV_FLAG_NONE
Definition call_env.h:74
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
size_t inst_size
Size of per call env.
Definition call_env.h:245
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
#define FR_CALL_ENV_PARSE_ONLY_OFFSET(_name, _cast_type, _flags, _struct, _parse_field)
Specify a call_env_parser_t which writes out the result of the parsing phase to the field specified.
Definition call_env.h:389
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:722
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:572
#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
#define FR_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:583
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:649
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:340
#define CF_IDENT_ANY
Definition cf_util.h:80
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:556
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
#define MEM(x)
Definition debug.h:38
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:316
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:329
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:335
fr_dict_enum_value_t const * fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len)
Definition dict_util.c:3778
Specifies an attribute which must be present for the module to function.
Definition dict.h:315
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:328
Value of an enumerated attribute.
Definition dict.h:277
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:242
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition dlist.h:468
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:620
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition dlist.h:145
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition dlist.h:128
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition dlist.h:360
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
talloc_free(hp)
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RWARN(fmt,...)
Definition log.h:314
#define RERROR(fmt,...)
Definition log.h:315
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition log.h:724
int map_to_vp(TALLOC_CTX *ctx, fr_pair_list_t *out, request_t *request, map_t const *map, UNUSED void *uctx)
Convert a map to a fr_pair_t.
Definition map.c:1566
int map_to_request(request_t *request, map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert map_t to fr_pair_t (s) and add them to a request_t.
Definition map.c:1846
#define fr_time()
Definition event.c:58
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
fr_sbuff_err_t
fr_sbuff_err_t fr_sbuff_out_bstrncpy_until(size_t *len, fr_sbuff_t *out, fr_sbuff_t *in, size_t max, fr_sbuff_term_t const *tt, fr_sbuff_unescape_rules_t const *u_rules)
unsigned char uint8_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
module_instance_t * mi
Module instance to detach.
Definition module_ctx.h:57
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for detach calls.
Definition module_ctx.h:56
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_da, fr_dict_enum_value_t const *enumv)
Set the next section type if it's not already set.
Definition module_rlm.c:405
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:162
static int mod_load(void)
Definition proto_arp.c:246
static void mod_unload(void)
Definition proto_arp.c:255
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
#define WARN(fmt,...)
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:178
The main red black tree structure.
Definition rb.h:71
#define RETURN_UNLANG_UPDATED
Definition rcode.h:76
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:55
#define RETURN_UNLANG_FAIL
Definition rcode.h:69
#define RETURN_UNLANG_REJECT
Definition rcode.h:68
#define RETURN_UNLANG_OK
Definition rcode.h:70
#define RETURN_UNLANG_NOOP
Definition rcode.h:75
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_always.c:136
static void cache_entry_free(rlm_cache_entry_t *c)
Locate a cache entry in memcached.
static fr_cmp_ret_t cache_entry_cmp(void const *one, void const *two)
Compare two entries by key.
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_chap.c:173
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_chap.c:225
uint8_t pmk[32]
Definition rlm_dpsk.c:84
tmpl_t * psk_identity_dest_tmpl
Definition rlm_dpsk.c:159
uint8_t rsc[8]
Definition rlm_dpsk.c:67
tmpl_t * psk_dest_tmpl
Definition rlm_dpsk.c:158
fr_rb_tree_t cache
Definition rlm_dpsk.c:100
uint16_t information
Definition rlm_dpsk.c:62
fr_value_box_t key_msg
Definition rlm_dpsk.c:149
fr_rb_node_t node
Definition rlm_dpsk.c:82
size_t ssid_len
Definition rlm_dpsk.c:87
struct rlm_dpsk_s rlm_dpsk_t
Definition rlm_dpsk.c:79
module_rlm_t rlm_dpsk
Definition rlm_dpsk.c:1000
uint8_t reserved[8]
Definition rlm_dpsk.c:68
fr_value_box_t username
Definition rlm_dpsk.c:151
static fr_dict_t const * dict_freeradius
Definition rlm_dpsk.c:115
tmpl_t * key_msg_tmpl
Definition rlm_dpsk.c:141
uint8_t mac[6]
Definition rlm_dpsk.c:83
fr_value_box_t masterkey
Definition rlm_dpsk.c:153
fr_dlist_head_t head
Definition rlm_dpsk.c:103
static fr_dict_attr_t const * attr_auth_type
Definition rlm_dpsk.c:123
fr_dict_autoload_t rlm_dpsk_dict[]
Definition rlm_dpsk.c:118
fr_value_box_t psk_identity
Definition rlm_dpsk.c:157
fr_value_box_t ssid
Definition rlm_dpsk.c:145
fr_dict_attr_autoload_t rlm_dpsk_dict_attr[]
Definition rlm_dpsk.c:126
pthread_mutex_t mutex
Held for every operation on cache, head, and the entries.
Definition rlm_dpsk.c:102
uint16_t length
Definition rlm_dpsk.c:63
uint8_t * ssid
Definition rlm_dpsk.c:86
uint8_t mic[16]
Definition rlm_dpsk.c:69
uint8_t iv[16]
Definition rlm_dpsk.c:66
uint8_t nonce[32]
Definition rlm_dpsk.c:65
tmpl_t * masterkey_tmpl
Definition rlm_dpsk.c:154
char * identity
Definition rlm_dpsk.c:89
uint8_t descriptor
Definition rlm_dpsk.c:61
uint32_t cache_size
Definition rlm_dpsk.c:109
fr_dlist_t dlist
Definition rlm_dpsk.c:96
fr_value_box_t filename
Definition rlm_dpsk.c:160
eapol_key_frame_t frame
Definition rlm_dpsk.c:75
tmpl_t * key_msg_tmpl
Definition rlm_dpsk.c:150
uint8_t header[4]
Definition rlm_dpsk.c:74
static const conf_parser_t module_config[]
Definition rlm_dpsk.c:132
size_t identity_len
Definition rlm_dpsk.c:90
uint16_t data_len
Definition rlm_dpsk.c:70
fr_dict_enum_value_t const * auth_type
Definition rlm_dpsk.c:107
fr_time_delta_t cache_lifetime
Definition rlm_dpsk.c:110
fr_value_box_t calledstation
Definition rlm_dpsk.c:152
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_dpsk.c:949
fr_value_box_t anonce
Definition rlm_dpsk.c:147
fr_value_box_t psk
Definition rlm_dpsk.c:155
uint8_t replay_counter[8]
Definition rlm_dpsk.c:64
fr_time_t expires
Definition rlm_dpsk.c:94
bool fr_sbuff_next_if_char(fr_sbuff_t *sbuff, char c)
Return true if the current char matches, and if it does, advance.
Definition sbuff.c:2247
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_adv_past_whitespace(_sbuff, _len, _tt)
#define FR_SBUFF_TERMS(...)
Initialise a terminal structure with a list of sorted strings.
Definition sbuff.h:191
#define fr_sbuff_adv_past_blank(_sbuff, _len, _tt)
#define FR_SBUFF_OUT(_start, _len_or_end)
Set of terminal elements.
File sbuff extension structure.
Definition sbuff.h:149
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
char const * name
Instance name e.g. user_database.
Definition module.h:361
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:355
size_t inst_size
Size of the module's instance data.
Definition module.h:213
void * data
Module's instance data.
Definition module.h:297
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:153
Named methods exported by a module.
Definition module.h:175
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:145
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules))
Initialise a tmpl without copying the input name string.
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
Value pair map.
Definition map.h:77
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition map.h:78
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition map.h:79
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
Definition talloc.c:693
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:599
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
#define fr_time_gt(_a, _b)
Definition time.h:237
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
@ T_BARE_WORD
Definition token.h:118
@ T_OP_SET
Definition token.h:82
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
static char const * fail_file
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:373
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:136
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:160
Definition for a single argument consumed by an xlat function.
Definition xlat.h:135
void fr_value_box_bstrndup_shallow(fr_value_box_t *dst, fr_dict_attr_t const *enumv, char const *src, size_t len, bool tainted)
Assign a string to to a fr_value_box_t.
Definition value.c:5101
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
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:668
static fr_sbuff_err_t char size_t fr_sbuff_t * in
Definition value.h:1061
static fr_slen_t data
Definition value.h:1366
static fr_sbuff_err_t char ** out
Definition value.h:1061
static fr_sbuff_err_t char size_t * len
Definition value.h:1061
int nonnull(2, 5))
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:374
xlat_t * xlat_func_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
Register an xlat function.
Definition xlat_func.c:225
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition xlat_func.c:520