]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
auth: Add PBKDF2 password scheme
authorAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 27 Apr 2016 06:14:29 +0000 (09:14 +0300)
committerAki Tuomi <aki.tuomi@dovecot.fi>
Wed, 27 Apr 2016 11:11:58 +0000 (14:11 +0300)
src/auth/Makefile.am
src/auth/password-scheme-pbkdf2.c [new file with mode: 0644]
src/auth/password-scheme.c
src/auth/password-scheme.h

index 3e00e2466254be9108eb615103edc694b0747410..4ba6bee12a52990c610c6402e23ec4bacc73e4f6 100644 (file)
@@ -47,7 +47,8 @@ libpassword_a_SOURCES = \
        password-scheme-md5crypt.c \
        password-scheme-scram.c \
        password-scheme-otp.c \
-       password-scheme-rpa.c
+       password-scheme-rpa.c \
+       password-scheme-pbkdf2.c
 
 auth_libs = \
        libpassword.a \
diff --git a/src/auth/password-scheme-pbkdf2.c b/src/auth/password-scheme-pbkdf2.c
new file mode 100644 (file)
index 0000000..3630588
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) 2015 Dovecot Oy, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "password-scheme.h"
+#include "hex-binary.h"
+#include "hash-method.h"
+#include "pkcs5.h"
+
+#define PBKDF2_KEY_SIZE_SHA1 20
+
+#define PBKDF2_GENERATE_SALT_LEN      16
+#define PBKDF2_ROUNDS_DEFAULT          5000
+
+static void
+pbkdf_run(const char *plaintext, const char *salt,
+         unsigned int rounds, unsigned char key_r[PBKDF2_KEY_SIZE_SHA1])
+{
+       memset(key_r, 0, PBKDF2_KEY_SIZE_SHA1);
+       buffer_t buf;
+       buffer_create_from_data(&buf, key_r, PBKDF2_KEY_SIZE_SHA1);
+
+       pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha1"),
+               (const unsigned char *)plaintext, strlen(plaintext),
+               (const unsigned char *)salt, strlen(salt),
+               rounds, PBKDF2_KEY_SIZE_SHA1, &buf);
+}
+
+void pbkdf2_generate(const char *plaintext, const char *user ATTR_UNUSED,
+               const unsigned char **raw_password_r, size_t *size_r)
+{
+       unsigned char key[PBKDF2_KEY_SIZE_SHA1];
+       const char *salt;
+       string_t *str = t_str_new(64);
+       unsigned int rounds = password_scheme_encryption_rounds;
+
+       if (rounds == 0)
+               rounds = PBKDF2_ROUNDS_DEFAULT;
+       salt = password_generate_salt(PBKDF2_GENERATE_SALT_LEN);
+       pbkdf_run(plaintext, salt, rounds, key);
+
+       str_printfa(str, "$1$%s$%u$", salt, rounds);
+       binary_to_hex_append(str, key, sizeof(key));
+
+       *raw_password_r = str_data(str);
+       *size_r = str_len(str);
+}
+
+int pbkdf2_verify(const char *plaintext, const char *user ATTR_UNUSED,
+             const unsigned char *raw_password, size_t size,
+             const char **error_r)
+{
+       const char *const *fields;
+       const char *salt;
+       unsigned int rounds;
+       unsigned char key1[PBKDF2_KEY_SIZE_SHA1], key2[PBKDF2_KEY_SIZE_SHA1];
+       buffer_t buf;
+
+       /* $1$salt$rounds$hash */
+       if (size < 3 || memcmp(raw_password, "$1$", 3) != 0) {
+               *error_r = "Invalid PBKDF2 passdb entry prefix";
+               return -1;
+       }
+
+       fields = t_strsplit(t_strndup(raw_password + 3, size - 3), "$");
+       salt = fields[0];
+       if (str_array_length(fields) != 3 ||
+           str_to_uint(fields[1], &rounds) < 0) {
+               *error_r = "Invalid PBKDF2 passdb entry format";
+               return -1;
+       }
+       buffer_create_from_data(&buf, key1, sizeof(key1));
+       if (strlen(fields[2]) != sizeof(key1)*2 ||
+           hex_to_binary(fields[2], &buf) < 0) {
+               *error_r = "PBKDF2 hash not 160bit hex-encoded";
+               return -1;
+       }
+
+       pbkdf_run(plaintext, salt, rounds, key2);
+       return memcmp(key1, key2, sizeof(key1)) == 0 ? 1 : 0;
+}
index baefb9532cbdca706c6be20428d4d4b9635d23d1..1633e50b1ea8947f2f388e6c14c519e0af613712 100644 (file)
@@ -825,7 +825,8 @@ static const struct password_scheme builtin_schemes[] = {
        { "NTLM", PW_ENCODING_HEX, NTLMSSP_HASH_SIZE, NULL, ntlm_generate },
        { "OTP", PW_ENCODING_NONE, 0, otp_verify, otp_generate },
        { "SKEY", PW_ENCODING_NONE, 0, otp_verify, skey_generate },
-       { "RPA", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, rpa_generate }
+       { "RPA", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, rpa_generate },
+        { "PBKDF2", PW_ENCODING_NONE, 0, pbkdf2_verify, pbkdf2_generate },
 };
 
 void password_scheme_register(const struct password_scheme *scheme)
index 438e849919d6a53ee3886d2ead42d02502659e59..65a92ef1c5ebc9e2f1994d76f34af08653437f38 100644 (file)
@@ -95,6 +95,11 @@ int scram_sha1_verify(const char *plaintext, const char *user ATTR_UNUSED,
                      const char **error_r ATTR_UNUSED);
 void scram_sha1_generate(const char *plaintext, const char *user ATTR_UNUSED,
                         const unsigned char **raw_password_r, size_t *size_r);
+void pbkdf2_generate(const char *plaintext, const char *user ATTR_UNUSED,
+                    const unsigned char **raw_password_r, size_t *size_r);
+int pbkdf2_verify(const char *plaintext, const char *user ATTR_UNUSED,
+                 const unsigned char *raw_password, size_t size,
+                 const char **error_r);
 
 /* check wich of the algorithms Blowfisch, SHA-256 and SHA-512 are
    supported by the used libc's/glibc's crypt() */