The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
crypto.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: 1fe763077bf31b06022d32388b2df92719e33f7f $
19 * @file eap_psk/crypto.c
20 * @brief The cryptographic primitives of EAP-PSK (RFC 4764).
21 *
22 * EAP-PSK is built entirely on AES-128. This file implements, using the
23 * OpenSSL EVP and CMAC APIs:
24 *
25 * - the key setup (PSK -> AK, KDK),
26 * - the session-key derivation (KDK, RAND_P -> TEK, MSK, EMSK),
27 * - the two authentication MACs (MAC_P and MAC_S), and
28 * - the EAX protected channel (encrypt / decrypt-and-verify).
29 *
30 * @copyright 2026 Network RADIUS SAS (legal@networkradius.com)
31 */
32RCSID("$Id: 1fe763077bf31b06022d32388b2df92719e33f7f $")
33USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
34
35#include <freeradius-devel/util/misc.h>
36
37#include <openssl/evp.h>
38#include <openssl/cmac.h>
39#include <openssl/crypto.h>
40
41#include "crypto.h"
42
43/** A single AES-128 ECB block encryption: out = AES-128(key, in)
44 *
45 * @param[out] out the encrypted 16-byte block.
46 * @param[in] key the 16-byte AES key.
47 * @param[in] in the 16-byte block to encrypt.
48 * @return
49 * - 0 on success.
50 * - -1 on OpenSSL failure.
51 */
52static int aes128_ecb_block(uint8_t out[static 16], uint8_t const key[static 16], uint8_t const in[static 16])
53{
54 EVP_CIPHER_CTX *ctx;
55 int len;
56 int rcode = -1;
57
58 MEM(ctx = EVP_CIPHER_CTX_new());
59
60 /*
61 * Disable padding so that a single 16-byte input block yields
62 * exactly 16 bytes out, with no trailing PKCS#7 block.
63 */
64 if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1) goto done;
65 EVP_CIPHER_CTX_set_padding(ctx, 0);
66
67 if (EVP_EncryptUpdate(ctx, out, &len, in, 16) != 1) goto done;
68 if (EVP_EncryptFinal_ex(ctx, out + len, &len) != 1) goto done;
69
70 rcode = 0;
71
72done:
73 EVP_CIPHER_CTX_free(ctx);
74 return rcode;
75}
76
77/** AES-128 in counter mode
78 *
79 * OpenSSL uses the 16-byte IV as the initial counter block and increments
80 * the whole 128-bit value as a big-endian integer, which is exactly what
81 * EAX (and hence EAP-PSK) requires. CTR is symmetric, so the function
82 * serves both encryption and decryption.
83 *
84 * @param[out] out receives len bytes.
85 * @param[in] key the 16-byte AES key.
86 * @param[in] ctr the initial 16-byte counter block.
87 * @param[in] in the data to encrypt or decrypt.
88 * @param[in] len length of in.
89 * @return
90 * - 0 on success.
91 * - -1 on OpenSSL failure.
92 */
93static int aes128_ctr(uint8_t *out, uint8_t const key[static 16], uint8_t const ctr[static 16],
94 uint8_t const *in, size_t len)
95{
96 int rcode = -1;
97 int outl;
98 EVP_CIPHER_CTX *ctx;
99
100 MEM(ctx = EVP_CIPHER_CTX_new());
101
102 if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, ctr) != 1) goto done;
103
104 if (EVP_EncryptUpdate(ctx, out, &outl, in, (int) len) != 1) goto done;
105 if (EVP_EncryptFinal_ex(ctx, out + outl, &outl) != 1) goto done;
106
107 rcode = 0;
108
109done:
110 EVP_CIPHER_CTX_free(ctx);
111 return rcode;
112}
113
114/** The "modified counter mode" of RFC 4764 Sections 3.1 and 3.2
115 *
116 * A length-increasing function that expands one 16-byte input block into
117 * 'count' output blocks:
118 *
119 * hash = AES-128(key, input)
120 * out_i = AES-128(key, hash XOR c_i) for i = 1 .. count
121 *
122 * where c_i is the integer i encoded as a 16-byte block. Since i is
123 * always small (<= 9) only the low-order byte is ever non-zero.
124 *
125 * @param[out] out receives count * 16 bytes.
126 * @param[in] key the 16-byte AES key.
127 * @param[in] input the 16-byte block to expand.
128 * @param[in] count how many output blocks to produce.
129 * @return
130 * - 0 on success.
131 * - -1 on OpenSSL failure.
132 */
133static int eap_psk_counter_mode(uint8_t *out, uint8_t const key[static 16], uint8_t const input[static 16],
134 size_t count)
135{
136 size_t i;
137 uint8_t hash[16];
138 uint8_t block[16];
139
140 if (aes128_ecb_block(hash, key, input) < 0) return -1;
141
142 for (i = 1; i <= count; i++) {
143 memcpy(block, hash, sizeof(block));
144 block[15] ^= (uint8_t) i;
145
146 if (aes128_ecb_block(out + ((i - 1) * 16), key, block) < 0) return -1;
147 }
148
149 return 0;
150}
151
152/** Key setup: derive AK (counter 1) and KDK (counter 2) from the PSK
153 *
154 * Expands a constant all-zero input block under the PSK using the modified
155 * counter mode. See RFC 4764 Section 3.1, Figure 3.
156 *
157 * @param[out] ak the derived authentication key.
158 * @param[out] kdk the derived key-derivation key.
159 * @param[in] psk the 16-byte pre-shared key.
160 * @return
161 * - 0 on success.
162 * - -1 on OpenSSL failure.
163 */
165 uint8_t const psk[static EAP_PSK_PSK_LEN])
166{
167 uint8_t input[16];
168 uint8_t out[2 * 16];
169
170 memset(input, 0, sizeof(input));
171
172 if (eap_psk_counter_mode(out, psk, input, 2) < 0) return -1;
173
174 memcpy(ak, out, EAP_PSK_AK_LEN);
175 memcpy(kdk, out + 16, EAP_PSK_KDK_LEN);
176
177 return 0;
178}
179
180/** Session-key derivation: expand RAND_P under KDK into nine output blocks
181 *
182 * Block 1 is the TEK, blocks 2..5 are the MSK, and blocks 6..9 are the
183 * EMSK. See RFC 4764 Section 3.2, Figure 7.
184 *
185 * @param[out] tek the transient EAP key, used for the protected channel.
186 * @param[out] msk the 64-byte master session key.
187 * @param[out] emsk the 64-byte extended master session key.
188 * @param[in] kdk the key-derivation key from eap_psk_derive_ak_kdk().
189 * @param[in] rand_p the peer's 16-byte nonce.
190 * @return
191 * - 0 on success.
192 * - -1 on OpenSSL failure.
193 */
195 uint8_t msk[static EAP_PSK_MSK_LEN],
196 uint8_t emsk[static EAP_PSK_EMSK_LEN],
197 uint8_t const kdk[static EAP_PSK_KDK_LEN],
198 uint8_t const rand_p[static EAP_PSK_RAND_LEN])
199{
200 uint8_t out[9 * 16];
201
202 if (eap_psk_counter_mode(out, kdk, rand_p, 9) < 0) return -1;
203
204 memcpy(tek, out, EAP_PSK_TEK_LEN); /* block 1 */
205 memcpy(msk, out + 1 * 16, EAP_PSK_MSK_LEN); /* blocks 2..5 */
206 memcpy(emsk, out + 5 * 16, EAP_PSK_EMSK_LEN); /* blocks 6..9 */
207
208 return 0;
209}
210
211/** AES-128 CMAC over one or more concatenated buffers
212 *
213 * A NULL buffer is skipped, so callers can pass a fixed set of segments.
214 *
215 * The low-level CMAC API is used because it is portable across
216 * OpenSSL 1.1 and 3.x; OpenSSL 3.0 deprecates it in favour of EVP_MAC,
217 * so the deprecation warning is suppressed the same way the rest of the
218 * tree handles deprecated OpenSSL calls.
219 *
220 * @param[out] mac the computed 16-byte CMAC.
221 * @param[in] key the 16-byte AES key.
222 * @param[in] seg1 first data segment. May be NULL.
223 * @param[in] len1 length of seg1.
224 * @param[in] seg2 second data segment. May be NULL.
225 * @param[in] len2 length of seg2.
226 * @param[in] seg3 third data segment. May be NULL.
227 * @param[in] len3 length of seg3.
228 * @param[in] seg4 fourth data segment. May be NULL.
229 * @param[in] len4 length of seg4.
230 * @return
231 * - 0 on success.
232 * - -1 on OpenSSL failure.
233 */
234DIAG_OFF(deprecated-declarations)
235static int eap_psk_cmac(uint8_t mac[static 16],
236 uint8_t const key[static 16],
237 uint8_t const *seg1, size_t len1,
238 uint8_t const *seg2, size_t len2,
239 uint8_t const *seg3, size_t len3,
240 uint8_t const *seg4, size_t len4)
241{
242 int rcode = -1;
243 size_t maclen;
244 CMAC_CTX *ctx;
245
246 MEM(ctx = CMAC_CTX_new());
247
248 if (CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL) != 1) goto done;
249
250 if (seg1 && (len1 > 0) && (CMAC_Update(ctx, seg1, len1) != 1)) goto done;
251 if (seg2 && (len2 > 0) && (CMAC_Update(ctx, seg2, len2) != 1)) goto done;
252 if (seg3 && (len3 > 0) && (CMAC_Update(ctx, seg3, len3) != 1)) goto done;
253 if (seg4 && (len4 > 0) && (CMAC_Update(ctx, seg4, len4) != 1)) goto done;
254
255 if (CMAC_Final(ctx, mac, &maclen) != 1) goto done;
256 if (maclen != 16) goto done;
257
258 rcode = 0;
259
260done:
261 CMAC_CTX_free(ctx);
262 return rcode;
263}
264DIAG_ON(deprecated-declarations)
265
266/** Compute MAC_P = CMAC-AES-128(AK, ID_P || ID_S || RAND_S || RAND_P)
267 *
268 * The peer proves possession of the PSK with MAC_P in the second message.
269 * See RFC 4764 Section 5.2.
270 *
271 * @param[out] mac_p the computed 16-byte MAC.
272 * @param[in] ak the authentication key.
273 * @param[in] id_p the peer's NAI. May not be NULL terminated.
274 * @param[in] id_p_len length of id_p.
275 * @param[in] id_s the server's NAI.
276 * @param[in] id_s_len length of id_s.
277 * @param[in] rand_s the server's nonce.
278 * @param[in] rand_p the peer's nonce.
279 * @return
280 * - 0 on success.
281 * - -1 on OpenSSL failure.
282 */
284 uint8_t const ak[static EAP_PSK_AK_LEN],
285 uint8_t const *id_p, size_t id_p_len,
286 uint8_t const *id_s, size_t id_s_len,
287 uint8_t const rand_s[static EAP_PSK_RAND_LEN],
288 uint8_t const rand_p[static EAP_PSK_RAND_LEN])
289{
290 return eap_psk_cmac(mac_p, ak,
291 id_p, id_p_len,
292 id_s, id_s_len,
293 rand_s, EAP_PSK_RAND_LEN,
294 rand_p, EAP_PSK_RAND_LEN);
295}
296
297/** Compute MAC_S = CMAC-AES-128(AK, ID_S || RAND_P)
298 *
299 * The server proves possession of the PSK with MAC_S in the third message.
300 * See RFC 4764 Section 5.3.
301 *
302 * @param[out] mac_s the computed 16-byte MAC.
303 * @param[in] ak the authentication key.
304 * @param[in] id_s the server's NAI.
305 * @param[in] id_s_len length of id_s.
306 * @param[in] rand_p the peer's nonce.
307 * @return
308 * - 0 on success.
309 * - -1 on OpenSSL failure.
310 */
312 uint8_t const ak[static EAP_PSK_AK_LEN],
313 uint8_t const *id_s, size_t id_s_len,
314 uint8_t const rand_p[static EAP_PSK_RAND_LEN])
315{
316 return eap_psk_cmac(mac_s, ak,
317 id_s, id_s_len,
318 rand_p, EAP_PSK_RAND_LEN,
319 NULL, 0,
320 NULL, 0);
321}
322
323/** The tweaked OMAC used by EAX: OMAC^t(M) = CMAC(K, [t]_16 || M)
324 *
325 * [t]_16 is the integer t encoded as a 16-byte block. t is only ever
326 * 0, 1 or 2 here, so only the low-order byte is non-zero.
327 *
328 * @param[out] out the computed 16-byte OMAC.
329 * @param[in] key the 16-byte AES key.
330 * @param[in] t the OMAC tweak.
331 * @param[in] data the data to authenticate.
332 * @param[in] data_len length of data.
333 * @return
334 * - 0 on success.
335 * - -1 on OpenSSL failure.
336 */
337static int eap_psk_omac(uint8_t out[static 16],
338 uint8_t const key[static 16], uint8_t t,
339 uint8_t const *data, size_t data_len)
340{
341 uint8_t tweak[16];
342
343 memset(tweak, 0, sizeof(tweak));
344 tweak[15] = t;
345
346 return eap_psk_cmac(out, key,
347 tweak, sizeof(tweak),
348 data, data_len,
349 NULL, 0,
350 NULL, 0);
351}
352
353/** Build the 16-byte EAX nonce block from the 4-byte EAP-PSK Nonce N
354 *
355 * N is padded with 96 zero high-order bits, i.e. 12 zero bytes followed
356 * by the 4-byte big-endian counter (RFC 4764 Section 3.3).
357 *
358 * @param[out] block the 16-byte nonce block.
359 * @param[in] nonce the PCHANNEL counter.
360 */
361static void eap_psk_nonce_block(uint8_t block[static 16], uint32_t nonce)
362{
363 memset(block, 0, 16);
364 block[12] = (uint8_t) (nonce >> 24);
365 block[13] = (uint8_t) (nonce >> 16);
366 block[14] = (uint8_t) (nonce >> 8);
367 block[15] = (uint8_t) (nonce);
368}
369
370/** EAX encrypt for the protected channel (RFC 4764 Section 3.3)
371 *
372 * Computes:
373 *
374 * N' = OMAC^0(nonce_block)
375 * H' = OMAC^1(header)
376 * C = CTR_{N'}(plain)
377 * C' = OMAC^2(C)
378 * tag = N' XOR H' XOR C'
379 *
380 * Either the plaintext or its length may be zero (EAP-PSK only ever
381 * protects a single byte, but the code does not rely on that).
382 *
383 * @param[out] cipher receives plain_len bytes of ciphertext.
384 * @param[out] tag receives the 16-byte authentication tag.
385 * @param[in] tek the transient EAP key.
386 * @param[in] nonce the 4-byte PCHANNEL counter (0 for the third message).
387 * @param[in] header the EAX header H to authenticate.
388 * @param[in] header_len length of header.
389 * @param[in] plain the plaintext to encrypt.
390 * @param[in] plain_len length of plain.
391 * @return
392 * - 0 on success.
393 * - -1 on OpenSSL failure.
394 */
396 uint8_t const tek[static EAP_PSK_TEK_LEN], uint32_t nonce,
397 uint8_t const *header, size_t header_len,
398 uint8_t const *plain, size_t plain_len)
399{
400 size_t i;
401 uint8_t nonce_block[16];
402 uint8_t n_omac[16], h_omac[16], c_omac[16];
403
404 eap_psk_nonce_block(nonce_block, nonce);
405
406 if (eap_psk_omac(n_omac, tek, 0, nonce_block, sizeof(nonce_block)) < 0) return -1;
407 if (eap_psk_omac(h_omac, tek, 1, header, header_len) < 0) return -1;
408
409 if ((plain_len > 0) &&
410 (aes128_ctr(cipher, tek, n_omac, plain, plain_len) < 0)) return -1;
411
412 if (eap_psk_omac(c_omac, tek, 2, cipher, plain_len) < 0) return -1;
413
414 for (i = 0; i < EAP_PSK_TAG_LEN; i++) {
415 tag[i] = n_omac[i] ^ h_omac[i] ^ c_omac[i];
416 }
417
418 return 0;
419}
420
421/** EAX decrypt-and-verify for the protected channel (RFC 4764 Section 3.3)
422 *
423 * Recomputes the tag over the received ciphertext and header, and compares
424 * the result (in constant time) against the received tag. Only if the tags
425 * match is the ciphertext decrypted.
426 *
427 * @param[out] plain receives cipher_len bytes of plaintext.
428 * @param[in] tek the transient EAP key.
429 * @param[in] nonce the 4-byte PCHANNEL counter (1 for the fourth message).
430 * @param[in] header the EAX header H to authenticate.
431 * @param[in] header_len length of header.
432 * @param[in] cipher the ciphertext to verify and decrypt.
433 * @param[in] cipher_len length of cipher.
434 * @param[in] tag the received 16-byte authentication tag.
435 * @return
436 * - 0 if the tag is valid and the plaintext was recovered.
437 * - -1 on a bad tag or an OpenSSL error.
438 */
440 uint8_t const tek[static EAP_PSK_TEK_LEN], uint32_t nonce,
441 uint8_t const *header, size_t header_len,
442 uint8_t const *cipher, size_t cipher_len,
443 uint8_t const tag[static EAP_PSK_TAG_LEN])
444{
445 size_t i;
446 uint8_t nonce_block[16];
447 uint8_t n_omac[16], h_omac[16], c_omac[16];
448 uint8_t want[16];
449
450 eap_psk_nonce_block(nonce_block, nonce);
451
452 if (eap_psk_omac(n_omac, tek, 0, nonce_block, sizeof(nonce_block)) < 0) return -1;
453 if (eap_psk_omac(h_omac, tek, 1, header, header_len) < 0) return -1;
454 if (eap_psk_omac(c_omac, tek, 2, cipher, cipher_len) < 0) return -1;
455
456 for (i = 0; i < EAP_PSK_TAG_LEN; i++) {
457 want[i] = n_omac[i] ^ h_omac[i] ^ c_omac[i];
458 }
459
460 /*
461 * Verify the tag BEFORE decrypting, as required by RFC
462 * 4764 section 3.3. Use a constant-time comparison in
463 * order to avoid a timing oracle.
464 */
465 if (fr_digest_cmp(want, tag, EAP_PSK_TAG_LEN) != 0) return -1;
466
467 if ((cipher_len > 0) &&
468 (aes128_ctr(plain, tek, n_omac, cipher, cipher_len) < 0)) return -1;
469
470 return 0;
471}
#define USES_APPLE_DEPRECATED_API
Definition build.h:499
#define RCSID(id)
Definition build.h:512
#define DIAG_ON(_x)
Definition build.h:487
#define DIAG_OFF(_x)
Definition build.h:486
Constants, session state, and crypto declarations for EAP-PSK (RFC 4764)
#define EAP_PSK_TAG_LEN
Definition crypto.h:43
#define EAP_PSK_MAC_LEN
Definition crypto.h:35
#define EAP_PSK_TEK_LEN
Definition crypto.h:39
#define EAP_PSK_PSK_LEN
Definition crypto.h:36
#define EAP_PSK_AK_LEN
Definition crypto.h:37
#define EAP_PSK_EMSK_LEN
Definition crypto.h:41
#define EAP_PSK_MSK_LEN
Definition crypto.h:40
#define EAP_PSK_RAND_LEN
Definition crypto.h:34
#define EAP_PSK_KDK_LEN
Definition crypto.h:38
#define MEM(x)
Definition debug.h:36
static fr_slen_t in
Definition dict.h:882
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
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
static int eap_psk_omac(uint8_t out[static 16], uint8_t const key[static 16], uint8_t t, uint8_t const *data, size_t data_len)
The tweaked OMAC used by EAX: OMAC^t(M) = CMAC(K, [t]_16 || M)
Definition crypto.c:337
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
static USES_APPLE_DEPRECATED_API int aes128_ecb_block(uint8_t out[static 16], uint8_t const key[static 16], uint8_t const in[static 16])
A single AES-128 ECB block encryption: out = AES-128(key, in)
Definition crypto.c:52
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
static int aes128_ctr(uint8_t *out, uint8_t const key[static 16], uint8_t const ctr[static 16], uint8_t const *in, size_t len)
AES-128 in counter mode.
Definition crypto.c:93
static int eap_psk_cmac(uint8_t mac[static 16], uint8_t const key[static 16], uint8_t const *seg1, size_t len1, uint8_t const *seg2, size_t len2, uint8_t const *seg3, size_t len3, uint8_t const *seg4, size_t len4)
AES-128 CMAC over one or more concatenated buffers.
Definition crypto.c:235
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 int eap_psk_counter_mode(uint8_t *out, uint8_t const key[static 16], uint8_t const input[static 16], size_t count)
The "modified counter mode" of RFC 4764 Sections 3.1 and 3.2.
Definition crypto.c:133
static void eap_psk_nonce_block(uint8_t block[static 16], uint32_t nonce)
Build the 16-byte EAX nonce block from the 4-byte EAP-PSK Nonce N.
Definition crypto.c:361
static bool done
Definition radclient.c:80
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
return count
Definition module.c:155
static fr_slen_t data
Definition value.h:1340
static size_t char ** out
Definition value.h:1030