password-scheme-crypt.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#include "lib.h"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#include "mycrypt.h"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#include "password-scheme.h"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#include "crypt-blowfish.h"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#include "randgen.h"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch/* Lengths and limits for some crypt() algorithms. */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_ROUNDS_DEFAULT 5
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_ROUNDS_MIN 4
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_ROUNDS_MAX 31
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_SALT_LEN 16 /* raw salt */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_PREFIX_LEN (7+22+1) /* $2.$nn$ + salt */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_BUFFER_LEN 128
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_BLF_PREFIX "$2y"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_SHA2_ROUNDS_DEFAULT 5000
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_SHA2_ROUNDS_MIN 1000
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_SHA2_ROUNDS_MAX 999999999
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_SHA2_SALT_LEN 16
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic void
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschcrypt_generate_des(const char *plaintext, const struct password_generate_params *params ATTR_UNUSED,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const unsigned char **raw_password_r, size_t *size_r)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch#define CRYPT_SALT_LEN 2
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *password, *salt;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch salt = password_generate_salt(CRYPT_SALT_LEN);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password = t_strdup(mycrypt(plaintext, salt));
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *raw_password_r = (const unsigned char *)password;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *size_r = strlen(password);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic void
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschcrypt_generate_blowfish(const char *plaintext, const struct password_generate_params *params,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const unsigned char **raw_password_r, size_t *size_r)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch char salt[CRYPT_BLF_SALT_LEN];
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch char password[CRYPT_BLF_BUFFER_LEN];
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch char magic_salt[CRYPT_BLF_PREFIX_LEN];
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch unsigned int rounds = params->rounds;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (rounds == 0)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_BLF_ROUNDS_DEFAULT;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds < CRYPT_BLF_ROUNDS_MIN)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_BLF_ROUNDS_MIN;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds > CRYPT_BLF_ROUNDS_MAX)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_BLF_ROUNDS_MAX;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch random_fill(salt, CRYPT_BLF_SALT_LEN);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (crypt_gensalt_blowfish_rn(CRYPT_BLF_PREFIX, rounds,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch salt, CRYPT_BLF_SALT_LEN,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch magic_salt, CRYPT_BLF_PREFIX_LEN) == NULL)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch i_fatal("crypt_gensalt_blowfish_rn failed: %m");
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (crypt_blowfish_rn(plaintext, magic_salt, password,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch CRYPT_BLF_BUFFER_LEN) == NULL)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch i_fatal("crypt_blowfish_rn failed: %m");
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *raw_password_r = (const unsigned char *)t_strdup(password);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *size_r = strlen(password);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic int
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschcrypt_verify_blowfish(const char *plaintext, const struct password_generate_params *params ATTR_UNUSED,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const unsigned char *raw_password, size_t size,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char **error_r)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *password;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *salt;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch char crypted[CRYPT_BLF_BUFFER_LEN];
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (size == 0) {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch /* the default mycrypt() handler would return match */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch return 0;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password = t_strndup(raw_password, size);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (size < CRYPT_BLF_PREFIX_LEN ||
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch strncmp(password, "$2", 2) != 0 ||
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password[2] < 'a' || password[2] > 'z' ||
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password[3] != '$') {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *error_r = "Password is not blowfish password";
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch return -1;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch salt = t_strndup(password, CRYPT_BLF_PREFIX_LEN);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (crypt_blowfish_rn(plaintext, salt, crypted, CRYPT_BLF_BUFFER_LEN) == NULL) {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch /* really shouldn't happen unless the system is broken */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *error_r = t_strdup_printf("crypt_blowfish_rn failed: %m");
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch return -1;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch return strcmp(crypted, password) == 0 ? 1 : 0;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic void
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschcrypt_generate_sha256(const char *plaintext, const struct password_generate_params *params,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const unsigned char **raw_password_r, size_t *size_r)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *password, *salt, *magic_salt;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch unsigned int rounds = params->rounds;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (rounds == 0)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_DEFAULT;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds < CRYPT_SHA2_ROUNDS_MIN)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_MIN;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds > CRYPT_SHA2_ROUNDS_MAX)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_MAX;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch salt = password_generate_salt(CRYPT_SHA2_SALT_LEN);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch magic_salt = t_strdup_printf("$5$%s", salt);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch magic_salt = t_strdup_printf("$5$rounds=%u$%s", rounds, salt);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password = t_strdup(mycrypt(plaintext, magic_salt));
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *raw_password_r = (const unsigned char *)password;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *size_r = strlen(password);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic void
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschcrypt_generate_sha512(const char *plaintext, const struct password_generate_params *params,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const unsigned char **raw_password_r, size_t *size_r)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *password, *salt, *magic_salt;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch unsigned int rounds = params->rounds;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (rounds == 0)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_DEFAULT;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds < CRYPT_SHA2_ROUNDS_MIN)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_MIN;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else if (rounds > CRYPT_SHA2_ROUNDS_MAX)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch rounds = CRYPT_SHA2_ROUNDS_MAX;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch salt = password_generate_salt(CRYPT_SHA2_SALT_LEN);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (rounds == CRYPT_SHA2_ROUNDS_DEFAULT)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch magic_salt = t_strdup_printf("$6$%s", salt);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch else
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch magic_salt = t_strdup_printf("$6$rounds=%u$%s", rounds, salt);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password = t_strdup(mycrypt(plaintext, magic_salt));
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *raw_password_r = (const unsigned char *)password;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch *size_r = strlen(password);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch/* keep in sync with the crypt_schemes struct below */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic const struct {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *key;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *salt;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *expected;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch} sample[] = {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "08/15!test~4711", "JB", "JBOZ0DgmtucwE" },
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "08/15!test~4711", "$2a$04$0123456789abcdefABCDEF",
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "$2a$04$0123456789abcdefABCDE.N.drYX5yIAL1LkTaaZotW3yI0hQhZru" },
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "08/15!test~4711", "$5$rounds=1000$0123456789abcdef",
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "$5$rounds=1000$0123456789abcdef$K/DksR0DT01hGc8g/kt"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "9McEgrbFMKi9qrb1jehe7hn4" },
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "08/15!test~4711", "$6$rounds=1000$0123456789abcdef",
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "$6$rounds=1000$0123456789abcdef$ZIAd5WqfyLkpvsVCVUU1GrvqaZTq"
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "vhJoouxdSqJO71l9Ld3tVrfOatEjarhghvEYADkq//LpDnTeO90tcbtHR1" }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch};
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch/* keep in sync with the sample struct above */
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic const struct password_scheme crypt_schemes[] = {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "DES-CRYPT", PW_ENCODING_NONE, 0, crypt_verify,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypt_generate_des },
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "SHA256-CRYPT", PW_ENCODING_NONE, 0, crypt_verify,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypt_generate_sha256 },
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch { "SHA512-CRYPT", PW_ENCODING_NONE, 0, crypt_verify,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypt_generate_sha512 }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch};
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic const struct password_scheme blf_crypt_scheme = {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "BLF-CRYPT", PW_ENCODING_NONE, 0, crypt_verify_blowfish,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypt_generate_blowfish
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch};
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschstatic const struct password_scheme default_crypt_scheme = {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch "CRYPT", PW_ENCODING_NONE, 0, crypt_verify,
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypt_generate_blowfish
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch};
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Boschvoid password_scheme_register_crypt(void)
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch{
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch unsigned int i;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch const char *crypted;
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch for (i = 0; i < N_ELEMENTS(crypt_schemes); i++) {
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch crypted = mycrypt(sample[i].key, sample[i].salt);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch if (crypted != NULL &&
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch (strcmp(crypted, sample[i].expected) == 0))
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password_scheme_register(&crypt_schemes[i]);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch }
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password_scheme_register(&blf_crypt_scheme);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch password_scheme_register(&default_crypt_scheme);
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch}
1bd20e2a575473f3d05499f05f1d72da59b34fd6Stephan Bosch