--- /dev/null
+/* 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]);
+ }
+}
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;
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;
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);
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)
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;
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;
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;
}
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"