changeset 2367:203938a7f45e HEAD

Added dovecotpw utility. Patch by Joshua Goodall
author Timo Sirainen <tss@iki.fi>
date Mon, 26 Jul 2004 19:21:29 +0300
parents 33c584ef528a
children 1ce02ffc7ec7
files configure.in src/auth/passdb.c src/auth/password-scheme.c src/auth/password-scheme.h src/lib/Makefile.am src/util/Makefile.am
diffstat 6 files changed, 227 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- 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,
--- 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,
--- 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 <openssl/sha.h>
-#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 }
 };
 
--- 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);
 
--- 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 \
--- 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