#include <freeradius-devel/server/password.h>
-#include <freeradius-devel/util/value.h>
-#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/base64.h>
+#include <freeradius-devel/util/md4.h>
+#include <freeradius-devel/util/md5.h>
+#include <freeradius-devel/util/misc.h>
+#include <freeradius-devel/util/sha1.h>
+#include <freeradius-devel/util/value.h>
#include <freeradius-devel/protocol/freeradius/freeradius.internal.password.h>
#ifdef HAVE_OPENSSL_EVP_H
# include <openssl/evp.h>
+# include <openssl/sha.h>
#endif
+typedef enum {
+ PASSWORD_CLEARTEXT = 0, //!< Variable length.
+ PASSWORD_HASH, //!< Fixed lenth.
+ PASSWORD_HASH_SALTED, //!< Fixed length hash, variable length salt.
+ PASSWORD_HASH_VARIABLE //!< Variable length everything.
+} password_type_t;
+
+/** Apply preprocessing logic to a password value
+ *
+ * @param[in] ctx to allocate returned value in.
+ * @
+ */
+typedef VALUE_PAIR *(*password_preprocess_t)(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *in);
+
+/** Password information
+ *
+ */
+typedef struct {
+ password_type_t type; //!< What type of password value this is.
+ fr_dict_attr_t const **da; //!< Dictionary attribute representing this type of password.
+ password_preprocess_t func; //!< Preprocessing function.
+ size_t min_hash_len; //!< Minimum length of the decoded string if normifying.
+ ///< If 0, will be ignored.
+ size_t max_hash_len; //!< Maximum length of the decoded string if normifying.
+ ///< If 0, will be ignored.
+ bool no_normify; //!< Don't attempt to normalise the contents of this
+ ///< attribute using the hex/base64 decoders.
+} password_info_t;
+
static fr_dict_t *dict_freeradius = NULL;
+static fr_dict_t *dict_radius = NULL;
static fr_dict_attr_t const *attr_cleartext_password;
static fr_dict_attr_t const *attr_password_with_header;
+static fr_dict_attr_t const *attr_password_root;
static fr_dict_attr_t const *attr_md5_password;
static fr_dict_attr_t const *attr_smd5_password;
static fr_dict_attr_t const *attr_crypt_password;
-static fr_dict_attr_t const *attr_sha_password;
-static fr_dict_attr_t const *attr_ssha_password;
+
+static fr_dict_attr_t const *attr_sha1_password;
+static fr_dict_attr_t const *attr_ssha1_password;
static fr_dict_attr_t const *attr_sha2_password;
+static fr_dict_attr_t const *attr_sha2_224_password;
+static fr_dict_attr_t const *attr_sha2_256_password;
+static fr_dict_attr_t const *attr_sha2_384_password;
+static fr_dict_attr_t const *attr_sha2_512_password;
+
static fr_dict_attr_t const *attr_ssha2_224_password;
static fr_dict_attr_t const *attr_ssha2_256_password;
static fr_dict_attr_t const *attr_ssha2_384_password;
static fr_dict_attr_t const *attr_ssha2_512_password;
static fr_dict_attr_t const *attr_sha3_password;
+static fr_dict_attr_t const *attr_sha3_224_password;
+static fr_dict_attr_t const *attr_sha3_256_password;
+static fr_dict_attr_t const *attr_sha3_384_password;
+static fr_dict_attr_t const *attr_sha3_512_password;
+
static fr_dict_attr_t const *attr_ssha3_224_password;
static fr_dict_attr_t const *attr_ssha3_256_password;
static fr_dict_attr_t const *attr_ssha3_384_password;
static fr_dict_attr_t const *attr_nt_password;
static fr_dict_attr_t const *attr_ns_mta_md5_password;
+static fr_dict_attr_t const *attr_user_password;
+
extern fr_dict_autoload_t password_dict[];
fr_dict_autoload_t password_dict[] = {
{ .out = &dict_freeradius, .proto = "freeradius" },
+ { .out = &dict_radius, .proto = "radius" },
{ NULL }
};
fr_dict_attr_autoload_t password_dict_attr[] = {
{ .out = &attr_cleartext_password, .name = "Cleartext-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
{ .out = &attr_password_with_header, .name = "Password-With-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+ { .out = &attr_password_root, .name = "Password-Root", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
{ .out = &attr_md5_password, .name = "MD5-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_smd5_password, .name = "SMD5-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_crypt_password, .name = "Crypt-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
- { .out = &attr_sha_password, .name = "SHA-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha_password, .name = "SSHA-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha1_password, .name = "SHA1-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_ssha1_password, .name = "SSHA1-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_sha2_password, .name = "SHA2-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha2_224_password, .name = "SHA2-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha2_256_password, .name = "SHA2-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha2_384_password, .name = "SHA2-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha2_512_password, .name = "SHA2-512-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+
{ .out = &attr_ssha2_224_password, .name = "SSHA2-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ssha2_256_password, .name = "SSHA2-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ssha2_384_password, .name = "SSHA2-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ssha2_512_password, .name = "SSHA2-512-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_sha3_password, .name = "SHA3-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha3_224_password, .name = "SHA3-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha3_256_password, .name = "SHA3-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha3_384_password, .name = "SHA3-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+ { .out = &attr_sha3_512_password, .name = "SHA3-512-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
+
{ .out = &attr_ssha3_224_password, .name = "SSHA3-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ssha3_256_password, .name = "SSHA3-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ssha3_384_password, .name = "SSHA3-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_nt_password, .name = "NT-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
{ .out = &attr_ns_mta_md5_password, .name = "NS-MTA-MD5-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+ { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius },
+
{ NULL }
};
-
typedef enum {
NORMALISED_NOTHING = 0,
NORMALISED_B64,
};
static size_t normalise_table_len = NUM_ELEMENTS(normalise_table);
+/*
+ * Headers for the Password-with-Header attribute
+ *
+ * @note Header comparison is case insensitive.
+ */
+static fr_table_num_sorted_t const password_header_table[] = {
+ { "{base64_md5}", FR_MD5_PASSWORD },
+ { "{clear}", FR_CLEARTEXT_PASSWORD },
+ { "{cleartext}", FR_CLEARTEXT_PASSWORD },
+ { "{crypt}", FR_CRYPT_PASSWORD },
+ { "{md4}", FR_NT_PASSWORD },
+ { "{md5}", FR_MD5_PASSWORD },
+ { "{ns-mta-md5}", FR_NS_MTA_MD5_PASSWORD },
+ { "{nt}", FR_NT_PASSWORD },
+ { "{nthash}", FR_NT_PASSWORD },
+
+#ifdef HAVE_OPENSSL_EVP_H
+ { "{sha224}", FR_SHA2_PASSWORD },
+ { "{sha256}", FR_SHA2_PASSWORD },
+ { "{sha2}", FR_SHA2_PASSWORD },
+ { "{sha384}", FR_SHA2_384_PASSWORD },
+ { "{sha512}", FR_SHA2_512_PASSWORD },
+#endif
+ { "{sha}", FR_SHA1_PASSWORD },
+ { "{smd5}", FR_SMD5_PASSWORD },
+#ifdef HAVE_OPENSSL_EVP_H
+ { "{ssha224}", FR_SSHA2_224_PASSWORD },
+ { "{ssha256}", FR_SSHA2_256_PASSWORD },
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+ { "{ssha3-224}", FR_SSHA3_224_PASSWORD },
+ { "{ssha3-256}", FR_SSHA3_256_PASSWORD },
+ { "{ssha3-384}", FR_SSHA3_384_PASSWORD },
+ { "{ssha3-512}", FR_SSHA3_512_PASSWORD },
+# endif
+ { "{ssha384}", FR_SSHA2_384_PASSWORD },
+ { "{ssha512}", FR_SSHA2_512_PASSWORD },
+#endif
+ { "{ssha}", FR_SSHA1_PASSWORD },
+ { "{x- orcllmv}", FR_LM_PASSWORD },
+ { "{x- orclntv}", FR_NT_PASSWORD },
+ { "{x-nthash}", FR_NT_PASSWORD },
+ { "{x-pbkdf2}", FR_PBKDF2_PASSWORD },
+};
+static size_t password_header_table_len = NUM_ELEMENTS(password_header_table);
+
+#ifdef HAVE_OPENSSL_EVP_H
+static VALUE_PAIR *password_process_sha2(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good);
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+static VALUE_PAIR *password_process_sha3(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good);
+# endif
+#endif
+static VALUE_PAIR *password_process_header(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good);
+
+/** Metdata for various password attributes
+ *
+ */
+static password_info_t password_info[] = {
+ [FR_CLEARTEXT_PASSWORD] = {
+ .type = PASSWORD_CLEARTEXT,
+ .da = &attr_cleartext_password,
+ .no_normify = true
+ },
+ [FR_CRYPT_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_crypt_password
+ },
+ [FR_LM_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_lm_password,
+ .min_hash_len = MD4_DIGEST_LENGTH
+ },
+ [FR_MD5_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_md5_password,
+ .min_hash_len = MD5_DIGEST_LENGTH
+ },
+ [FR_NS_MTA_MD5_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_ns_mta_md5_password
+ },
+ [FR_NT_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_nt_password,
+ .min_hash_len = MD4_DIGEST_LENGTH
+ },
+ [FR_PASSWORD_WITH_HEADER] = {
+ .type = PASSWORD_HASH_VARIABLE,
+ .da = &attr_password_with_header,
+ .func = password_process_header
+ },
+ [FR_PBKDF2_PASSWORD] = {
+ .type = PASSWORD_HASH_VARIABLE,
+ .da = &attr_pbkdf2_password
+ },
+ [FR_SHA1_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha1_password,
+ .min_hash_len = SHA1_DIGEST_LENGTH
+ },
+#ifdef HAVE_OPENSSL_EVP_H
+ [FR_SHA2_PASSWORD] = {
+ .type = PASSWORD_HASH_VARIABLE,
+ .da = &attr_sha2_password,
+ .func = password_process_sha2,
+ .min_hash_len = SHA224_DIGEST_LENGTH,
+ .max_hash_len = SHA512_DIGEST_LENGTH
+ },
+ [FR_SHA2_224_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha2_224_password,
+ .min_hash_len = SHA224_DIGEST_LENGTH,
+ },
+ [FR_SHA2_256_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha2_256_password,
+ .min_hash_len = SHA256_DIGEST_LENGTH,
+ },
+ [FR_SHA2_384_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha2_384_password,
+ .min_hash_len = SHA384_DIGEST_LENGTH,
+ },
+ [FR_SHA2_512_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha2_512_password,
+ .min_hash_len = SHA512_DIGEST_LENGTH,
+ },
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+ [FR_SHA3_PASSWORD] = {
+ .type = PASSWORD_HASH_VARIABLE,
+ .da = &attr_sha3_password,
+ .func = password_process_sha3,
+ .min_hash_len = SHA224_DIGEST_LENGTH,
+ },
+ [FR_SHA3_224_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha3_224_password,
+ .min_hash_len = SHA224_DIGEST_LENGTH,
+ },
+ [FR_SHA3_256_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha3_256_password,
+ .min_hash_len = SHA256_DIGEST_LENGTH,
+ },
+ [FR_SHA3_384_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha3_384_password,
+ .min_hash_len = SHA384_DIGEST_LENGTH,
+ },
+ [FR_SHA3_512_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_sha3_512_password,
+ .min_hash_len = SHA512_DIGEST_LENGTH
+ },
+# endif
+#endif
+ [FR_SMD5_PASSWORD] = {
+ .type = PASSWORD_HASH,
+ .da = &attr_smd5_password,
+ .min_hash_len = MD5_DIGEST_LENGTH
+ },
+ [FR_SSHA1_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha1_password,
+ .min_hash_len = SHA1_DIGEST_LENGTH
+ },
+#ifdef HAVE_OPENSSL_EVP_H
+ [FR_SSHA2_224_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha2_224_password,
+ .min_hash_len = SHA224_DIGEST_LENGTH
+ },
+ [FR_SSHA2_256_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha2_256_password,
+ .min_hash_len = SHA256_DIGEST_LENGTH
+ },
+ [FR_SSHA2_384_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha2_384_password,
+ .min_hash_len = SHA384_DIGEST_LENGTH
+ },
+ [FR_SSHA2_512_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha2_512_password,
+ .min_hash_len = SHA512_DIGEST_LENGTH
+ },
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+ [FR_SSHA3_224_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha3_224_password,
+ .min_hash_len = SHA224_DIGEST_LENGTH,
+ },
+ [FR_SSHA3_256_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha3_256_password,
+ .min_hash_len = SHA256_DIGEST_LENGTH
+ },
+ [FR_SSHA3_384_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha3_384_password,
+ .min_hash_len = SHA384_DIGEST_LENGTH
+ },
+ [FR_SSHA3_512_PASSWORD] = {
+ .type = PASSWORD_HASH_SALTED,
+ .da = &attr_ssha3_512_password,
+ .min_hash_len = SHA512_DIGEST_LENGTH
+ }
+# endif
+#endif
+};
+
+#define MIN_LEN(_info) (info->type == PASSWORD_HASH_SALTED ? (info->min_hash_len + 1) : info->min_hash_len)
+
static ssize_t normify(normalise_t *action, uint8_t *buffer, size_t bufflen,
char const *known_good, size_t len, size_t min_len)
{
+ /*
+ * Else unknown encoding, or already binary. Leave it.
+ */
+ if (action) *action = NORMALISED_NOTHING;
+
if (min_len >= bufflen) return 0; /* paranoia */
/*
}
}
- /*
- * Else unknown encoding, or already binary. Leave it.
- */
- if (action) *action = NORMALISED_NOTHING;
return 0;
}
* @param[in] ctx to allocate new pairs in.
* @param[in] request The current request.
* @param[in] known_good password to normify.
- * @param[in] min_len we expect the decoded version to be.
* @return
- * - NULL if known_good was already normalised.
+ * - NULL if known_good was already normalised, or couldn't be normalised.
* - A new normalised password pair.
*/
-VALUE_PAIR *password_normify(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR const *known_good, size_t min_len)
+static VALUE_PAIR *password_normify(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR const *known_good)
{
uint8_t buffer[256];
ssize_t decoded;
VALUE_PAIR *out;
- normalise_t normalised = NORMALISED_NOTHING;
+ normalise_t normalised;
+ password_info_t *info;
+ size_t min_len;
+
+ if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
+ info = &password_info[known_good->da->attr];
+ min_len = MIN_LEN(info);
if (min_len >= sizeof(buffer)) return NULL; /* paranoia */
switch (known_good->da->type) {
known_good->vp_length, decoded);
MEM(out = fr_pair_afrom_da(ctx, known_good->da));
fr_pair_value_memcpy(out, buffer, decoded, known_good->vp_tainted);
+ return out;
}
/*
return NULL;
}
+#ifdef HAVE_OPENSSL_EVP_H
+/** Split SHA2 hashes into separate attributes based on their length
+ *
+ * @param[in] ctx to allocate attributes in.
+ * @param[in] request The current request.
+ * @param[in] known_good attribute to split.
+ * @return
+ * - A SHA2 length specific attribute.
+ * - NULL on error.
+ */
+static VALUE_PAIR *password_process_sha2(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good)
+{
+ VALUE_PAIR *out, *normalised;
+
+ switch (known_good->vp_length) {
+ case SHA224_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha2_224_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA256_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha2_256_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA384_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha2_384_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA512_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha2_512_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ default:
+ out = password_normify(ctx, request, known_good);
+ if (!out) return NULL;
+
+ normalised = password_process_sha2(ctx, request, out);
+ talloc_list_free(&out);
+
+ return normalised;
+ }
+}
+
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+/** Split SHA3 hashes into separate attributes based on their length
+ *
+ * @param[in] ctx to allocate attributes in.
+ * @param[in] request The current request.
+ * @param[in] known_good attribute to split.
+ * @return
+ * - A SHA3 length specific attribute.
+ * - NULL on error.
+ */
+static VALUE_PAIR *password_process_sha3(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good)
+{
+ VALUE_PAIR *out, *normalised;
+
+ switch (known_good->vp_length) {
+ case SHA224_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha3_224_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA256_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha3_256_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA384_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha3_384_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ case SHA512_DIGEST_LENGTH:
+ out = fr_pair_afrom_da(ctx, attr_sha3_512_password);
+ fr_pair_value_copy(out, known_good);
+ return out;
+
+ default:
+ out = password_normify(ctx, request, known_good);
+ if (!out) return NULL;
+
+ normalised = password_process_sha3(ctx, request, out);
+ talloc_list_free(out);
+
+ return normalised;
+ }
+}
+# endif
+#endif
+
/** Convert a Password-With-Header attribute to the correct type
*
* Attribute may be base64 encoded, in which case it will be decoded
* first, then evaluated.
*
- * @note The buffer for octets type attributes is extended by one byte
+ * @note The buffer for octets types\ attributes is extended by one byte
* and '\0' terminated, to allow it to be used as a char buff.
*
* @param[in] ctx to allocate new pairs in.
* @param[in] request Current request.
* @param[in] known_good Password-With-Header attribute to convert.
- * @param[in] func to convert header strings to fr_dict_attr_t.
- * @param[in] def Default attribute to copy value to if we
- * don't recognise the header.
* @return
- * - New #VALUE_PAIR on success.
+ * - Buffer containing normified value on success.
* - NULL on error.
*/
-VALUE_PAIR *password_normify_with_header(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good,
- password_header_lookup_t func, fr_dict_attr_t const *def)
+static VALUE_PAIR *password_process_header(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good)
{
- char const *p, *q, *end;
+ char const *p, *q, *end;
- uint8_t n1[256], n2[256];
- ssize_t decoded;
+ uint8_t n1[256], n2[256];
+ ssize_t decoded;
- char header[128];
- normalise_t normalised;
+ char header[128];
+ normalise_t normalised;
- int i;
-
- VALUE_PAIR *new;
+ VALUE_PAIR *new;
+ fr_dict_attr_t const *def = attr_cleartext_password;
VP_VERIFY(known_good);
end = p + known_good->vp_length;
/*
- * Only allow one additional level of
- * normification and header parsing.
+ * Has a header {...} prefix
*/
- for (i = 0; i <= 1; i++) {
- /*
- * Has a header {...} prefix
- */
- if ((*p == '{') && (q = memchr(p, '}', end - p))) {
- size_t hlen;
- fr_dict_attr_t const *da;
- ssize_t slen;
-
- hlen = (q - p) + 1;
- if (hlen >= sizeof(header)) {
- REDEBUG("Password header too long. Got %zu bytes must be less than %zu bytes",
- hlen, sizeof(header));
- return NULL;
- }
-
- memcpy(header, p, hlen);
- header[hlen] = '\0';
-
- slen = func(&da, header);
- if (slen <= 0) {
- /*
- * header buffer retains { and }
- */
- if (RDEBUG_ENABLED3) {
- RDEBUG3("Unknown header %s in %pP, re-writing to %s",
- header, known_good, def->name);
- } else {
- RDEBUG2("Unknown header %s in %s, re-writing to %s",
- header, known_good->da->name, def->name);
- }
- goto unknown_header;
- }
+do_header:
+ if ((*p == '{') && (q = memchr(p, '}', end - p))) {
+ size_t hlen;
+ int attr;
+ password_info_t *info;
+
+ hlen = (q - p) + 1;
+ if (hlen >= sizeof(header)) {
+ REDEBUG("Password header too long. Got %zu bytes must be less than %zu bytes",
+ hlen, sizeof(header));
+ return NULL;
+ }
- p = q + 1;
+ memcpy(header, p, hlen);
+ header[hlen] = '\0';
+ attr = fr_table_value_by_substr(password_header_table, header, hlen, -1);
+ if (attr < 0) {
/*
- * Try and base64 decode, and if we can
- * use the decoded value.
- *
- * FIXME: Should pass in min length for
- * password hash da represents.
+ * header buffer retains { and }
*/
- decoded = normify(&normalised, n1, sizeof(n1), p, end - p, 1);
- if (decoded > 0) {
- RDEBUG2("Normalizing %s %s encoding, %zu bytes -> %zu bytes",
- da->name, fr_table_str_by_value(normalise_table, normalised, 0),
- (end - p), decoded);
- p = (char const *)n1;
- end = p + decoded;
+ if (RDEBUG_ENABLED3) {
+ RDEBUG3("Unknown header %s in %pP, re-writing to %s",
+ header, known_good, def->name);
+ } else {
+ RDEBUG2("Unknown header %s in %s, re-writing to %s",
+ header, known_good->da->name, def->name);
}
+ p = q + 1;
+ goto bad_header;
+ }
- new = fr_pair_afrom_da(ctx, da);
- switch (da->type) {
- case FR_TYPE_OCTETS:
- fr_pair_value_memcpy(new, (uint8_t const *)p, end - p, true);
- break;
+ p = q + 1;
- case FR_TYPE_STRING:
- fr_pair_value_bstrncpy(new, (uint8_t const *)p, end - p);
- break;
+ if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
+ info = &password_info[attr];
- default:
- if (!fr_cond_assert(0)) return NULL;
- }
- return new;
- }
+ new = fr_pair_afrom_da(ctx, *(info->da));
+ switch ((*(info->da))->type) {
+ case FR_TYPE_OCTETS:
+ fr_pair_value_memcpy(new, (uint8_t const *)p, end - p, true);
+ break;
- /*
- * Doesn't have a header {...} prefix
- *
- * See if it's base64 or hex, if it is, decode it and check again!
- */
- decoded = normify(&normalised, n1, sizeof(n1), p, end - p, 1);
- if (decoded > 0) {
- if ((n1[0] == '{') && (memchr(n1, '}', decoded) != NULL)) {
- RDEBUG2("Normalizing %s %s encoding, %zu bytes -> %zu bytes",
- known_good->da->name, fr_table_str_by_value(normalise_table, normalised, 0),
- known_good->vp_length, decoded);
+ case FR_TYPE_STRING:
+ fr_pair_value_bstrncpy(new, (uint8_t const *)p, end - p);
+ break;
- /*
- * Password-With-Header is a string attribute.
- * Even though we're handling binary data, the header
- * must be \0 terminated.
- */
- memcpy(n2, n1, decoded);
- p = (char const *)n2;
- end = p + decoded;
- continue;
- }
+ default:
+ if (!fr_cond_assert(0)) return NULL;
}
+ return new;
+ }
- break;
+ /*
+ * Doesn't have a header {...} prefix
+ *
+ * See if it's base64 or hex, if it is, decode it and check again!
+ *
+ * We ignore request not to normify, as curly braces aren't
+ * in either of the character sets for the encoding schemes
+ * we're normifying, so there's not the possibility for error
+ * as there is normifying other password hashes.
+ */
+ decoded = normify(&normalised, n1, sizeof(n1), p, end - p, 4); /* { + <char> + } + <char> */
+ if (decoded > 0) {
+ if ((n1[0] == '{') && (memchr(n1, '}', decoded) != NULL)) {
+ RDEBUG2("Normalizing %s %s encoding, %zu bytes -> %zu bytes",
+ known_good->da->name, fr_table_str_by_value(normalise_table, normalised, 0),
+ known_good->vp_length, decoded);
+
+ /*
+ * Password-With-Header is a string attribute.
+ * Even though we're handling binary data, the header
+ * must be \0 terminated.
+ */
+ memcpy(n2, n1, decoded);
+ p = (char const *)n2;
+ end = p + decoded;
+ goto do_header;
+ }
}
+ /*
+ * Rewrite to the default attribute type
+ * currently Cleartext-Password.
+ *
+ * This is usually correct if there's no
+ * header to indicate hash type.
+ */
if (RDEBUG_ENABLED3) {
- RDEBUG3("No {...} in &%pP, re-writing to %s", known_good, def->name);
+ RDEBUG3("No {...} in &control:%pP, re-writing to %s", known_good, def->name);
} else {
- RDEBUG2("No {...} in &%s, re-writing to %s", known_good->da->name, def->name);
+ RDEBUG2("No {...} in &control:%s, re-writing to %s", known_good->da->name, def->name);
}
-unknown_header:
+bad_header:
new = fr_pair_afrom_da(request, def);
fr_pair_value_bstrncpy(new, p, end - p);
return new;
}
-
-/*
- * For auto-header discovery.
- *
- * @note Header comparison is case insensitive.
- *
- * We don't put the *value* of "attr_foo" here, as those
- * values are loaded at run time. Instead, we point to
- * the attr_foo definition, which is then a static pointer
- * to a known variable.
- */
-static fr_table_ptr_ordered_t const header_names[] = {
- { "X- orclntv}", &attr_nt_password },
- { "{base64_md5}", &attr_md5_password },
- { "{cleartext}", &attr_cleartext_password },
- { "{clear}", &attr_cleartext_password },
- { "{crypt}", &attr_crypt_password },
- { "{md4}", &attr_nt_password },
- { "{md5}", &attr_md5_password },
- { "{ns-mta-md5}", &attr_ns_mta_md5_password },
- { "{nthash}", &attr_nt_password },
- { "{nt}", &attr_nt_password },
-#ifdef HAVE_OPENSSL_EVP_H
- { "{sha224}", &attr_sha2_password },
- { "{sha256}", &attr_sha2_password },
- { "{sha2}", &attr_sha2_password },
- { "{sha384}", &attr_sha2_password },
- { "{sha512}", &attr_sha2_password },
-#endif
- { "{sha}", &attr_sha_password },
- { "{smd5}", &attr_smd5_password },
-#ifdef HAVE_OPENSSL_EVP_H
- { "{ssha224}", &attr_ssha2_224_password },
- { "{ssha256}", &attr_ssha2_256_password },
-# if OPENSSL_VERSION_NUMBER >= 0x10101000L
- { "{ssha3-224}", &attr_ssha3_224_password },
- { "{ssha3-256}", &attr_ssha3_256_password },
- { "{ssha3-384}", &attr_ssha3_384_password },
- { "{ssha3-512}", &attr_ssha3_512_password },
-# endif
- { "{ssha384}", &attr_ssha2_384_password },
- { "{ssha512}", &attr_ssha2_512_password },
-#endif
- { "{ssha}", &attr_ssha_password },
- { "{x- orcllmv}", &attr_lm_password },
- { "{x-nthash}", &attr_nt_password },
- { "{x-pbkdf2}", &attr_pbkdf2_password },
-};
-static size_t header_names_len = NUM_ELEMENTS(header_names);
-
-
-static ssize_t known_password_header(fr_dict_attr_t const **out, char const *header)
-{
- fr_dict_attr_t const **da;
-
- da = fr_table_value_by_str(header_names, header, NULL);
- if (!da || !*da) return -1;
-
- *out = *da;
- return strlen(header);
-}
-
-static const size_t fr_password_length[] = {
- [FR_LM_PASSWORD - 5000 ] = 16,
- [FR_MD5_PASSWORD - 5000 ] = 16,
- [FR_MS_CHAP_PASSWORD - 5000 ] = 0, /* used only by radclient */
- [FR_NS_MTA_MD5_PASSWORD - 5000 ] = 0, /* not used at all by anyone/// */
- [FR_NT_PASSWORD - 5000 ] = 16,
- [FR_PBKDF2_PASSWORD - 5000 ] = 0, /* already normalized */
- [FR_SHA_PASSWORD - 5000 ] = 20,
- [FR_SHA1_PASSWORD - 5000 ] = 20,
- [FR_SHA2_PASSWORD - 5000 ] = 28,
- [FR_SHA3_PASSWORD - 5000 ] = 28,
- [FR_SMD5_PASSWORD - 5000 ] = 16,
- [FR_SSHA_PASSWORD - 5000 ] = 20,
- [FR_SSHA1_PASSWORD - 5000 ] = 20,
- [FR_SSHA2_224_PASSWORD - 5000 ] = 28,
- [FR_SSHA2_256_PASSWORD - 5000 ] = 32,
- [FR_SSHA2_384_PASSWORD - 5000 ] = 48,
- [FR_SSHA2_512_PASSWORD - 5000 ] = 64,
- [FR_SSHA3_224_PASSWORD - 5000 ] = 28,
- [FR_SSHA3_256_PASSWORD - 5000 ] = 32,
- [FR_SSHA3_384_PASSWORD - 5000 ] = 48,
- [FR_SSHA3_512_PASSWORD - 5000 ] = 64,
-};
-#define MAX_KNOWN_PASSWORD (sizeof(fr_password_length) / sizeof(fr_password_length[0]))
-
-
-/** Normalise passwords.
- *
- * @param request the request to process
- * @param normalise whether or not we normalise the passwords
+/** Apply any processing and normification
*
*/
-VALUE_PAIR *password_normalise(REQUEST *request, bool normalise)
+static VALUE_PAIR *password_process(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *known_good, bool normify)
{
- VALUE_PAIR *vp, *found_pw = NULL;
- fr_cursor_t cursor;
- fr_dict_attr_t const *root;
-
- root = fr_dict_root(dict_freeradius);
- if (!root) return NULL;
+ password_info_t *info;
+ VALUE_PAIR *out;
- for (vp = fr_cursor_init(&cursor, &request->control);
- vp;
- vp = fr_cursor_next(&cursor)) {
- VP_VERIFY(vp);
- VALUE_PAIR *new;
+ info = &password_info[known_good->da->attr];
+ if (info->func) {
+ VALUE_PAIR *from_func, *from_recurse;
- next:
/*
- * Look only for out attributes. Note that we no
- * longer complain about User-Password being in
- * the control list. That functionality has been
- * deprecated for 10 years. If the admins still
- * do it, too bad.
+ * Pass our input attribute to a custom preprocessing
+ * function to manipulate it.
*/
- if (vp->da->parent != root) continue;
+ from_func = info->func(ctx, request, known_good);
+ if (!from_func) return NULL;
- if ((vp->da->attr < 5000) || (vp->da->attr >= (5000 + MAX_KNOWN_PASSWORD))) continue;
+ /*
+ * Processing function may have produced a different
+ * password type, recurse to deal with it...
+ */
+ from_recurse = password_process(ctx, request, from_func, normify);
/*
- * Remove the header, and convert it to something sane.
+ * Cleanup any intermediary password attributes created
+ * from running the different normalisation and parsing
+ * operations.
*/
- if (vp->da->attr == FR_PASSWORD_WITH_HEADER) {
- /*
- * Password already exists: use that instead of this one.
- */
- if (fr_pair_find_by_da(request->control, attr_cleartext_password, TAG_ANY)) {
- RWDEBUG("Config already contains a \"known good\" password "
- "(&control:%s). Ignoring &control:%s",
- attr_cleartext_password->name, vp->da->name);
- break;
- }
+ if (!from_recurse) {
+ if (from_func != known_good) talloc_list_free(&from_func);
+ return NULL;
+ }
+ if ((from_func != known_good) && (from_recurse != from_func)) talloc_list_free(&from_func);
- MEM(new = password_normify_with_header(request, request, vp,
- known_password_header,
- attr_cleartext_password));
- if (RDEBUG_ENABLED3) {
- RDEBUG3("Normalized &control:%pP -> &control:%pP", vp, new);
- } else {
- RDEBUG2("Normalized &control:%s -> &control:%s", vp->da->name, new->da->name);
- }
+ return from_recurse;
+ }
- RDEBUG2("Removing &control:%s", vp->da->name);
- fr_cursor_free_item(&cursor); /* advances the cursor for us */
- fr_cursor_append(&cursor, new); /* inserts at the end of the list */
+ /*
+ * Only normify if we're told to, and we have more data
+ * than the minimum length.
+ */
+ if (normify && !info->no_normify && (known_good->vp_length > info->min_hash_len)) {
+ VALUE_PAIR *from_normify;
- found_pw = new;
+ from_normify = password_normify(ctx, request, known_good);
+ out = from_normify ? from_normify : known_good;
+ } else {
+ out = known_good;
+ }
- vp = fr_cursor_current(&cursor);
- if (vp) goto next;
- break;
+ /*
+ * Sanity checks - Too short
+ */
+ if (info->min_hash_len && (out->vp_length < MIN_LEN(info))) {
+ if (RDEBUG_ENABLED3) {
+ RWDEBUG3("&control:%pP too short, expected %zu bytes, got %zu bytes",
+ out, MIN_LEN(info), out->vp_length);
+ } else {
+ RWDEBUG2("&control:%s too short, expected %zu bytes, got %zu bytes",
+ out->da->name, MIN_LEN(info), out->vp_length);
}
+ invalid:
+ if (out != known_good) talloc_list_free(&out); /* Free attribute we won't be returning */
+ return NULL;
+ }
- found_pw = vp;
+ /*
+ * Sanity checks - Too long
+ */
+ if (info->max_hash_len && (out->vp_length > info->max_hash_len)) {
+ if (RDEBUG_ENABLED3) {
+ RWDEBUG3("&control:%pP too long, expected %zu bytes, got %zu bytes",
+ out, info->max_hash_len, out->vp_length);
+ } else {
+ RWDEBUG2("&control:%s too long, expected %zu bytes, got %zu bytes",
+ out->da->name, info->max_hash_len, out->vp_length);
+ }
+ goto invalid;
+ }
- /*
- * Don't normalise Cleartext Passwords
- */
- if (vp->da->attr == FR_CLEARTEXT_PASSWORD) break;
+ /*
+ * Sanity checks - Hashes are a fixed length
+ */
+ if ((info->type == PASSWORD_HASH) && (out->vp_length != info->min_hash_len)) {
- if (!normalise) break;
+ if (RDEBUG_ENABLED3) {
+ RWDEBUG3("&control:%pP incorrect length, expected %zu bytes, got %zu bytes",
+ out, info->min_hash_len, out->vp_length);
+ } else {
+ RWDEBUG2("&control:%s incorrect length, expected %zu bytes, got %zu bytes",
+ out->da->name, info->min_hash_len, out->vp_length);
+ }
+ goto invalid;
+ }
- if (!fr_password_length[vp->da->attr - 5000]) break;
+ return out;
+}
- new = password_normify(request, request, vp, fr_password_length[vp->da->attr - 5000]);
- if (new) {
- fr_cursor_free_item(&cursor); /* free the old pasword */
- fr_cursor_append(&cursor, new); /* inserts at the end of the list */
+/** Find all password attributes in the control list of a request and normalise them
+ *
+ * @param[in] request The current request.
+ * @param[in] normify Apply hex/base64 normalisation to attributes.
+ * @return the number of attributes normalised.
+ */
+int password_normalise_and_replace(REQUEST *request, bool normify)
+{
+ fr_cursor_t cursor;
+ int replaced = 0;
+ VALUE_PAIR *known_good, *new;
- vp = fr_cursor_current(&cursor);
- if (vp) goto next;
- break;
- }
+ for (known_good = fr_cursor_iter_by_ancestor_init(&cursor, &request->control, attr_password_root);
+ known_good;
+ known_good = fr_cursor_next(&cursor)) {
+ if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return -1;
/*
- * Can't normalise this one, but there might be
- * another one that we can normalise.
+ * Apply preprocessing steps and normalisation.
*/
- }
+ new = password_process(request, request, known_good, normify);
+ if (!new) break; /* Process next input attribute */
- /*
- * Print helpful warnings if there was no password.
- */
- if (found_pw) {
if (RDEBUG_ENABLED3) {
- RDEBUG("Found \"known good\" password in &control:%s = \"%pV\"", found_pw->da->name, &found_pw->data);
+ RDEBUG3("Replacing &control:%pP with &control:%pP",
+ known_good, new);
+
} else {
- RDEBUG("Found \"known good\" password in &control:%s", found_pw->da->name);
+ RDEBUG2("Replacing &control:%s with &control:%s",
+ known_good->da->name, new->da->name);
}
+ fr_cursor_free_item(&cursor);
+ fr_cursor_prepend(&cursor, new);
+ replaced++;
+ }
- } else {
- RWDEBUG("No \"known good\" password found for the user.");
- RWDEBUG("Authentication may fail unless a \"known good\" password is available");
+ return replaced;
+}
+
+/** Find a "known good" password in the control list of a request
+ *
+ * Searches for a "known good" password attribute, and applies any processing
+ * and normification operations to it, returning a new mormalised VALUE_PAIR.
+ *
+ * The ctx passed in should be freed when the caller is done with the returned
+ * VALUE_PAIR. The returned pair *MUST NOT BE FREED*, it may be an attribute
+ * in the request->control list.
+ *
+ * @param[in] ctx Ephemeral ctx to allocate new attributes in.
+ * @param[in] request The current request.
+ * @param[in] allowed_attrs Optional list of allowed attributes.
+ * @param[in] allowed_attrs_len Length of allowed attributes list.
+ * @param[in] normify Apply hex/base64 normalisation to attributes.
+ * @return
+ * - A VALUE_PAIR containing a "known good" password.
+ * - NULL on error, or if no usable password attributes were found.
+ */
+VALUE_PAIR const *password_find(TALLOC_CTX *ctx, REQUEST *request,
+ fr_dict_attr_t const *allowed_attrs[], size_t allowed_attrs_len, bool normify)
+{
+ fr_cursor_t cursor;
+ size_t i, j;
+ VALUE_PAIR *known_good;
+
+ for (known_good = fr_cursor_iter_by_ancestor_init(&cursor, &request->control, attr_password_root);
+ known_good;
+ known_good = fr_cursor_next(&cursor)) {
+ if (known_good->da == attr_user_password) {
+ RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RWDEBUG("!!! Ignoring control:User-Password. Update your !!!");
+ RWDEBUG("!!! configuration so that the \"known good\" clear text !!!");
+ RWDEBUG("!!! password is in Cleartext-Password and NOT in !!!");
+ RWDEBUG("!!! User-Password. !!!");
+ RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ continue;
+ }
+
+ for (i = 0; i < allowed_attrs_len; i++) {
+ VALUE_PAIR *new;
+
+ if (allowed_attrs[i] != known_good->da) continue;
+
+ if (!fr_cond_assert(known_good->da->attr < NUM_ELEMENTS(password_info))) return NULL;
+
+ /*
+ * Apply preprocessing steps and normalisation.
+ */
+ new = password_process(ctx, request, known_good, normify);
+ if (!new) break; /* Process next input attribute */
+
+ /*
+ * If new != known_good, then we need
+ * to check what was produced is still
+ * acceptable.
+ */
+ if (new->da != known_good->da) {
+ for (j = 0; j < allowed_attrs_len; j++) if (allowed_attrs[j] == new->da) return new;
+
+ /*
+ * New attribute not in our allowed list
+ */
+ talloc_list_free(&new); /* da didn't match, treat as ephemeral */
+ break; /* Process next input attribute */
+ }
+
+ /*
+ * Return attribute for processing
+ */
+ return new;
+ }
}
- return found_pw;
+ return NULL;
}
/** Load our dictionaries
bool normify;
} rlm_pap_t;
+typedef rlm_rcode_t (*pap_auth_func_t)(rlm_pap_t const *, REQUEST *, VALUE_PAIR const *, VALUE_PAIR const *);
+
static const CONF_PARSER module_config[] = {
{ FR_CONF_OFFSET("normalise", FR_TYPE_BOOL, rlm_pap_t, normify), .dflt = "yes" },
CONF_PARSER_TERMINATOR
static fr_dict_t *dict_freeradius;
static fr_dict_t *dict_radius;
-extern fr_dict_autoload_t rlm_pap_dict[];
-fr_dict_autoload_t rlm_pap_dict[] = {
+static fr_dict_autoload_t rlm_pap_dict[] = {
{ .out = &dict_freeradius, .proto = "freeradius" },
{ .out = &dict_radius, .proto = "radius" },
{ NULL }
};
static fr_dict_attr_t const *attr_auth_type;
-
-static fr_dict_attr_t const *attr_cleartext_password;
-static fr_dict_attr_t const *attr_password_with_header;
-
-static fr_dict_attr_t const *attr_md5_password;
-static fr_dict_attr_t const *attr_smd5_password;
-static fr_dict_attr_t const *attr_crypt_password;
-static fr_dict_attr_t const *attr_sha_password;
-static fr_dict_attr_t const *attr_ssha_password;
-
-static fr_dict_attr_t const *attr_sha2_password;
-static fr_dict_attr_t const *attr_ssha2_224_password;
-static fr_dict_attr_t const *attr_ssha2_256_password;
-static fr_dict_attr_t const *attr_ssha2_384_password;
-static fr_dict_attr_t const *attr_ssha2_512_password;
-
-static fr_dict_attr_t const *attr_sha3_password;
-static fr_dict_attr_t const *attr_ssha3_224_password;
-static fr_dict_attr_t const *attr_ssha3_256_password;
-static fr_dict_attr_t const *attr_ssha3_384_password;
-static fr_dict_attr_t const *attr_ssha3_512_password;
-
-static fr_dict_attr_t const *attr_pbkdf2_password;
-static fr_dict_attr_t const *attr_lm_password;
-static fr_dict_attr_t const *attr_nt_password;
-static fr_dict_attr_t const *attr_ns_mta_md5_password;
+static fr_dict_attr_t const *attr_password_root;
static fr_dict_attr_t const *attr_user_password;
-extern fr_dict_attr_autoload_t rlm_pap_dict_attr[];
-fr_dict_attr_autoload_t rlm_pap_dict_attr[] = {
+static fr_dict_attr_autoload_t rlm_pap_dict_attr[] = {
{ .out = &attr_auth_type, .name = "Auth-Type", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
-
- { .out = &attr_cleartext_password, .name = "Cleartext-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
- { .out = &attr_password_with_header, .name = "Password-With-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
-
- { .out = &attr_md5_password, .name = "MD5-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_smd5_password, .name = "SMD5-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_crypt_password, .name = "Crypt-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
- { .out = &attr_sha_password, .name = "SHA-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha_password, .name = "SSHA-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
-
- { .out = &attr_sha2_password, .name = "SHA2-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha2_224_password, .name = "SSHA2-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha2_256_password, .name = "SSHA2-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha2_384_password, .name = "SSHA2-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha2_512_password, .name = "SSHA2-512-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
-
- { .out = &attr_sha3_password, .name = "SHA3-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha3_224_password, .name = "SSHA3-224-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha3_256_password, .name = "SSHA3-256-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha3_384_password, .name = "SSHA3-384-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ssha3_512_password, .name = "SSHA3-512-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
-
- { .out = &attr_pbkdf2_password, .name = "PBKDF2-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_lm_password, .name = "LM-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_nt_password, .name = "NT-Password", .type = FR_TYPE_OCTETS, .dict = &dict_freeradius },
- { .out = &attr_ns_mta_md5_password, .name = "NS-MTA-MD5-Password", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
+ { .out = &attr_password_root, .name = "Password-Root", .type = FR_TYPE_TLV, .dict = &dict_freeradius },
{ .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius },
#ifdef HAVE_OPENSSL_EVP_H
static fr_table_num_sorted_t const pbkdf2_crypt_names[] = {
- { "HMACSHA1", FR_SSHA_PASSWORD },
+ { "HMACSHA1", FR_SSHA1_PASSWORD },
{ "HMACSHA2+224", FR_SSHA2_224_PASSWORD },
{ "HMACSHA2+256", FR_SSHA2_256_PASSWORD },
{ "HMACSHA2+384", FR_SSHA2_384_PASSWORD },
static size_t pbkdf2_crypt_names_len = NUM_ELEMENTS(pbkdf2_crypt_names);
static fr_table_num_sorted_t const pbkdf2_passlib_names[] = {
- { "sha1", FR_SSHA_PASSWORD },
+ { "sha1", FR_SSHA1_PASSWORD },
{ "sha256", FR_SSHA2_256_PASSWORD },
{ "sha512", FR_SSHA2_512_PASSWORD }
};
static size_t pbkdf2_passlib_names_len = NUM_ELEMENTS(pbkdf2_passlib_names);
#endif
+static fr_dict_attr_t const **pap_allowed_passwords;
+
/*
* Authorize the user for PAP authentication.
*
*/
static rlm_rcode_t CC_HINT(nonnull) pap_auth_clear(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
if ((known_good->vp_length != password->vp_length) ||
(fr_digest_cmp(known_good->vp_octets, password->vp_octets, known_good->vp_length) != 0)) {
#ifdef HAVE_CRYPT
static rlm_rcode_t CC_HINT(nonnull) pap_auth_crypt(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
if (fr_crypt_check(password->vp_strvalue, known_good->vp_strvalue) != 0) {
REDEBUG("Crypt digest does not match \"known good\" digest");
#endif
static rlm_rcode_t CC_HINT(nonnull) pap_auth_md5(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
uint8_t digest[MD5_DIGEST_LENGTH];
static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
fr_md5_ctx_t *md5_ctx;
uint8_t digest[MD5_DIGEST_LENGTH];
return RLM_MODULE_OK;
}
-static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha1(UNUSED rlm_pap_t const *inst, REQUEST *request,
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
fr_sha1_ctx sha1_context;
uint8_t digest[SHA1_DIGEST_LENGTH];
return RLM_MODULE_OK;
}
-#ifdef HAVE_OPENSSL_EVP_H
-static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha1(UNUSED rlm_pap_t const *inst, REQUEST *request,
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
fr_sha1_ctx sha1_context;
uint8_t digest[SHA1_DIGEST_LENGTH];
return RLM_MODULE_OK;
}
-static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha_evp(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+#ifdef HAVE_OPENSSL_EVP_H
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_evp_md(UNUSED rlm_pap_t const *inst, REQUEST *request,
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password,
+ char const *name, EVP_MD const *md)
{
EVP_MD_CTX *ctx;
- EVP_MD const *md;
- char const *name;
uint8_t digest[EVP_MAX_MD_SIZE];
unsigned int digest_len;
- if (known_good->da == attr_sha2_password) {
- /*
- * All the SHA-2 algorithms produce digests of different lengths,
- * so it's trivial to determine which EVP_MD to use.
- */
- switch (known_good->vp_length) {
- /* SHA2-224 */
- case SHA224_DIGEST_LENGTH:
- name = "SHA2-224";
- md = EVP_sha224();
- break;
-
- /* SHA2-256 */
- case SHA256_DIGEST_LENGTH:
- name = "SHA2-256";
- md = EVP_sha256();
- break;
-
- /* SHA2-384 */
- case SHA384_DIGEST_LENGTH:
- name = "SHA2-384";
- md = EVP_sha384();
- break;
-
- /* SHA2-512 */
- case SHA512_DIGEST_LENGTH:
- name = "SHA2-512";
- md = EVP_sha512();
- break;
-
- default:
- REDEBUG("\"known good\" digest length (%zu) does not match output length of any SHA-2 digests",
- known_good->vp_length);
- return RLM_MODULE_INVALID;
- }
- }
-# if OPENSSL_VERSION_NUMBER >= 0x10101000L
- else if (known_good->da == attr_sha3_password) {
- /*
- * All the SHA-3 algorithms produce digests of different lengths,
- * so it's trivial to determine which EVP_MD to use.
- */
- switch (known_good->vp_length) {
- /* SHA3-224 */
- case SHA224_DIGEST_LENGTH:
- name = "SHA3-224";
- md = EVP_sha3_224();
- break;
-
- /* SHA3-256 */
- case SHA256_DIGEST_LENGTH:
- name = "SHA3-256";
- md = EVP_sha3_256();
- break;
-
- /* SHA3-384 */
- case SHA384_DIGEST_LENGTH:
- name = "SHA3-384";
- md = EVP_sha3_384();
- break;
-
- /* SHA3-512 */
- case SHA512_DIGEST_LENGTH:
- name = "SHA3-512";
- md = EVP_sha3_512();
- break;
-
- default:
- REDEBUG("\"known good\" digest length (%zu) does not match output length of any SHA-3 digests",
- known_good->vp_length);
- return RLM_MODULE_INVALID;
- }
- }
-# endif
- else {
- rad_assert(0);
- return RLM_MODULE_INVALID;
- }
-
ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(ctx, md, NULL);
EVP_DigestUpdate(ctx, password->vp_octets, password->vp_length);
return RLM_MODULE_OK;
}
-static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha_evp(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_evp_md_salted(UNUSED rlm_pap_t const *inst, REQUEST *request,
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password,
+ char const *name, EVP_MD const *md)
{
EVP_MD_CTX *ctx;
- EVP_MD const *md = NULL;
- char const *name = NULL;
uint8_t digest[EVP_MAX_MD_SIZE];
- unsigned int digest_len, min_len = 0;
-
- if (known_good->da == attr_ssha2_224_password) {
- name = "SSHA2-224";
- md = EVP_sha224();
- min_len = SHA224_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha2_256_password) {
- name = "SSHA2-256";
- md = EVP_sha256();
- min_len = SHA256_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha2_384_password) {
- name = "SSHA2-384";
- md = EVP_sha384();
- min_len = SHA384_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha2_512_password) {
- name = "SSHA2-512";
- min_len = SHA512_DIGEST_LENGTH;
- md = EVP_sha512();
- }
-#if OPENSSL_VERSION_NUMBER >= 0x10101000L
- else if (known_good->da == attr_ssha3_224_password) {
- name = "SSHA3-224";
- md = EVP_sha3_224();
- min_len = SHA224_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha3_256_password) {
- name = "SSHA3-256";
- md = EVP_sha3_256();
- min_len = SHA256_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha3_384_password) {
- name = "SSHA3-384";
- md = EVP_sha3_384();
- min_len = SHA384_DIGEST_LENGTH;
- } else if (known_good->da == attr_ssha3_512_password) {
- name = "SSHA3-512";
- min_len = SHA512_DIGEST_LENGTH;
- md = EVP_sha3_512();
- }
-#endif
- else {
- rad_assert(0);
- return RLM_MODULE_INVALID;
- }
-
- if (known_good->vp_length <= min_len) {
- REDEBUG("\"known-good\" %s-Password has incorrect length, got %zu bytes, need at least %u bytes",
- name, known_good->vp_length, min_len + 1);
- return RLM_MODULE_INVALID;
- }
+ unsigned int digest_len, min_len;
+ min_len = EVP_MD_size(md);
ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(ctx, md, NULL);
EVP_DigestUpdate(ctx, password->vp_octets, password->vp_length);
return RLM_MODULE_OK;
}
+/** Define a new OpenSSL EVP based password hashing function
+ *
+ */
+#define PAP_AUTH_EVP_MD(_func, _new_func, _name, _md) \
+static rlm_rcode_t CC_HINT(nonnull) _new_func(rlm_pap_t const *inst, REQUEST *request, \
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password) \
+{ \
+ return _func(inst, request, known_good, password, _name, _md); \
+}
+
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_224, "SHA2-224", EVP_sha224());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_256, "SHA2-256", EVP_sha256());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_384, "SHA2-384", EVP_sha384());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha2_512, "SHA2-512", EVP_sha512());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_224, "SSHA2-224", EVP_sha224());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_256, "SSHA2-256", EVP_sha256());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_384, "SSHA2-384", EVP_sha384());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha2_512, "SSHA2-512", EVP_sha512());
+
+# if OPENSSL_VERSION_NUMBER >= 0x10101000L
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_224, "SHA3-224", EVP_sha3_224());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_256, "SHA3-256", EVP_sha3_256());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_384, "SHA3-384", EVP_sha3_384());
+PAP_AUTH_EVP_MD(pap_auth_evp_md, pap_auth_sha3_512, "SHA3-512", EVP_sha3_512());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_224, "SSHA3-224", EVP_sha3_224());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_256, "SSHA3-256", EVP_sha3_256());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_384, "SSHA3-384", EVP_sha3_384());
+PAP_AUTH_EVP_MD(pap_auth_evp_md_salted, pap_auth_ssha3_512, "SSHA3-512", EVP_sha3_512());
+# endif
+
/** Validates Crypt::PBKDF2 LDAP format strings
*
* @param[in] request The current request.
digest_type = fr_table_value_by_substr(hash_names, (char const *)p, q - p, -1);
switch (digest_type) {
- case FR_SSHA_PASSWORD:
+ case FR_SSHA1_PASSWORD:
evp_md = EVP_sha1();
digest_len = SHA1_DIGEST_LENGTH;
break;
static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2(UNUSED rlm_pap_t const *inst,
REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
uint8_t const *p = known_good->vp_octets, *q, *end = p + known_good->vp_length;
#endif
static rlm_rcode_t CC_HINT(nonnull) pap_auth_nt(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
ssize_t len;
uint8_t digest[MD4_DIGEST_LENGTH];
}
static rlm_rcode_t CC_HINT(nonnull) pap_auth_lm(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, UNUSED VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, UNUSED VALUE_PAIR const *password)
{
uint8_t digest[MD4_DIGEST_LENGTH];
char charbuf[32 + 1];
}
static rlm_rcode_t CC_HINT(nonnull) pap_auth_ns_mta_md5(UNUSED rlm_pap_t const *inst, REQUEST *request,
- VALUE_PAIR *known_good, VALUE_PAIR const *password)
+ VALUE_PAIR const *known_good, VALUE_PAIR const *password)
{
uint8_t digest[128];
uint8_t buff[FR_MAX_STRING_LEN];
RDEBUG2("Using NT-MTA-MD5-Password");
if (known_good->vp_length != 64) {
- REDEBUG("\"known good\" NS-MTA-MD5-Password has incorrect length, expected 64 got %zu", known_good->vp_length);
+ REDEBUG("\"known good\" NS-MTA-MD5-Password has incorrect length, expected 64 got %zu",
+ known_good->vp_length);
return RLM_MODULE_INVALID;
}
return RLM_MODULE_OK;
}
-typedef rlm_rcode_t (*pap_auth_func_t)(rlm_pap_t const *, REQUEST *, VALUE_PAIR *, VALUE_PAIR const *);
+/** Auth func for password types that should have been normalised away
+ *
+ */
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_dummy(UNUSED rlm_pap_t const *inst, UNUSED REQUEST *request,
+ UNUSED VALUE_PAIR const *known_good, UNUSED VALUE_PAIR const *password)
+{
+ return RLM_MODULE_FAIL;
+}
+/** Table of password types we can process
+ *
+ */
static const pap_auth_func_t auth_func_table[] = {
- [FR_CLEARTEXT_PASSWORD - 5000 ] = pap_auth_clear,
- [FR_LM_PASSWORD - 5000 ] = pap_auth_lm,
- [FR_MD5_PASSWORD - 5000 ] = pap_auth_md5,
- [FR_SMD5_PASSWORD - 5000 ] = pap_auth_smd5,
+ [FR_CLEARTEXT_PASSWORD] = pap_auth_clear,
+ [FR_LM_PASSWORD] = pap_auth_lm,
+ [FR_MD5_PASSWORD] = pap_auth_md5,
+ [FR_SMD5_PASSWORD] = pap_auth_smd5,
#ifdef HAVE_CRYPT
- [FR_CRYPT_PASSWORD - 5000 ] = pap_auth_crypt,
+ [FR_CRYPT_PASSWORD] = pap_auth_crypt,
#endif
- [FR_NS_MTA_MD5_PASSWORD - 5000 ] = pap_auth_ns_mta_md5,
- [FR_NT_PASSWORD - 5000 ] = pap_auth_nt,
- [FR_SHA_PASSWORD - 5000 ] = pap_auth_sha,
- [FR_SHA1_PASSWORD - 5000 ] = pap_auth_sha,
+ [FR_NS_MTA_MD5_PASSWORD] = pap_auth_ns_mta_md5,
+ [FR_NT_PASSWORD] = pap_auth_nt,
+ [FR_PASSWORD_WITH_HEADER] = pap_auth_dummy,
+ [FR_SHA1_PASSWORD] = pap_auth_sha1,
#ifdef HAVE_OPENSSL_EVP_H
- [FR_SHA2_PASSWORD - 5000 ] = pap_auth_sha_evp,
- [FR_SSHA_PASSWORD - 5000 ] = pap_auth_ssha,
- [FR_SSHA1_PASSWORD - 5000 ] = pap_auth_ssha,
- [FR_SSHA2_224_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA2_256_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA2_384_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA2_512_PASSWORD - 5000 ] = pap_auth_ssha_evp,
-
- [FR_PBKDF2_PASSWORD - 5000 ] = pap_auth_pbkdf2,
+ [FR_PBKDF2_PASSWORD] = pap_auth_pbkdf2,
+ [FR_SHA2_PASSWORD] = pap_auth_dummy,
+ [FR_SHA2_224_PASSWORD] = pap_auth_sha2_224,
+ [FR_SHA2_256_PASSWORD] = pap_auth_sha2_256,
+ [FR_SHA2_384_PASSWORD] = pap_auth_sha2_384,
+ [FR_SHA2_512_PASSWORD] = pap_auth_sha2_512,
+ [FR_SSHA1_PASSWORD] = pap_auth_ssha1,
+ [FR_SSHA2_224_PASSWORD] = pap_auth_ssha2_224,
+ [FR_SSHA2_256_PASSWORD] = pap_auth_ssha2_256,
+ [FR_SSHA2_384_PASSWORD] = pap_auth_ssha2_384,
+ [FR_SSHA2_512_PASSWORD] = pap_auth_ssha2_512,
# if OPENSSL_VERSION_NUMBER >= 0x10101000L
- [FR_SHA3_PASSWORD - 5000 ] = pap_auth_sha_evp,
- [FR_SSHA3_224_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA3_256_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA3_384_PASSWORD - 5000 ] = pap_auth_ssha_evp,
- [FR_SSHA3_512_PASSWORD - 5000 ] = pap_auth_ssha_evp,
-#endif
+ [FR_SHA3_PASSWORD] = pap_auth_dummy,
+ [FR_SHA3_224_PASSWORD] = pap_auth_sha3_224,
+ [FR_SHA3_256_PASSWORD] = pap_auth_sha3_256,
+ [FR_SHA3_384_PASSWORD] = pap_auth_sha3_384,
+ [FR_SHA3_512_PASSWORD] = pap_auth_sha3_512,
+ [FR_SSHA3_224_PASSWORD] = pap_auth_ssha3_224,
+ [FR_SSHA3_256_PASSWORD] = pap_auth_ssha3_256,
+ [FR_SSHA3_384_PASSWORD] = pap_auth_ssha3_384,
+ [FR_SSHA3_512_PASSWORD] = pap_auth_ssha3_512,
+# endif
#endif /* HAVE_OPENSSL_EVP_H */
};
-#define MAX_KNOWN_PASSWORD (sizeof(auth_func_table) / sizeof(auth_func_table[0]))
/*
* Authenticate the user via one of any well-known password.
*/
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED void *thread, REQUEST *request)
{
- rlm_pap_t const *inst = instance;
- VALUE_PAIR *known_good, *password;
- rlm_rcode_t rcode = RLM_MODULE_INVALID;
- pap_auth_func_t auth_func;
+ rlm_pap_t const *inst = instance;
+ VALUE_PAIR const *known_good;
+ VALUE_PAIR *password;
+ rlm_rcode_t rcode = RLM_MODULE_INVALID;
+ pap_auth_func_t auth_func;
+ TALLOC_CTX *tmp_ctx;
password = fr_pair_find_by_da(request->packet->vps, attr_user_password, TAG_ANY);
if (!password) {
}
if (RDEBUG_ENABLED3) {
- RDEBUG3("Login attempt with password \"%pV\" (%zd)",
- fr_box_strvalue_len(password->vp_strvalue, password->vp_length),
- password->vp_length);
+ RDEBUG3("Login attempt with %pP (%zd)", password, password->vp_length);
} else {
RDEBUG2("Login attempt with password");
}
/*
- * Normalise passwords, if it hasn't already been done.
- *
- * This function returns the "known good" password, and
- * prefers to return Cleartext-Password over everything
- * else.
+ * Holds any ephemeral attributes
*/
- known_good = password_normalise(request, inst->normify);
- if (!known_good) {
- REDEBUG("No \"known good\" password found for user.");
- REDEBUG("Some other module must authenticate the user.");
- return RLM_MODULE_NOOP;
- }
+ MEM(tmp_ctx = talloc_named_const(request, 0, "password_tmp_ctx"));
- if ((known_good->da->attr < 5000) || (known_good->da->attr >= (5000 + MAX_KNOWN_PASSWORD)) ||
- !auth_func_table[known_good->da->attr - 5000]) {
- /*
- * It's a known password, but we don't know what it is.
- */
- REDEBUG("No \"known good\" password was found for user");
+ /*
+ * Retrieve the normalised version of
+ * the known_good password, without
+ * mangling the current password attributes
+ * in the request.
+ */
+ known_good = password_find(tmp_ctx, request,
+ pap_allowed_passwords, talloc_array_length(pap_allowed_passwords),
+ inst->normify);
+ if (!known_good) {
+ REDEBUG("No \"known good\" password found for user");
+ talloc_free(tmp_ctx);
return RLM_MODULE_FAIL;
}
- auth_func = auth_func_table[known_good->da->attr - 5000];
+ rad_assert(known_good->da->attr < NUM_ELEMENTS(auth_func_table));
+
+ auth_func = auth_func_table[known_good->da->attr];
+ rad_assert(auth_func);
if (RDEBUG_ENABLED3) {
RDEBUG3("Comparing with \"known good\" %pP (%zu)", known_good, known_good->vp_length);
default:
break;
}
+ talloc_free(tmp_ctx);
return rcode;
}
return 0;
}
+static int mod_load(void)
+{
+ size_t i, j = 0;
+ size_t allowed = 0;
+
+ /*
+ * Load the dictionaries early
+ */
+ if (fr_dict_autoload(rlm_pap_dict) < 0) {
+ PERROR("%s", __FUNCTION__);
+ return -1;
+ }
+ if (fr_dict_attr_autoload(rlm_pap_dict_attr) < 0) {
+ PERROR("%s", __FUNCTION__);
+ fr_dict_autofree(rlm_pap_dict);
+ return -1;
+ }
+
+ /*
+ * Figure out how many password types we allow
+ */
+ for (i = 0; i < NUM_ELEMENTS(auth_func_table); i++) {
+ if (auth_func_table[i] == NULL) continue;
+
+ allowed++;
+ }
+
+ /*
+ * Get a list of the DAs that match are allowed
+ * functions.
+ */
+ pap_allowed_passwords = talloc_array(NULL, fr_dict_attr_t const *, allowed);
+ for (i = 0; i < NUM_ELEMENTS(auth_func_table); i++) {
+ fr_dict_attr_t const *password_da;
+
+ if (auth_func_table[i] == NULL) continue;
+
+ password_da = fr_dict_attr_child_by_num(attr_password_root, i);
+ if (!fr_cond_assert(password_da)) {
+ ERROR("Could not resolve password attribute %zu", i);
+ talloc_free(pap_allowed_passwords);
+ return -1;
+ }
+
+ pap_allowed_passwords[j++] = password_da;
+ }
+
+ return 0;
+}
+
+static void mod_unload(void)
+{
+ talloc_free(pap_allowed_passwords);
+ fr_dict_autofree(rlm_pap_dict);
+}
+
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
.magic = RLM_MODULE_INIT,
.name = "pap",
.inst_size = sizeof(rlm_pap_t),
+ .onload = mod_load,
+ .unload = mod_unload,
.config = module_config,
.bootstrap = mod_bootstrap,
.methods = {