The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 1f80c6de46b7458c9866d1ff76961884ec32bc1e $
22 * @file rlm_dpsk.c
23 * @brief Dynamic PSK for WiFi
24 *
25 * @copyright 2025 Network RADIUS SAS (legal@networkradius.com)
26 */
27RCSID("$Id: 1f80c6de46b7458c9866d1ff76961884ec32bc1e $")
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#include <freeradius-devel/util/rb.h>
35
36#include <openssl/ssl.h>
37#include <openssl/evp.h>
38#include <openssl/hmac.h>
39
40#include <ctype.h>
41
42/*
43 Header: 02030075
44
45 descriptor 02
46 information 010a
47 length 0010
48 replay counter 000000000000001
49 snonce c3bb319516614aacfb44e933bf1671131fb1856e5b2721952d414ce3f5aa312b
50 IV 0000000000000000000000000000000
51 rsc 0000000000000000
52 reserved 0000000000000000
53 mic 35cddcedad0dfb6a12a2eca55c17c323
54 data length 0016
55 data 30140100000fac040100000fac040100000fac028c00
56
57 30
58 14 length of data
59 01 ...
60*/
61
62typedef struct eapol_key_frame_t {
63 uint8_t descriptor; // message number 2
65 uint16_t length; // always 0010, for 16 octers
66 uint8_t replay_counter[8]; // usually "1"
67 uint8_t nonce[32]; // random token
68 uint8_t iv[16]; // zeroes
69 uint8_t rsc[8]; // zeros
70 uint8_t reserved[8]; // zeroes
71 uint8_t mic[16]; // calculated data
72 uint16_t data_len; // various other things we don't need.
73} CC_HINT(__packed__) eapol_key_frame_t;
74
75typedef struct eapol_attr_t {
76 uint8_t header[4]; // 02030075
78} CC_HINT(__packed__) eapol_attr_t;
79
80#ifdef HAVE_PTHREAD_H
81#define PTHREAD_MUTEX_LOCK pthread_mutex_lock
82#define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
83#else
84#define PTHREAD_MUTEX_LOCK(_x)
85#define PTHREAD_MUTEX_UNLOCK(_x)
86#endif
87
88typedef struct rlm_dpsk_s rlm_dpsk_t;
89
90typedef struct {
92 uint8_t mac[6];
93 uint8_t pmk[32];
94
96 size_t ssid_len;
97
98 char *identity;
100
101 char *psk;
102 size_t psk_len;
104
108
109typedef struct {
111
112#ifdef HAVE_PTHREAD_H
113 pthread_mutex_t mutex;
114#endif
117
126
128
131 { .out = &dict_freeradius, .proto = "freeradius" },
132 { NULL }
133};
134
136
139 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
140
141 { NULL }
142};
143
144static const conf_parser_t module_config[] = {
145 { FR_CONF_OFFSET("cache_size", rlm_dpsk_t, cache_size) },
146 { FR_CONF_OFFSET("cache_lifetime", rlm_dpsk_t, cache_lifetime) },
147
149};
150
155
174
175#ifdef WITH_TLS
176static const call_env_method_t dpsk_autz_method_env = {
178 .inst_type = "dpsk_autz_call_env_t",
179 .env = (call_env_parser_t[]) {
181 dpsk_autz_call_env_t, anonce_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-Anonce",
182 .pair.dflt_quote = T_BARE_WORD },
184 dpsk_autz_call_env_t, key_msg_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
185 .pair.dflt_quote = T_BARE_WORD },
187 }
188};
189
190static const call_env_method_t dpsk_auth_method_env = {
192 .inst_type = "dpsk_auth_call_env_t",
193 .env = (call_env_parser_t[]) {
196 dpsk_auth_call_env_t, ssid, ssid_tmpl), .pair.dflt = "Called-Station-SSID",
197 .pair.dflt_quote = T_BARE_WORD },
200 dpsk_auth_call_env_t, anonce, anonce_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-Anonce",
201 .pair.dflt_quote = T_BARE_WORD },
204 dpsk_auth_call_env_t, key_msg, key_msg_tmpl), .pair.dflt = "FreeRADIUS-EV5.802_1X-EAPoL-Key-Msg",
205 .pair.dflt_quote = T_BARE_WORD },
207 dpsk_auth_call_env_t, username), .pair.dflt = "User-Name", .pair.dflt_quote = T_BARE_WORD },
209 dpsk_auth_call_env_t, calledstation), .pair.dflt = "Called-Station-MAC", .pair.dflt_quote = T_BARE_WORD },
211 dpsk_auth_call_env_t, masterkey, masterkey_tmpl), .pair.dflt = "control.Pairwise-Master-Key", .pair.dflt_quote = T_BARE_WORD },
213 dpsk_auth_call_env_t, psk, psk_tmpl), .pair.dflt = "control.Pre-Shared-Key", .pair.dflt_quote = T_BARE_WORD },
215 dpsk_auth_call_env_t, psk_identity), .pair.dflt = "control.PSK-Identity", .pair.dflt_quote = T_BARE_WORD },
217 dpsk_auth_call_env_t, psk_dest_tmpl), .pair.dflt = "reply.Pre-Shared-Key", .pair.dflt_quote = T_BARE_WORD },
219 dpsk_auth_call_env_t, psk_identity_dest_tmpl), .pair.dflt = "reply.PSK-Identity", .pair.dflt_quote = T_BARE_WORD },
222 }
223};
224
225/*
226 * mod_authorize() - authorize user if we can authenticate
227 * it later. Add Auth-Type attribute if present in module
228 * configuration (usually Auth-Type must be "DPSK")
229 */
230static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
231{
233 dpsk_autz_call_env_t *env = talloc_get_type_abort(mctx->env_data, dpsk_autz_call_env_t);
235 fr_dcursor_t cursor;
236 fr_pair_t *vp;
237
238 vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, env->anonce_tmpl);
241
242 vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, env->key_msg_tmpl);
245
246 if (!inst->auth_type) {
247 WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup DPSK authentication.",
248 mctx->mi->name, mctx->mi->name);
250 }
251
253
255}
256
257static rlm_dpsk_cache_t *dpsk_cache_find(request_t *request, rlm_dpsk_t const *inst, uint8_t *buffer, size_t buflen,
258 fr_value_box_t *ssid, uint8_t const *mac)
259{
260 rlm_dpsk_cache_t *entry, my_entry;
261
262 memcpy(my_entry.mac, mac, sizeof(my_entry.mac));
263 memcpy(&my_entry.ssid, &ssid->vb_octets, sizeof(my_entry.ssid)); /* const issues */
264 my_entry.ssid_len = ssid->vb_length;
265
266 entry = fr_rb_find(&inst->mutable->cache, &my_entry);
267 if (entry) {
268 if fr_time_gt(entry->expires, fr_time()) {
269 RDEBUG3("Cache entry found");
270 memcpy(buffer, entry->pmk, buflen);
271 return entry;
272 }
273
274 RDEBUG3("Cache entry has expired");
275 fr_rb_delete(&inst->mutable->cache, entry);
276 }
277
278 return NULL;
279}
280
281
282static int generate_pmk(request_t *request, uint8_t *buffer, size_t buflen, fr_value_box_t *ssid, char const *psk, size_t psk_len)
283{
284 fr_assert(buflen == 32);
285
286 if (PKCS5_PBKDF2_HMAC_SHA1((const char *) psk, psk_len, (const unsigned char *) ssid->vb_strvalue,
287 ssid->vb_length, 4096, buflen, buffer) == 0) {
288 RERROR("Failed calling OpenSSL to calculate the PMK");
289 return -1;
290 }
291
292 return 0;
293}
294
295/*
296 * Verify the DPSK information.
297 */
298static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
299{
300 rlm_dpsk_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
301 dpsk_auth_call_env_t *env = talloc_get_type_abort(mctx->env_data, dpsk_auth_call_env_t);
302 rlm_dpsk_cache_t *entry = NULL;
303 int lineno = 0;
304 int stage = 0;
306 size_t psk_len = 0;
307 unsigned int digest_len, mic_len;
308 eapol_attr_t const *eapol;
309 eapol_attr_t *zeroed;
310 FILE *fp = NULL;
311 char const *filename = (env->filename.type == FR_TYPE_STRING) ? env->filename.vb_strvalue : NULL;
312 char const *psk_identity = NULL, *psk = NULL;
313 uint8_t *p;
314 uint8_t const *snonce, *ap_mac;
315 uint8_t const *min_mac, *max_mac;
316 uint8_t const *min_nonce, *max_nonce;
317 uint8_t pmk[32];
318 uint8_t s_mac[6], message[sizeof("Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128];
319 uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE];
320 char token_identity[256];
321 char token_psk[256];
322
323 /*
324 * Search for the information in a bunch of attributes.
325 */
326 if (env->anonce.vb_length != 32) {
327 RWARN("%s has incorrect length (%zu, not 32)", env->anonce_tmpl->name, env->anonce.vb_length);
329 }
330
331 if (env->key_msg.vb_length < sizeof(*eapol)) {
332 RWARN("%s has incorrect length (%zu < %zu)", env->key_msg_tmpl->name, env->key_msg.vb_length, sizeof(*eapol));
334 }
335
336 if (env->key_msg.vb_length > sizeof(frame)) {
337 RWARN("%s has incorrect length (%zu > %zu)", env->key_msg_tmpl->name, env->key_msg.vb_length, sizeof(frame));
339 }
340
341 /*
342 * At this point, the request has the relevant DPSK
343 * attributes. The module now should return FAIL for
344 * missing / invalid attributes, or REJECT for
345 * authentication failure.
346 *
347 * If the entry is found in a VP or a cache, the module
348 * returns OK. This means that the caller should not
349 * save &control:Pre-Shared-Key somewhere.
350 *
351 * If the module found a matching entry in the file, it
352 * returns UPDATED to indicate that the caller should
353 * update the database with the PSK which was found.
354 */
355
356#ifdef __COVERITY__
357 /*
358 * Coverity doesn't see that fr_base16_decode will populate s_mac
359 */
360 memset(s_mac, 0, 6);
361#endif
362 /*
363 * Get supplicant MAC address from the User-Name
364 */
365 if (fr_base16_decode(NULL, &FR_DBUFF_TMP(s_mac, sizeof(s_mac)),
366 &FR_SBUFF_IN(env->username.vb_strvalue, env->username.vb_length), false) != 6) {
367 RERROR("User-Name is not a recognizable hex MAC address");
369 }
370
371 if (env->calledstation.vb_length != 6) {
372 RERROR("Called-Station-MAC is not a recognizable MAC address");
374 }
375
376 ap_mac = env->calledstation.vb_octets;
377
378 /*
379 * Sort the MACs
380 */
381 if (memcmp(s_mac, ap_mac, 6) <= 0) {
382 min_mac = s_mac;
383 max_mac = ap_mac;
384 } else {
385 min_mac = ap_mac;
386 max_mac = s_mac;
387 }
388
389 eapol = (eapol_attr_t const *) env->key_msg.vb_octets;
390
391 /*
392 * Get supplicant nonce and AP nonce.
393 *
394 * Then sort the nonces.
395 */
396 snonce = env->key_msg.vb_octets + 17;
397 if (memcmp(snonce, env->anonce.vb_octets, 32) <= 0) {
398 min_nonce = snonce;
399 max_nonce = env->anonce.vb_octets;
400 } else {
401 min_nonce = env->anonce.vb_octets;
402 max_nonce = snonce;
403 }
404
405 /*
406 * Create the base message which we will hash.
407 */
408 memcpy(message, "Pairwise key expansion", sizeof("Pairwise key expansion")); /* including trailing NUL */
409 p = &message[sizeof("Pairwise key expansion")];
410
411 memcpy(p, min_mac, 6);
412 memcpy(p + 6, max_mac, 6);
413 p += 12;
414
415 memcpy(p, min_nonce, 32);
416 memcpy(p + 32, max_nonce, 32);
417 p += 64;
418 *p = '\0';
419 fr_assert(sizeof(message) == (p + 1 - message));
420
421 /*
422 * If we're caching, then check the cache first, before
423 * trying the file. This check allows us to avoid the
424 * PMK calculation in many situations, as that can be
425 * expensive.
426 */
427 if (inst->cache_size) {
428 entry = dpsk_cache_find(request, inst, pmk, sizeof(pmk), &env->ssid, s_mac);
429 if (entry) {
430 psk_identity = entry->identity;
431 psk = entry->psk;
432 psk_len = entry->psk_len;
433 goto make_digest;
434 }
435 }
436
437 /*
438 * No cache, or no cache entry. Look for an external PMK
439 * taken from a database.
440 */
441stage1:
442 stage = 1;
443
444 if (env->masterkey.type == FR_TYPE_OCTETS) {
445 if (env->masterkey.vb_length != sizeof(pmk)) {
446 RWARN("%s has incorrect length (%zu != %zu) - ignoring it", env->masterkey_tmpl->name,
447 env->masterkey.vb_length, sizeof(pmk));
448 } else {
449 RDEBUG2("Using %s", env->masterkey_tmpl->name);
450 memcpy(pmk, env->masterkey.vb_octets, sizeof(pmk));
451 goto make_digest;
452 }
453 }
454
455 /*
456 * No external PMK. Try an external PSK.
457 */
458 if (env->psk.type == FR_TYPE_STRING) {
459 RDEBUG3("Trying %s", env->psk_tmpl->name);
460 if (generate_pmk(request, pmk, sizeof(pmk), &env->ssid, env->psk.vb_strvalue, env->psk.vb_length) < 0) {
461 fr_assert(!fp);
463 }
464
465 if (env->psk_identity.type == FR_TYPE_STRING) {
466 psk_identity = env->psk_identity.vb_strvalue;
467 } else {
468 psk_identity = env->username.vb_strvalue;
469 }
470
471 psk = env->psk.vb_strvalue;
472 psk_len = env->psk.vb_length;
473
474 goto make_digest;
475 }
476
477 /*
478 * No external PSK was found. If there's no file, then
479 * we can't do anything else.
480 */
481stage2:
482 stage = 2;
483
484 if (!filename) {
485 RERROR("No %s was found, and no 'filename' was configured", env->psk_tmpl->name);
487 }
488
489 /*
490 * If there's an PSK from an external database, then we
491 * never read the filename.
492 */
493 if (filename) {
494 char token_mac[256];
495 char buffer[1024];
496 fr_sbuff_t sbuff;
498 size_t len;
499 fr_sbuff_term_t const terms = FR_SBUFF_TERMS(L("\n"),L("\r"),L(","));
500 fr_sbuff_term_t const quoted_terms = FR_SBUFF_TERMS(L("\""));
501 bool quoted = false;
502
503 RDEBUG3("Looking for PSK in file %s", filename);
504
505 fp = fopen(filename, "r");
506 if (!fp) {
507 REDEBUG("Failed opening %s - %s", filename, fr_syserror(errno));
509 }
510
511 fr_sbuff_init_file(&sbuff, &fctx, buffer, sizeof(buffer), fp, SIZE_MAX);
512
513stage2a:
514 lineno++;
515 fr_sbuff_adv_past_whitespace(&sbuff, SIZE_MAX, NULL);
516 quoted = fr_sbuff_next_if_char(&sbuff, '"');
517 len = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(token_identity, sizeof(token_identity)), &sbuff,
518 sizeof(token_identity), quoted ? &quoted_terms : &terms, NULL);
519 if (len == 0) {
520 RDEBUG("Failed to find matching PSK or MAC in %s", filename);
521 fail_file:
522 fclose(fp);
524 }
525 if (quoted) {
526 fr_sbuff_next_if_char(&sbuff, '"');
527 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
528 }
529
530 if (!fr_sbuff_next_if_char(&sbuff, ',')) {
531 RDEBUG("%s[%d] Failed to find ',' after identity", filename, lineno);
532 goto fail_file;
533 }
534
535 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
536 quoted = fr_sbuff_next_if_char(&sbuff, '"');
537 len = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(token_psk, sizeof(token_psk)), &sbuff,
538 sizeof(token_identity), quoted ? &quoted_terms : &terms, NULL);
539 if (len == 0) {
540 RDEBUG("%s[%d] Failed parsing PSK", filename, lineno);
541 goto fail_file;
542 }
543 if (quoted) {
544 fr_sbuff_next_if_char(&sbuff, '"');
545 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
546 }
547
548 /*
549 * The MAC is optional. If there is a MAC, we
550 * loop over the file until we find a matching
551 * one.
552 */
553 if (fr_sbuff_next_if_char(&sbuff, ',')) {
554
555 fr_sbuff_adv_past_blank(&sbuff, SIZE_MAX, NULL);
556 quoted = fr_sbuff_next_if_char(&sbuff, '"');
557 len = fr_sbuff_out_bstrncpy_until(&FR_SBUFF_OUT(token_mac, sizeof(token_mac)), &sbuff,
558 sizeof(token_identity), quoted ? &quoted_terms : &terms, NULL);
559 if (len == 0) {
560 RERROR("%s[%d] Failed parsing MAC", filename, lineno);
561 goto fail_file;
562 }
563
564 /*
565 * See if the MAC matches. If not, skip
566 * this entry. That's a basic negative cache.
567 */
568 if ((len != 12) ||
569 (fr_base16_decode(NULL, &FR_DBUFF_TMP((uint8_t *)token_mac, 6),
570 &FR_SBUFF_IN(token_mac, 12), false) != 6)) {
571 RERROR("%s[%d] Failed parsing MAC", filename, lineno);
572 goto fail_file;
573 }
574 if (quoted) fr_sbuff_next_if_char(&sbuff, '"');
575
576 /*
577 * The MAC doesn't match, don't even bother trying to generate the PMK.
578 */
579 if (memcmp(s_mac, token_mac, 6) != 0) {
580 goto stage2a;
581 }
582
583 RDEBUG3("Found matching MAC");
584 stage = 3;
585 }
586
587 /*
588 * Generate the PMK using the SSID, this MAC, and the PSK we just read.
589 */
590 psk = token_psk;
591 psk_len = strlen(token_psk);
592 psk_identity = token_identity;
593
594 RDEBUG3("%s[%d] Trying PSK %s", filename, lineno, token_psk);
595 if (generate_pmk(request, pmk, sizeof(pmk), &env->ssid, psk, psk_len) < 0) {
596 goto fail_file;
597 }
598 }
599
600 /*
601 * HMAC = HMAC_SHA1(pmk, message);
602 *
603 * We need the first 16 octets of this.
604 */
605make_digest:
606 digest_len = sizeof(digest);
607#ifdef __COVERITY__
608 /*
609 * Coverity doesn't see that HMAC will populate digest
610 */
611 memset(digest, 0, digest_len);
612#endif
613 HMAC(EVP_sha1(), pmk, sizeof(pmk), message, sizeof(message), digest, &digest_len);
614
615 RHEXDUMP3(message, sizeof(message), "message:");
616 RHEXDUMP3(pmk, sizeof(pmk), "pmk :");
617 RHEXDUMP3(digest, 16, "kck :");
618
619 /*
620 * Create the frame with the middle field zero, and hash it with the KCK digest we calculated from the key expansion.
621 */
622 memcpy(frame, env->key_msg.vb_octets, env->key_msg.vb_length);
623 zeroed = (eapol_attr_t *) &frame[0];
624 memset(&zeroed->frame.mic[0], 0, 16);
625
626 RHEXDUMP3(frame, env->key_msg.vb_length, "zeroed:");
627
628 mic_len = sizeof(mic);
629#ifdef __COVERITY__
630 /*
631 * Coverity doesn't see that HMAC will populate mic
632 */
633 memset(mic, 0, mic_len);
634#endif
635 HMAC(EVP_sha1(), digest, 16, frame, env->key_msg.vb_length, mic, &mic_len);
636
637 /*
638 * The MICs don't match.
639 */
640 if (memcmp(&eapol->frame.mic[0], mic, 16) != 0) {
641 RDEBUG3("Stage %d", stage);
642 RHEXDUMP3(mic, 16, "calculated mic:");
643 RHEXDUMP3(eapol->frame.mic, 16, "packet mic :");
644
645 psk_identity = NULL;
646 psk = NULL;
647 psk_len = 0;
648
649 /*
650 * Found a cached entry, but it didn't match. Go
651 * check external PMK / PSK.
652 */
653 if (stage == 0) {
654 fr_assert(entry != NULL);
655 fr_rb_delete(&inst->mutable->cache, entry); /* locks and unlinks the entry */
656 entry = NULL;
657 goto stage1;
658 }
659
660 /*
661 * Found an external PMK or PSK, but it didn't
662 * match. Go check the file.
663 */
664 if (stage == 1) {
665 if (env->psk.type == FR_TYPE_STRING) RWARN("%s did not match", env->psk_tmpl->name);
666
667 if (filename) {
668 RDEBUG("Checking file %s for PSK and MAC", filename);
669 goto stage2;
670 }
671
672 RWARN("No 'filename' was configured.");
674 }
675
676 /*
677 * The file is open, so we keep reading it until
678 * we find a matching entry.
679 */
680 fr_assert(fp);
681
682 if (stage == 2) goto stage2a;
683
684 fclose(fp);
685
686 /*
687 * We found a PSK associated with this MAC in the
688 * file. But it didn't match, so we're done.
689 */
690 fr_assert(stage == 3);
691
692 RWARN("Found matching MAC in %s, but the PSK does not match", filename);
694 }
695
696 /*
697 * We found a matching PSK. If we read it from the file,
698 * then close the file, and ensure that we return
699 * UPDATED. This tells the caller to write the entry
700 * into the database, so that we don't need to scan the
701 * file again.
702 */
703 if (fp) {
704 rcode = RLM_MODULE_UPDATED;
705 fr_assert(psk == token_psk);
706 fr_assert(psk_identity == token_identity);
707 fclose(fp);
708 }
709
710 /*
711 * Extend the lifetime of the cache entry, or add the
712 * cache entry if necessary. We only add / update the
713 * cache entry if the PSK was not found in a VP.
714 *
715 * If the caller gave us only a PMK, then don't cache anything.
716 */
717 if (inst->cache_size && psk && psk_identity) {
718 rlm_dpsk_cache_t my_entry;
719
720 /*
721 * We've found an entry. Just update it.
722 */
723 if (entry) goto update_entry;
724
725 /*
726 * No cached entry, or the PSK in the cached
727 * entry didn't match. We need to create one.
728 */
729 memcpy(my_entry.mac, s_mac, sizeof(my_entry.mac));
730 memcpy(&my_entry.ssid, env->ssid.vb_octets, sizeof(my_entry.ssid)); /* const ptr issues */
731 my_entry.ssid_len = env->ssid.vb_length;
732
733 entry = fr_rb_find(&inst->mutable->cache, &my_entry);
734 if (!entry) {
735 /*
736 * Maybe there are oo many entries in the
737 * cache. If so, delete the oldest one.
738 */
739 if (fr_rb_num_elements(&inst->mutable->cache) > inst->cache_size) {
740 PTHREAD_MUTEX_LOCK(&inst->mutable->mutex);
741 entry = fr_dlist_head(&inst->mutable->head);
742 PTHREAD_MUTEX_UNLOCK(&inst->mutable->mutex);
743
744 fr_rb_delete(&inst->mutable->cache, entry); /* locks and unlinks the entry */
745 }
746
747 MEM(entry = talloc_zero(&inst->mutable->cache, rlm_dpsk_cache_t));
748
749 memcpy(entry->mac, s_mac, sizeof(entry->mac));
750 memcpy(entry->pmk, pmk, sizeof(entry->pmk));
751
752 entry->inst = inst;
753
754 /*
755 * Save the SSID, PSK, and PSK identity in the cache entry.
756 */
757 MEM(entry->ssid = talloc_memdup(entry, env->ssid.vb_octets, env->ssid.vb_length));
758 entry->ssid_len = env->ssid.vb_length;
759
760 MEM(entry->psk = talloc_strdup(entry, psk));
761 entry->psk_len = psk_len;
762
763 entry->identity_len = strlen(psk_identity);
764 MEM(entry->identity = talloc_strdup(entry, psk_identity));
765
766 /*
767 * Cache it.
768 */
769 if (!fr_rb_insert(&inst->mutable->cache, entry)) {
770 TALLOC_FREE(entry);
771 goto update_attributes;
772 }
773 RDEBUG3("Cache entry saved");
774 }
775
776 update_entry:
777 PTHREAD_MUTEX_LOCK(&inst->mutable->mutex);
778 entry->expires = fr_time_add(fr_time(), inst->cache_lifetime);
779 if (fr_dlist_entry_in_list(&entry->dlist)) fr_dlist_remove(&inst->mutable->head, entry);
780 fr_dlist_insert_tail(&inst->mutable->head, entry);
781 PTHREAD_MUTEX_UNLOCK(&inst->mutable->mutex);
782
783 /*
784 * Add the PSK to the reply items, if it was cached.
785 */
786 if (entry->psk) {
787 tmpl_t psk_rhs;
788 map_t psk_map = {
789 .lhs = env->psk_dest_tmpl,
790 .op = T_OP_SET,
791 .rhs = &psk_rhs
792 };
793
795 fr_value_box_bstrndup_shallow(&psk_map.rhs->data.literal,
796 NULL, entry->psk, entry->psk_len, true);
797 if (map_to_request(request, &psk_map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
798 }
799 }
800
801update_attributes:
802 /*
803 * We found a cache entry, or an external PSK. Don't
804 * create new attributes.
805 */
806 if (rcode == RLM_MODULE_OK) RETURN_UNLANG_OK;
807
808 fr_assert(psk != NULL);
809 fr_assert(psk_identity != NULL);
810
811 {
812 tmpl_t rhs;
813 map_t map = {
814 .lhs = env->psk_dest_tmpl,
815 .op = T_OP_SET,
816 .rhs = &rhs
817 };
818
819 RDEBUG2("Creating %s and %s", env->psk_dest_tmpl->name, env->psk_identity_dest_tmpl->name);
821 fr_value_box_bstrndup_shallow(&map.rhs->data.literal, NULL, psk, psk_len, true);
822 if (map_to_request(request, &map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
823
824 map.lhs = env->psk_identity_dest_tmpl;
825 fr_value_box_bstrndup_shallow(&map.rhs->data.literal, NULL, psk_identity, strlen(psk_identity), true);
826 if (map_to_request(request, &map, map_to_vp, NULL) < 0) RETURN_UNLANG_FAIL;
827 }
828
830}
831
832static xlat_arg_parser_t const dpsk_pmk_xlat_arg[] = {
833 { .required = true, .concat = true, .type = FR_TYPE_STRING },
834 { .required = true, .concat = true, .type = FR_TYPE_STRING },
836};
837
838/** xlat to generate the PMK from SSID and Pre-Shared-Key
839 *
840 * Example:
841 @verbatim
842 %dpsk.pmk(Calling-Station-SSID, Pre-Shared-Key)
843 @endverbatim
844 *
845 * @ingroup xlat_functions
846 */
847static xlat_action_t dpsk_pmk_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, UNUSED xlat_ctx_t const *xctx,
848 request_t *request, fr_value_box_list_t *in)
849{
850 fr_value_box_t *ssid, *psk, *vb;
851 uint8_t buffer[32];
852
853 XLAT_ARGS(in, &ssid, &psk);
854
855 if (PKCS5_PBKDF2_HMAC_SHA1(psk->vb_strvalue, psk->vb_length,
856 (const unsigned char *) ssid->vb_strvalue, ssid->vb_length,
857 4096, sizeof(buffer), buffer) == 0) {
858 RERROR("Failed calling OpenSSL to calculate the PMK");
859 return XLAT_ACTION_FAIL;
860 }
861
862 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_OCTETS, NULL));
863 fr_value_box_memdup(vb, vb, NULL, buffer, sizeof(buffer), true);
864
866
867 return XLAT_ACTION_DONE;
868}
869
870static int mod_load(void)
871{
872 xlat_t *xlat;
873 if (unlikely((xlat = xlat_func_register(NULL, "dpsk.pmk", dpsk_pmk_xlat, FR_TYPE_OCTETS)) == NULL)) return -1;
874 xlat_func_args_set(xlat, dpsk_pmk_xlat_arg);
875
876 return 0;
877}
878
879static void mod_unload(void)
880{
881 xlat_func_unregister("dpsk.pmk");
882}
883
884static int8_t cmp_cache_entry(void const *one, void const *two)
885{
886 rlm_dpsk_cache_t const *a = (rlm_dpsk_cache_t const *) one;
887 rlm_dpsk_cache_t const *b = (rlm_dpsk_cache_t const *) two;
888 int rcode;
889
890 rcode = memcmp(a->mac, b->mac, sizeof(a->mac));
891 if (rcode != 0) return rcode;
892
893 if (a->ssid_len < b->ssid_len) return -1;
894 if (a->ssid_len > b->ssid_len) return +1;
895
896 return CMP(memcmp(a->ssid, b->ssid, a->ssid_len), 0);
897}
898
899static void free_cache_entry(void *data)
900{
902
903 PTHREAD_MUTEX_LOCK(&entry->inst->mutable->mutex);
905 PTHREAD_MUTEX_UNLOCK(&entry->inst->mutable->mutex);
906
907 talloc_free(entry);
908}
909
910static int mod_detach(const module_detach_ctx_t *mctx)
911{
912 rlm_dpsk_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
913
914 if (!inst->cache_size) return 0;
915
916#ifdef HAVE_PTHREAD_H
917 pthread_mutex_destroy(&inst->mutable->mutex);
918#endif
919
920 talloc_free(inst->mutable);
921
922 return 0;
923}
924#endif
925
926static int mod_instantiate(module_inst_ctx_t const *mctx)
927{
928#ifdef WITH_TLS
929 rlm_dpsk_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_dpsk_t);
930
931 inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->mi->name, -1);
932 if (!inst->auth_type) {
933 WARN("Failed to find 'authenticate %s {...}' section. DPSK will likely not work",
934 mctx->mi->name);
935 }
936
937 /*
938 * We can still use a cache if we're getting PSKs from a
939 * database. The PMK calculation can take time, so
940 * caching the PMK still saves us time.
941 */
942 if (!inst->cache_size) return 0;
943
944 FR_INTEGER_BOUND_CHECK("cache_size", inst->cache_size, <=, ((uint32_t) 1) << 16);
945
946 FR_TIME_DELTA_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, <=, fr_time_delta_from_sec(7 * 86400));
947 FR_TIME_DELTA_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, >=, fr_time_delta_from_sec(3600));
948
949 inst->mutable = talloc_zero(NULL, rlm_dpsk_mutable_t);
950
951 fr_rb_inline_init(&inst->mutable->cache, rlm_dpsk_cache_t, node, cmp_cache_entry, free_cache_entry);
952
953 fr_dlist_init(&inst->mutable->head, rlm_dpsk_cache_t, dlist);
954#ifdef HAVE_PTHREAD_H
955 if (pthread_mutex_init(&inst->mutable->mutex, NULL) < 0) {
956 cf_log_err(mctx->mi->conf, "Failed creating mutex");
957 return -1;
958 }
959#endif
960
961 return 0;
962#else
963 cf_log_err(mctx->mi->conf, "rlm_dpsk requires OpenSSL");
964 return 0;
965#endif
966}
967
968/*
969 * The module name should be the only globally exported symbol.
970 * That is, everything else should be 'static'.
971 *
972 * If the module needs to temporarily modify it's instantiation
973 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
974 * The server will then take care of ensuring that the module
975 * is single-threaded.
976 */
979 .common = {
980 .magic = MODULE_MAGIC_INIT,
981 .name = "dpsk",
982 .inst_size = sizeof(rlm_dpsk_t),
983 .instantiate = mod_instantiate,
985#ifdef WITH_TLS
986 .detach = mod_detach,
987 .onload = mod_load,
988 .unload = mod_unload,
989#endif
990 },
991#ifdef WITH_TLS
992 .method_group = {
993 .bindings = (module_method_binding_t[]){
994 { .section = SECTION_NAME("recv", CF_IDENT_ANY), .method = mod_authorize, .method_env = &dpsk_autz_method_env },
995 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &dpsk_auth_method_env },
996
998 }
999 }
1000#endif
1001};
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:576
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:95
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#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:112
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#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
size_t inst_size
Size of per call env.
Definition call_env.h:245
@ 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
#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:660
#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:520
#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:283
#define FR_TIME_DELTA_BOUND_CHECK(_name, _var, _op, _bound)
Definition cf_parse.h:531
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:597
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:286
#define CF_IDENT_ANY
Definition cf_util.h:78
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:408
#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:287
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:300
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:3532
static fr_slen_t in
Definition dict.h:861
Specifies an attribute which must be present for the module to function.
Definition dict.h:286
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:299
Value of an enumerated attribute.
Definition dict.h:246
#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:260
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:486
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition dlist.h:638
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:163
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:146
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:378
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition log.h:705
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:1592
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:1872
talloc_free(reap)
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
size_t fr_sbuff_out_bstrncpy_until(fr_sbuff_t *out, fr_sbuff_t *in, size_t len, 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:417
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
static const conf_parser_t config[]
Definition base.c:186
static int mod_load(void)
Definition proto_arp.c:236
static void mod_unload(void)
Definition proto_arp.c:245
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RDEBUG(fmt,...)
Definition radclient.h:53
#define WARN(fmt,...)
Definition radclient.h:47
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:781
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
bool 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:741
#define fr_rb_inline_init(_tree, _type, _field, _data_cmp, _data_free)
Initialises a red black tree.
Definition rb.h:180
The main red black tree structure.
Definition rb.h:73
#define RETURN_UNLANG_UPDATED
Definition rcode.h:66
#define RETURN_UNLANG_FAIL
Definition rcode.h:59
#define RETURN_UNLANG_REJECT
Definition rcode.h:58
#define RETURN_UNLANG_OK
Definition rcode.h:60
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:45
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:51
#define RETURN_UNLANG_NOOP
Definition rcode.h:65
static int mod_detach(module_detach_ctx_t const *mctx)
Definition rlm_always.c:137
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_chap.c:176
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_chap.c:228
uint8_t pmk[32]
Definition rlm_dpsk.c:93
tmpl_t * psk_identity_dest_tmpl
Definition rlm_dpsk.c:171
uint8_t rsc[8]
Definition rlm_dpsk.c:69
tmpl_t * psk_dest_tmpl
Definition rlm_dpsk.c:170
fr_rb_tree_t cache
Definition rlm_dpsk.c:110
uint16_t information
Definition rlm_dpsk.c:64
fr_value_box_t key_msg
Definition rlm_dpsk.c:161
fr_rb_node_t node
Definition rlm_dpsk.c:91
size_t ssid_len
Definition rlm_dpsk.c:96
struct rlm_dpsk_s rlm_dpsk_t
Definition rlm_dpsk.c:88
module_rlm_t rlm_dpsk
Definition rlm_dpsk.c:978
uint8_t reserved[8]
Definition rlm_dpsk.c:70
fr_value_box_t username
Definition rlm_dpsk.c:163
static fr_dict_t const * dict_freeradius
Definition rlm_dpsk.c:127
tmpl_t * key_msg_tmpl
Definition rlm_dpsk.c:153
uint8_t mac[6]
Definition rlm_dpsk.c:92
fr_value_box_t masterkey
Definition rlm_dpsk.c:165
#define PTHREAD_MUTEX_LOCK(_x)
Definition rlm_dpsk.c:84
fr_dlist_head_t head
Definition rlm_dpsk.c:115
static fr_dict_attr_t const * attr_auth_type
Definition rlm_dpsk.c:135
fr_dict_autoload_t rlm_dpsk_dict[]
Definition rlm_dpsk.c:130
fr_value_box_t psk_identity
Definition rlm_dpsk.c:169
fr_value_box_t ssid
Definition rlm_dpsk.c:157
rlm_dpsk_t const * inst
Definition rlm_dpsk.c:106
fr_dict_attr_autoload_t rlm_dpsk_dict_attr[]
Definition rlm_dpsk.c:138
uint16_t length
Definition rlm_dpsk.c:65
uint8_t * ssid
Definition rlm_dpsk.c:95
uint8_t mic[16]
Definition rlm_dpsk.c:71
uint8_t iv[16]
Definition rlm_dpsk.c:68
uint8_t nonce[32]
Definition rlm_dpsk.c:67
tmpl_t * masterkey_tmpl
Definition rlm_dpsk.c:166
char * identity
Definition rlm_dpsk.c:98
uint8_t descriptor
Definition rlm_dpsk.c:63
uint32_t cache_size
Definition rlm_dpsk.c:121
fr_dlist_t dlist
Definition rlm_dpsk.c:105
fr_value_box_t filename
Definition rlm_dpsk.c:172
eapol_key_frame_t frame
Definition rlm_dpsk.c:77
#define PTHREAD_MUTEX_UNLOCK(_x)
Definition rlm_dpsk.c:85
tmpl_t * key_msg_tmpl
Definition rlm_dpsk.c:162
uint8_t header[4]
Definition rlm_dpsk.c:76
static const conf_parser_t module_config[]
Definition rlm_dpsk.c:144
rlm_dpsk_mutable_t * mutable
Definition rlm_dpsk.c:124
size_t identity_len
Definition rlm_dpsk.c:99
uint16_t data_len
Definition rlm_dpsk.c:72
fr_dict_enum_value_t const * auth_type
Definition rlm_dpsk.c:119
fr_time_delta_t cache_lifetime
Definition rlm_dpsk.c:122
fr_value_box_t calledstation
Definition rlm_dpsk.c:164
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_dpsk.c:926
fr_value_box_t anonce
Definition rlm_dpsk.c:159
fr_value_box_t psk
Definition rlm_dpsk.c:167
uint8_t replay_counter[8]
Definition rlm_dpsk.c:66
fr_time_t expires
Definition rlm_dpsk.c:103
username
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:2116
#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:192
#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:150
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
char const * name
Instance name e.g. user_database.
Definition module.h:355
CONF_SECTION * conf
Module's instance configuration.
Definition module.h:349
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:291
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition tmpl.h:138
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
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
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
#define talloc_get_type_abort_const
Definition talloc.h:287
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#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:120
@ T_OP_SET
Definition token.h:84
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
uint8_t required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
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
Definition for a single argument consumend by an xlat function.
Definition xlat.h:145
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:4673
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:4830
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:643
static fr_slen_t data
Definition value.h:1293
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1023
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:363
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:216
void xlat_func_unregister(char const *name)
Unregister an xlat function.
Definition xlat_func.c:516