26 RCSID(
"$Id: be9cb41684d143ba3162e051b7617272dc1e5aa0 $")
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/base64.h>
32 #include <freeradius-devel/rad_assert.h>
36 #include "../../include/md5.h"
37 #include "../../include/sha1.h"
39 #ifdef HAVE_OPENSSL_EVP_H
40 # include <openssl/evp.h>
77 {
"{clear}", PW_CLEARTEXT_PASSWORD },
78 {
"{cleartext}", PW_CLEARTEXT_PASSWORD },
79 {
"{md5}", PW_MD5_PASSWORD },
80 {
"{base64_md5}", PW_MD5_PASSWORD },
81 {
"{smd5}", PW_SMD5_PASSWORD },
82 {
"{crypt}", PW_CRYPT_PASSWORD },
83 #ifdef HAVE_OPENSSL_EVP_H
89 {
"{sha2}", PW_SHA2_PASSWORD },
90 {
"{sha224}", PW_SHA2_PASSWORD },
91 {
"{sha256}", PW_SHA2_PASSWORD },
92 {
"{sha384}", PW_SHA2_PASSWORD },
93 {
"{sha512}", PW_SHA2_PASSWORD },
94 {
"{ssha224}", PW_SSHA2_224_PASSWORD },
95 {
"{ssha256}", PW_SSHA2_256_PASSWORD },
96 {
"{ssha384}", PW_SSHA2_384_PASSWORD },
97 {
"{ssha512}", PW_SSHA2_512_PASSWORD },
99 {
"{sha}", PW_SHA_PASSWORD },
100 {
"{ssha}", PW_SSHA_PASSWORD },
101 {
"{md4}", PW_NT_PASSWORD },
102 {
"{nt}", PW_NT_PASSWORD },
103 {
"{nthash}", PW_NT_PASSWORD },
104 {
"{x-nthash}", PW_NT_PASSWORD },
105 {
"{ns-mta-md5}", PW_NS_MTA_MD5_PASSWORD },
106 {
"{x- orcllmv}", PW_LM_PASSWORD },
107 {
"{X- orclntv}", PW_NT_PASSWORD },
151 if (min_len >=
sizeof(buffer))
return;
159 if (!(vp->vp_length & 0x01) && vp->vp_length >= (2 * min_len)) {
162 decoded =
fr_hex2bin(buffer,
sizeof(buffer), vp->vp_strvalue, vp->vp_length);
163 if (decoded == (vp->vp_length >> 1)) {
164 RDEBUG2(
"Normalizing %s from hex encoding, %zu bytes -> %zu bytes",
165 vp->
da->
name, vp->vp_length, decoded);
175 if ((vp->vp_length * 3) >= ((min_len * 4))) {
177 decoded =
fr_base64_decode(buffer,
sizeof(buffer), vp->vp_strvalue, vp->vp_length);
178 if (decoded < 0)
return;
179 if (decoded >= (ssize_t) min_len) {
180 RDEBUG2(
"Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
181 vp->
da->
name, vp->vp_length, decoded);
239 if (hlen >=
sizeof(buffer)) {
240 REDEBUG(
"Password header too long. Got %zu bytes must be less than %zu bytes",
241 hlen,
sizeof(buffer));
245 memcpy(buffer, p, hlen);
251 RDEBUG3(
"Unknown header {%s} in Password-With-Header = \"%s\", re-writing to "
252 "Cleartext-Password", buffer, vp->vp_strvalue);
254 RDEBUG(
"Unknown header {%s} in Password-With-Header, re-writing to "
255 "Cleartext-Password", buffer);
268 new->vp_length = (len - hlen);
274 char *old_value, *new_value;
278 RDEBUG3(
"Converted: %s = '%s' -> %s = '%s'", vp->
da->
name, old_value, new->da->name, new_value);
279 talloc_free(old_value);
280 talloc_free(new_value);
294 if ((decoded > 0) && (digest[0] ==
'{') && (memchr(digest,
'}', decoded) != NULL)) {
295 RDEBUG2(
"Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
296 vp->
da->
name, vp->vp_length, decoded);
308 RDEBUG3(
"No {...} in Password-With-Header = \"%s\", re-writing to "
309 "Cleartext-Password", vp->vp_strvalue);
311 RDEBUG(
"No {...} in Password-With-Header, re-writing to Cleartext-Password");
330 bool auth_type =
false;
331 bool found_pw =
false;
341 case PW_USER_PASSWORD:
342 RWDEBUG(
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
343 RWDEBUG(
"!!! Ignoring control:User-Password. Update your !!!");
344 RWDEBUG(
"!!! configuration so that the \"known good\" clear text !!!");
345 RWDEBUG(
"!!! password is in Cleartext-Password and NOT in !!!");
346 RWDEBUG(
"!!! User-Password. !!!");
347 RWDEBUG(
"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
350 case PW_PASSWORD_WITH_HEADER:
358 RWDEBUG(
"Config already contains a \"known good\" password "
359 "(&control:Cleartext-Password). Ignoring &config:Password-With-Header");
366 RDEBUG2(
"Removing &control:Password-With-Header");
377 case PW_CLEARTEXT_PASSWORD:
378 case PW_CRYPT_PASSWORD:
379 case PW_NS_MTA_MD5_PASSWORD:
383 case PW_MD5_PASSWORD:
384 case PW_SMD5_PASSWORD:
393 #ifdef HAVE_OPENSSL_EVP_H
394 case PW_SHA2_PASSWORD:
401 case PW_SSHA2_224_PASSWORD:
408 case PW_SSHA2_256_PASSWORD:
415 case PW_SSHA2_384_PASSWORD:
422 case PW_SSHA2_512_PASSWORD:
430 case PW_SHA_PASSWORD:
431 case PW_SSHA_PASSWORD:
442 case PW_PROXY_TO_REALM:
458 if ((vp->vp_integer == 254) ||
459 (vp->vp_integer == 4)) {
488 ((vp->vp_integer == 13) ||
489 (vp->vp_integer == 21) ||
490 (vp->vp_integer == 25))) {
494 RWDEBUG(
"No \"known good\" password found for the user. Not setting Auth-Type");
495 RWDEBUG(
"Authentication will fail unless a \"known good\" password is available");
503 if (auth_type != inst->
auth_type)
RWDEBUG2(
"Auth-Type already set. Not setting to PAP");
510 if (!request->password ||
511 (request->password->da->attr != PW_USER_PASSWORD)) {
512 RDEBUG2(
"No User-Password attribute in the request. Cannot do PAP");
532 RDEBUG3(
"Comparing with \"known good\" Cleartext-Password \"%s\" (%zd)", vp->vp_strvalue, vp->vp_length);
534 RDEBUG(
"Comparing with \"known good\" Cleartext-Password");
537 if ((vp->vp_length != request->password->vp_length) ||
539 request->password->vp_octets,
540 vp->vp_length) != 0)) {
541 REDEBUG(
"Cleartext password does not match \"known good\" password");
550 RDEBUG3(
"Comparing with \"known good\" Crypt-Password \"%s\"", vp->vp_strvalue);
552 RDEBUG(
"Comparing with \"known-good\" Crypt-password");
556 vp->vp_strvalue) != 0) {
557 REDEBUG(
"Crypt digest does not match \"known good\" digest");
568 RDEBUG(
"Comparing with \"known-good\" MD5-Password");
573 if (vp->vp_length != 16) {
574 REDEBUG(
"\"known-good\" MD5 password has incorrect length");
580 request->password->vp_length);
584 REDEBUG(
"MD5 digest does not match \"known good\" digest");
597 RDEBUG(
"Comparing with \"known-good\" SMD5-Password");
602 if (vp->vp_length <= 16) {
603 REDEBUG(
"\"known-good\" SMD5-Password has incorrect length");
609 request->password->vp_length);
610 fr_md5_update(&md5_context, &vp->vp_octets[16], vp->vp_length - 16);
617 REDEBUG(
"SMD5 digest does not match \"known good\" digest");
629 RDEBUG(
"Comparing with \"known-good\" SHA-Password");
634 if (vp->vp_length != 20) {
635 REDEBUG(
"\"known-good\" SHA1-password has incorrect length");
641 request->password->vp_length);
645 REDEBUG(
"SHA1 digest does not match \"known good\" digest");
657 RDEBUG(
"Comparing with \"known-good\" SSHA-Password");
662 if (vp->vp_length <= 20) {
663 REDEBUG(
"\"known-good\" SSHA-Password has incorrect length");
668 fr_sha1_update(&sha1_context, request->password->vp_octets, request->password->vp_length);
670 fr_sha1_update(&sha1_context, &vp->vp_octets[20], vp->vp_length - 20);
674 REDEBUG(
"SSHA digest does not match \"known good\" digest");
681 #ifdef HAVE_OPENSSL_EVP_H
687 uint8_t digest[EVP_MAX_MD_SIZE];
688 unsigned int digest_len;
690 RDEBUG(
"Comparing with \"known-good\" SHA2-Password");
692 if (inst->normify)
normify(request, vp, 28);
698 switch (vp->vp_length) {
724 REDEBUG(
"\"known good\" digest length (%zu) does not match output length of any SHA-2 digests",
729 ctx = EVP_MD_CTX_create();
730 EVP_DigestInit_ex(ctx, md, NULL);
731 EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
732 EVP_DigestFinal_ex(ctx, digest, &digest_len);
733 EVP_MD_CTX_destroy(ctx);
735 rad_assert((
size_t) digest_len == vp->vp_length);
738 REDEBUG(
"%s digest does not match \"known good\" digest", name);
748 EVP_MD
const *md = NULL;
749 char const *name = NULL;
750 uint8_t digest[EVP_MAX_MD_SIZE];
751 unsigned int digest_len, min_len = 0;
753 switch (vp->da->attr) {
754 case PW_SSHA2_224_PASSWORD:
760 case PW_SSHA2_256_PASSWORD:
766 case PW_SSHA2_384_PASSWORD:
772 case PW_SSHA2_512_PASSWORD:
782 RDEBUG(
"Comparing with \"known-good\" %s-Password", name);
789 if (inst->normify)
normify(request, vp, min_len + 1);
791 if (vp->vp_length <= min_len) {
792 REDEBUG(
"\"known-good\" %s-Password has incorrect length, got %zu bytes, need at least %u bytes",
793 name, vp->vp_length, min_len + 1);
797 ctx = EVP_MD_CTX_create();
798 EVP_DigestInit_ex(ctx, md, NULL);
799 EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->vp_length);
800 EVP_DigestUpdate(ctx, &vp->vp_octets[min_len], vp->vp_length - min_len);
801 EVP_DigestFinal_ex(ctx, digest, &digest_len);
802 EVP_MD_CTX_destroy(ctx);
810 REDEBUG(
"%s digest does not match \"known good\" digest", name);
822 uint8_t ucs2_password[512];
824 RDEBUG(
"Comparing with \"known-good\" NT-Password");
827 rad_assert(request->password->da->attr == PW_USER_PASSWORD);
833 if (vp->vp_length != 16) {
834 REDEBUG(
"\"known good\" NT-Password has incorrect length");
838 len =
fr_utf8_to_ucs2(ucs2_password,
sizeof(ucs2_password), request->password->vp_strvalue, request->password->vp_length);
840 REDEBUG(
"User-Password is not in UCS2 format");
844 fr_md4_calc(digest, (uint8_t *) ucs2_password, len);
847 REDEBUG(
"NT digest does not match \"known good\" digest");
858 char charbuf[32 + 1];
861 RDEBUG(
"Comparing with \"known-good\" LM-Password");
866 if (vp->vp_length != 16) {
867 REDEBUG(
"\"known good\" LM-Password has incorrect length");
871 len =
radius_xlat(charbuf,
sizeof(charbuf), request,
"%{mschap:LM-Hash %{User-Password}}", NULL, NULL);
876 if ((
fr_hex2bin(digest,
sizeof(digest), charbuf, len) != vp->vp_length) ||
878 REDEBUG(
"LM digest does not match \"known good\" digest");
892 RDEBUG(
"Using NT-MTA-MD5-Password");
894 if (vp->vp_length != 64) {
895 REDEBUG(
"\"known good\" NS-MTA-MD5-Password has incorrect length");
902 if (
fr_hex2bin(digest,
sizeof(digest), vp->vp_strvalue, vp->vp_length) != 16) {
903 REDEBUG(
"\"known good\" NS-MTA-MD5-Password has invalid value");
912 if (request->password->vp_length >= (
sizeof(buff) - 2 - 2 * 32)) {
913 REDEBUG(
"\"known good\" NS-MTA-MD5-Password is too long");
923 memcpy(p, &vp->vp_octets[32], 32);
926 strcpy(p, request->password->vp_strvalue);
929 memcpy(p, &vp->vp_octets[32], 32);
938 REDEBUG(
"NS-MTA-MD5 digest does not match \"known good\" digest");
957 if (!request->password ||
958 (request->password->da->vendor != 0) ||
959 (request->password->da->attr != PW_USER_PASSWORD)) {
960 REDEBUG(
"You set 'Auth-Type = PAP' for a request that does not contain a User-Password attribute!");
967 if (request->password->vp_length == 0) {
968 REDEBUG(
"Password must not be empty");
973 RDEBUG3(
"Login attempt with password \"%s\" (%zd)", request->password->vp_strvalue, request->password->vp_length);
975 RDEBUG(
"Login attempt with password");
987 case PW_CLEARTEXT_PASSWORD:
988 auth_func = &pap_auth_clear;
991 case PW_CRYPT_PASSWORD:
992 auth_func = &pap_auth_crypt;
995 case PW_MD5_PASSWORD:
996 auth_func = &pap_auth_md5;
999 case PW_SMD5_PASSWORD:
1000 auth_func = &pap_auth_smd5;
1003 #ifdef HAVE_OPENSSL_EVP_H
1004 case PW_SHA2_PASSWORD:
1005 auth_func = &pap_auth_sha2;
1008 case PW_SSHA2_224_PASSWORD:
1009 case PW_SSHA2_256_PASSWORD:
1010 case PW_SSHA2_384_PASSWORD:
1011 case PW_SSHA2_512_PASSWORD:
1012 auth_func = &pap_auth_ssha2;
1016 case PW_SHA_PASSWORD:
1017 auth_func = &pap_auth_sha;
1020 case PW_SSHA_PASSWORD:
1021 auth_func = &pap_auth_ssha;
1024 case PW_NT_PASSWORD:
1025 auth_func = &pap_auth_nt;
1028 case PW_LM_PASSWORD:
1029 auth_func = &pap_auth_lm;
1032 case PW_NS_MTA_MD5_PASSWORD:
1033 auth_func = &pap_auth_ns_mta_md5;
1040 if (auth_func != NULL)
break;
1047 RDEBUG(
"No password configured for the user. Cannot do authentication");
1054 rc = auth_func(inst, request, vp);
1057 RDEBUG(
"Passwords don't match");
1061 RDEBUG(
"User authenticated successfully");
1083 .config = module_config,
void fr_sha1_update(fr_sha1_ctx *context, uint8_t const *data, size_t len)
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
The module is OK, continue.
Metadata exported by the module.
VALUE_PAIR * fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int vendor, unsigned int attr)
Create a new valuepair.
VALUE_PAIR * radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor)
Create a VALUE_PAIR and add it to a list of VALUE_PAIR s.
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
Handle authorization requests using Couchbase document data.
static rlm_rcode_t CC_HINT(nonnull)
void fr_md5_init(FR_MD5_CTX *ctx)
Initialise a new MD5 context.
#define CONF_PARSER_TERMINATOR
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
void fr_md4_calc(uint8_t out[MD4_DIGEST_LENGTH], uint8_t const *in, size_t inlen)
Calculate the MD4 hash of the contents of a buffer.
static VALUE_PAIR * normify_with_header(REQUEST *request, VALUE_PAIR *vp)
Convert a Password-With-Header attribute to the correct type.
The module considers the request invalid.
#define RLM_TYPE_HUP_SAFE
Will be restarted on HUP.
static int mod_instantiate(CONF_SECTION *conf, void *instance)
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull)
ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Defines a CONF_PAIR to C data type mapping.
fr_dict_enum_t * fr_dict_enum_by_name(fr_dict_t *dict, fr_dict_attr_t const *da, char const *val)
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
void fr_sha1_init(fr_sha1_ctx *context)
char * fr_pair_value_asprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp, char quote)
Print one attribute value to a string.
void fr_md5_update(FR_MD5_CTX *ctx, uint8_t const *in, size_t inlen) CC_BOUNDED(__string__
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
void fr_pair_value_strcpy(VALUE_PAIR *vp, char const *src)
Copy data into an "string" data type.
USES_APPLE_DEPRECATED_API struct rlm_pap_t rlm_pap_t
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
size_t fr_hex2bin(uint8_t *bin, size_t outlen, char const *hex, size_t inlen)
Convert hex strings to binary data.
unsigned int attr
Attribute number.
Immediately reject the request.
static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_len)
Hex or base64 or bin auto-discovery.
unsigned int vendor
Vendor that defines this attribute.
Stores an attribute, a value and various bits of other data.
VALUE_PAIR * fr_cursor_current(vp_cursor_t *cursor)
Return the VALUE_PAIR the cursor current points to.
void fr_sha1_final(uint8_t digest[20], fr_sha1_ctx *context)
0 methods index for authenticate section.
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape, void *escape_ctx) CC_HINT(nonnull(1
#define RWDEBUG2(fmt,...)
ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
Convert UTF8 string to UCS2 encoding.
int fr_crypt_check(char const *key, char const *salt)
char const * cf_section_name1(CONF_SECTION const *cs)
Module succeeded without doing anything.
static const FR_NAME_NUMBER header_names[]
char name[1]
Attribute name.
REALM * realm_find(char const *name)
static const CONF_PARSER module_config[]
uint64_t magic
Used to validate module struct.
Module failed, don't reply.
#define FR_CONF_OFFSET(_n, _t, _s, _f)
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
int fr_radius_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL digest.
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
VALUE_PAIR * fr_cursor_remove(vp_cursor_t *cursor)
Remove the current pair.
void void fr_md5_final(uint8_t out[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx) CC_BOUNDED(__minbytes__
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
void fr_pair_value_bstrncpy(VALUE_PAIR *vp, void const *src, size_t len)
Copy data into an "string" data type.
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
String of printable characters.
1 methods index for authorize section.
Value of an enumerated attribute.
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
char const * cf_section_name2(CONF_SECTION const *cs)
#define USES_APPLE_DEPRECATED_API