The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_pap.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 (at
5 * 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: a5883672a7884147069189082cfbe63afc0a4348 $
19 * @file rlm_pap.c
20 * @brief Hashes plaintext passwords to compare against a prehashed reference.
21 *
22 * @copyright 2001-2012 The FreeRADIUS server project.
23 * @copyright 2012 Matthew Newton (matthew@newtoncomputing.co.uk)
24 * @copyright 2001 Kostas Kalevras (kkalev@noc.ntua.gr)
25 */
26RCSID("$Id: a5883672a7884147069189082cfbe63afc0a4348 $")
28
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/tls/base.h>
32#include <freeradius-devel/tls/log.h>
33
34#include <freeradius-devel/util/base64.h>
35#include <freeradius-devel/util/base16.h>
36#include <freeradius-devel/util/md5.h>
37#include <freeradius-devel/util/sha1.h>
38
39
40#include <freeradius-devel/protocol/freeradius/freeradius.internal.password.h>
41
42
43#ifdef HAVE_CRYPT_H
44# include <crypt.h>
45#endif
46
47#ifdef HAVE_OPENSSL_EVP_H
48# include <openssl/evp.h>
49#endif
50
51/*
52 * We don't have threadsafe crypt, so we have to wrap
53 * calls in a mutex
54 */
55#ifndef HAVE_CRYPT_R
56# include <pthread.h>
57static pthread_mutex_t fr_crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
58#endif
59
60/*
61 * Define a structure for our module configuration.
62 *
63 * These variables do not need to be in a structure, but it's
64 * a lot cleaner to do so, and a pointer to the structure can
65 * be used as the instance handle.
66 */
67typedef struct {
69 bool normify;
70} rlm_pap_t;
71
72typedef unlang_action_t (*pap_auth_func_t)(unlang_result_t *p_result, rlm_pap_t const *inst, request_t *request, fr_pair_t const *, fr_value_box_t const *);
73
74static const conf_parser_t module_config[] = {
75 { FR_CONF_OFFSET("normalise", rlm_pap_t, normify), .dflt = "yes" },
77};
78
83
85 .inst_size = sizeof(pap_call_env_t),
86 .inst_type = "pap_call_env_t",
87 .env = (call_env_parser_t[]) {
88 { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
90 pap_call_env_t, password, password_tmpl), .pair.dflt = "User-Password", .pair.dflt_quote = T_BARE_WORD },
92 }
93};
94
96
98 { .out = &dict_freeradius, .proto = "freeradius" },
100};
101
104
106 { .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
107 { .out = &attr_root, .name = "Password", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
108
110};
111
112#ifdef HAVE_OPENSSL_EVP_H
113static fr_table_num_sorted_t const pbkdf2_crypt_names[] = {
114 { L("HMACSHA1"), FR_SSHA1 },
115 { L("HMACSHA2+224"), FR_SSHA2_224 },
116 { L("HMACSHA2+256"), FR_SSHA2_256 },
117 { L("HMACSHA2+384"), FR_SSHA2_384 },
118 { L("HMACSHA2+512"), FR_SSHA2_512 },
119 { L("HMACSHA3+224"), FR_SSHA3_224 },
120 { L("HMACSHA3+256"), FR_SSHA3_256 },
121 { L("HMACSHA3+384"), FR_SSHA3_384 },
122 { L("HMACSHA3+512"), FR_SSHA3_512 },
123};
124static size_t pbkdf2_crypt_names_len = NUM_ELEMENTS(pbkdf2_crypt_names);
125
126static fr_table_num_sorted_t const pbkdf2_passlib_names[] = {
127 { L("sha1"), FR_SSHA1 },
128 { L("sha256"), FR_SSHA2_256 },
129 { L("sha512"), FR_SSHA2_512 }
130};
131static size_t pbkdf2_passlib_names_len = NUM_ELEMENTS(pbkdf2_passlib_names);
132#endif
133
135
136/*
137 * Authorize the user for PAP authentication.
138 *
139 * This isn't strictly necessary, but it does make the
140 * server simpler to configure.
141 */
142static unlang_action_t CC_HINT(nonnull) mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
143{
145 pap_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, pap_call_env_t);
146
147 if (fr_pair_find_by_da(&request->control_pairs, NULL, attr_auth_type) != NULL) {
148 RDEBUG3("Auth-Type is already set. Not setting 'Auth-Type := %s'", mctx->mi->name);
150 }
151
152 if (env_data->password.type != FR_TYPE_STRING) {
153 RDEBUG2("No %s attribute in the request. Cannot do PAP", env_data->password_tmpl->name);
155 }
156
157 if (!inst->auth_type) {
158 WARN("No 'authenticate %s {...}' section or 'Auth-Type = %s' set. Cannot setup PAP authentication.",
159 mctx->mi->name, mctx->mi->name);
161 }
162
164
166}
167
168/*
169 * PAP authentication functions
170 */
171
173 UNUSED rlm_pap_t const *inst, request_t *request,
174 fr_pair_t const *known_good, fr_value_box_t const *password)
175{
176 if ((known_good->vp_length != password->vb_length) ||
177 (fr_digest_cmp(known_good->vp_octets, password->vb_octets, known_good->vp_length) != 0)) {
178 REDEBUG("Cleartext password does not match \"known good\" password");
180 REDEBUG3("Password : %pV", password);
181 REDEBUG3("Expected : %pV", &known_good->data);
183 }
185}
186
187#ifdef HAVE_CRYPT
188static unlang_action_t CC_HINT(nonnull) pap_auth_crypt(unlang_result_t *p_result,
189 UNUSED rlm_pap_t const *inst, request_t *request,
190 fr_pair_t const *known_good, fr_value_box_t const *password)
191{
192 char *crypt_out;
193 int cmp = 0;
194
195#ifdef HAVE_CRYPT_R
196 struct crypt_data crypt_data = { .initialized = 0 };
197
198 crypt_out = crypt_r(password->vb_strvalue, known_good->vp_strvalue, &crypt_data);
199 if (crypt_out) cmp = strcmp(known_good->vp_strvalue, crypt_out);
200#else
201 /*
202 * Ensure we're thread-safe, as crypt() isn't.
203 */
204 pthread_mutex_lock(&fr_crypt_mutex);
205 crypt_out = crypt(password->vb_strvalue, known_good->vp_strvalue);
206
207 /*
208 * Got something, check it within the lock. This is
209 * faster than copying it to a local buffer, and the
210 * time spent within the lock is critical.
211 */
212 if (crypt_out) cmp = strcmp(known_good->vp_strvalue, crypt_out);
213 pthread_mutex_unlock(&fr_crypt_mutex);
214#endif
215
216 /*
217 * Error.
218 */
219 if (!crypt_out || (cmp != 0)) {
220 REDEBUG("Crypt digest does not match \"known good\" digest");
222 }
223
225}
226#endif
227
229 UNUSED rlm_pap_t const *inst, request_t *request,
230 fr_pair_t const *known_good, fr_value_box_t const *password)
231{
233
234 if (known_good->vp_length != MD5_DIGEST_LENGTH) {
235 REDEBUG("\"known-good\" MD5 password has incorrect length, expected 16 got %zu", known_good->vp_length);
237 }
238
239 fr_md5_calc(digest, password->vb_octets, password->vb_length);
240
241 if (fr_digest_cmp(digest, known_good->vp_octets, known_good->vp_length) != 0) {
242 REDEBUG("MD5 digest does not match \"known good\" digest");
244 REDEBUG3("Password : %pV", password);
245 REDEBUG3("Calculated : %pH", fr_box_octets(digest, MD5_DIGEST_LENGTH));
246 REDEBUG3("Expected : %pH", fr_box_octets(known_good->vp_octets, MD5_DIGEST_LENGTH));
248 }
249
251}
252
253
255 UNUSED rlm_pap_t const *inst, request_t *request,
256 fr_pair_t const *known_good, fr_value_box_t const *password)
257{
258 fr_md5_ctx_t *md5_ctx;
260
261 if (known_good->vp_length <= MD5_DIGEST_LENGTH) {
262 REDEBUG("\"known-good\" Password.SMD5 has incorrect length, expected 16 got %zu", known_good->vp_length);
264 }
265
266 md5_ctx = fr_md5_ctx_alloc_from_list();
267 fr_md5_update(md5_ctx, password->vb_octets, password->vb_length);
268 fr_md5_update(md5_ctx, known_good->vp_octets + MD5_DIGEST_LENGTH, known_good->vp_length - MD5_DIGEST_LENGTH);
269 fr_md5_final(digest, md5_ctx);
271
272 /*
273 * Compare only the MD5 hash results, not the salt.
274 */
275 if (fr_digest_cmp(digest, known_good->vp_octets, MD5_DIGEST_LENGTH) != 0) {
276 REDEBUG("SMD5 digest does not match \"known good\" digest");
278 REDEBUG3("Password : %pV", password);
279 REDEBUG3("Calculated : %pH", fr_box_octets(digest, MD5_DIGEST_LENGTH));
280 REDEBUG3("Expected : %pH", fr_box_octets(known_good->vp_octets, MD5_DIGEST_LENGTH));
282 }
283
285}
286
288 UNUSED rlm_pap_t const *inst, request_t *request,
289 fr_pair_t const *known_good, fr_value_box_t const *password)
290{
291 fr_sha1_ctx sha1_context;
293
294 if (known_good->vp_length != SHA1_DIGEST_LENGTH) {
295 REDEBUG("\"known-good\" Password.SHA1 has incorrect length, expected 20 got %zu", known_good->vp_length);
297 }
298
299 fr_sha1_init(&sha1_context);
300 fr_sha1_update(&sha1_context, password->vb_octets, password->vb_length);
301 fr_sha1_final(digest,&sha1_context);
302
303 if (fr_digest_cmp(digest, known_good->vp_octets, known_good->vp_length) != 0) {
304 REDEBUG("SHA1 digest does not match \"known good\" digest");
306 REDEBUG3("Password : %pV", password);
307 REDEBUG3("Calculated : %pH", fr_box_octets(digest, SHA1_DIGEST_LENGTH));
308 REDEBUG3("Expected : %pH", fr_box_octets(known_good->vp_octets, SHA1_DIGEST_LENGTH));
310 }
311
313}
314
316 UNUSED rlm_pap_t const *inst, request_t *request,
317 fr_pair_t const *known_good, fr_value_box_t const *password)
318{
319 fr_sha1_ctx sha1_context;
321
322 if (known_good->vp_length <= SHA1_DIGEST_LENGTH) {
323 REDEBUG("\"known-good\" Password.SSHA has incorrect length, expected > 20 got %zu", known_good->vp_length);
325 }
326
327 fr_sha1_init(&sha1_context);
328 fr_sha1_update(&sha1_context, password->vb_octets, password->vb_length);
329
330 fr_sha1_update(&sha1_context, known_good->vp_octets + SHA1_DIGEST_LENGTH, known_good->vp_length - SHA1_DIGEST_LENGTH);
331 fr_sha1_final(digest, &sha1_context);
332
333 if (fr_digest_cmp(digest, known_good->vp_octets, SHA1_DIGEST_LENGTH) != 0) {
334 REDEBUG("SSHA digest does not match \"known good\" digest");
336 REDEBUG3("Password : %pV", password);
337 REDEBUG3("Salt : %pH", fr_box_octets(known_good->vp_octets + SHA1_DIGEST_LENGTH,
338 known_good->vp_length - SHA1_DIGEST_LENGTH));
339 REDEBUG3("Calculated : %pH", fr_box_octets(digest, SHA1_DIGEST_LENGTH));
340 REDEBUG3("Expected : %pH", fr_box_octets(known_good->vp_octets, SHA1_DIGEST_LENGTH));
342 }
343
345}
346
347#ifdef HAVE_OPENSSL_EVP_H
348static unlang_action_t CC_HINT(nonnull) pap_auth_evp_md(unlang_result_t *p_result,
349 UNUSED rlm_pap_t const *inst, request_t *request,
350 fr_pair_t const *known_good, fr_value_box_t const *password,
351 char const *name, EVP_MD const *md)
352{
353 EVP_MD_CTX *ctx;
354 uint8_t digest[EVP_MAX_MD_SIZE];
355 unsigned int digest_len;
356
357 ctx = EVP_MD_CTX_create();
358 if (!ctx) return UNLANG_ACTION_FAIL;
359
360 EVP_DigestInit_ex(ctx, md, NULL);
361 EVP_DigestUpdate(ctx, password->vb_octets, password->vb_length);
362 EVP_DigestFinal_ex(ctx, digest, &digest_len);
363 EVP_MD_CTX_destroy(ctx);
364
365 if ((size_t) digest_len != known_good->vp_length) return UNLANG_ACTION_FAIL; /* This would be an OpenSSL bug... */
366
367 if (fr_digest_cmp(digest, known_good->vp_octets, known_good->vp_length) != 0) {
368 REDEBUG("%s digest does not match \"known good\" digest", name);
370 REDEBUG3("Password : %pV", password);
371 REDEBUG3("Calculated : %pH", fr_box_octets(digest, digest_len));
372 REDEBUG3("Expected : %pH", &known_good->data);
374 }
375
377}
378
379static unlang_action_t CC_HINT(nonnull) pap_auth_evp_md_salted(unlang_result_t *p_result,
380 UNUSED rlm_pap_t const *inst, request_t *request,
381 fr_pair_t const *known_good, fr_value_box_t const *password,
382 char const *name, EVP_MD const *md)
383{
384 EVP_MD_CTX *ctx;
385 uint8_t digest[EVP_MAX_MD_SIZE];
386 unsigned int digest_len, min_len;
387
388 min_len = EVP_MD_size(md);
389 ctx = EVP_MD_CTX_create();
390 if (!ctx) return UNLANG_ACTION_FAIL;
391
392 EVP_DigestInit_ex(ctx, md, NULL);
393 EVP_DigestUpdate(ctx, password->vb_octets, password->vb_length);
394 EVP_DigestUpdate(ctx, known_good->vp_octets + min_len, known_good->vp_length - min_len);
395 EVP_DigestFinal_ex(ctx, digest, &digest_len);
396 EVP_MD_CTX_destroy(ctx);
397
398 if ((size_t) digest_len != min_len) return UNLANG_ACTION_FAIL; /* This would be an OpenSSL bug... */
399
400 /*
401 * Only compare digest_len bytes, the rest is salt.
402 */
403 if (fr_digest_cmp(digest, known_good->vp_octets, (size_t)digest_len) != 0) {
404 REDEBUG("%s digest does not match \"known good\" digest", name);
406 REDEBUG3("Password : %pV", password);
407 REDEBUG3("Salt : %pH",
408 fr_box_octets(known_good->vp_octets + digest_len, known_good->vp_length - digest_len));
409 REDEBUG3("Calculated : %pH", fr_box_octets(digest, digest_len));
410 REDEBUG3("Expected : %pH", fr_box_octets(known_good->vp_octets, digest_len));
412 }
413
415}
416
417/** Define a new OpenSSL EVP based password hashing function
418 *
419 */
420#define PAP_AUTH_EVP_MD(_func, _new_func, _name, _md) \
421static unlang_action_t CC_HINT(nonnull) _new_func(unlang_result_t *p_result, \
422 rlm_pap_t const *inst, request_t *request, \
423 fr_pair_t const *known_good, fr_value_box_t const *password) \
424{ \
425 return _func(p_result, inst, request, known_good, password, _name, _md); \
426}
427
428PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_224, "SHA2-224", EVP_sha224())
429PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_256, "SHA2-256", EVP_sha256())
430PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_384, "SHA2-384", EVP_sha384())
431PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_512, "SHA2-512", EVP_sha512())
432PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_224, "SSHA2-224", EVP_sha224())
433PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_256, "SSHA2-256", EVP_sha256())
434PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_384, "SSHA2-384", EVP_sha384())
435PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_512, "SSHA2-512", EVP_sha512())
436
437PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_224, "SHA3-224", EVP_sha3_224())
438PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_256, "SHA3-256", EVP_sha3_256())
439PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_384, "SHA3-384", EVP_sha3_384())
440PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_512, "SHA3-512", EVP_sha3_512())
441PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_224, "SSHA3-224", EVP_sha3_224())
442PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_256, "SSHA3-256", EVP_sha3_256())
443PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_384, "SSHA3-384", EVP_sha3_384())
444PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_512, "SSHA3-512", EVP_sha3_512())
445
446/** Validates Crypt::PBKDF2 LDAP format strings
447 *
448 * @param[out] p_result The result of comparing the pbkdf2 hash with the password.
449 * @param[in] request The current request.
450 * @param[in] str Raw PBKDF2 string.
451 * @param[in] len Length of string.
452 * @param[in] digest_type Digest type to use.
453 * @param[in] iter_sep Separation character between the iterations and the next component.
454 * @param[in] salt_sep Separation character between the salt and the next component.
455 * @param[in] iter_is_base64 Whether the iterations is are encoded as base64.
456 * @param[in] password to validate.
457 * @return
458 * - RLM_MODULE_REJECT
459 * - RLM_MODULE_OK
460 */
461static inline CC_HINT(nonnull) unlang_action_t pap_auth_pbkdf2_parse_digest(unlang_result_t *p_result,
462 request_t *request, const uint8_t *str, size_t len,
463 int digest_type, char iter_sep, char salt_sep,
464 bool iter_is_base64, fr_value_box_t const *password)
465{
467
468 uint8_t const *p, *q, *end;
469 ssize_t slen;
470
471 EVP_MD const *evp_md;
472 size_t digest_len;
473
474 uint32_t iterations = 1;
475
476 uint8_t *salt = NULL;
477 size_t salt_len;
478 uint8_t hash[EVP_MAX_MD_SIZE];
479 uint8_t digest[EVP_MAX_MD_SIZE];
480
481 /*
482 * Parse PBKDF string for given digest = <iterations><iter_sep>b64(<salt>)<salt_sep>b64(<hash>)
483 */
484 p = str;
485 end = p + len;
486
487 switch (digest_type) {
488 case FR_SSHA1:
489 evp_md = EVP_sha1();
490 digest_len = SHA1_DIGEST_LENGTH;
491 break;
492
493 case FR_SSHA2_224:
494 evp_md = EVP_sha224();
495 digest_len = SHA224_DIGEST_LENGTH;
496 break;
497
498 case FR_SSHA2_256:
499 evp_md = EVP_sha256();
500 digest_len = SHA256_DIGEST_LENGTH;
501 break;
502
503 case FR_SSHA2_384:
504 evp_md = EVP_sha384();
505 digest_len = SHA384_DIGEST_LENGTH;
506 break;
507
508 case FR_SSHA2_512:
509 evp_md = EVP_sha512();
510 digest_len = SHA512_DIGEST_LENGTH;
511 break;
512
513 case FR_SSHA3_224:
514 evp_md = EVP_sha3_224();
515 digest_len = SHA224_DIGEST_LENGTH;
516 break;
517
518 case FR_SSHA3_256:
519 evp_md = EVP_sha3_256();
520 digest_len = SHA256_DIGEST_LENGTH;
521 break;
522
523 case FR_SSHA3_384:
524 evp_md = EVP_sha3_384();
525 digest_len = SHA384_DIGEST_LENGTH;
526 break;
527
528 case FR_SSHA3_512:
529 evp_md = EVP_sha3_512();
530 digest_len = SHA512_DIGEST_LENGTH;
531 break;
532
533 default:
534 REDEBUG("Unknown PBKDF2 digest type \"%d\"", digest_type);
535 goto finish;
536 }
537
538 if (((end - p) < 1) || !(q = memchr(p, iter_sep, end - p))) {
539 REDEBUG("Password.PBKDF2 missing iterations component");
540 goto finish;
541 }
542
543 if ((q - p) == 0) {
544 REDEBUG("Password.PBKDF2 iterations component too short");
545 goto finish;
546 }
547
548 /*
549 * If it's not base64 encoded, assume it's ascii
550 */
551 if (!iter_is_base64) {
552 unsigned long iterations_long;
553 char *qq;
554 char iterations_buff[sizeof("4294967295")];
555
556 /*
557 * While passwords come from "trusted" sources, we don't trust them too much!
558 */
559 if ((size_t) (q - p) >= sizeof(iterations_buff)) {
560 REMARKER((char const *) p, q - p,
561 "Password.PBKDF2 iterations field is too large");
562 goto finish;
563 }
564
565 strlcpy(iterations_buff, (char const *)p, (q - p) + 1);
566
567 iterations_long = strtoul(iterations_buff, &qq, 10);
568 if (*qq != '\0') {
569 REMARKER(iterations_buff, qq - iterations_buff,
570 "Password.PBKDF2 iterations field contains an invalid character");
571 goto finish;
572 }
573 if (iterations_long > UINT32_MAX) {
574 REMARKER(iterations_buff, qq - iterations_buff,
575 "Password.PBKDF2 iterations field contains too large iteration count");
576 goto finish;
577 }
578 iterations = iterations_long;
579
580 p = q + 1;
581 /*
582 * base64 encoded and big endian
583 */
584 } else {
586 slen = fr_base64_decode(&FR_DBUFF_TMP((uint8_t *)&iterations, sizeof(iterations)),
587 &FR_SBUFF_IN((char const *)p, (char const *)q), false, false);
588 if (slen <= 0) {
589 RPEDEBUG("Failed decoding Password.PBKDF2 iterations component (%.*s)", (int)(q - p), p);
590 goto finish;
591 }
592 if (slen != sizeof(iterations)) {
593 REDEBUG("Decoded Password.PBKDF2 iterations component is wrong size");
594 goto finish;
595 }
596
597 iterations = ntohl(iterations);
598
599 p = q + 1;
600 }
601
602 /*
603 * 0 iterations is invalid (we need at least one)
604 */
605 if (iterations == 0) iterations = 1;
606
607 if (((end - p) < 1) || !(q = memchr(p, salt_sep, end - p))) {
608 REDEBUG("Password.PBKDF2 missing salt component");
609 goto finish;
610 }
611
612 if ((q - p) == 0) {
613 REDEBUG("Password.PBKDF2 salt component too short");
614 goto finish;
615 }
616
617 MEM(salt = talloc_array(request, uint8_t, FR_BASE64_DEC_LENGTH(q - p)));
618 slen = fr_base64_decode(&FR_DBUFF_TMP(salt, talloc_array_length(salt)),
619 &FR_SBUFF_IN((char const *) p, (char const *)q), false, false);
620 if (slen <= 0) {
621 RPEDEBUG("Failed decoding Password.PBKDF2 salt component");
622 goto finish;
623 }
624 salt_len = (size_t)slen;
625
626 p = q + 1;
627
628 if ((end - p) == 0) {
629 REDEBUG("Password.PBKDF2 hash component too short");
630 goto finish;
631 }
632
633 slen = fr_base64_decode(&FR_DBUFF_TMP(hash, sizeof(hash)),
634 &FR_SBUFF_IN((char const *)p, (char const *)end), false, false);
635 if (slen <= 0) {
636 RPEDEBUG("Failed decoding Password.PBKDF2 hash component");
637 goto finish;
638 }
639
640 if ((size_t)slen != digest_len) {
641 REDEBUG("Password.PBKDF2 hash component length is incorrect for hash type, expected %zu, got %zd",
642 digest_len, slen);
643
644 RHEXDUMP2(hash, slen, "hash component");
645
646 goto finish;
647 }
648
649 RDEBUG2("PBKDF2 %s: Iterations %u, salt length %zu, hash length %zd",
650 fr_table_str_by_value(pbkdf2_crypt_names, digest_type, "<UNKNOWN>"),
651 iterations, salt_len, slen);
652
653 /*
654 * Extreme corner case: prevent integer overflow when we change data types via a cast.
655 */
656 if ((password->vb_length > INT_MAX) || (salt_len > INT_MAX) ||
657 (iterations > INT_MAX) || (digest_len > INT_MAX)) {
658 return UNLANG_ACTION_FAIL;
659 }
660
661 /*
662 * Hash and compare
663 */
664 if (PKCS5_PBKDF2_HMAC((char const *)password->vb_octets, (int)password->vb_length,
665 (unsigned char const *)salt, (int)salt_len,
666 (int)iterations,
667 evp_md,
668 (int)digest_len, (unsigned char *)digest) == 0) {
669 fr_tls_log(request, "PBKDF2 digest failure");
670 goto finish;
671 }
672
673 if (fr_digest_cmp(digest, hash, (size_t)digest_len) != 0) {
674 REDEBUG("PBKDF2 digest does not match \"known good\" digest");
676 REDEBUG3("Salt : %pH", fr_box_octets(salt, salt_len));
677 REDEBUG3("Calculated : %pH", fr_box_octets(digest, digest_len));
678 REDEBUG3("Expected : %pH", fr_box_octets(hash, slen));
679 rcode = RLM_MODULE_REJECT;
680 } else {
681 rcode = RLM_MODULE_OK;
682 }
683
684finish:
685 talloc_free(salt);
686
687 RETURN_UNLANG_RCODE(rcode);
688}
689
690/** Validates Crypt::PBKDF2 LDAP format strings
691 *
692 * @param[out] p_result The result of comparing the pbkdf2 hash with the password.
693 * @param[in] request The current request.
694 * @param[in] str Raw PBKDF2 string.
695 * @param[in] len Length of string.
696 * @param[in] hash_names Table containing valid hash names.
697 * @param[in] hash_names_len How long the table is.
698 * @param[in] scheme_sep Separation character between the scheme and the next component.
699 * @param[in] iter_sep Separation character between the iterations and the next component.
700 * @param[in] salt_sep Separation character between the salt and the next component.
701 * @param[in] iter_is_base64 Whether the iterations is are encoded as base64.
702 * @param[in] password to validate.
703 * @return
704 * - RLM_MODULE_REJECT
705 * - RLM_MODULE_OK
706 */
707static inline CC_HINT(nonnull) unlang_action_t pap_auth_pbkdf2_parse(unlang_result_t *p_result,
708 request_t *request, const uint8_t *str, size_t len,
709 fr_table_num_sorted_t const hash_names[], size_t hash_names_len,
710 char scheme_sep, char iter_sep, char salt_sep,
711 bool iter_is_base64, fr_value_box_t const *password)
712{
714
715 uint8_t const *p, *q, *end;
716 int digest_type;
717
718 RDEBUG2("Comparing with \"known-good\" Password.PBKDF2");
719
720 if (len <= 1) {
721 REDEBUG("Password.PBKDF2 is too short");
722 goto finish;
723 }
724
725 /*
726 * Parse PBKDF string = {hash_algorithm}<scheme_sep><iterations><iter_sep>b64(<salt>)<salt_sep>b64(<hash>)
727 */
728 p = str;
729 end = p + len;
730
731 q = memchr(p, scheme_sep, end - p);
732 if (!q) {
733 REDEBUG("Password.PBKDF2 has no component separators");
734 goto finish;
735 }
736
737 digest_type = fr_table_value_by_substr(hash_names, (char const *)p, q - p, -1);
738
739 p = q + 1;
740
741 return pap_auth_pbkdf2_parse_digest(p_result, request, p, end - p, digest_type, iter_sep, salt_sep, iter_is_base64, password);
742
743finish:
744 RETURN_UNLANG_RCODE(rcode);
745}
746
747static inline unlang_action_t CC_HINT(nonnull) pap_auth_pbkdf2(unlang_result_t *p_result,
748 UNUSED rlm_pap_t const *inst,
749 request_t *request,
750 fr_pair_t const *known_good, fr_value_box_t const *password)
751{
752 uint8_t const *p = known_good->vp_octets, *q, *end = p + known_good->vp_length;
753
754 if ((end - p) < 2) {
755 REDEBUG("Password.PBKDF2 too short");
757 }
758
759 /*
760 * If it doesn't begin with a $ assume
761 * it's Crypt::PBKDF2 LDAP format
762 *
763 * {X-PBKDF2}<digest>:<b64 rounds>:<b64_salt>:<b64_hash>
764 *
765 * or 389ds LDAP format
766 *
767 * {PBKDF2-SHA512}<round>$<b64_salt>$<b64_hash>
768 */
769 if (*p != '$') {
770 if ((size_t)(end - p) >= sizeof("{PBKDF2-") && (memcmp(p, "{PBKDF2-", sizeof("{PBKDF2-") - 1) == 0)) {
771 p += sizeof("{PBKDF2-") - 1;
772 return pap_auth_pbkdf2_parse(p_result, request, p, end - p,
773 pbkdf2_passlib_names, pbkdf2_passlib_names_len,
774 '}', '$', '$', false, password);
775 } else {
776 /*
777 * Strip the header if it's present
778 */
779 if (*p == '{') {
780 q = memchr(p, '}', end - p);
781 if (!q) {
782 REDEBUG("Password.PBKDF2 is missing '}");
784 }
785 p = q + 1;
786 }
787 return pap_auth_pbkdf2_parse(p_result, request, p, end - p,
788 pbkdf2_crypt_names, pbkdf2_crypt_names_len,
789 ':', ':', ':', true, password);
790 }
791 }
792
793 /*
794 * Crypt::PBKDF2 Crypt format
795 *
796 * $PBKDF2$<digest>:<rounds>:<b64_salt>$<b64_hash>
797 */
798 if ((size_t)(end - p) >= sizeof("$PBKDF2$") && (memcmp(p, "$PBKDF2$", sizeof("$PBKDF2$") - 1) == 0)) {
799 p += sizeof("$PBKDF2$") - 1;
800 return pap_auth_pbkdf2_parse(p_result, request, p, end - p,
801 pbkdf2_crypt_names, pbkdf2_crypt_names_len,
802 ':', ':', '$', false, password);
803 }
804
805 /*
806 * Python's passlib format
807 *
808 * $pbkdf2-<digest>$<rounds>$<alt_b64_salt>$<alt_b64_hash>
809 *
810 * Note: Our base64 functions also work with alt_b64
811 */
812 if ((size_t)(end - p) >= sizeof("$pbkdf2-") && (memcmp(p, "$pbkdf2-", sizeof("$pbkdf2-") - 1) == 0)) {
813 p += sizeof("$pbkdf2-") - 1;
814 return pap_auth_pbkdf2_parse(p_result, request, p, end - p,
815 pbkdf2_passlib_names, pbkdf2_passlib_names_len,
816 '$', '$', '$', false, password);
817 }
818
819 REDEBUG("Can't determine format of Password.PBKDF2");
820
822}
823
824/*
825 * 389ds pbkdf2 passwords
826 *
827 * {PBKDF2-<digest>}<rounds>$<b64_salt>$<b64_hash>
828 */
829static inline unlang_action_t CC_HINT(nonnull) pap_auth_pbkdf2_sha1(unlang_result_t *p_result,
830 UNUSED rlm_pap_t const *inst,
831 request_t *request,
832 fr_pair_t const *known_good, fr_value_box_t const *password)
833{
834 uint8_t const *p = known_good->vp_octets, *end = p + known_good->vp_length;
835
836 if ((end - p) < 2) {
837 REDEBUG("Password.With-Header {PBKDF2-SHA1} too short");
839 }
840
841 return pap_auth_pbkdf2_parse_digest(p_result, request, p, end - p, FR_SSHA1, '$', '$', false, password);
842}
843
844static inline unlang_action_t CC_HINT(nonnull) pap_auth_pbkdf2_sha256(unlang_result_t *p_result,
845 UNUSED rlm_pap_t const *inst,
846 request_t *request,
847 fr_pair_t const *known_good, fr_value_box_t const *password)
848{
849 uint8_t const *p = known_good->vp_octets, *end = p + known_good->vp_length;
850
851 if ((end - p) < 2) {
852 REDEBUG("Password.With-Header {PBKDF2-SHA256} too short");
854 }
855
856 return pap_auth_pbkdf2_parse_digest(p_result, request, p, end - p, FR_SSHA2_256, '$', '$', false, password);
857}
858
859static inline unlang_action_t CC_HINT(nonnull) pap_auth_pbkdf2_sha512(unlang_result_t *p_result,
860 UNUSED rlm_pap_t const *inst,
861 request_t *request,
862 fr_pair_t const *known_good, fr_value_box_t const *password)
863{
864 uint8_t const *p = known_good->vp_octets, *end = p + known_good->vp_length;
865
866 if ((end - p) < 2) {
867 REDEBUG("Password.With-Header {PBKDF2-SHA512} too short");
869 }
870
871 return pap_auth_pbkdf2_parse_digest(p_result, request, p, end - p, FR_SSHA2_512, '$', '$', false, password);
872}
873
874/*
875 * 389ds pbkdf2 legacy password with header {PBKDF2_SHA256}
876 *
877 * this was the first implementation in 389ds using a fixed length struct as base64.
878 * at some point it was the default scheme, although it's not recommened anymore.
879 *
880 * content struct is
881 * 4 bytes iterations (value 8192)
882 * 64 bytes salt
883 * 256 bytes hash
884 */
885static inline unlang_action_t CC_HINT(nonnull) pap_auth_pbkdf2_sha256_legacy(unlang_result_t *p_result,
886 UNUSED rlm_pap_t const *inst,
887 request_t *request,
888 fr_pair_t const *known_good, fr_value_box_t const *password)
889{
890#define PBKDF2_SHA256_LEGACY_SALT_LENGTH 64
891#define PBKDF2_SHA256_LEGACY_ITERATIONS_LENGTH 4
892#define PBKDF2_SHA256_LEGACY_HASH_LENGTH 256
893#define PBKDF2_SHA256_LEGACY_TOTAL_LENGTH (PBKDF2_SHA256_LEGACY_ITERATIONS_LENGTH + PBKDF2_SHA256_LEGACY_SALT_LENGTH + PBKDF2_SHA256_LEGACY_HASH_LENGTH)
894#define PBKDF2_SHA256_LEGACY_ITERATIONS 8192
895#define PBKDF2_SHA256_LEGACY_B64_LENGTH (PBKDF2_SHA256_LEGACY_TOTAL_LENGTH * 4 / 3)
896
897 struct pbkdf2_bufs {
898 uint32_t iterations;
899 uint8_t salt[PBKDF2_SHA256_LEGACY_SALT_LENGTH];
900 uint8_t hash[PBKDF2_SHA256_LEGACY_HASH_LENGTH];
901 };
902 struct pbkdf2_bufs pbkdf2_buf = { .iterations = PBKDF2_SHA256_LEGACY_ITERATIONS };
903
904 ssize_t slen;
905 uint8_t const *p = known_good->vp_octets, *end = p + known_good->vp_length;
906
907 EVP_MD const *evp_md = EVP_sha256();
908 size_t digest_len = SHA256_DIGEST_LENGTH;
909 uint8_t digest[SHA256_DIGEST_LENGTH];
910
911 if ((end - p) != PBKDF2_SHA256_LEGACY_B64_LENGTH) {
912 REDEBUG("Password.With-Header {PBKDF2_SHA256} has incorrect size %zd instead of %d.", known_good->vp_length, PBKDF2_SHA256_LEGACY_B64_LENGTH);
914 }
915
916 slen = fr_base64_decode(&FR_DBUFF_TMP((uint8_t *) &pbkdf2_buf, sizeof(pbkdf2_buf)),
917 &FR_SBUFF_IN((char const *) p, (char const *)end), false, false);
918
919 if (slen <= 0) {
920 RPEDEBUG("Failed decoding Password.With-Header {PBKDF2_SHA256}: \"%.*s\"", (int)(end -p), p);
922 }
923
924 if (slen != PBKDF2_SHA256_LEGACY_TOTAL_LENGTH) {
925 REDEBUG("Password.With-Header {PBKDF2_SHA256} has incorrect decoded size %zd instead of %d.", slen, PBKDF2_SHA256_LEGACY_TOTAL_LENGTH);
927 }
928
929 pbkdf2_buf.iterations = ntohl(pbkdf2_buf.iterations);
930
931 if (pbkdf2_buf.iterations < 1) {
932 REDEBUG("Password.With-Header {PBKDF2_SHA256} has invalid number of iterations %d.", pbkdf2_buf.iterations);
934 }
935
936 if (PKCS5_PBKDF2_HMAC((char const *)password->vb_octets, (int)password->vb_length,
937 (unsigned char const *)pbkdf2_buf.salt, (int)PBKDF2_SHA256_LEGACY_SALT_LENGTH,
938 (int)pbkdf2_buf.iterations,
939 evp_md,
940 (int)digest_len, (unsigned char *)digest) == 0) {
941 fr_tls_log(request, "PBKDF2_SHA256 digest failure");
943 }
944
945 if (fr_digest_cmp(digest, pbkdf2_buf.hash, (size_t)digest_len) != 0) {
946 REDEBUG("PBKDF2_SHA256 digest does not match \"known good\" digest");
948 REDEBUG3("Salt : %pH", fr_box_octets(pbkdf2_buf.salt, PBKDF2_SHA256_LEGACY_SALT_LENGTH));
949 REDEBUG3("Calculated : %pH", fr_box_octets(digest, digest_len));
950 REDEBUG3("Expected : %pH", fr_box_octets(pbkdf2_buf.hash, PBKDF2_SHA256_LEGACY_HASH_LENGTH));
952 } else {
954 }
955}
956#endif
957
959 UNUSED rlm_pap_t const *inst, request_t *request,
960 fr_pair_t const *known_good, fr_value_box_t const *password)
961{
962 ssize_t len;
964 uint8_t ucs2[512];
965
966 RDEBUG2("Comparing with \"known-good\" Password.NT");
967
968 if (known_good->vp_length != MD4_DIGEST_LENGTH) {
969 REDEBUG("\"known good\" Password.NT has incorrect length, expected 16 got %zu", known_good->vp_length);
971 }
972
973 len = fr_utf8_to_ucs2(ucs2, sizeof(ucs2),
974 password->vb_strvalue, password->vb_length);
975 if (len < 0) {
976 REDEBUG("User-Password is not in UCS2 format");
978 }
979
980 fr_md4_calc(digest, (uint8_t *)ucs2, len);
981
982 if (fr_digest_cmp(digest, known_good->vp_octets, known_good->vp_length) != 0) {
983 REDEBUG("NT digest does not match \"known good\" digest");
985 REDEBUG3("Calculated : %pH", fr_box_octets(digest, sizeof(digest)));
986 REDEBUG3("Expected : %pH", &known_good->data);
988 }
989
991}
992
994 UNUSED rlm_pap_t const *inst, request_t *request,
995 fr_pair_t const *known_good, fr_value_box_t const *password)
996{
997 uint8_t digest[128];
999 uint8_t buff2[FR_MAX_STRING_LEN + 50];
1000 fr_dbuff_t digest_dbuff = FR_DBUFF_TMP(digest, sizeof(digest));
1001
1002 RDEBUG2("Using Password.NT-MTA-MD5");
1003
1004 if (known_good->vp_length != 64) {
1005 REDEBUG("\"known good\" Password.NS-MTA-MD5 has incorrect length, expected 64 got %zu",
1006 known_good->vp_length);
1008 }
1009
1010 /*
1011 * Sanity check the value of Password.NS-MTA-MD5
1012 */
1013 if (fr_base16_decode(NULL, &digest_dbuff,
1014 &FR_SBUFF_IN(known_good->vp_strvalue, known_good->vp_length), false) != 16) {
1015 REDEBUG("\"known good\" Password.NS-MTA-MD5 has invalid value");
1017 }
1018
1019 /*
1020 * Ensure we don't have buffer overflows.
1021 *
1022 * This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
1023 */
1024 if (password->vb_length >= (sizeof(buff) - 2 - 2 * 32)) {
1025 REDEBUG("\"known good\" Password.NS-MTA-MD5 is too long");
1027 }
1028
1029 /*
1030 * Set up the algorithm.
1031 */
1032 {
1033 uint8_t *p = buff2;
1034
1035 memcpy(p, &known_good->vp_octets[32], 32);
1036 p += 32;
1037 *(p++) = 89;
1038 memcpy(p, password->vb_strvalue, password->vb_length);
1039 p += password->vb_length;
1040 *(p++) = 247;
1041 memcpy(p, &known_good->vp_octets[32], 32);
1042 p += 32;
1043
1044 fr_md5_calc(buff, (uint8_t *) buff2, p - buff2);
1045 }
1046
1047 if (fr_digest_cmp(fr_dbuff_start(&digest_dbuff), buff, 16) != 0) {
1048 REDEBUG("NS-MTA-MD5 digest does not match \"known good\" digest");
1050 }
1051
1053}
1054
1055/** Auth func for password types that should have been normalised away
1056 *
1057 */
1059 UNUSED rlm_pap_t const *inst, UNUSED request_t *request,
1060 UNUSED fr_pair_t const *known_good, UNUSED fr_value_box_t const *password)
1061{
1063}
1064
1065/** Table of password types we can process
1066 *
1067 */
1069 [FR_CLEARTEXT] = pap_auth_clear,
1070 [FR_MD5] = pap_auth_md5,
1071 [FR_SMD5] = pap_auth_smd5,
1072
1073#ifdef HAVE_CRYPT
1074 [FR_CRYPT] = pap_auth_crypt,
1075#endif
1076 [FR_NS_MTA_MD5] = pap_auth_ns_mta_md5,
1077 [FR_NT] = pap_auth_nt,
1078 [FR_WITH_HEADER] = pap_auth_dummy,
1079 [FR_SHA1] = pap_auth_sha1,
1080 [FR_SSHA1] = pap_auth_ssha1,
1081
1082#ifdef HAVE_OPENSSL_EVP_H
1083 [FR_PBKDF2] = pap_auth_pbkdf2,
1084 [FR_PBKDF2_SHA1] = pap_auth_pbkdf2_sha1,
1085 [FR_PBKDF2_SHA256] = pap_auth_pbkdf2_sha256,
1086 [FR_PBKDF2_SHA512] = pap_auth_pbkdf2_sha512,
1087 [FR_PBKDF2_SHA256_LEGACY] = pap_auth_pbkdf2_sha256_legacy,
1088 [FR_SHA2] = pap_auth_dummy,
1089 [FR_SHA2_224] = pap_auth_sha2_224,
1090 [FR_SHA2_256] = pap_auth_sha2_256,
1091 [FR_SHA2_384] = pap_auth_sha2_384,
1092 [FR_SHA2_512] = pap_auth_sha2_512,
1093 [FR_SSHA2_224] = pap_auth_ssha2_224,
1094 [FR_SSHA2_256] = pap_auth_ssha2_256,
1095 [FR_SSHA2_384] = pap_auth_ssha2_384,
1096 [FR_SSHA2_512] = pap_auth_ssha2_512,
1097 [FR_SHA3] = pap_auth_dummy,
1098 [FR_SHA3_224] = pap_auth_sha3_224,
1099 [FR_SHA3_256] = pap_auth_sha3_256,
1100 [FR_SHA3_384] = pap_auth_sha3_384,
1101 [FR_SHA3_512] = pap_auth_sha3_512,
1102 [FR_SSHA3_224] = pap_auth_ssha3_224,
1103 [FR_SSHA3_256] = pap_auth_ssha3_256,
1104 [FR_SSHA3_384] = pap_auth_ssha3_384,
1105 [FR_SSHA3_512] = pap_auth_ssha3_512,
1106#endif /* HAVE_OPENSSL_EVP_H */
1107};
1108
1109/*
1110 * Authenticate the user via one of any well-known password.
1111 */
1112static unlang_action_t CC_HINT(nonnull) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
1113{
1115 fr_pair_t *known_good;
1116 pap_auth_func_t auth_func;
1117 bool ephemeral;
1118 pap_call_env_t *env_data = talloc_get_type_abort(mctx->env_data, pap_call_env_t);
1119
1120 p_result->rcode = RLM_MODULE_INVALID;
1121
1122 if (env_data->password.type != FR_TYPE_STRING) {
1123 REDEBUG("You set 'Auth-Type = PAP' for a request that does not contain a %s attribute!",
1124 env_data->password_tmpl->name);
1126 }
1127
1128 /*
1129 * The user MUST supply a non-zero-length password.
1130 */
1131 if (env_data->password.vb_length == 0) {
1132 REDEBUG("Password must not be empty");
1134 }
1135
1136 if (RDEBUG_ENABLED3) {
1137 RDEBUG3("Login attempt with %s = %pV (%zd)", env_data->password_tmpl->name,
1138 &env_data->password, env_data->password.vb_length);
1139 } else {
1140 RDEBUG2("Login attempt with password");
1141 }
1142
1143 /*
1144 * Retrieve the normalised version of
1145 * the known_good password, without
1146 * mangling the current password attributes
1147 * in the request.
1148 */
1149 known_good = password_find(&ephemeral, request, request,
1150 pap_alloweds, talloc_array_length(pap_alloweds),
1151 inst->normify);
1152 if (!known_good) {
1153 REDEBUG("No \"known good\" password found for user");
1155 }
1156
1157 fr_assert(known_good->da->attr < NUM_ELEMENTS(auth_func_table));
1158
1159 auth_func = auth_func_table[known_good->da->attr];
1160 fr_assert(auth_func);
1161
1162 if (RDEBUG_ENABLED3) {
1163 RDEBUG3("Comparing with \"known good\" %pP (%zu)", known_good, known_good->vp_length);
1164 } else {
1165 RDEBUG2("Comparing with \"known-good\" %s (%zu)", known_good->da->name, known_good->vp_length);
1166 }
1167
1168 /*
1169 * Authenticate, and return.
1170 */
1171 auth_func(p_result, inst, request, known_good, &env_data->password);
1172 if (ephemeral) TALLOC_FREE(known_good);
1173 switch (p_result->rcode) {
1174 case RLM_MODULE_REJECT:
1175 REDEBUG("Password incorrect");
1176 break;
1177
1178 case RLM_MODULE_OK:
1179 RDEBUG2("User authenticated successfully");
1180 break;
1181
1182 default:
1183 break;
1184 }
1185
1187}
1188
1189static int mod_instantiate(module_inst_ctx_t const *mctx)
1190{
1191 rlm_pap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_pap_t);
1192
1193 inst->auth_type = fr_dict_enum_by_name(attr_auth_type, mctx->mi->name, -1);
1194 if (!inst->auth_type) {
1195 WARN("Failed to find 'authenticate %s {...}' section. PAP will likely not work",
1196 mctx->mi->name);
1197 }
1198
1199 return 0;
1200}
1201
1202static int mod_load(void)
1203{
1204 size_t i, j = 0;
1205 size_t allowed = 0;
1206
1207 /*
1208 * Load the dictionaries early
1209 */
1210 if (fr_dict_autoload(rlm_pap_dict) < 0) {
1211 PERROR("%s", __FUNCTION__);
1212 return -1;
1213 }
1215 PERROR("%s", __FUNCTION__);
1217 return -1;
1218 }
1219
1220 /*
1221 * Figure out how many password types we allow
1222 */
1223 for (i = 0; i < NUM_ELEMENTS(auth_func_table); i++) {
1224 if (auth_func_table[i] == NULL) continue;
1225
1226 allowed++;
1227 }
1228
1229 /*
1230 * Get a list of the DAs that match are allowed
1231 * functions.
1232 */
1233 pap_alloweds = talloc_array(NULL, fr_dict_attr_t const *, allowed);
1234 for (i = 0; i < NUM_ELEMENTS(auth_func_table); i++) {
1235 fr_dict_attr_t const *password_da;
1236
1237 if (auth_func_table[i] == NULL) continue;
1238
1239 password_da = fr_dict_attr_child_by_num(attr_root, i);
1240 if (!fr_cond_assert(password_da)) {
1241 ERROR("Could not resolve password attribute %zu", i);
1244 return -1;
1245 }
1246
1247 pap_alloweds[j++] = password_da;
1248 }
1249
1250 return 0;
1251}
1252
1253static void mod_unload(void)
1254{
1257}
1258
1259/*
1260 * The module name should be the only globally exported symbol.
1261 * That is, everything else should be 'static'.
1262 *
1263 * If the module needs to temporarily modify it's instantiation
1264 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1265 * The server will then take care of ensuring that the module
1266 * is single-threaded.
1267 */
1268extern module_rlm_t rlm_pap;
1270 .common = {
1271 .magic = MODULE_MAGIC_INIT,
1272 .name = "pap",
1273 .inst_size = sizeof(rlm_pap_t),
1274 .onload = mod_load,
1275 .unload = mod_unload,
1277 .instantiate = mod_instantiate
1278 },
1279 .method_group = {
1280 .bindings = (module_method_binding_t[]){
1281 /*
1282 * Hack to support old configurations
1283 */
1284 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &pap_method_env },
1285 { .section = SECTION_NAME("authorize", CF_IDENT_ANY), .method = mod_authorize, .method_env = &pap_method_env },
1286 { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_authorize, .method_env = &pap_method_env },
1287
1289 }
1290 }
1291};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#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 FR_BASE64_DEC_LENGTH(_inlen)
Definition base64.h:41
#define USES_APPLE_DEPRECATED_API
Definition build.h:493
#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 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_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
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
#define CF_IDENT_ANY
Definition cf_util.h:75
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition dbuff.h:906
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
#define MEM(x)
Definition debug.h:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#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
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3593
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:3701
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
Value of an enumerated attribute.
Definition dict.h:253
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
talloc_free(hp)
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
#define PERROR(_fmt,...)
Definition log.h:228
#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 REDEBUG3(fmt,...)
Definition log.h:385
#define REMARKER(_str, _marker_idx, _marker,...)
Output string with error marker, showing where format error occurred.
Definition log.h:510
#define RPEDEBUG(fmt,...)
Definition log.h:388
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
#define RHEXDUMP2(_data, _len, _fmt,...)
Definition log.h:716
void fr_md4_calc(uint8_t out[static MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
Definition md4.c:473
#define MD4_DIGEST_LENGTH
Definition md4.h:22
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
Free function for MD5 digest ctx.
Definition md5.c:515
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
Allocation function for MD5 digest context.
Definition md5.c:470
#define fr_md5_final(_out, _ctx)
Finalise the ctx, producing the digest.
Definition md5.h:90
void fr_md5_ctx_t
Definition md5.h:25
#define fr_md5_update(_ctx, _in, _inlen)
Ingest plaintext into the digest.
Definition md5.h:83
#define MD5_DIGEST_LENGTH
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
unsigned int uint32_t
long int ssize_t
void fr_md5_calc(uint8_t out[static MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Perform a single digest operation on a single input buffer.
unsigned char uint8_t
unsigned long int size_t
ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Convert UTF8 string to UCS2 encoding.
Definition misc.c:347
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
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
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 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:418
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
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
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
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 const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define WARN(fmt,...)
#define RETURN_UNLANG_UPDATED
Definition rcode.h:70
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:47
#define RETURN_UNLANG_NOOP
Definition rcode.h:69
static const pap_auth_func_t auth_func_table[]
Table of password types we can process.
Definition rlm_pap.c:1068
static int mod_load(void)
Definition rlm_pap.c:1202
unlang_action_t(* pap_auth_func_t)(unlang_result_t *p_result, rlm_pap_t const *inst, request_t *request, fr_pair_t const *, fr_value_box_t const *)
Definition rlm_pap.c:72
static fr_dict_attr_autoload_t rlm_pap_dict_attr[]
Definition rlm_pap.c:105
static unlang_action_t pap_auth_md5(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:228
static fr_dict_t const * dict_freeradius
Definition rlm_pap.c:95
module_rlm_t rlm_pap
Definition rlm_pap.c:1269
fr_value_box_t password
Definition rlm_pap.c:80
static const call_env_method_t pap_method_env
Definition rlm_pap.c:84
fr_dict_enum_value_t const * auth_type
Definition rlm_pap.c:68
static fr_dict_autoload_t rlm_pap_dict[]
Definition rlm_pap.c:97
static unlang_action_t pap_auth_nt(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:958
static fr_dict_attr_t const * attr_auth_type
Definition rlm_pap.c:102
static unlang_action_t mod_authorize(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_pap.c:142
static void mod_unload(void)
Definition rlm_pap.c:1253
static unlang_action_t pap_auth_sha1(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:287
static unlang_action_t pap_auth_clear(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:172
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_pap.c:1112
static fr_dict_attr_t const * attr_root
Definition rlm_pap.c:103
static unlang_action_t pap_auth_dummy(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, UNUSED request_t *request, UNUSED fr_pair_t const *known_good, UNUSED fr_value_box_t const *password)
Auth func for password types that should have been normalised away.
Definition rlm_pap.c:1058
static unlang_action_t pap_auth_ns_mta_md5(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:993
static unlang_action_t pap_auth_ssha1(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:315
static fr_dict_attr_t const ** pap_alloweds
Definition rlm_pap.c:134
static USES_APPLE_DEPRECATED_API pthread_mutex_t fr_crypt_mutex
Definition rlm_pap.c:57
static const conf_parser_t module_config[]
Definition rlm_pap.c:74
bool normify
Definition rlm_pap.c:69
tmpl_t * password_tmpl
Definition rlm_pap.c:81
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_pap.c:1189
static unlang_action_t pap_auth_smd5(unlang_result_t *p_result, UNUSED rlm_pap_t const *inst, request_t *request, fr_pair_t const *known_good, fr_value_box_t const *password)
Definition rlm_pap.c:254
static unsigned int hash(char const *username, unsigned int tablesize)
Definition rlm_passwd.c:132
static char const * name
#define FR_SBUFF_IN(_start, _len_or_end)
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
char const * name
Instance name e.g. user_database.
Definition module.h:357
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
void fr_sha1_init(fr_sha1_ctx *context)
Definition sha1.c:93
void fr_sha1_final(uint8_t digest[static SHA1_DIGEST_LENGTH], fr_sha1_ctx *context)
Definition sha1.c:141
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *in, size_t len)
Definition sha1.c:105
#define SHA1_DIGEST_LENGTH
Definition sha1.h:29
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
eap_aka_sim_process_conf_t * inst
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
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 talloc_get_type_abort_const
Definition talloc.h:110
@ T_BARE_WORD
Definition token.h:118
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define FR_MAX_STRING_LEN
Definition value.h:30
int nonnull(2, 5))
#define fr_box_octets(_val, _len)
Definition value.h:311