# HG changeset patch # User Timo Sirainen # Date 1090858889 -10800 # Node ID 203938a7f45ebc88ea7906368069b0bfbbcdfd8c # Parent 33c584ef528ad6a9ebfdf28f2f64c920675b01fc Added dovecotpw utility. Patch by Joshua Goodall diff -r 33c584ef528a -r 203938a7f45e configure.in --- a/configure.in Mon Jul 26 18:42:52 2004 +0300 +++ b/configure.in Mon Jul 26 19:21:29 2004 +0300 @@ -1,7 +1,7 @@ AC_INIT(src) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(dovecot, 1.0-test29) +AM_INIT_AUTOMAKE(dovecot, 1.0-test30) AM_MAINTAINER_MODE @@ -588,13 +588,6 @@ fi AC_SUBST(RAND_LIBS) -AC_CHECK_LIB(crypto, SHA1_Init, [ - AC_CHECK_HEADER(openssl/sha.h, [ - AC_DEFINE(HAVE_OPENSSL_SHA1,, Define if you have SHA1 in OpenSSL) - AUTH_LIBS=-lcrypto - ]) -]) - dnl * do we have tm_gmtoff AC_MSG_CHECKING([for tm_gmtoff]) AC_CACHE_VAL(i_cv_field_tm_gmtoff, diff -r 33c584ef528a -r 203938a7f45e src/auth/passdb.c --- a/src/auth/passdb.c Mon Jul 26 18:42:52 2004 +0300 +++ b/src/auth/passdb.c Mon Jul 26 19:21:29 2004 +0300 @@ -52,7 +52,8 @@ if (password != NULL) { wanted_scheme = passdb_credentials_to_str(credentials); if (strcasecmp(scheme, wanted_scheme) != 0) { - if (strcasecmp(scheme, "PLAIN") == 0) { + if (strcasecmp(scheme, "PLAIN") == 0 || + strcasecmp(scheme, "CLEARTEXT") == 0) { /* we can generate anything out of plaintext passwords */ password = password_generate(password, user, diff -r 33c584ef528a -r 203938a7f45e src/auth/password-scheme.c --- a/src/auth/password-scheme.c Mon Jul 26 18:42:52 2004 +0300 +++ b/src/auth/password-scheme.c Mon Jul 26 19:21:29 2004 +0300 @@ -8,13 +8,10 @@ #include "module-dir.h" #include "mycrypt.h" #include "randgen.h" +#include "sha1.h" #include "str.h" #include "password-scheme.h" -#ifdef HAVE_OPENSSL_SHA1 -# include -#endif - static const char salt_chars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -40,6 +37,19 @@ return -1; } +const char *password_list_schemes(const struct password_scheme **listptr) +{ + if (*listptr == NULL) + *listptr = schemes; + + if ((*listptr)->name == NULL) { + *listptr = NULL; + return NULL; + } + + return (*listptr)++->name; +} + const char *password_get_scheme(const char **password) { const char *p, *scheme; @@ -68,6 +78,14 @@ scheme = t_strdup_until(*password + 1, p); *password = p + 1; + + /* LDAP's RFC2307 specifies the MD5 scheme for what we call PLAIN-MD5, + only base64-encoded rather than hex-encoded. + We can detect this case - base64 doesn't use '$'. */ + if (strncasecmp(scheme, "MD5", 3) == 0 && + strncmp(*password, "$1$", 3) != 0) { + scheme = "LDAP-MD5"; + } return scheme; } @@ -124,20 +142,146 @@ return password_generate_md5_crypt(plaintext, salt); } -#ifdef HAVE_OPENSSL_SHA1 -static int sha_verify(const char *plaintext, const char *password, +static const char *sha1_generate(const char *plaintext, + const char *user __attr_unused__) +{ + unsigned char digest[SHA1_RESULTLEN]; + string_t *str; + + sha1_get_digest(plaintext, strlen(plaintext), digest); + str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(digest)+1)); + base64_encode(digest, sizeof(digest), str); + return str_c(str); +} + +static int sha1_verify(const char *plaintext, const char *password, const char *user __attr_unused__) { - unsigned char digest[SHA_DIGEST_LENGTH]; + unsigned char sha1_digest[SHA1_RESULTLEN]; + const char *data; + buffer_t *buf; + size_t size; + + sha1_get_digest(plaintext, strlen(plaintext), sha1_digest); + + buf = buffer_create_static(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(strlen(password)+1)); + + if (base64_decode(password, strlen(password), NULL, buf) <= 0) { + i_error("sha1_verify(%s): failed decoding SHA base64", user); + return 0; + } + + data = buffer_get_data(buf, &size); + if (size < SHA1_RESULTLEN) { + i_error("sha1_verify(%s): invalid SHA base64 decode", user); + return 0; + } + + return memcmp(sha1_digest, data, SHA1_RESULTLEN) == 0; +} + +static const char *ssha_generate(const char *plaintext, + const char *user __attr_unused__) +{ + unsigned char ssha_digest[SHA1_RESULTLEN+4]; + unsigned char *salt = &ssha_digest[SHA1_RESULTLEN]; + struct sha1_ctxt ctx; string_t *str; - SHA1(plaintext, strlen(plaintext), digest); + random_fill(salt, 4); + + sha1_init(&ctx); + sha1_loop(&ctx, plaintext, strlen(plaintext)); + sha1_loop(&ctx, salt, 4); + sha1_result(&ctx, ssha_digest); + + str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(ssha_digest))+1); + base64_encode(ssha_digest, sizeof(ssha_digest), str); + return str_c(str); +} + +static int ssha_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + unsigned char sha1_digest[SHA1_RESULTLEN]; + buffer_t *buf; + const char *data; + size_t size; + struct sha1_ctxt ctx; + + /* format: base64-encoded MD5 hash and salt */ + buf = buffer_create_static(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(strlen(password)+1)); + + if (base64_decode(password, strlen(password), NULL, buf) <= 0) { + i_error("ssha_verify(%s): failed decoding SSHA base64", user); + return 0; + } + + data = buffer_get_data(buf, &size); + if (size <= SHA1_RESULTLEN) { + i_error("ssha_verify(%s): invalid SSHA base64 decode", user); + return 0; + } + + sha1_init(&ctx); + sha1_loop(&ctx, plaintext, strlen(plaintext)); + sha1_loop(&ctx, &data[SHA1_RESULTLEN], size-SHA1_RESULTLEN); + sha1_result(&ctx, sha1_digest); + return memcmp(sha1_digest, data, SHA1_RESULTLEN) == 0; +} - str = t_str_new(64); - base64_encode(digest, sizeof(digest), str); - return strcasecmp(str_c(str), password) == 0; +static const char *smd5_generate(const char *plaintext, + const char *user __attr_unused__) +{ + unsigned char smd5_digest[20]; + unsigned char *salt = &smd5_digest[16]; + struct md5_context ctx; + string_t *str; + + random_fill(salt, 4); + + md5_init(&ctx); + md5_update(&ctx, plaintext, strlen(plaintext)); + md5_update(&ctx, salt, 4); + md5_final(&ctx, smd5_digest); + + str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(smd5_digest))+1); + base64_encode(smd5_digest, sizeof(smd5_digest), str); + return str_c(str); } -#endif + +static int smd5_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + unsigned char md5_digest[16]; + buffer_t *buf; + const char *data; + size_t size; + struct md5_context ctx; + + /* format: base64-encoded MD5 hash and salt */ + buf = buffer_create_static(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(strlen(password)+1)); + + if (base64_decode(password, strlen(password), NULL, buf) <= 0) { + i_error("smd5_verify(%s): failed decoding SMD5 base64", user); + return 0; + } + + data = buffer_get_data(buf, &size); + if (size <= 16) { + i_error("smd5_verify(%s): invalid SMD5 base64 decode", user); + return 0; + } + + md5_init(&ctx); + md5_update(&ctx, plaintext, strlen(plaintext)); + md5_update(&ctx, &data[16], size-16); + md5_final(&ctx, md5_digest); + return memcmp(md5_digest, data, 16) == 0; +} static int plain_verify(const char *plaintext, const char *password, const char *user __attr_unused__) @@ -216,17 +360,59 @@ return binary_to_hex(digest, sizeof(digest)); } +static const char *ldap_md5_generate(const char *plaintext, + const char *user __attr_unused__) +{ + unsigned char digest[16]; + string_t *str; + + md5_get_digest(plaintext, strlen(plaintext), digest); + str = t_str_new(MAX_BASE64_ENCODED_SIZE(sizeof(digest)+1)); + base64_encode(digest, sizeof(digest), str); + return str_c(str); +} + +static int ldap_md5_verify(const char *plaintext, const char *password, + const char *user __attr_unused__) +{ + unsigned char md5_digest[16]; + buffer_t *buf; + const char *data; + size_t size; + + md5_get_digest(plaintext, strlen(plaintext), md5_digest); + + buf = buffer_create_static(pool_datastack_create(), + MAX_BASE64_DECODED_SIZE(strlen(password)+1)); + + if (base64_decode(password, strlen(password), NULL, buf) <= 0) { + i_error("ldap_md5_verify(%s): failed decoding MD5 base64", + user); + return 0; + } + + data = buffer_get_data(buf, &size); + if (size != 16) { + i_error("ldap_md5_verify(%s): invalid MD5 base64 decode", user); + return 0; + } + + return memcmp(md5_digest, data, 16) == 0; +} + static const struct password_scheme default_schemes[] = { { "CRYPT", crypt_verify, crypt_generate }, { "MD5", md5_verify, md5_generate }, -#ifdef HAVE_OPENSSL_SHA1 - { "SHA", sha_verify, NULL }, - { "SHA1", sha_verify, NULL }, -#endif + { "SHA", sha1_verify, sha1_generate }, + { "SHA1", sha1_verify, sha1_generate }, + { "SMD5", smd5_verify, smd5_generate }, + { "SSHA", ssha_verify, ssha_generate }, { "PLAIN", plain_verify, plain_generate }, + { "CLEARTEXT", plain_verify, plain_generate }, { "HMAC-MD5", hmac_md5_verify, hmac_md5_generate }, { "DIGEST-MD5", digest_md5_verify, digest_md5_generate }, { "PLAIN-MD5", plain_md5_verify, plain_md5_generate }, + { "LDAP-MD5", ldap_md5_verify, ldap_md5_generate }, { NULL, NULL, NULL } }; diff -r 33c584ef528a -r 203938a7f45e src/auth/password-scheme.h --- a/src/auth/password-scheme.h Mon Jul 26 18:42:52 2004 +0300 +++ b/src/auth/password-scheme.h Mon Jul 26 19:21:29 2004 +0300 @@ -21,6 +21,9 @@ const char *password_generate(const char *plaintext, const char *user, const char *scheme); +/* Iterate through the list of password schemes, returning names */ +const char *password_list_schemes(const struct password_scheme **listptr); + void password_schemes_init(void); void password_schemes_deinit(void); diff -r 33c584ef528a -r 203938a7f45e src/lib/Makefile.am --- a/src/lib/Makefile.am Mon Jul 26 18:42:52 2004 +0300 +++ b/src/lib/Makefile.am Mon Jul 26 19:21:29 2004 +0300 @@ -54,6 +54,7 @@ safe-memset.c \ safe-mkdir.c \ sendfile-util.c \ + sha1.c \ str.c \ strescape.c \ strfuncs.c \ @@ -109,6 +110,7 @@ safe-memset.h \ safe-mkdir.h \ sendfile-util.h \ + sha1.h \ str.h \ strescape.h \ strfuncs.h \ diff -r 33c584ef528a -r 203938a7f45e src/util/Makefile.am --- a/src/util/Makefile.am Mon Jul 26 18:42:52 2004 +0300 +++ b/src/util/Makefile.am Mon Jul 26 19:21:29 2004 +0300 @@ -1,12 +1,28 @@ pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = rawlog +sbin_PROGRAMS = dovecotpw INCLUDES = \ - -I$(top_srcdir)/src/lib + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/auth rawlog_LDADD = \ ../lib/liblib.a rawlog_SOURCES = \ rawlog.c + +dovecotpw_INCLUDES = \ + -DAUTH_MODULE_DIR=\""$(moduledir)/auth"\" + +dovecotpw_LDADD = \ + ../lib/liblib.a \ + ../auth/password-scheme.o \ + ../auth/password-scheme-cram-md5.o \ + ../auth/password-scheme-md5crypt.o \ + ../auth/mycrypt.o \ + $(AUTH_LIBS) + +dovecotpw_SOURCES = \ + dovecotpw.c