From: Pascal Volk Date: Sun, 9 May 2010 20:57:27 +0000 (+0000) Subject: auth: added new password scheme names {BLF,SHA256,SHA512}-CRYPT. X-Git-Tag: 2.0.beta6~252 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=42fb278a57f1c6d7d5d0c7bd2318edb721dc0ec0;p=thirdparty%2Fdovecot%2Fcore.git auth: added new password scheme names {BLF,SHA256,SHA512}-CRYPT. Their availability depends on the used libc. doveadm pw: added '-r rounds' option for the password schemes mentioned above. --HG-- branch : HEAD --- diff --git a/src/auth/Makefile.am b/src/auth/Makefile.am index b32b136986..28c7f47872 100644 --- a/src/auth/Makefile.am +++ b/src/auth/Makefile.am @@ -34,6 +34,7 @@ auth_LDFLAGS = -export-dynamic libpassword_a_SOURCES = \ mycrypt.c \ password-scheme.c \ + password-scheme-crypt.c \ password-scheme-md5crypt.c \ password-scheme-otp.c \ password-scheme-rpa.c diff --git a/src/auth/password-scheme-crypt.c b/src/auth/password-scheme-crypt.c new file mode 100644 index 0000000000..737b967576 --- /dev/null +++ b/src/auth/password-scheme-crypt.c @@ -0,0 +1,132 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mycrypt.h" +#include "password-scheme.h" + +/* Lengths and limits for some crypt() algorithms. */ +#define CRYPT_BLF_ROUNDS_DEFAULT 5 +#define CRYPT_BLF_ROUNDS_MIN 4 +#define CRYPT_BLF_ROUNDS_MAX 31 +#define CRYPT_BLF_SALT_LEN 22 +#define CRYPT_SHA2_ROUNDS_DEFAULT 5000 +#define CRYPT_SHA2_ROUNDS_MIN 1000 +#define CRYPT_SHA2_ROUNDS_MAX 999999999 +#define CRYPT_SHA2_SALT_LEN 16 + +static unsigned int encryption_rounds = 0; + +void password_set_encryption_rounds(unsigned int rounds) +{ + /* just take the new value. crypt_generate_*() will enforce their + limits. */ + encryption_rounds = rounds; +} + +static void +crypt_generate_blowfisch(const char *plaintext, const char *user ATTR_UNUSED, + const unsigned char **raw_password_r, size_t *size_r) +{ + const char *password, *salt, *magic_salt; + unsigned int rounds = encryption_rounds; + + if (rounds == 0) + rounds = CRYPT_BLF_ROUNDS_DEFAULT; + else if (rounds < CRYPT_BLF_ROUNDS_MIN) + rounds = CRYPT_BLF_ROUNDS_MIN; + else if (rounds > CRYPT_BLF_ROUNDS_MAX) + rounds = CRYPT_BLF_ROUNDS_MAX; + + salt = password_generate_salt(CRYPT_BLF_SALT_LEN); + magic_salt = t_strdup_printf("$2a$%02u$%s", rounds, salt); + password = t_strdup(mycrypt(plaintext, magic_salt)); + *raw_password_r = (const unsigned char *)password; + *size_r = strlen(password); +} + +static void +crypt_generate_sha256(const char *plaintext, const char *user ATTR_UNUSED, + const unsigned char **raw_password_r, size_t *size_r) +{ + const char *password, *salt, *magic_salt; + unsigned int rounds = encryption_rounds; + + if (rounds == 0) + rounds = CRYPT_SHA2_ROUNDS_DEFAULT; + else if (rounds < CRYPT_SHA2_ROUNDS_MIN) + rounds = CRYPT_SHA2_ROUNDS_MIN; + else if (rounds > CRYPT_SHA2_ROUNDS_MAX) + rounds = CRYPT_SHA2_ROUNDS_MAX; + + salt = password_generate_salt(CRYPT_SHA2_SALT_LEN); + if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT) + magic_salt = t_strdup_printf("$5$%s", salt); + else + magic_salt = t_strdup_printf("$5$rounds=%u$%s", rounds, salt); + password = t_strdup(mycrypt(plaintext, magic_salt)); + *raw_password_r = (const unsigned char *)password; + *size_r = strlen(password); +} + +static void +crypt_generate_sha512(const char *plaintext, const char *user ATTR_UNUSED, + const unsigned char **raw_password_r, size_t *size_r) +{ + const char *password, *salt, *magic_salt; + unsigned int rounds = encryption_rounds; + + if (rounds == 0) + rounds = CRYPT_SHA2_ROUNDS_DEFAULT; + else if (rounds < CRYPT_SHA2_ROUNDS_MIN) + rounds = CRYPT_SHA2_ROUNDS_MIN; + else if (rounds > CRYPT_SHA2_ROUNDS_MAX) + rounds = CRYPT_SHA2_ROUNDS_MAX; + + salt = password_generate_salt(CRYPT_SHA2_SALT_LEN); + if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT) + magic_salt = t_strdup_printf("$6$%s", salt); + else + magic_salt = t_strdup_printf("$6$rounds=%u$%s", rounds, salt); + password = t_strdup(mycrypt(plaintext, magic_salt)); + *raw_password_r = (const unsigned char *)password; + *size_r = strlen(password); +} + +/* keep in sync with the crypt_schemes struct below */ +static const struct { + const char *key; + const char *salt; + const char *expected; +} sample[] = { + { "08/15!test~4711", "$2a$04$0123456789abcdefABCDEF", + "$2a$04$0123456789abcdefABCDE.N.drYX5yIAL1LkTaaZotW3yI0hQhZru" }, + { "08/15!test~4711", "$5$rounds=1000$0123456789abcdef", + "$5$rounds=1000$0123456789abcdef$K/DksR0DT01hGc8g/kt" + "9McEgrbFMKi9qrb1jehe7hn4" }, + { "08/15!test~4711", "$6$rounds=1000$0123456789abcdef", + "$6$rounds=1000$0123456789abcdef$ZIAd5WqfyLkpvsVCVUU1GrvqaZTq" + "vhJoouxdSqJO71l9Ld3tVrfOatEjarhghvEYADkq//LpDnTeO90tcbtHR1" } +}; + +/* keep in sync with the sample struct above */ +static const struct password_scheme crypt_schemes[] = { + { "BLF-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, + crypt_generate_blowfisch }, + { "SHA256-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, + crypt_generate_sha256 }, + { "SHA512-CRYPT", PW_ENCODING_NONE, 0, crypt_verify, + crypt_generate_sha512 } +}; + +void password_scheme_register_crypt(void) +{ + unsigned int i; + const char *crypted; + + for (i = 0; i < N_ELEMENTS(crypt_schemes); i++) { + crypted = mycrypt(sample[i].key, sample[i].salt); + if (crypted != NULL && + (strcmp(crypted, sample[i].expected) == 0)) + password_scheme_register(&crypt_schemes[i]); + } +} diff --git a/src/auth/password-scheme.c b/src/auth/password-scheme.c index 2bef16446c..c342ee459d 100644 --- a/src/auth/password-scheme.c +++ b/src/auth/password-scheme.c @@ -227,6 +227,19 @@ bool password_generate_encoded(const char *plaintext, const char *user, return TRUE; } +const char *password_generate_salt(size_t len) +{ + unsigned int i; + char *salt; + + salt = t_malloc(len + 1); + random_fill(salt, len); + for (i = 0; i < len; i++) + salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)]; + salt[len] = '\0'; + return salt; +} + bool password_scheme_is_alias(const char *scheme1, const char *scheme2) { const struct password_scheme *const *schemes, *s1 = NULL, *s2 = NULL; @@ -273,9 +286,8 @@ password_scheme_detect(const char *plain_password, const char *crypted_password, return NULL; } -static bool -crypt_verify(const char *plaintext, const char *user ATTR_UNUSED, - const unsigned char *raw_password, size_t size) +bool crypt_verify(const char *plaintext, const char *user ATTR_UNUSED, + const unsigned char *raw_password, size_t size) { const char *password, *crypted; @@ -299,14 +311,10 @@ static void crypt_generate(const char *plaintext, const char *user ATTR_UNUSED, const unsigned char **raw_password_r, size_t *size_r) { - char salt[3]; - const char *password; - - random_fill(salt, sizeof(salt)-1); - salt[0] = salt_chars[salt[0] % (sizeof(salt_chars)-1)]; - salt[1] = salt_chars[salt[1] % (sizeof(salt_chars)-1)]; - salt[2] = '\0'; +#define CRYPT_SALT_LEN 2 + const char *password, *salt; + salt = password_generate_salt(CRYPT_SALT_LEN); password = t_strdup(mycrypt(plaintext, salt)); *raw_password_r = (const unsigned char *)password; *size_r = strlen(password); @@ -784,6 +792,7 @@ void password_schemes_init(void) i_array_init(&password_schemes, N_ELEMENTS(builtin_schemes) + 4); for (i = 0; i < N_ELEMENTS(builtin_schemes); i++) password_scheme_register(&builtin_schemes[i]); + password_scheme_register_crypt(); } void password_schemes_deinit(void) diff --git a/src/auth/password-scheme.h b/src/auth/password-scheme.h index 8527d874c0..5b43a02741 100644 --- a/src/auth/password-scheme.h +++ b/src/auth/password-scheme.h @@ -65,10 +65,22 @@ void password_scheme_unregister(const struct password_scheme *scheme); void password_schemes_init(void); void password_schemes_deinit(void); +/* some password schemes/algorithms supports a variable number of + encryption rounds. */ +void password_set_encryption_rounds(unsigned int rounds); + /* INTERNAL: */ +const char *password_generate_salt(size_t len); const char *password_generate_md5_crypt(const char *pw, const char *salt); const char *password_generate_otp(const char *pw, const char *state, unsigned int algo); void password_generate_rpa(const char *pw, unsigned char result[]); +bool crypt_verify(const char *plaintext, const char *user, + const unsigned char *raw_password, size_t size); + +/* check wich of the algorithms Blowfisch, SHA-256 and SHA-512 are + supported by the used libc's/glibc's crypt() */ +void password_scheme_register_crypt(void); + #endif diff --git a/src/doveadm/doveadm-pw.c b/src/doveadm/doveadm-pw.c index dbe2878648..4704933cc6 100644 --- a/src/doveadm/doveadm-pw.c +++ b/src/doveadm/doveadm-pw.c @@ -22,11 +22,12 @@ static void cmd_pw(int argc, char *argv[]) const char *scheme = NULL; const char *plaintext = NULL; int ch, lflag = 0, Vflag = 0; + unsigned int rounds = 0; random_init(); password_schemes_init(); - while ((ch = getopt(argc, argv, "lp:s:u:V")) != -1) { + while ((ch = getopt(argc, argv, "lp:r:s:u:V")) != -1) { switch (ch) { case 'l': lflag = 1; @@ -34,6 +35,10 @@ static void cmd_pw(int argc, char *argv[]) case 'p': plaintext = optarg; break; + case 'r': + if (str_to_uint(optarg, &rounds) < 0) + i_fatal("Invalid number of rounds: %s", optarg); + break; case 's': scheme = optarg; break; @@ -64,6 +69,9 @@ static void cmd_pw(int argc, char *argv[]) help(&doveadm_cmd_pw); scheme = scheme == NULL ? DEFAULT_SCHEME : t_str_ucase(scheme); + if (rounds > 0) + password_set_encryption_rounds(rounds); + while (plaintext == NULL) { const char *check; static int lives = 3; @@ -107,9 +115,11 @@ static void cmd_pw(int argc, char *argv[]) } struct doveadm_cmd doveadm_cmd_pw = { - cmd_pw, "pw", "[-l] [-p plaintext] [-s scheme] [-u user] [-V]", + cmd_pw, "pw", + "[-l] [-p plaintext] [-r rounds] [-s scheme] [-u user] [-V]", " -l List known password schemes\n" " -p plaintext New password\n" +" -r rounds Number of encryption rounds (if scheme uses it)\n" " -s scheme Password scheme\n" " -u user Username (if scheme uses it)\n" " -V Internally verify the hash\n"