]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Implement support for 389ds password hashes PBKDF2-SHA{1,256,512} and (#5576)
authorGerald Vogt <gvde@users.noreply.github.com>
Thu, 8 May 2025 14:42:36 +0000 (16:42 +0200)
committerGitHub <noreply@github.com>
Thu, 8 May 2025 14:42:36 +0000 (08:42 -0600)
legacy PBKDF2_SHA256. Backport of PR #5564

16 files changed:
share/dictionary.freeradius.internal
src/modules/rlm_pap/rlm_pap.c
src/tests/modules/pap/pbkfd2_sha1_389ds.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha1_389ds.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_389ds.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_389ds.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha512_389ds.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha512_389ds.unlang [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.attrs [new file with mode: 0644]
src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.unlang [new file with mode: 0644]

index 82b5f7185cdb9083c3082b516d39f427093dcaf9..e8cde0c231ff13b963ac8f52c0f2ce0196506a70 100644 (file)
@@ -291,6 +291,11 @@ ATTRIBUTE  SSHA3-256-Password                      1183    octets
 ATTRIBUTE      SSHA3-384-Password                      1184    octets
 ATTRIBUTE      SSHA3-512-Password                      1185    octets
 
+ATTRIBUTE      PBKDF2-SHA1-Password                    1186    octets
+ATTRIBUTE      PBKDF2-SHA256-Password                  1187    octets
+ATTRIBUTE      PBKDF2-SHA512-Password                  1188    octets
+ATTRIBUTE      PBKDF2-SHA256-LEGACY-Password           1189    octets
+
 ATTRIBUTE      MS-CHAP-Peer-Challenge                  1192    octets
 ATTRIBUTE      Home-Server-Name                        1193    string
 ATTRIBUTE      Originating-Realm-Key                   1194    string
index 463ff66b7100bf15656efb90b55adc28e21e5e1f..a51febaab3af68bdda852c3c8ba01cc188ab4552 100644 (file)
@@ -96,6 +96,10 @@ static const FR_NAME_NUMBER header_names[] = {
        { "{ssha384}",          PW_SSHA2_384_PASSWORD },
        { "{ssha512}",          PW_SSHA2_512_PASSWORD },
        { "{x-pbkdf2}",         PW_PBKDF2_PASSWORD },
+       { "{pbkdf2-sha1}",      PW_PBKDF2_SHA1_PASSWORD },
+       { "{pbkdf2-sha256}",    PW_PBKDF2_SHA256_PASSWORD },
+       { "{pbkdf2-sha512}",    PW_PBKDF2_SHA512_PASSWORD },
+       { "{pbkdf2_sha256}",    PW_PBKDF2_SHA256_LEGACY_PASSWORD },
 #endif
        { "{sha}",              PW_SHA_PASSWORD },
        { "{ssha}",             PW_SSHA_PASSWORD },
@@ -857,10 +861,9 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha2(rlm_pap_t *inst, REQUEST *req
  *     - RLM_MODULE_REJECT
  *     - RLM_MODULE_OK
  */
-static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse(REQUEST *request, const uint8_t *str, size_t len,
-                                                                const FR_NAME_NUMBER hash_names[],
-                                                                char scheme_sep, char iter_sep, char salt_sep,
-                                                                bool iter_is_base64, VALUE_PAIR const *password)
+static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse_digest(REQUEST *request, const uint8_t *str, size_t len,
+                                                                       int digest_type, char iter_sep, char salt_sep,
+                                                                       bool iter_is_base64, VALUE_PAIR const *password)
 {
        rlm_rcode_t             rcode = RLM_MODULE_INVALID;
 
@@ -868,7 +871,6 @@ static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse(REQUEST *reques
        ssize_t                 slen;
 
        EVP_MD const            *evp_md;
-       int                     digest_type;
        size_t                  digest_len;
 
        uint32_t                iterations;
@@ -878,36 +880,12 @@ static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse(REQUEST *reques
        uint8_t                 hash[EVP_MAX_MD_SIZE];
        uint8_t                 digest[EVP_MAX_MD_SIZE];
 
-       char                    hash_token[128];
-
-       RDEBUG2("Comparing with \"known-good\" PBKDF2-Password");
-
-       if (len <= 1) {
-               REDEBUG("PBKDF2-Password is too short");
-               goto finish;
-       }
-
        /*
-        *      Parse PBKDF string = {hash_algorithm}<scheme_sep><iterations><iter_sep>b64(<salt>)<salt_sep>b64(<hash>)
+        *      Parse PBKDF string for given digest = <iterations><iter_sep>b64(<salt>)<salt_sep>b64(<hash>)
         */
        p = str;
        end = p + len;
 
-       q = memchr(p, scheme_sep, end - p);
-       if (!q) {
-               REDEBUG("PBKDF2-Password has no component separators");
-               goto finish;
-       }
-
-       if ((q-p) >= (int)sizeof(hash_token)) {
-               REDEBUG("PBKDF2-Password has invalid hash token");
-               goto finish;
-       }
-
-       memcpy(hash_token, (char const *)p, (q - p));
-       hash_token[q - p] = '\0';
-
-       digest_type = fr_str2int(hash_names, hash_token, -1);
        switch (digest_type) {
        case PW_SSHA1_PASSWORD:
                evp_md = EVP_sha1();
@@ -957,12 +935,10 @@ static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse(REQUEST *reques
 #  endif
 
        default:
-               REDEBUG("Unknown PBKDF2 hash method \"%.*s\"", (int)(q - p), p);
+               REDEBUG("Unknown PBKDF2 digest type \"%id\"", digest_type);
                goto finish;
        }
 
-       p = q + 1;
-
        if (((end - p) < 1) || !(q = memchr(p, iter_sep, end - p))) {
                REDEBUG("PBKDF2-Password missing iterations component");
                goto finish;
@@ -1085,6 +1061,65 @@ finish:
        return rcode;
 }
 
+/** Validates Crypt::PBKDF2 LDAP format strings
+ *
+ * @param[in] request  The current request.
+ * @param[in] str      Raw PBKDF2 string.
+ * @param[in] len      Length of string.
+ * @return
+ *     - RLM_MODULE_REJECT
+ *     - RLM_MODULE_OK
+ */
+static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_parse(REQUEST *request, const uint8_t *str, size_t len,
+                                                                const FR_NAME_NUMBER hash_names[],
+                                                                char scheme_sep, char iter_sep, char salt_sep,
+                                                                bool iter_is_base64, VALUE_PAIR const *password)
+{
+       rlm_rcode_t             rcode = RLM_MODULE_INVALID;
+
+       uint8_t const           *p, *q, *end;
+       int                     digest_type;
+
+       char                    hash_token[128];
+
+       RDEBUG2("Comparing with \"known-good\" PBKDF2-Password");
+
+       if (len <= 1) {
+               REDEBUG("PBKDF2-Password is too short");
+               goto finish;
+       }
+
+       /*
+        *      Parse PBKDF string = {hash_algorithm}<scheme_sep><iterations><iter_sep>b64(<salt>)<salt_sep>b64(<hash>)
+        */
+       p = str;
+       end = p + len;
+
+       q = memchr(p, scheme_sep, end - p);
+       if (!q) {
+               REDEBUG("PBKDF2-Password has no component separators");
+               goto finish;
+       }
+
+       if ((q-p) >= (int)sizeof(hash_token)) {
+               REDEBUG("PBKDF2-Password has invalid hash token");
+               goto finish;
+       }
+
+       memcpy(hash_token, (char const *)p, (q - p));
+       hash_token[q - p] = '\0';
+
+       digest_type = fr_str2int(hash_names, hash_token, -1);
+
+       p = q + 1;
+
+       return pap_auth_pbkdf2_parse_digest(request, p, end - p, digest_type, iter_sep, salt_sep, iter_is_base64, password);
+
+finish:
+
+       return rcode;
+}
+
 static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *password)
 {
        uint8_t const *p = password->vp_octets, *q, *end = p + password->vp_length;
@@ -1099,20 +1134,30 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2(UNUSED rlm_pap_t *inst, REQU
 
        /*
         *      If it doesn't begin with a $ assume
-        *      It's Crypt::PBKDF2 LDAP format
+        *      it's Crypt::PBKDF2 LDAP format
         *
         *      {X-PBKDF2}<digest>:<b64 rounds>:<b64_salt>:<b64_hash>
+        *
+        *      or 389ds LDAP format
+        *
+        *      {PBKDF2-SHA512}<round>$<b64_salt>$<b64_hash>
         */
        if (*p != '$') {
-               /*
-                *      Strip the header if it's present
-                */
-               if (*p == '{') {
-                       q = memchr(p, '}', end - p);
-                       p = q + 1;
+               if ((size_t)(end - p) >= sizeof("{PBKDF2-") && (memcmp(p, "{PBKDF2-", sizeof("{PBKDF2-") - 1) == 0)) {
+                       p += sizeof("{PBKDF2-") - 1;
+                       return pap_auth_pbkdf2_parse(request, p, end - p,
+                                                    pbkdf2_passlib_names, '}', '$', '$', false, request->password);
+               } else {
+                       /*
+                        *      Strip the header if it's present
+                        */
+                       if (*p == '{') {
+                               q = memchr(p, '}', end - p);
+                               p = q + 1;
+                       }
+                       return pap_auth_pbkdf2_parse(request, p, end - p,
+                                                    pbkdf2_crypt_names, ':', ':', ':', true, request->password);
                }
-               return pap_auth_pbkdf2_parse(request, p, end - p,
-                                            pbkdf2_crypt_names, ':', ':', ':', true, request->password);
        }
 
        /*
@@ -1143,6 +1188,132 @@ static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2(UNUSED rlm_pap_t *inst, REQU
 
        return RLM_MODULE_INVALID;
 }
+
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_sha1(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *password)
+{
+       uint8_t const *p = password->vp_octets, *end = p + password->vp_length;
+
+       rad_assert(request->password != NULL);
+       rad_assert(request->password->da->attr == PW_USER_PASSWORD);
+
+       if ((end - p) < 2) {
+               REDEBUG("Password-With-Header {PBKDF2-SHA1} too short");
+               return RLM_MODULE_INVALID;
+       }
+
+       return pap_auth_pbkdf2_parse_digest(request, p, end - p, PW_SSHA1_PASSWORD, '$', '$', false, request->password);
+}
+
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_sha256(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *password)
+{
+       uint8_t const *p = password->vp_octets, *end = p + password->vp_length;
+
+       rad_assert(request->password != NULL);
+       rad_assert(request->password->da->attr == PW_USER_PASSWORD);
+
+       if ((end - p) < 2) {
+               REDEBUG("Password-With-Header {PBKDF2-SHA256} too short");
+               return RLM_MODULE_INVALID;
+       }
+
+       return pap_auth_pbkdf2_parse_digest(request, p, end - p, PW_SSHA2_256_PASSWORD, '$', '$', false, request->password);
+}
+
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_sha512(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *password)
+{
+       uint8_t const *p = password->vp_octets, *end = p + password->vp_length;
+
+       rad_assert(request->password != NULL);
+       rad_assert(request->password->da->attr == PW_USER_PASSWORD);
+
+       if ((end - p) < 2) {
+               REDEBUG("Password-With-Header {PBKDF2-SHA512} too short");
+               return RLM_MODULE_INVALID;
+       }
+
+       return pap_auth_pbkdf2_parse_digest(request, p, end - p, PW_SSHA2_512_PASSWORD, '$', '$', false, request->password);
+}
+
+/*
+ *     389ds pbkdf2 legacy password with header {PBKDF2_SHA256}
+ *
+ *     this was the first implementation in 389ds using a fixed length struct as base64.
+ *     at some point it was the default scheme, although it's not recommened anymore.
+ *
+ *      content struct is
+ *      4 bytes iterations (value 8192)
+ *      64 bytes salt
+ *     256 bytes hash
+ */
+static inline rlm_rcode_t CC_HINT(nonnull) pap_auth_pbkdf2_sha256_legacy(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *password)
+{
+#define PBKDF2_SHA256_LEGACY_SALT_LENGTH 64
+#define PBKDF2_SHA256_LEGACY_ITERATIONS_LENGTH 4
+#define PBKDF2_SHA256_LEGACY_HASH_LENGTH 256
+#define PBKDF2_SHA256_LEGACY_TOTAL_LENGTH (PBKDF2_SHA256_LEGACY_ITERATIONS_LENGTH + PBKDF2_SHA256_LEGACY_SALT_LENGTH + PBKDF2_SHA256_LEGACY_HASH_LENGTH)
+#define PBKDF2_SHA256_LEGACY_ITERATIONS 8192
+#define PBKDF2_SHA256_LEGACY_B64_LENGTH (PBKDF2_SHA256_LEGACY_TOTAL_LENGTH * 4 / 3)
+
+       struct pbkdf2_bufs {
+               uint32_t        iterations;
+               uint8_t         salt[PBKDF2_SHA256_LEGACY_SALT_LENGTH];
+               uint8_t         hash[PBKDF2_SHA256_LEGACY_HASH_LENGTH];
+               uint8_t         padding[1]; /* extra padding because fr_base64_decode incorrectly determines minimal size */
+       };
+       struct pbkdf2_bufs      pbkdf2_buf = { .iterations = PBKDF2_SHA256_LEGACY_ITERATIONS };
+
+       ssize_t                 slen;
+       uint8_t const           *p = password->vp_octets, *end = p + password->vp_length;
+
+       EVP_MD const            *evp_md = EVP_sha256();
+       size_t                  digest_len = SHA256_DIGEST_LENGTH;
+       uint8_t                 digest[SHA256_DIGEST_LENGTH];
+
+       rad_assert(request->password != NULL);
+       rad_assert(request->password->da->attr == PW_USER_PASSWORD);
+
+       if ((end - p) != PBKDF2_SHA256_LEGACY_B64_LENGTH) {
+               REDEBUG("Password-With-Header {PBKDF2_SHA256} has incorrect size %zd instead of %d.", password->vp_length, PBKDF2_SHA256_LEGACY_B64_LENGTH);
+               return RLM_MODULE_INVALID;
+       }
+
+       (void)fr_strerror();
+       slen = fr_base64_decode((uint8_t *)&pbkdf2_buf, sizeof(pbkdf2_buf), (char const *)p, end - p);
+
+       if (slen <= 0) {
+               REDEBUG("Failed decoding Password-With-Header {PBKDF2_SHA256}: \"%.*s\"", (int)(end - p), p);
+               return RLM_MODULE_INVALID;
+       }
+
+       if (slen != PBKDF2_SHA256_LEGACY_TOTAL_LENGTH) {
+               REDEBUG("Password-With-Header {PBKDF2_SHA256} has incorrect decoded size %zd instead of %d.", slen, PBKDF2_SHA256_LEGACY_TOTAL_LENGTH);
+               return RLM_MODULE_INVALID;
+       }
+
+       pbkdf2_buf.iterations = ntohl(pbkdf2_buf.iterations);
+
+       if (pbkdf2_buf.iterations != PBKDF2_SHA256_LEGACY_ITERATIONS) {
+               REDEBUG("Password-With-Header {PBKDF2_SHA256} has unexpected number of iterations %d instead of %d.", pbkdf2_buf.iterations, PBKDF2_SHA256_LEGACY_ITERATIONS);
+               return RLM_MODULE_INVALID;
+       }
+
+       if (PKCS5_PBKDF2_HMAC((char const *)request->password->vp_octets, (int)request->password->vp_length,
+                             (unsigned char const *)pbkdf2_buf.salt, (int)PBKDF2_SHA256_LEGACY_SALT_LENGTH,
+                             (int)pbkdf2_buf.iterations,
+                             evp_md,
+                             (int)digest_len, (unsigned char *)digest) == 0) {
+               REDEBUG("PBKDF2_SHA256 digest failure");
+               return RLM_MODULE_INVALID;
+       }
+
+       if (rad_digest_cmp(digest, pbkdf2_buf.hash, (size_t)digest_len) != 0) {
+               REDEBUG("PBKDF2_SHA256 digest does not match \"known good\" digest");
+               return RLM_MODULE_REJECT;
+       } else {
+               return RLM_MODULE_OK;
+       }
+}
+
 #endif
 
 static rlm_rcode_t CC_HINT(nonnull) pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
@@ -1344,6 +1515,22 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *re
                case PW_PBKDF2_PASSWORD:
                        auth_func = &pap_auth_pbkdf2;
                        break;
+
+               case PW_PBKDF2_SHA1_PASSWORD:
+                       auth_func = &pap_auth_pbkdf2_sha1;
+                       break;
+
+               case PW_PBKDF2_SHA256_PASSWORD:
+                       auth_func = &pap_auth_pbkdf2_sha256;
+                       break;
+
+               case PW_PBKDF2_SHA512_PASSWORD:
+                       auth_func = &pap_auth_pbkdf2_sha512;
+                       break;
+
+               case PW_PBKDF2_SHA256_LEGACY_PASSWORD:
+                       auth_func = &pap_auth_pbkdf2_sha256_legacy;
+                       break;
 #endif
 
                case PW_SHA_PASSWORD:
diff --git a/src/tests/modules/pap/pbkfd2_sha1_389ds.attrs b/src/tests/modules/pap/pbkfd2_sha1_389ds.attrs
new file mode 100644 (file)
index 0000000..bea77bd
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha1_389ds'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha1_389ds.unlang b/src/tests/modules/pap/pbkfd2_sha1_389ds.unlang
new file mode 100644 (file)
index 0000000..6c11f35
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha1_389ds') {
+       update control {
+               &PBKDF2-Password := '{PBKDF2-SHA1}10000$13QEHaJHNKHjlJEXX3ddjG2PpUjx1a83$dzrOWMeLso6J/K+evi4eZbOMcOk='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.attrs b/src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.attrs
new file mode 100644 (file)
index 0000000..b4b42d7
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha1_389ds_with_header'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.unlang b/src/tests/modules/pap/pbkfd2_sha1_389ds_with_header.unlang
new file mode 100644 (file)
index 0000000..ec14af9
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha1_389ds_with_header') {
+       update control {
+               Password-With-Header := '{PBKDF2-SHA1}10000$ooYqHQ0zx2JcxgadlJAVebeCOVYGxwz9$zh5v1+QTaJcRv3yqAuLn7ONN8VE='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha256_389ds.attrs b/src/tests/modules/pap/pbkfd2_sha256_389ds.attrs
new file mode 100644 (file)
index 0000000..5cb8ef3
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha256_389ds'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha256_389ds.unlang b/src/tests/modules/pap/pbkfd2_sha256_389ds.unlang
new file mode 100644 (file)
index 0000000..c8f31ec
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha256_389ds') {
+       update control {
+               &PBKDF2-Password := '{PBKDF2-SHA256}10000$9JjBuTTp9OCwANVzqUrq0QUvXnmDUJrm$f8Jrky2bX3z5eQ2SghxA38QSJ2PQwNenecmV4e4Ejyw='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.attrs b/src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.attrs
new file mode 100644 (file)
index 0000000..b5b3267
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha256_389ds_with_header'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.unlang b/src/tests/modules/pap/pbkfd2_sha256_389ds_with_header.unlang
new file mode 100644 (file)
index 0000000..1027098
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha256_389ds_with_header') {
+       update control {
+               Password-With-Header := '{PBKDF2-SHA256}10000$Y79u61tRN/dthaamikuxU/1PkOSprfbN$stej1qVq0CjglfdkLmwW2C94Hr5WQ20Nj6bIRumMy+E='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.attrs b/src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.attrs
new file mode 100644 (file)
index 0000000..f31ce1f
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha256_legacy_389ds_with_header'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.unlang b/src/tests/modules/pap/pbkfd2_sha256_legacy_389ds_with_header.unlang
new file mode 100644 (file)
index 0000000..bc7b561
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha256_legacy_389ds_with_header') {
+       update control {
+               Password-With-Header := '{PBKDF2_SHA256}AAAgAC8OScrYDwaJYr29/qoQ/bcEf+WsQmkZjIFLSIKm1lxzNOSGPQEkueb1J5mWQy8a17m+UAM5jFKKuktMWSTDag9LNVfHTwHocrIsi4GGLf+PSn/eA3+lu5JHVXQo49gbZ+SALQ90cOvor+v+/xa4Q1VPUr42PuvmsdQln6i9jm/j9tVkO06W+CJXvTiCpR+oHiog9v/3s6bHUrWvwNksUe2r2d+MV5IaNdnil7P39qr1BxiKtDiG4d6HyI5aq1VEhJBg6S5ITYQ9iPlDbpoy2MHDLZHNqQduAVnwJuKuHVU3dU+VkynHWm3nGa2hfeV/bD8F8+WYehgL7bJndEzECx1QKuAZO+lK92vIMbZArdMvU6WRjncVlgoJbZtko6tFp1Ew+0VMVX+b6dGED0L391crdsudrTO+/fvU66xH515L'
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha512_389ds.attrs b/src/tests/modules/pap/pbkfd2_sha512_389ds.attrs
new file mode 100644 (file)
index 0000000..f51bd33
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha512_389ds'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha512_389ds.unlang b/src/tests/modules/pap/pbkfd2_sha512_389ds.unlang
new file mode 100644 (file)
index 0000000..6791096
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha512_389ds') {
+       update control {
+               &PBKDF2-Password := '{PBKDF2-SHA512}10000$N3A054iTXUWq4pN2r/2WuCdRFn6ZJ1G+$5MlwC7KpKbXvjIuCaMaDKYmXSVwbUsgzmovEx73cmuuuA1Vet3IOnV5Mj7nZhb9Y4VlDfT6XLJt0xJlBGWESjw=='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}
diff --git a/src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.attrs b/src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.attrs
new file mode 100644 (file)
index 0000000..9163b50
--- /dev/null
@@ -0,0 +1,10 @@
+#
+#  Input packet
+#
+User-Name = 'pbkfd2_sha512_389ds_with_header'
+User-Password = 'password'
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.unlang b/src/tests/modules/pap/pbkfd2_sha512_389ds_with_header.unlang
new file mode 100644 (file)
index 0000000..5cddb47
--- /dev/null
@@ -0,0 +1,17 @@
+if ("${feature.tls}" == no) {
+       test_pass
+       return
+}
+
+if (User-Name == 'pbkfd2_sha512_389ds_with_header') {
+       update control {
+               Password-With-Header := '{PBKDF2-SHA512}10000$N3A054iTXUWq4pN2r/2WuCdRFn6ZJ1G+$5MlwC7KpKbXvjIuCaMaDKYmXSVwbUsgzmovEx73cmuuuA1Vet3IOnV5Mj7nZhb9Y4VlDfT6XLJt0xJlBGWESjw=='
+       }
+       pap.authorize
+       pap.authenticate
+       if (!ok) {
+               test_fail
+       } else {
+               test_pass
+       }
+}