The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
password.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 * @file src/lib/server/password.c
19 * @brief Password normalisation functions
20 *
21 * @copyright 2019 The FreeRADIUS server project
22 * @copyright 2019 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23 */
24RCSID("$Id: ba86f76061c336b20bcb97c0783eff8cae971a05 $")
25
26#include <freeradius-devel/server/password.h>
27
28#include <freeradius-devel/util/atexit.h>
29#include <freeradius-devel/util/base64.h>
30#include <freeradius-devel/util/base16.h>
31#include <freeradius-devel/util/md4.h>
32#include <freeradius-devel/util/md5.h>
33#include <freeradius-devel/util/misc.h>
34#include <freeradius-devel/util/sha1.h>
35#include <freeradius-devel/util/value.h>
36
37#include <freeradius-devel/protocol/freeradius/freeradius.internal.password.h>
38
39#ifdef HAVE_OPENSSL_EVP_H
40# include <freeradius-devel/tls/openssl_user_macros.h>
41# include <openssl/evp.h>
42# include <openssl/sha.h>
43#endif
44
45typedef enum {
46 PASSWORD_CLEARTEXT = 0, //!< Variable length.
47 PASSWORD_HASH, //!< Fixed length.
48 PASSWORD_HASH_SALTED, //!< Fixed length hash, variable length salt.
49 PASSWORD_HASH_VARIABLE //!< Variable length everything.
51
52/** Apply preprocessing logic to a password value
53 *
54 * @param[in] ctx to allocate returned value in.
55 * @param[in] request currently being processed.
56 * @param[in] in Pair containing the password to process.
57 * @
58 */
59typedef fr_pair_t *(*password_preprocess_t)(TALLOC_CTX *ctx, request_t *request, fr_pair_t *in);
60
61/** Password information
62 *
63 */
64typedef struct {
65 password_type_t type; //!< What type of password value this is.
66 fr_dict_attr_t const **da; //!< Dictionary attribute representing this type of password.
67 password_preprocess_t func; //!< Preprocessing function.
68 size_t min_hash_len; //!< Minimum length of the decoded string if normifying.
69 ///< If 0, will be ignored.
70 size_t max_hash_len; //!< Maximum length of the decoded string if normifying.
71 ///< If 0, will be ignored.
72 bool no_normify; //!< Don't attempt to normalise the contents of this
73 ///< attribute using the hex/base64 decoders.
74 bool always_allow; //!< Always allow processing of this attribute, irrespective
75 ///< of what the caller says.
77
78static fr_dict_t const *dict_freeradius = NULL;
79static fr_dict_t const *dict_radius = NULL;
80
84
88
91
97
102
108
113
122
124
127 { .out = &dict_freeradius, .proto = "freeradius" },
128 { .out = &dict_radius, .proto = "radius" },
130};
131
134 { .out = &attr_cleartext, .name = "Password.Cleartext", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
135 { .out = &attr_with_header, .name = "Password.With-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
136 { .out = &attr_root, .name = "Password", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
137
138 { .out = &attr_md5, .name = "Password.MD5", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
139 { .out = &attr_smd5, .name = "Password.SMD5", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
140 { .out = &attr_crypt, .name = "Password.Crypt", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
141 { .out = &attr_sha1, .name = "Password.SHA1", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
142 { .out = &attr_ssha1, .name = "Password.SSHA1", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
143
144 { .out = &attr_sha2, .name = "Password.SHA2", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
145 { .out = &attr_sha2_224, .name = "Password.SHA2-224", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
146 { .out = &attr_sha2_256, .name = "Password.SHA2-256", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
147 { .out = &attr_sha2_384, .name = "Password.SHA2-384", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
148 { .out = &attr_sha2_512, .name = "Password.SHA2-512", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
149
150 { .out = &attr_ssha2_224, .name = "Password.SSHA2-224", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
151 { .out = &attr_ssha2_256, .name = "Password.SSHA2-256", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
152 { .out = &attr_ssha2_384, .name = "Password.SSHA2-384", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
153 { .out = &attr_ssha2_512, .name = "Password.SSHA2-512", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
154
155 { .out = &attr_sha3, .name = "Password.SHA3", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
156 { .out = &attr_sha3_224, .name = "Password.SHA3-224", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
157 { .out = &attr_sha3_256, .name = "Password.SHA3-256", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
158 { .out = &attr_sha3_384, .name = "Password.SHA3-384", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
159 { .out = &attr_sha3_512, .name = "Password.SHA3-512", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
160
161 { .out = &attr_ssha3_224, .name = "Password.SSHA3-224", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
162 { .out = &attr_ssha3_256, .name = "Password.SSHA3-256", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
163 { .out = &attr_ssha3_384, .name = "Password.SSHA3-384", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
164 { .out = &attr_ssha3_512, .name = "Password.SSHA3-512", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
165
166 { .out = &attr_pbkdf2, .name = "Password.PBKDF2", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
167 { .out = &attr_pbkdf2_sha1, .name = "Password.PBKDF2-SHA1", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
168 { .out = &attr_pbkdf2_sha256, .name = "Password.PBKDF2-SHA256", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
169 { .out = &attr_pbkdf2_sha512, .name = "Password.PBKDF2-SHA512", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
170 { .out = &attr_pbkdf2_sha256_legacy, .name = "Password.PBKDF2-SHA256-LEGACY", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
171 { .out = &attr_lm, .name = "Password.LM", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
172 { .out = &attr_nt, .name = "Password.NT", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
173 { .out = &attr_ns_mta_md5, .name = "Password.NS-MTA-MD5", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
174
175 { .out = &attr_user, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius },
176
178};
179
185
187 { L("base64"), NORMALISED_B64 },
188 { L("hex"), NORMALISED_HEX },
189 { L("nothing"), NORMALISED_NOTHING }
190};
192
194 { L("cleartext"), PASSWORD_CLEARTEXT },
195 { L("hashed"), PASSWORD_HASH },
196 { L("salted-hash"), PASSWORD_HASH_SALTED },
197 { L("variable-length-hash"), PASSWORD_HASH_VARIABLE }
198};
200
201/*
202 * Headers for the Password-with-Header attribute
203 *
204 * @note Header comparison is case insensitive.
205 */
207 { L("{base64_md5}"), FR_MD5 },
208 { L("{clear}"), FR_CLEARTEXT },
209 { L("{cleartext}"), FR_CLEARTEXT },
210 { L("{crypt}"), FR_CRYPT },
211 { L("{md4}"), FR_NT },
212 { L("{md5}"), FR_MD5 },
213 { L("{ns-mta-md5}"), FR_NS_MTA_MD5 },
214 { L("{nt}"), FR_NT },
215 { L("{nthash}"), FR_NT },
216
217 { L("{pbkdf2-sha1}"), FR_PBKDF2_SHA1 },
218 { L("{pbkdf2-sha256}"), FR_PBKDF2_SHA256 },
219 { L("{pbkdf2-sha512}"), FR_PBKDF2_SHA512 },
220 { L("{pbkdf2_sha256}"), FR_PBKDF2_SHA256_LEGACY },
221
222#ifdef HAVE_OPENSSL_EVP_H
223 { L("{sha224}"), FR_SHA2 },
224 { L("{sha256}"), FR_SHA2 },
225 { L("{sha2}"), FR_SHA2 },
226 { L("{sha384}"), FR_SHA2_384 },
227 { L("{sha512}"), FR_SHA2_512 },
228#endif
229 { L("{sha}"), FR_SHA1 },
230 { L("{smd5}"), FR_SMD5 },
231#ifdef HAVE_OPENSSL_EVP_H
232 { L("{ssha224}"), FR_SSHA2_224 },
233 { L("{ssha256}"), FR_SSHA2_256 },
234 { L("{ssha3-224}"), FR_SSHA3_224 },
235 { L("{ssha3-256}"), FR_SSHA3_256 },
236 { L("{ssha3-384}"), FR_SSHA3_384 },
237 { L("{ssha3-512}"), FR_SSHA3_512 },
238 { L("{ssha384}"), FR_SSHA2_384 },
239 { L("{ssha512}"), FR_SSHA2_512 },
240#endif
241 { L("{ssha}"), FR_SSHA1 },
242 { L("{x- orcllmv}"), FR_LM },
243 { L("{x- orclntv}"), FR_NT },
244 { L("{x-nthash}"), FR_NT },
245 { L("{x-pbkdf2}"), FR_PBKDF2 },
246};
248
249#ifdef HAVE_OPENSSL_EVP_H
250static fr_pair_t *password_process_sha2(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good);
251static fr_pair_t *password_process_sha3(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good);
252#endif
253static fr_pair_t *password_process_header(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good);
254
255/** Metadata for various password attributes
256 *
257 */
259 [FR_CLEARTEXT] = {
261 .da = &attr_cleartext,
262 .no_normify = true
263 },
264 [FR_CRYPT] = {
265 .type = PASSWORD_HASH,
266 .da = &attr_crypt
267 },
268 [FR_LM] = {
269 .type = PASSWORD_HASH,
270 .da = &attr_lm,
271 .min_hash_len = MD4_DIGEST_LENGTH
272 },
273 [FR_MD5] = {
274 .type = PASSWORD_HASH,
275 .da = &attr_md5,
276 .min_hash_len = MD5_DIGEST_LENGTH
277 },
278 [FR_NS_MTA_MD5] = {
279 .type = PASSWORD_HASH,
280 .da = &attr_ns_mta_md5
281 },
282 [FR_NT] = {
283 .type = PASSWORD_HASH,
284 .da = &attr_nt,
285 .min_hash_len = MD4_DIGEST_LENGTH
286 },
287 [FR_WITH_HEADER] = {
289 .da = &attr_with_header,
291 .always_allow = true
292 },
293 [FR_PBKDF2] = {
295 .da = &attr_pbkdf2
296 },
297 [FR_PBKDF2_SHA1] = {
299 .da = &attr_pbkdf2_sha1
300 },
301 [FR_PBKDF2_SHA256] = {
303 .da = &attr_pbkdf2_sha256
304 },
305 [FR_PBKDF2_SHA512] = {
307 .da = &attr_pbkdf2_sha512
308 },
309 [FR_PBKDF2_SHA256_LEGACY] = {
312 },
313 [FR_SHA1] = {
314 .type = PASSWORD_HASH,
315 .da = &attr_sha1,
316 .min_hash_len = SHA1_DIGEST_LENGTH
317 },
318#ifdef HAVE_OPENSSL_EVP_H
319 [FR_SHA2] = {
321 .da = &attr_sha2,
322 .func = password_process_sha2,
323 .min_hash_len = SHA224_DIGEST_LENGTH,
324 .max_hash_len = SHA512_DIGEST_LENGTH
325 },
326 [FR_SHA2_224] = {
327 .type = PASSWORD_HASH,
328 .da = &attr_sha2_224,
329 .min_hash_len = SHA224_DIGEST_LENGTH,
330 },
331 [FR_SHA2_256] = {
332 .type = PASSWORD_HASH,
333 .da = &attr_sha2_256,
334 .min_hash_len = SHA256_DIGEST_LENGTH,
335 },
336 [FR_SHA2_384] = {
337 .type = PASSWORD_HASH,
338 .da = &attr_sha2_384,
339 .min_hash_len = SHA384_DIGEST_LENGTH,
340 },
341 [FR_SHA2_512] = {
342 .type = PASSWORD_HASH,
343 .da = &attr_sha2_512,
344 .min_hash_len = SHA512_DIGEST_LENGTH,
345 },
346 [FR_SHA3] = {
348 .da = &attr_sha3,
349 .func = password_process_sha3,
350 .min_hash_len = SHA224_DIGEST_LENGTH,
351 },
352 [FR_SHA3_224] = {
353 .type = PASSWORD_HASH,
354 .da = &attr_sha3_224,
355 .min_hash_len = SHA224_DIGEST_LENGTH,
356 },
357 [FR_SHA3_256] = {
358 .type = PASSWORD_HASH,
359 .da = &attr_sha3_256,
360 .min_hash_len = SHA256_DIGEST_LENGTH,
361 },
362 [FR_SHA3_384] = {
363 .type = PASSWORD_HASH,
364 .da = &attr_sha3_384,
365 .min_hash_len = SHA384_DIGEST_LENGTH,
366 },
367 [FR_SHA3_512] = {
368 .type = PASSWORD_HASH,
369 .da = &attr_sha3_512,
370 .min_hash_len = SHA512_DIGEST_LENGTH
371 },
372#endif
373 [FR_SMD5] = {
374 .type = PASSWORD_HASH,
375 .da = &attr_smd5,
376 .min_hash_len = MD5_DIGEST_LENGTH
377 },
378 [FR_SSHA1] = {
379 .type = PASSWORD_HASH_SALTED,
380 .da = &attr_ssha1,
381 .min_hash_len = SHA1_DIGEST_LENGTH
382 },
383#ifdef HAVE_OPENSSL_EVP_H
384 [FR_SSHA2_224] = {
385 .type = PASSWORD_HASH_SALTED,
386 .da = &attr_ssha2_224,
387 .min_hash_len = SHA224_DIGEST_LENGTH
388 },
389 [FR_SSHA2_256] = {
390 .type = PASSWORD_HASH_SALTED,
391 .da = &attr_ssha2_256,
392 .min_hash_len = SHA256_DIGEST_LENGTH
393 },
394 [FR_SSHA2_384] = {
395 .type = PASSWORD_HASH_SALTED,
396 .da = &attr_ssha2_384,
397 .min_hash_len = SHA384_DIGEST_LENGTH
398 },
399 [FR_SSHA2_512] = {
400 .type = PASSWORD_HASH_SALTED,
401 .da = &attr_ssha2_512,
402 .min_hash_len = SHA512_DIGEST_LENGTH
403 },
404 [FR_SSHA3_224] = {
405 .type = PASSWORD_HASH_SALTED,
406 .da = &attr_ssha3_224,
407 .min_hash_len = SHA224_DIGEST_LENGTH,
408 },
409 [FR_SSHA3_256] = {
410 .type = PASSWORD_HASH_SALTED,
411 .da = &attr_ssha3_256,
412 .min_hash_len = SHA256_DIGEST_LENGTH
413 },
414 [FR_SSHA3_384] = {
415 .type = PASSWORD_HASH_SALTED,
416 .da = &attr_ssha3_384,
417 .min_hash_len = SHA384_DIGEST_LENGTH
418 },
419 [FR_SSHA3_512] = {
420 .type = PASSWORD_HASH_SALTED,
421 .da = &attr_ssha3_512,
422 .min_hash_len = SHA512_DIGEST_LENGTH
423 }
424#endif
425};
426
427#define MIN_LEN(_info) ((_info)->type == PASSWORD_HASH_SALTED ? ((_info)->min_hash_len + 1) : (_info)->min_hash_len)
428
429static ssize_t normify(normalise_t *action, uint8_t *buffer, size_t bufflen,
430 char const *known_good, size_t len, size_t min_len)
431{
432 /*
433 * Else unknown encoding, or already binary. Leave it.
434 */
435 if (action) *action = NORMALISED_NOTHING;
436
437 if (min_len >= bufflen) return 0; /* paranoia */
438
439 /*
440 * Hex encoding. Length is even, and it's greater than
441 * twice the minimum length.
442 */
443 if (!(len & 0x01) && len >= (2 * min_len)) {
444 ssize_t decoded;
445
446 buffer[0] = 0x00; /* clang scan */
447
448 decoded = fr_base16_decode(NULL, &FR_DBUFF_TMP(buffer, bufflen), &FR_SBUFF_IN(known_good, len), true);
449 if (decoded == (ssize_t)(len >> 1)) {
450 if (action) *action = NORMALISED_HEX;
451 return decoded;
452 }
453 }
454
455 /*
456 * Base 64 encoding. It's at least 4/3 the original size,
457 * and we want to avoid division...
458 */
459 if ((len * 3) >= ((min_len * 4))) {
460 ssize_t decoded;
461
462 decoded = fr_base64_decode(&FR_DBUFF_TMP(buffer, bufflen), &FR_SBUFF_IN(known_good, len), true, true);
463 if (decoded <= 0) return 0;
464 if (decoded >= (ssize_t) min_len) {
465 if (action) *action = NORMALISED_B64;
466 return decoded;
467 }
468 }
469
470 return 0;
471}
472
473/** Hex or base64 or bin auto-discovery
474 *
475 * Here we try and autodiscover what encoding was used for the password/hash, and
476 * convert it back to binary or plaintext.
477 *
478 * @note Earlier versions used a 0x prefix as a hard indicator that the string was
479 * hex encoded, and would fail if the 0x was present but the string didn't
480 * consist of hexits. The base64 char set is a superset of hex, and it was
481 * observed in the wild, that occasionally base64 encoded data really could
482 * start with 0x. That's why min_len (and decodability) are used as the
483 * only heuristics now.
484 *
485 * @param[in] ctx to allocate new pairs in.
486 * @param[in] request The current request.
487 * @param[in] known_good password to normify.
488 * @return
489 * - NULL if known_good was already normalised, or couldn't be normalised.
490 * - A new normalised password pair.
491 */
492static fr_pair_t *password_normify(TALLOC_CTX *ctx, request_t *request, fr_pair_t const *known_good)
493{
494 uint8_t buffer[256];
495 ssize_t decoded;
496 fr_pair_t *out;
497 normalise_t normalised;
498 password_info_t *info;
499 size_t min_len;
500
501 if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
502
503 info = &password_info[known_good->da->attr];
504 min_len = MIN_LEN(info);
505 if (min_len >= sizeof(buffer)) return NULL; /* paranoia */
506
507 switch (known_good->vp_type) {
508 case FR_TYPE_OCTETS:
509 decoded = normify(&normalised, buffer, sizeof(buffer),
510 (char const *)known_good->vp_octets, known_good->vp_length, min_len);
511 break;
512
513 case FR_TYPE_STRING:
514 decoded = normify(&normalised, buffer, sizeof(buffer),
515 known_good->vp_strvalue, known_good->vp_length, min_len);
516 break;
517
518 default:
519 return NULL;
520 }
521
522 if (normalised != NORMALISED_NOTHING) {
523 RDEBUG2("Normalizing %s %s encoding, %zu bytes -> %zu bytes",
524 known_good->da->name, fr_table_str_by_value(normalise_table, normalised, 0),
525 known_good->vp_length, decoded);
526 MEM(out = fr_pair_afrom_da(ctx, known_good->da));
527 fr_pair_value_memdup(out, buffer, decoded, known_good->vp_tainted);
528 return out;
529 }
530
531 /*
532 * Else unknown encoding, or already binary. Leave it.
533 */
534 return NULL;
535}
536
537#ifdef HAVE_OPENSSL_EVP_H
538/** Split SHA2 hashes into separate attributes based on their length
539 *
540 * @param[in] ctx to allocate attributes in.
541 * @param[in] request The current request.
542 * @param[in] known_good attribute to split.
543 * @return
544 * - A SHA2 length specific attribute.
545 * - NULL on error.
546 */
547static fr_pair_t *password_process_sha2(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good)
548{
549 fr_pair_t *out, *normalised;
550
551 switch (known_good->vp_length) {
552 case SHA224_DIGEST_LENGTH:
554 fr_pair_value_copy(out, known_good);
555 return out;
556
557 case SHA256_DIGEST_LENGTH:
559 fr_pair_value_copy(out, known_good);
560 return out;
561
562 case SHA384_DIGEST_LENGTH:
564 fr_pair_value_copy(out, known_good);
565 return out;
566
567 case SHA512_DIGEST_LENGTH:
569 fr_pair_value_copy(out, known_good);
570 return out;
571
572 default:
573 out = password_normify(ctx, request, known_good);
574 if (!out) return NULL;
575
576 normalised = password_process_sha2(ctx, request, out);
577 TALLOC_FREE(out);
578
579 return normalised;
580 }
581}
582
583/** Split SHA3 hashes into separate attributes based on their length
584 *
585 * @param[in] ctx to allocate attributes in.
586 * @param[in] request The current request.
587 * @param[in] known_good attribute to split.
588 * @return
589 * - A SHA3 length specific attribute.
590 * - NULL on error.
591 */
592static fr_pair_t *password_process_sha3(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good)
593{
594 fr_pair_t *out, *normalised;
595
596 switch (known_good->vp_length) {
597 case SHA224_DIGEST_LENGTH:
599 fr_pair_value_copy(out, known_good);
600 return out;
601
602 case SHA256_DIGEST_LENGTH:
604 fr_pair_value_copy(out, known_good);
605 return out;
606
607 case SHA384_DIGEST_LENGTH:
609 fr_pair_value_copy(out, known_good);
610 return out;
611
612 case SHA512_DIGEST_LENGTH:
614 fr_pair_value_copy(out, known_good);
615 return out;
616
617 default:
618 out = password_normify(ctx, request, known_good);
619 if (!out) return NULL;
620
621 normalised = password_process_sha3(ctx, request, out);
622 TALLOC_FREE(out);
623
624 return normalised;
625 }
626}
627#endif
628
629/** Convert a Password.With-Header attribute to the correct type
630 *
631 * Attribute may be base64 encoded, in which case it will be decoded
632 * first, then evaluated.
633 *
634 * @note The buffer for octets types\ attributes is extended by one byte
635 * and '\0' terminated, to allow it to be used as a char buff.
636 *
637 * @param[in] ctx to allocate new pairs in.
638 * @param[in] request Current request.
639 * @param[in] known_good Password.With-Header attribute to convert.
640 * @return
641 * - Buffer containing normified value on success.
642 * - NULL on error.
643 */
644static fr_pair_t *password_process_header(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good)
645{
646 char const *p, *q, *end;
647
648 uint8_t n1[256], n2[256];
649 ssize_t decoded;
650
651 char header[128];
652 normalise_t normalised;
653
654 fr_pair_t *new;
655 fr_dict_attr_t const *def = attr_cleartext;
656
657 PAIR_VERIFY(known_good);
658
659 /*
660 * Ensure this is only ever called with a
661 * string type attribute.
662 */
663 fr_assert(known_good->vp_type == FR_TYPE_STRING);
664
665 p = known_good->vp_strvalue;
666 end = p + known_good->vp_length;
667
668 /*
669 * Has a header {...} prefix
670 */
671do_header:
672 if ((*p == '{') && (q = memchr(p, '}', end - p))) {
673 size_t hlen;
674 int attr;
675 password_info_t *info;
676
677 hlen = (q - p) + 1;
678 if (hlen >= sizeof(header)) {
679 REDEBUG("Password header too long. Got %zu bytes must be less than %zu bytes",
680 hlen, sizeof(header));
681 return NULL;
682 }
683
684 memcpy(header, p, hlen);
685 header[hlen] = '\0';
686
687 attr = fr_table_value_by_substr(password_header_table, header, hlen, -1);
688 if (attr < 0) {
689 /*
690 * header buffer retains { and }
691 */
692 if (RDEBUG_ENABLED3) {
693 RDEBUG3("Unknown header %s in %pP, re-writing to %s",
694 header, known_good, def->name);
695 } else {
696 RDEBUG2("Unknown header %s in %s, re-writing to %s",
697 header, known_good->da->name, def->name);
698 }
699 p = q + 1;
700 goto bad_header;
701 }
702
703 p = q + 1;
704
705 if (!fr_cond_assert((size_t) attr < NUM_ELEMENTS(password_info))) return NULL;
706 info = &password_info[attr];
707
708 MEM(new = fr_pair_afrom_da(ctx, *(info->da)));
709 switch ((*(info->da))->type) {
710 case FR_TYPE_OCTETS:
711 fr_pair_value_memdup(new, (uint8_t const *)p, end - p, true);
712 break;
713
714 case FR_TYPE_STRING:
715 fr_pair_value_bstrndup(new, p, end - p, true);
716 break;
717
718 default:
719 talloc_free(new);
720 fr_assert_fail(NULL);
721 return NULL;
722 }
723 return new;
724 }
725
726#ifdef STATIC_ANALYZER
727 /*
728 * static analyzer isn't smart enough to notice that "normify" clears out n1.
729 */
730 memset(n1, 0, sizeof(n1));
731#endif
732
733 /*
734 * Doesn't have a header {...} prefix
735 *
736 * See if it's base64 or hex, if it is, decode it and check again!
737 *
738 * We ignore request not to normify, as curly braces aren't
739 * in either of the character sets for the encoding schemes
740 * we're normifying, so there's not the possibility for error
741 * as there is normifying other password hashes.
742 */
743 decoded = normify(&normalised, n1, sizeof(n1), p, end - p, 4); /* { + <char> + } + <char> */
744 if (decoded > 0) {
745 if ((n1[0] == '{') && (memchr(n1, '}', decoded) != NULL)) {
746 RDEBUG2("Normalizing %s %s encoding, %zu bytes -> %zu bytes",
747 known_good->da->name, fr_table_str_by_value(normalise_table, normalised, 0),
748 known_good->vp_length, decoded);
749
750 /*
751 * Password.With-Header is a string attribute.
752 * Even though we're handling binary data, the header
753 * must be \0 terminated.
754 */
755 memcpy(n2, n1, decoded);
756 p = (char const *)n2;
757 end = p + decoded;
758 goto do_header;
759 }
760 }
761
762 /*
763 * Rewrite to the default attribute type
764 * currently Password.Cleartext.
765 *
766 * This is usually correct if there's no
767 * header to indicate hash type.
768 */
769 if (RDEBUG_ENABLED3) {
770 RDEBUG3("No {...} in control.%pP, re-writing to %s", known_good, def->name);
771 } else {
772 RDEBUG2("No {...} in control.%s, re-writing to %s", known_good->da->name, def->name);
773 }
774
775bad_header:
776 MEM(new = fr_pair_afrom_da(ctx, def));
777 fr_pair_value_bstrndup(new, p, end - p, true);
778
779 return new;
780}
781
782/** Apply any processing and normification
783 *
784 */
785static fr_pair_t *password_process(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good, bool normify)
786{
787 password_info_t *info;
788 fr_pair_t *out;
789
790 if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
791
792 info = &password_info[known_good->da->attr];
793 if (info->func) {
794 fr_pair_t *from_func, *from_recurse;
795
796 /*
797 * Pass our input attribute to a custom preprocessing
798 * function to manipulate it.
799 */
800 from_func = info->func(ctx, request, known_good);
801 if (!from_func) return NULL;
802
803 /*
804 * Processing function may have produced a different
805 * password type, recurse to deal with it...
806 */
807 from_recurse = password_process(ctx, request, from_func, normify);
808
809 /*
810 * Cleanup any intermediary password attributes created
811 * from running the different normalisation and parsing
812 * operations.
813 */
814 if (!from_recurse) {
815 if (from_func != known_good) TALLOC_FREE(from_func);
816 return NULL;
817 }
818 if ((from_func != known_good) && (from_recurse != from_func)) TALLOC_FREE(from_func);
819
820 return from_recurse;
821 }
822
823 /*
824 * Only normify if we're told to, and we have more data
825 * than the minimum length.
826 */
827 if (normify && !info->no_normify && (known_good->vp_length > info->min_hash_len)) {
828 fr_pair_t *from_normify;
829
830 from_normify = password_normify(ctx, request, known_good);
831 out = from_normify ? from_normify : known_good;
832 } else {
833 out = known_good;
834 }
835
836 /*
837 * Sanity checks - Too short
838 */
839 if (info->min_hash_len && (out->vp_length < MIN_LEN(info))) {
840 if (RDEBUG_ENABLED3) {
841 RWDEBUG3("control.%pP too short, expected %zu bytes, got %zu bytes",
842 out, MIN_LEN(info), out->vp_length);
843 } else {
844 RWDEBUG2("control.%s too short, expected %zu bytes, got %zu bytes",
845 out->da->name, MIN_LEN(info), out->vp_length);
846 }
847 invalid:
848 if (out != known_good) TALLOC_FREE(out); /* Free attribute we won't be returning */
849 return NULL;
850 }
851
852 /*
853 * Sanity checks - Too long
854 */
855 if (info->max_hash_len && (out->vp_length > info->max_hash_len)) {
856 if (RDEBUG_ENABLED3) {
857 RWDEBUG3("control.%pP too long, expected %zu bytes, got %zu bytes",
858 out, info->max_hash_len, out->vp_length);
859 } else {
860 RWDEBUG2("control.%s too long, expected %zu bytes, got %zu bytes",
861 out->da->name, info->max_hash_len, out->vp_length);
862 }
863 goto invalid;
864 }
865
866 /*
867 * Sanity checks - Hashes are a fixed length
868 */
869 if ((info->type == PASSWORD_HASH) && (out->vp_length != info->min_hash_len)) {
870
871 if (RDEBUG_ENABLED3) {
872 RWDEBUG3("control.%pP incorrect length, expected %zu bytes, got %zu bytes",
873 out, info->min_hash_len, out->vp_length);
874 } else {
875 RWDEBUG2("control.%s incorrect length, expected %zu bytes, got %zu bytes",
876 out->da->name, info->min_hash_len, out->vp_length);
877 }
878 goto invalid;
879 }
880
881 return out;
882}
883
884/** Find all password attributes in the control list of a request and normalise them
885 *
886 * @param[in] request The current request.
887 * @param[in] normify Apply hex/base64 normalisation to attributes.
888 * @return the number of attributes normalised.
889 */
891{
892 fr_dcursor_t cursor;
893 int replaced = 0;
894 fr_pair_t *known_good, *new;
895
896 for (known_good = fr_pair_dcursor_by_ancestor_init(&cursor, &request->control_pairs, attr_root);
897 known_good;
898 known_good = fr_dcursor_next(&cursor)) {
899 if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return -1;
900
901 /*
902 * Apply preprocessing steps and normalisation.
903 */
904 new = password_process(request, request, known_good, normify);
905 if (!new) continue; /* Process next input attribute */
906
907 /*
908 * If we didn't do anything to it, we do nothing.
909 */
910 if (new == known_good) {
911 replaced++;
912 continue;
913 }
914
915 if (RDEBUG_ENABLED3) {
916 RDEBUG3("Replacing control.%pP with control.%pP",
917 known_good, new);
918
919 } else {
920 RDEBUG2("Replacing control.%s with control.%s",
921 known_good->da->name, new->da->name);
922 }
923 fr_dcursor_free_item(&cursor);
924 fr_dcursor_prepend(&cursor, new);
925 replaced++;
926 }
927
928 return replaced;
929}
930
931static fr_pair_t *password_normalise_and_recheck(TALLOC_CTX *ctx, request_t *request,
932 fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len,
933 bool normify, fr_pair_t *const known_good)
934{
935 fr_pair_t *new;
936 size_t j;
937
938 if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
939
940 /*
941 * Apply preprocessing steps and normalisation.
942 */
943 new = password_process(ctx, request, known_good, normify);
944 if (!new) return NULL;
945
946 /*
947 * If new != known_good, then we need
948 * to check what was produced is still
949 * acceptable.
950 */
951 if (new->da != known_good->da) {
952 for (j = 0; j < allowed_attrs_len; j++) if (allowed_attrs[j] == new->da) return new;
953
954 /*
955 * New attribute not in our allowed list
956 */
957 TALLOC_FREE(new); /* da didn't match, treat as ephemeral */
958 return NULL; /* Process next input attribute */
959 }
960
961 /*
962 * Return attribute for processing
963 */
964 return new;
965}
966
967/** Find a "known good" password in the control list of a request
968 *
969 * Searches for a "known good" password attribute, and applies any processing
970 * and normification operations to it, returning a new normalised fr_pair_t.
971 *
972 * The ctx passed in should be freed when the caller is done with the returned
973 * fr_pair_t, or alternatively, a persistent ctx may be used and the value
974 * of ephemeral checked.
975 * If ephemeral is false the returned pair *MUST NOT BE FREED*, it may be an
976 * attribute in the request->control_pairs list. If ephemeral is true, the returned
977 * pair *MUST* be freed, or added to one of the pair lists appropriate to the
978 * ctx passed in.
979 *
980 * @param[out] ephemeral If true, the caller must use TALLOC_FREE
981 * to free the return value of this function.
982 * Alternatively 'ctx' can be freed, which is
983 * simpler and cleaner, but some people have
984 * religious objections to that.
985 * @param[in] ctx Ephemeral ctx to allocate new attributes in.
986 * @param[in] request The current request.
987 * @param[in] allowed_attrs Optional list of allowed attributes.
988 * @param[in] allowed_attrs_len Length of allowed attributes list.
989 * @param[in] normify Apply hex/base64 normalisation to attributes.
990 * @return
991 * - A fr_pair_t containing a "known good" password.
992 * - NULL on error, or if no usable password attributes were found.
993 */
994fr_pair_t *password_find(bool *ephemeral, TALLOC_CTX *ctx, request_t *request,
995 fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len, bool normify)
996{
997 fr_dcursor_t cursor;
998 fr_pair_t *known_good;
999
1000 if (fr_pair_find_by_da(&request->control_pairs, NULL, attr_user) != NULL) {
1001 RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
1002 RWDEBUG("!!! Ignoring control.User-Password. Update your !!!");
1003 RWDEBUG("!!! configuration so that the \"known good\" clear text !!!");
1004 RWDEBUG("!!! password is in Password.Cleartext and NOT in !!!");
1005 RWDEBUG("!!! User-Password. !!!");
1006 RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
1007 }
1008
1009 for (known_good = fr_pair_dcursor_by_ancestor_init(&cursor, &request->control_pairs, attr_root);
1010 known_good;
1011 known_good = fr_dcursor_next(&cursor)) {
1012 password_info_t *info;
1013 fr_pair_t *out;
1014 size_t i;
1015
1016 if (known_good->da->attr >= NUM_ELEMENTS(password_info)) continue;
1017
1018 info = &password_info[known_good->da->attr];
1019
1020 /*
1021 * Minor reduction in work for the caller
1022 * for a moderate increase in code complexity.
1023 */
1024 if (info->always_allow) {
1025 out = password_normalise_and_recheck(ctx, request,
1026 allowed_attrs, allowed_attrs_len,
1027 normify, known_good);
1028 if (!out) continue;
1029 done:
1030 if (RDEBUG_ENABLED3) {
1031 RDEBUG3("Using \"known good\" %s password %pP",
1033 password_info[out->da->attr].type,
1034 "<INVALID>"), out);
1035 } else {
1036 RDEBUG2("Using \"known good\" %s password %s",
1038 password_info[out->da->attr].type,
1039 "<INVALID>"), out->da->name);
1040 }
1041 if (ephemeral) *ephemeral = (known_good != out);
1042 return out;
1043 }
1044
1045 for (i = 0; i < allowed_attrs_len; i++) {
1046 if (allowed_attrs[i] != known_good->da) continue;
1047
1048 out = password_normalise_and_recheck(ctx, request,
1049 allowed_attrs, allowed_attrs_len,
1050 normify, known_good);
1051 if (!out) continue;
1052 goto done;
1053 }
1054 }
1055
1056 return NULL;
1057}
1058
1059static int _password_init(UNUSED void *uctx)
1060{
1062 PERROR("%s", __FUNCTION__);
1063 return -1;
1064 }
1066 PERROR("%s", __FUNCTION__);
1068 return -1;
1069 }
1070
1071 return 0;
1072}
1073
1074static int _password_free(UNUSED void *uctx)
1075{
1077
1078 return 0;
1079}
1080
1081/** Load our dictionaries
1082 *
1083 */
1085{
1086 int ret;
1087
1088 fr_atexit_global_once_ret(&ret, _password_init, _password_free, NULL);
1089
1090 return ret;
1091}
static int const char char buffer[256]
Definition acutest.h:576
#define fr_base16_decode(_err, _out, _in, _no_trailing)
Definition base16.h:92
#define fr_base64_decode(_out, _in, _expect_padding, _no_trailing)
Definition base64.h:78
#define RCSID(id)
Definition build.h:506
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void fr_dcursor_free_item(fr_dcursor_t *cursor)
talloc_free the current item
Definition dcursor.h:781
static int fr_dcursor_prepend(fr_dcursor_t *cursor, void *v)
Insert a single item at the start of the list.
Definition dcursor.h:376
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition debug.h:218
#define MEM(x)
Definition debug.h:46
#define fr_dict_autofree(_to_free)
Definition dict.h:915
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4395
#define fr_dict_autoload(_to_load)
Definition dict.h:912
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
talloc_free(hp)
#define PERROR(_fmt,...)
Definition log.h:228
#define RWDEBUG(fmt,...)
Definition log.h:373
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:347
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RWDEBUG2(fmt,...)
Definition log.h:374
#define RWDEBUG3(fmt,...)
Definition log.h:375
#define MD4_DIGEST_LENGTH
Definition md4.h:22
#define MD5_DIGEST_LENGTH
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
long int ssize_t
unsigned char uint8_t
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2962
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:290
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition pair.c:2812
int fr_pair_value_copy(fr_pair_t *dst, fr_pair_t *src)
Copy the value from one pair to another.
Definition pair.c:2591
#define MIN_LEN(_info)
Definition password.c:427
static fr_dict_attr_t const * attr_lm
Definition password.c:119
password_type_t
Definition password.c:45
@ PASSWORD_HASH
Fixed length.
Definition password.c:47
@ PASSWORD_HASH_VARIABLE
Variable length everything.
Definition password.c:49
@ PASSWORD_HASH_SALTED
Fixed length hash, variable length salt.
Definition password.c:48
@ PASSWORD_CLEARTEXT
Variable length.
Definition password.c:46
static fr_dict_attr_t const * attr_pbkdf2_sha256_legacy
Definition password.c:118
static fr_dict_attr_t const * attr_ssha2_512
Definition password.c:101
static fr_table_num_sorted_t const password_type_table[]
Definition password.c:193
size_t max_hash_len
Maximum length of the decoded string if normifying.
Definition password.c:70
password_preprocess_t func
Preprocessing function.
Definition password.c:67
static fr_pair_t * password_normalise_and_recheck(TALLOC_CTX *ctx, request_t *request, fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len, bool normify, fr_pair_t *const known_good)
Definition password.c:931
static fr_dict_attr_t const * attr_sha2_256
Definition password.c:94
password_type_t type
What type of password value this is.
Definition password.c:65
static fr_dict_attr_t const * attr_nt
Definition password.c:120
static fr_dict_attr_t const * attr_ssha2_384
Definition password.c:100
static fr_dict_attr_t const * attr_pbkdf2
Definition password.c:114
static fr_dict_attr_t const * attr_pbkdf2_sha512
Definition password.c:117
static int _password_init(UNUSED void *uctx)
Definition password.c:1059
static fr_dict_attr_t const * attr_crypt
Definition password.c:87
static fr_pair_t * password_process(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good, bool normify)
Apply any processing and normification.
Definition password.c:785
static fr_dict_attr_t const * attr_sha3_256
Definition password.c:105
static fr_dict_t const * dict_freeradius
Definition password.c:78
static ssize_t normify(normalise_t *action, uint8_t *buffer, size_t bufflen, char const *known_good, size_t len, size_t min_len)
Definition password.c:429
static fr_dict_attr_t const * attr_sha2_512
Definition password.c:96
static fr_dict_attr_t const * attr_pbkdf2_sha256
Definition password.c:116
static fr_dict_attr_t const * attr_ns_mta_md5
Definition password.c:121
int password_init(void)
Load our dictionaries.
Definition password.c:1084
static size_t password_header_table_len
Definition password.c:247
static fr_dict_t const * dict_radius
Definition password.c:79
static fr_dict_attr_t const * attr_user
Definition password.c:123
fr_pair_t *(* password_preprocess_t)(TALLOC_CTX *ctx, request_t *request, fr_pair_t *in)
Apply preprocessing logic to a password value.
Definition password.c:59
static fr_dict_attr_t const * attr_pbkdf2_sha1
Definition password.c:115
static password_info_t password_info[]
Metadata for various password attributes.
Definition password.c:258
static fr_dict_attr_t const * attr_sha2_224
Definition password.c:93
size_t min_hash_len
Minimum length of the decoded string if normifying.
Definition password.c:68
static int _password_free(UNUSED void *uctx)
Definition password.c:1074
bool always_allow
Always allow processing of this attribute, irrespective of what the caller says.
Definition password.c:74
static fr_dict_attr_t const * attr_ssha3_224
Definition password.c:109
normalise_t
Definition password.c:180
@ NORMALISED_B64
Definition password.c:182
@ NORMALISED_HEX
Definition password.c:183
@ NORMALISED_NOTHING
Definition password.c:181
static fr_pair_t * password_normify(TALLOC_CTX *ctx, request_t *request, fr_pair_t const *known_good)
Hex or base64 or bin auto-discovery.
Definition password.c:492
static fr_pair_t * password_process_header(TALLOC_CTX *ctx, request_t *request, fr_pair_t *known_good)
Convert a Password.With-Header attribute to the correct type.
Definition password.c:644
static fr_dict_attr_t const * attr_md5
Definition password.c:85
static fr_dict_attr_t const * attr_sha3_224
Definition password.c:104
static fr_dict_attr_t const * attr_ssha2_256
Definition password.c:99
bool no_normify
Don't attempt to normalise the contents of this attribute using the hex/base64 decoders.
Definition password.c:72
static fr_dict_attr_t const * attr_root
Definition password.c:83
static fr_dict_attr_t const * attr_sha3_384
Definition password.c:106
static fr_dict_attr_t const * attr_ssha3_256
Definition password.c:110
static fr_dict_attr_t const * attr_sha2
Definition password.c:92
int password_normalise_and_replace(request_t *request, bool normify)
Find all password attributes in the control list of a request and normalise them.
Definition password.c:890
fr_dict_autoload_t password_dict[]
Definition password.c:126
static fr_table_num_sorted_t const password_header_table[]
Definition password.c:206
static fr_dict_attr_t const * attr_ssha3_512
Definition password.c:112
static fr_dict_attr_t const * attr_sha3
Definition password.c:103
static fr_dict_attr_t const * attr_ssha2_224
Definition password.c:98
static size_t normalise_table_len
Definition password.c:191
fr_pair_t * password_find(bool *ephemeral, TALLOC_CTX *ctx, request_t *request, fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len, bool normify)
Find a "known good" password in the control list of a request.
Definition password.c:994
static fr_dict_attr_t const * attr_sha2_384
Definition password.c:95
static fr_dict_attr_t const * attr_ssha1
Definition password.c:90
static size_t password_type_table_len
Definition password.c:199
static fr_dict_attr_t const * attr_cleartext
Definition password.c:81
static fr_dict_attr_t const * attr_ssha3_384
Definition password.c:111
fr_dict_attr_autoload_t password_dict_attr[]
Definition password.c:133
static fr_dict_attr_t const * attr_smd5
Definition password.c:86
static fr_dict_attr_t const * attr_sha1
Definition password.c:89
static fr_dict_attr_t const * attr_sha3_512
Definition password.c:107
fr_dict_attr_t const ** da
Dictionary attribute representing this type of password.
Definition password.c:66
static fr_table_num_sorted_t const normalise_table[]
Definition password.c:186
static fr_dict_attr_t const * attr_with_header
Definition password.c:82
Password information.
Definition password.c:64
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
static bool done
Definition radclient.c:80
#define FR_SBUFF_IN(_start, _len_or_end)
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
#define fr_table_value_by_substr(_table, _name, _name_len, _def)
Convert a partial string to a value using an ordered or sorted table.
Definition table.h:693
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
#define PAIR_VERIFY(_x)
Definition pair.h:204
#define fr_pair_dcursor_by_ancestor_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes descended from the specified fr_dict_attr_t.
Definition pair.h:657
static size_t char ** out
Definition value.h:1030