[dovecot-cvs] dovecot/src/auth Makefile.am, 1.53, 1.54 auth-request.c, 1.98, 1.99 auth-request.h, 1.38, 1.39 auth-worker-client.c, 1.34, 1.35 mech-apop.c, 1.27, 1.28 mech-cram-md5.c, 1.29, 1.30 mech-digest-md5.c, 1.46, 1.47 mech-ntlm.c, 1.30, 1.31 mech-otp.c, 1.4, 1.5 mech-rpa.c, 1.32, 1.33 mech-skey.c, 1.4, 1.5 passdb-ldap.c, 1.62, 1.63 passdb-passwd-file.c, 1.31, 1.32 passdb.c, 1.50, 1.51 passdb.h, 1.40, 1.41 password-scheme-cram-md5.c, 1.5, NONE password-scheme-ntlm.c, 1.2, NONE password-scheme-rpa.c, 1.3, 1.4 password-scheme.c, 1.33, 1.34 password-scheme.h, 1.11, 1.12 userdb-static.c, 1.26, 1.27

tss at dovecot.org tss at dovecot.org
Sun May 13 15:17:12 EEST 2007


Update of /var/lib/cvs/dovecot/src/auth
In directory talvi:/tmp/cvs-serv10070

Modified Files:
	Makefile.am auth-request.c auth-request.h auth-worker-client.c 
	mech-apop.c mech-cram-md5.c mech-digest-md5.c mech-ntlm.c 
	mech-otp.c mech-rpa.c mech-skey.c passdb-ldap.c 
	passdb-passwd-file.c passdb.c passdb.h password-scheme-rpa.c 
	password-scheme.c password-scheme.h userdb-static.c 
Removed Files:
	password-scheme-cram-md5.c password-scheme-ntlm.c 
Log Message:
All password schemes can now be encoded with base64 or hex. The encoding is
".b64", ".base64" or ".hex" suffix in the scheme, eg. {plain.b64}.

Password scheme verification function can now be set to NULL, in which case
the verification is done by generating a new crypted password from given
plaintext password and comparing it.



Index: Makefile.am
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/Makefile.am,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -d -r1.53 -r1.54
--- Makefile.am	22 Feb 2007 21:50:47 -0000	1.53
+++ Makefile.am	13 May 2007 12:17:09 -0000	1.54
@@ -20,8 +20,6 @@
 	mycrypt.c \
 	password-scheme.c \
 	password-scheme-md5crypt.c \
-	password-scheme-cram-md5.c \
-	password-scheme-ntlm.c \
 	password-scheme-otp.c \
 	password-scheme-rpa.c
 

Index: auth-request.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request.c,v
retrieving revision 1.98
retrieving revision 1.99
diff -u -d -r1.98 -r1.99
--- auth-request.c	13 May 2007 08:24:06 -0000	1.98
+++ auth-request.c	13 May 2007 12:17:09 -0000	1.99
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "buffer.h"
 #include "hash.h"
+#include "hex-binary.h"
 #include "str.h"
 #include "safe-memset.h"
 #include "str-sanitize.h"
@@ -449,9 +450,10 @@
 }
 
 static void
-auth_request_lookup_credentials_callback_finish(enum passdb_result result,
-						const char *password,
-						struct auth_request *request)
+auth_request_lookup_credentials_finish(enum passdb_result result,
+				       const unsigned char *credentials,
+				       size_t size,
+				       struct auth_request *request)
 {
 	if (!auth_request_handle_passdb_callback(&result, request)) {
 		/* try next passdb */
@@ -462,18 +464,20 @@
 		if (request->auth->verbose_debug_passwords &&
 		    result == PASSDB_RESULT_OK) {
 			auth_request_log_debug(request, "password",
-				"Credentials: %s", password);
+				"Credentials: %s",
+				binary_to_hex(credentials, size));
 		}
 		request->private_callback.
-			lookup_credentials(result, password, request);
+			lookup_credentials(result, credentials, size, request);
 	}
 }
 
 void auth_request_lookup_credentials_callback(enum passdb_result result,
-					      const char *password,
+					      const unsigned char *credentials,
+					      size_t size,
 					      struct auth_request *request)
 {
-	const char *scheme;
+	const char *cache_cred, *cache_scheme;
 
 	i_assert(request->state == AUTH_REQUEST_STATE_PASSDB);
 
@@ -488,18 +492,21 @@
 		const char *cache_key = request->passdb->passdb->cache_key;
 
 		if (passdb_cache_lookup_credentials(request, cache_key,
-						    &password, &scheme,
+						    &cache_cred, &cache_scheme,
 						    &result, TRUE)) {
 			auth_request_log_info(request, "passdb",
 				"Fallbacking to expired data from cache");
-			password = result != PASSDB_RESULT_OK ? NULL :
-				passdb_get_credentials(request, password,
-						       scheme);
+		}
+		if (result == PASSDB_RESULT_OK) {
+			if (!passdb_get_credentials(request, cache_cred,
+						    cache_scheme,
+						    &credentials, &size))
+				result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE;
 		}
 	}
 
-	auth_request_lookup_credentials_callback_finish(result, password,
-							request);
+	auth_request_lookup_credentials_finish(result, credentials, size,
+					       request);
 }
 
 void auth_request_lookup_credentials(struct auth_request *request,
@@ -508,6 +515,8 @@
 {
 	struct passdb_module *passdb = request->passdb->passdb;
 	const char *cache_key, *cache_cred, *cache_scheme;
+	const unsigned char *credentials;
+	size_t size;
 	enum passdb_result result;
 
 	i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
@@ -520,11 +529,13 @@
 		if (passdb_cache_lookup_credentials(request, cache_key,
 						    &cache_cred, &cache_scheme,
 						    &result, FALSE)) {
-			cache_cred = result != PASSDB_RESULT_OK ? NULL :
-				passdb_get_credentials(request, cache_cred,
-						       cache_scheme);
-			auth_request_lookup_credentials_callback_finish(
-				result, cache_cred, request);
+			if (result == PASSDB_RESULT_OK &&
+			    !passdb_get_credentials(request, cache_cred,
+						    cache_scheme,
+						    &credentials, &size))
+				result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE;
+			auth_request_lookup_credentials_finish(
+				result, credentials, size, request);
 			return;
 		}
 	}
@@ -539,7 +550,7 @@
 	} else {
 		/* this passdb doesn't support credentials */
 		auth_request_lookup_credentials_callback(
-			PASSDB_RESULT_SCHEME_NOT_AVAILABLE, NULL, request);
+			PASSDB_RESULT_SCHEME_NOT_AVAILABLE, NULL, 0, request);
 	}
 }
 
@@ -1021,6 +1032,9 @@
 				 const char *crypted_password,
 				 const char *scheme, const char *subsystem)
 {
+	const unsigned char *raw_password;
+	size_t raw_password_size;
+	const char *user;
 	int ret;
 
 	if (request->skip_password_check) {
@@ -1034,16 +1048,29 @@
 		return 0;
 	}
 
+	ret = password_decode(crypted_password, scheme,
+			      &raw_password, &raw_password_size);
+	if (ret <= 0) {
+		if (ret < 0) {
+			auth_request_log_error(request, subsystem,
+				"Invalid password format for scheme %s",
+				scheme);
+		} else {
+			auth_request_log_error(request, subsystem,
+					       "Unknown scheme %s", scheme);
+		}
+		return -1;
+	}
+
 	/* If original_username is set, use it. It may be important for some
 	   password schemes (eg. digest-md5). Otherwise the username is used
 	   only for logging purposes. */
-	ret = password_verify(plain_password, crypted_password, scheme,
-			      request->original_username != NULL ?
-			      request->original_username : request->user);
-	if (ret < 0) {
-		auth_request_log_error(request, subsystem,
-				       "Unknown password scheme %s", scheme);
-	} else if (ret == 0) {
+	user = request->original_username != NULL ?
+		request->original_username : request->user;
+	ret = password_verify(plain_password, user, scheme,
+			      raw_password, raw_password_size);
+	i_assert(ret >= 0);
+	if (ret == 0) {
 		auth_request_log_info(request, subsystem,
 				      "Password mismatch");
 		if (request->auth->verbose_debug_passwords) {

Index: auth-request.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-request.h,v
retrieving revision 1.38
retrieving revision 1.39
diff -u -d -r1.38 -r1.39
--- auth-request.h	13 May 2007 08:24:06 -0000	1.38
+++ auth-request.h	13 May 2007 12:17:09 -0000	1.39
@@ -157,7 +157,8 @@
 void auth_request_verify_plain_callback(enum passdb_result result,
 					struct auth_request *request);
 void auth_request_lookup_credentials_callback(enum passdb_result result,
-					      const char *credentials,
+					      const unsigned char *credentials,
+					      size_t size,
 					      struct auth_request *request);
 void auth_request_set_credentials(struct auth_request *request,
 				  const char *scheme, const char *data,

Index: auth-worker-client.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/auth-worker-client.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- auth-worker-client.c	13 May 2007 08:24:06 -0000	1.34
+++ auth-worker-client.c	13 May 2007 12:17:09 -0000	1.35
@@ -1,6 +1,7 @@
 /* Copyright (C) 2005 Timo Sirainen */
 
 #include "common.h"
+#include "base64.h"
 #include "ioloop.h"
 #include "network.h"
 #include "istream.h"
@@ -165,7 +166,8 @@
 }
 
 static void
-lookup_credentials_callback(enum passdb_result result, const char *credentials,
+lookup_credentials_callback(enum passdb_result result,
+			    const unsigned char *credentials, size_t size,
 			    struct auth_request *request)
 {
 	struct auth_worker_client *client = request->context;
@@ -180,8 +182,10 @@
 	if (result != PASSDB_RESULT_OK)
 		str_printfa(str, "FAIL\t%d", result);
 	else {
-		str_printfa(str, "OK\t%s\t{%s}%s\t", request->user,
-			    request->credentials_scheme, credentials);
+		str_printfa(str, "OK\t%s\t{%s.b64}", request->user,
+			    request->credentials_scheme);
+		base64_encode(credentials, size, str);
+		str_append_c(str, '\t');
 		if (request->extra_fields != NULL) {
 			const char *field =
 				auth_stream_reply_export(request->extra_fields);

Index: mech-apop.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-apop.c,v
retrieving revision 1.27
retrieving revision 1.28
diff -u -d -r1.27 -r1.28
--- mech-apop.c	13 May 2007 08:24:06 -0000	1.27
+++ mech-apop.c	13 May 2007 12:17:09 -0000	1.28
@@ -30,14 +30,14 @@
 };
 
 static bool verify_credentials(struct apop_auth_request *request,
-			       const char *credentials)
+			       const unsigned char *credentials, size_t size)
 {
 	unsigned char digest[16];
 	struct md5_context ctx;
 
 	md5_init(&ctx);
 	md5_update(&ctx, request->challenge, strlen(request->challenge));
-	md5_update(&ctx, credentials, strlen(credentials));
+	md5_update(&ctx, credentials, size);
 	md5_final(&ctx, digest);
 
 	return memcmp(digest, request->digest, 16) == 0;
@@ -45,7 +45,7 @@
 
 static void
 apop_credentials_callback(enum passdb_result result,
-			  const char *credentials,
+			  const unsigned char *credentials, size_t size,
 			  struct auth_request *auth_request)
 {
 	struct apop_auth_request *request =
@@ -53,7 +53,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (verify_credentials(request, credentials))
+		if (verify_credentials(request, credentials, size))
 			auth_request_success(auth_request, NULL, 0);
 		else
 			auth_request_fail(auth_request);

Index: mech-cram-md5.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-cram-md5.c,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -d -r1.29 -r1.30
--- mech-cram-md5.c	13 May 2007 10:18:51 -0000	1.29
+++ mech-cram-md5.c	13 May 2007 12:17:09 -0000	1.30
@@ -47,26 +47,20 @@
 }
 
 static bool verify_credentials(struct cram_auth_request *request,
-			       const char *credentials)
+			       const unsigned char *credentials, size_t size)
 {
 	
 	unsigned char digest[MD5_RESULTLEN];
-	unsigned char context_digest[CRAM_MD5_CONTEXTLEN];
         struct hmac_md5_context ctx;
-	buffer_t *context_digest_buf;
 	const char *response_hex;
 
-	context_digest_buf =
-		buffer_create_data(pool_datastack_create(),
-				   context_digest, sizeof(context_digest));
-
-	if (hex_to_binary(credentials, context_digest_buf) < 0) {
+	if (size != CRAM_MD5_CONTEXTLEN) {
                 auth_request_log_error(&request->auth_request, "cram-md5",
-				       "passdb credentials are not in hex");
+				       "invalid credentials length");
 		return FALSE;
 	}
 
-	hmac_md5_set_cram_context(&ctx, context_digest);
+	hmac_md5_set_cram_context(&ctx, credentials);
 	hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
 	hmac_md5_final(&ctx, digest);
 
@@ -109,7 +103,7 @@
 }
 
 static void credentials_callback(enum passdb_result result,
-				 const char *credentials,
+				 const unsigned char *credentials, size_t size,
 				 struct auth_request *auth_request)
 {
 	struct cram_auth_request *request =
@@ -117,7 +111,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (verify_credentials(request, credentials))
+		if (verify_credentials(request, credentials, size))
 			auth_request_success(auth_request, NULL, 0);
 		else
 			auth_request_fail(auth_request);

Index: mech-digest-md5.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-digest-md5.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -u -d -r1.46 -r1.47
--- mech-digest-md5.c	13 May 2007 08:24:06 -0000	1.46
+++ mech-digest-md5.c	13 May 2007 12:17:09 -0000	1.47
@@ -118,26 +118,17 @@
 }
 
 static bool verify_credentials(struct digest_auth_request *request,
-			       const char *credentials)
+			       const unsigned char *credentials, size_t size)
 {
 	struct md5_context ctx;
-	unsigned char digest[16];
+	unsigned char digest[MD5_RESULTLEN];
 	const char *a1_hex, *a2_hex, *response_hex;
-	buffer_t *digest_buf;
 	int i;
 
 	/* get the MD5 password */
-	if (strlen(credentials) != sizeof(digest)*2) {
-                auth_request_log_error(&request->auth_request, "digest-md5",
-				       "passdb credentials' length is wrong");
-		return FALSE;
-	}
-
-	digest_buf = buffer_create_data(pool_datastack_create(),
-					digest, sizeof(digest));
-	if (hex_to_binary(credentials, digest_buf) < 0) {
+	if (size != MD5_RESULTLEN) {
                 auth_request_log_error(&request->auth_request, "digest-md5",
-				       "passdb credentials are not in hex");
+				       "invalid credentials length");
 		return FALSE;
 	}
 
@@ -164,7 +155,7 @@
 
 	/* A1 */
 	md5_init(&ctx);
-	md5_update(&ctx, digest, 16);
+	md5_update(&ctx, credentials, size);
 	md5_update(&ctx, ":", 1);
 	md5_update(&ctx, request->nonce, strlen(request->nonce));
 	md5_update(&ctx, ":", 1);
@@ -519,7 +510,7 @@
 }
 
 static void credentials_callback(enum passdb_result result,
-				 const char *credentials,
+				 const unsigned char *credentials, size_t size,
 				 struct auth_request *auth_request)
 {
 	struct digest_auth_request *request =
@@ -527,7 +518,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (!verify_credentials(request, credentials)) {
+		if (!verify_credentials(request, credentials, size)) {
 			auth_request_fail(auth_request);
 			return;
 		}

Index: mech-ntlm.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-ntlm.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -d -r1.30 -r1.31
--- mech-ntlm.c	13 May 2007 08:24:06 -0000	1.30
+++ mech-ntlm.c	13 May 2007 12:17:09 -0000	1.31
@@ -30,14 +30,18 @@
 	struct ntlmssp_response *response;
 };
 
-static int lm_verify_credentials(struct ntlm_auth_request *request,
-				 const char *credentials)
+static bool lm_verify_credentials(struct ntlm_auth_request *request,
+				  const unsigned char *credentials, size_t size)
 {
 	const unsigned char *client_response;
 	unsigned char lm_response[LM_RESPONSE_SIZE];
-	unsigned char hash[LM_HASH_SIZE];
 	unsigned int response_length;
-	buffer_t *hash_buffer;
+
+	if (size != LM_HASH_SIZE) {
+                auth_request_log_error(&request->auth_request, "lm",
+				       "invalid credentials length");
+		return FALSE;
+	}
 
 	response_length =
 		ntlmssp_buffer_length(request->response, lm_response);
@@ -49,21 +53,13 @@
 		return FALSE;
 	}
 
-	hash_buffer = buffer_create_data(request->auth_request.pool,
-					 hash, sizeof(hash));
-	if (hex_to_binary(credentials, hash_buffer) < 0) {
-                auth_request_log_error(&request->auth_request, "ntlm",
-				       "passdb credentials are not in hex");
-		return FALSE;
-	}
-
-	ntlmssp_v1_response(hash, request->challenge, lm_response);
+	ntlmssp_v1_response(credentials, request->challenge, lm_response);
 	return memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0;
 }
 
 static void
 lm_credentials_callback(enum passdb_result result,
-			const char *credentials,
+			const unsigned char *credentials, size_t size,
 			struct auth_request *auth_request)
 {
 	struct ntlm_auth_request *request =
@@ -71,7 +67,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (lm_verify_credentials(request, credentials))
+		if (lm_verify_credentials(request, credentials, size))
 			auth_request_success(auth_request, NULL, 0);
 		else
 			auth_request_fail(auth_request);
@@ -85,14 +81,13 @@
 	}
 }
 
-static int ntlm_verify_credentials(struct ntlm_auth_request *request,
-				   const char *credentials)
+static int
+ntlm_verify_credentials(struct ntlm_auth_request *request,
+			const unsigned char *credentials, size_t size)
 {
         struct auth_request *auth_request = &request->auth_request;
 	const unsigned char *client_response;
-	unsigned char hash[NTLMSSP_HASH_SIZE];
 	unsigned int response_length;
-	buffer_t *hash_buffer;
 
 	response_length =
 		ntlmssp_buffer_length(request->response, ntlm_response);
@@ -103,12 +98,10 @@
 		return request->ntlm2_negotiated ? -1 : 0;
 	}
 
-	hash_buffer = buffer_create_data(auth_request->pool,
-					 hash, sizeof(hash));
-	if (hex_to_binary(credentials, hash_buffer) < 0) {
+	if (size != NTLMSSP_HASH_SIZE) {
                 auth_request_log_error(&request->auth_request, "ntlm",
-				       "passdb credentials are not in hex");
-		return 0;
+				       "invalid credentials length");
+		return -1;
 	}
 
 	if (response_length > NTLMSSP_RESPONSE_SIZE) {
@@ -121,7 +114,7 @@
 		 * as a standalone server, not as NT domain member.
 		 */
 		ntlmssp_v2_response(auth_request->user, NULL,
-				    hash, request->challenge, blob,
+				    credentials, request->challenge, blob,
 				    response_length - NTLMSSP_V2_RESPONSE_SIZE,
 				    ntlm_v2_response);
 
@@ -133,11 +126,11 @@
 			ntlmssp_buffer_data(request->response, lm_response);
 
 		if (request->ntlm2_negotiated)
-			ntlmssp2_response(hash, request->challenge,
+			ntlmssp2_response(credentials, request->challenge,
 					  client_lm_response,
 					  ntlm_response);
 		else 
-			ntlmssp_v1_response(hash, request->challenge,
+			ntlmssp_v1_response(credentials, request->challenge,
 					    ntlm_response);
 
 		return memcmp(ntlm_response, client_response,
@@ -147,7 +140,7 @@
 
 static void
 ntlm_credentials_callback(enum passdb_result result,
-			  const char *credentials,
+			  const unsigned char *credentials, size_t size,
 			  struct auth_request *auth_request)
 {
 	struct ntlm_auth_request *request =
@@ -156,7 +149,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		ret = ntlm_verify_credentials(request, credentials);
+		ret = ntlm_verify_credentials(request, credentials, size);
 		if (ret > 0) {
 			auth_request_success(auth_request, NULL, 0);
 			return;

Index: mech-otp.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-otp.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- mech-otp.c	13 May 2007 08:24:06 -0000	1.4
+++ mech-otp.c	13 May 2007 12:17:09 -0000	1.5
@@ -17,13 +17,14 @@
 
 static void 
 otp_send_challenge(struct auth_request *auth_request,
-		   const char *credentials)
+		   const unsigned char *credentials, size_t size)
 {
 	struct otp_auth_request *request =
 		(struct otp_auth_request *)auth_request;
 	const char *answer;
 
-	if (otp_parse_dbentry(credentials, &request->state) != 0) {
+	if (otp_parse_dbentry(t_strndup(credentials, size),
+			      &request->state) != 0) {
 		auth_request_log_error(&request->auth_request, "otp",
 				       "invalid OTP data in passdb");
 		auth_request_fail(auth_request);
@@ -56,12 +57,12 @@
 
 static void
 skey_credentials_callback(enum passdb_result result,
-			  const char *credentials,
+			  const unsigned char *credentials, size_t size,
 			  struct auth_request *auth_request)
 {
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		otp_send_challenge(auth_request, credentials);
+		otp_send_challenge(auth_request, credentials, size);
 		break;
 	case PASSDB_RESULT_INTERNAL_FAILURE:
 		auth_request_internal_failure(auth_request);
@@ -74,12 +75,12 @@
 
 static void
 otp_credentials_callback(enum passdb_result result,
-			 const char *credentials,
+			 const unsigned char *credentials, size_t size,
 			 struct auth_request *auth_request)
 {
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		otp_send_challenge(auth_request, credentials);
+		otp_send_challenge(auth_request, credentials, size);
 		break;
 	case PASSDB_RESULT_INTERNAL_FAILURE:
 		auth_request_internal_failure(auth_request);

Index: mech-rpa.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-rpa.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -d -r1.32 -r1.33
--- mech-rpa.c	13 May 2007 11:53:05 -0000	1.32
+++ mech-rpa.c	13 May 2007 12:17:09 -0000	1.33
@@ -438,26 +438,24 @@
 }
 
 static bool verify_credentials(struct rpa_auth_request *request,
-			       const char *credentials)
+			       const unsigned char *credentials, size_t size)
 {
 	unsigned char response[MD5_RESULTLEN];
-	buffer_t *hash_buffer;
 
-	if (strlen(credentials) != sizeof(request->pwd_md5)*2)
-		return FALSE;
-
-	hash_buffer = buffer_create_data(request->pool, request->pwd_md5,
-					 sizeof(request->pwd_md5));
-	if (hex_to_binary(credentials, hash_buffer) < 0)
+	if (size != sizeof(request->pwd_md5)) {
+                auth_request_log_error(&request->auth_request, "rpa",
+				       "invalid credentials length");
 		return FALSE;
+	}
 
+	memcpy(request->pwd_md5, credentials, sizeof(request->pwd_md5));
 	rpa_user_response(request, response);
 	return memcmp(response, request->user_response, sizeof(response)) == 0;
 }
 
 static void
 rpa_credentials_callback(enum passdb_result result,
-			 const char *credentials,
+			 const unsigned char *credentials, size_t size,
 			 struct auth_request *auth_request)
 {
 	struct rpa_auth_request *request =
@@ -467,7 +465,7 @@
 
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		if (!verify_credentials(request, credentials))
+		if (!verify_credentials(request, credentials, size))
 			auth_request_fail(auth_request);
 		else {
 			token4 = mech_rpa_build_token4(request, &token4_size);

Index: mech-skey.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/mech-skey.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- mech-skey.c	13 May 2007 08:24:06 -0000	1.4
+++ mech-skey.c	13 May 2007 12:17:09 -0000	1.5
@@ -17,13 +17,14 @@
 
 static void 
 skey_send_challenge(struct auth_request *auth_request,
-		    const char *credentials)
+		    const unsigned char *credentials, size_t size)
 {
 	struct otp_auth_request *request =
 		(struct otp_auth_request *)auth_request;
 	const char *answer;
 
-	if (otp_parse_dbentry(credentials, &request->state) != 0) {
+	if (otp_parse_dbentry(t_strndup(credentials, size),
+			      &request->state) != 0) {
 		auth_request_log_error(&request->auth_request, "skey",
 				       "invalid OTP data in passdb");
 		auth_request_fail(auth_request);
@@ -62,12 +63,12 @@
 
 static void
 otp_credentials_callback(enum passdb_result result,
-			 const char *credentials,
+			 const unsigned char *credentials, size_t size,
 			 struct auth_request *auth_request)
 {
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		skey_send_challenge(auth_request, credentials);
+		skey_send_challenge(auth_request, credentials, size);
 		break;
 	case PASSDB_RESULT_INTERNAL_FAILURE:
 		auth_request_internal_failure(auth_request);
@@ -80,12 +81,12 @@
 
 static void
 skey_credentials_callback(enum passdb_result result,
-			  const char *credentials,
+			  const unsigned char *credentials, size_t size,
 			  struct auth_request *auth_request)
 {
 	switch (result) {
 	case PASSDB_RESULT_OK:
-		skey_send_challenge(auth_request, credentials);
+		skey_send_challenge(auth_request, credentials, size);
 		break;
 	case PASSDB_RESULT_INTERNAL_FAILURE:
 		auth_request_internal_failure(auth_request);

Index: passdb-ldap.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/passdb-ldap.c,v
retrieving revision 1.62
retrieving revision 1.63
diff -u -d -r1.62 -r1.63
--- passdb-ldap.c	13 May 2007 08:24:06 -0000	1.62
+++ passdb-ldap.c	13 May 2007 12:17:09 -0000	1.63
@@ -174,7 +174,7 @@
 	}
 
 	if (auth_request->credentials_scheme != NULL) {
-		request->callback.lookup_credentials(passdb_result, NULL,
+		request->callback.lookup_credentials(passdb_result, NULL, 0,
 						     auth_request);
 	} else {
 		request->callback.verify_plain(passdb_result, auth_request);

Index: passdb-passwd-file.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/passdb-passwd-file.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -d -r1.31 -r1.32
--- passdb-passwd-file.c	8 Nov 2006 20:22:08 -0000	1.31
+++ passdb-passwd-file.c	13 May 2007 12:17:09 -0000	1.32
@@ -102,7 +102,7 @@
 
 	pu = db_passwd_file_lookup(module->pwf, request);
 	if (pu == NULL) {
-		callback(PASSDB_RESULT_USER_UNKNOWN, NULL, request);
+		callback(PASSDB_RESULT_USER_UNKNOWN, NULL, 0, request);
 		return;
 	}
 

Index: passdb.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/passdb.c,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -d -r1.50 -r1.51
--- passdb.c	13 May 2007 08:24:06 -0000	1.50
+++ passdb.c	13 May 2007 12:17:09 -0000	1.51
@@ -53,32 +53,53 @@
 	NULL
 };
 
-const char *
-passdb_get_credentials(struct auth_request *auth_request,
-		       const char *password, const char *scheme)
+bool passdb_get_credentials(struct auth_request *auth_request,
+			    const char *input, const char *input_scheme,
+			    const unsigned char **credentials_r, size_t *size_r)
 {
 	const char *wanted_scheme = auth_request->credentials_scheme;
+	const char *plaintext;
+	int ret;
 
-	if (strcasecmp(wanted_scheme, "CRYPT") == 0) {
+	if (*wanted_scheme == '\0') {
 		/* anything goes */
-		return t_strdup_printf("{%s}%s", scheme, password);
+		*credentials_r = (const unsigned char *)input;
+		*size_r = strlen(input);
+		return TRUE;
 	}
 
-	if (!password_scheme_is_alias(scheme, wanted_scheme)) {
-		if (!password_scheme_is_alias(scheme, "PLAIN")) {
+	ret = password_decode(input, input_scheme, credentials_r, size_r);
+	if (ret <= 0) {
+		if (ret < 0) {
+			auth_request_log_error(auth_request, "password",
+				"Invalid password format for scheme %s",
+				input_scheme);
+		} else {
+			auth_request_log_error(auth_request, "password",
+				"Unknown scheme %s", input_scheme);
+		}
+		return FALSE;
+	}
+
+	if (!password_scheme_is_alias(input_scheme, wanted_scheme)) {
+		if (!password_scheme_is_alias(input_scheme, "PLAIN")) {
 			auth_request_log_info(auth_request, "password",
 				"Requested %s scheme, but we have only %s",
-				wanted_scheme, scheme);
-			return NULL;
+				wanted_scheme, input_scheme);
+			return FALSE;
 		}
 
 		/* we can generate anything out of plaintext passwords */
-		password = password_generate(password, auth_request->user,
-					     wanted_scheme);
-		i_assert(password != NULL);
+		plaintext = t_strndup(*credentials_r, *size_r);
+		if (!password_generate(plaintext, auth_request->user,
+				       wanted_scheme, credentials_r, size_r)) {
+			auth_request_log_error(auth_request, "password",
+				"Requested unknown scheme %s", wanted_scheme);
+			return FALSE;
+		}
 	}
 
-	return password;
+	return TRUE;
 }
 
 void passdb_handle_credentials(enum passdb_result result,
@@ -86,16 +107,20 @@
 			       lookup_credentials_callback_t *callback,
                                struct auth_request *auth_request)
 {
+	const unsigned char *credentials;
+	size_t size = 0;
+
 	if (result != PASSDB_RESULT_OK) {
-		callback(result, NULL, auth_request);
+		callback(result, NULL, 0, auth_request);
 		return;
 	}
 
-	password = password == NULL ? NULL :
-		passdb_get_credentials(auth_request, password, scheme);
-	if (password == NULL)
+	if (password == NULL ||
+	    !passdb_get_credentials(auth_request, password, scheme,
+				    &credentials, &size))
 		result = PASSDB_RESULT_SCHEME_NOT_AVAILABLE;
-	callback(result, password, auth_request);
+
+	callback(result, credentials, size, auth_request);
 }
 
 struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver,

Index: passdb.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/passdb.h,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -d -r1.40 -r1.41
--- passdb.h	13 May 2007 08:24:06 -0000	1.40
+++ passdb.h	13 May 2007 12:17:09 -0000	1.41
@@ -21,7 +21,8 @@
 typedef void verify_plain_callback_t(enum passdb_result result,
 				     struct auth_request *request);
 typedef void lookup_credentials_callback_t(enum passdb_result result,
-					   const char *password,
+					   const unsigned char *credentials,
+					   size_t size,
 					   struct auth_request *request);
 typedef void set_credentials_callback_t(enum passdb_result result,
 					struct auth_request *request);
@@ -62,9 +63,17 @@
 	struct passdb_module_interface iface;
 };
 
-const char *
-passdb_get_credentials(struct auth_request *auth_request,
-		       const char *password, const char *scheme);
+/* Try to get credentials in wanted scheme (request->credentials_scheme) from
+   given input. Returns FALSE if this wasn't possible (unknown scheme,
+   conversion not possible or invalid credentials).
+
+   If wanted scheme is "", the credentials are returned as-is without any
+   checks. This is useful mostly just to see if there exist any credentials
+   at all. */
+bool passdb_get_credentials(struct auth_request *auth_request,
+			    const char *input, const char *input_scheme,
+			    const unsigned char **credentials_r,
+			    size_t *size_r);
 
 void passdb_handle_credentials(enum passdb_result result,
 			       const char *password, const char *scheme,

--- password-scheme-cram-md5.c DELETED ---

--- password-scheme-ntlm.c DELETED ---

Index: password-scheme-rpa.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/password-scheme-rpa.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- password-scheme-rpa.c	8 Oct 2006 23:18:18 -0000	1.3
+++ password-scheme-rpa.c	13 May 2007 12:17:09 -0000	1.4
@@ -24,17 +24,12 @@
 	return buffer_free_without_data(buf);
 }
 
-const char *password_generate_rpa(const char *pw)
+void password_generate_rpa(const char *pw, unsigned char result[])
 {
-	unsigned char hash[MD5_RESULTLEN];
 	unsigned char *ucs2be_pw;
 	size_t size;
 
 	ucs2be_pw = ucs2be_str(unsafe_data_stack_pool, pw, &size);
-
-	md5_get_digest(ucs2be_pw, size, hash);
-
+	md5_get_digest(ucs2be_pw, size, result);
 	safe_memset(ucs2be_pw, 0, size);
-
-	return binary_to_hex(hash, sizeof(hash));
 }

Index: password-scheme.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/password-scheme.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -d -r1.33 -r1.34
--- password-scheme.c	13 May 2007 11:30:16 -0000	1.33
+++ password-scheme.c	13 May 2007 12:17:09 -0000	1.34
@@ -6,6 +6,8 @@
 #include "hex-binary.h"
 #include "md4.h"
 #include "md5.h"
+#include "hmac-md5.h"
+#include "ntlm.h"
 #include "module-dir.h"
 #include "mycrypt.h"
 #include "randgen.h"
@@ -23,20 +25,65 @@
 static struct module *scheme_modules;
 #endif
 
-int password_verify(const char *plaintext, const char *password,
-		    const char *scheme, const char *user)
+/* Lookup scheme and encoding by given name. The encoding is taken from
+   ".base64", ".b64" or ".hex" suffix if it exists, otherwise the default
+   encoding is used. */
+static const struct password_scheme *
+password_scheme_lookup(const char *scheme, enum password_encoding *encoding_r)
 {
 	const struct password_scheme *s;
+	const char *encoding = NULL;
+	unsigned int scheme_len;
 
-	if (password == NULL)
-		return 0;
+	*encoding_r = PW_ENCODING_NONE;
+
+	for (scheme_len = 0; scheme[scheme_len] != '\0'; scheme_len++) {
+		if (scheme[scheme_len] == '.') {
+			encoding = scheme + scheme_len + 1;
+			break;
+		}
+	}
 
 	for (s = schemes; s->name != NULL; s++) {
-		if (strcasecmp(s->name, scheme) == 0)
-			return s->password_verify(plaintext, password, user);
+		if (strncasecmp(s->name, scheme, scheme_len) == 0 &&
+		    s->name[scheme_len] == '\0') {
+			if (encoding == NULL)
+				*encoding_r = s->default_encoding;
+			else if (strcasecmp(encoding, "b64") == 0 ||
+				 strcasecmp(encoding, "base64") == 0)
+				*encoding_r = PW_ENCODING_BASE64;
+			else if (strcasecmp(encoding, "hex") == 0)
+				*encoding_r = PW_ENCODING_HEX;
+			else {
+				/* unknown encoding. treat as invalid scheme. */
+				return NULL;
+			}
+			return s;
+		}
 	}
+	return NULL;
+}
 
-	return -1;
+int password_verify(const char *plaintext, const char *user, const char *scheme,
+		    const unsigned char *raw_password, size_t size)
+{
+	const struct password_scheme *s;
+	enum password_encoding encoding;
+	const unsigned char *generated;
+	size_t generated_size;
+
+	s = password_scheme_lookup(scheme, &encoding);
+	if (s == NULL)
+		return -1;
+
+	if (s->password_verify != NULL)
+		return s->password_verify(plaintext, user, raw_password, size);
+
+	/* generic verification handler: generate the password and compare it
+	   to the one in database */
+	s->password_generate(plaintext, user, &generated, &generated_size);
+	return size != generated_size ? 0 :
+		memcmp(generated, raw_password, size) == 0;
 }
 
 const char *password_list_schemes(const struct password_scheme **listptr)
@@ -52,25 +99,6 @@
 	return (*listptr)++->name;
 }
 
-bool password_scheme_is_alias(const char *scheme1, const char *scheme2)
-{
-	const struct password_scheme *s, *s1 = NULL, *s2 = NULL;
-
-	if (strcasecmp(scheme1, scheme2) == 0)
-		return TRUE;
-
-	for (s = schemes; s->name != NULL; s++) {
-		if (strcasecmp(s->name, scheme1) == 0)
-			s1 = s;
-		else if (strcasecmp(s->name, scheme2) == 0)
-			s2 = s;
-	}
-
-	/* if they've the same verify function, they're equivalent */
-	return s1 != NULL && s2 != NULL &&
-		s1->password_verify == s2->password_verify;
-}
-
 const char *password_get_scheme(const char **password)
 {
 	const char *p, *scheme;
@@ -102,421 +130,447 @@
 	return scheme;
 }
 
-const char *password_generate(const char *plaintext, const char *user,
-			      const char *scheme)
+int password_decode(const char *password, const char *scheme,
+		    const unsigned char **raw_password_r, size_t *size_r)
 {
 	const struct password_scheme *s;
+	enum password_encoding encoding;
+	buffer_t *buf;
+	unsigned int len;
 
-	for (s = schemes; s->name != NULL; s++) {
-		if (strcasecmp(s->name, scheme) == 0)
-			return s->password_generate(plaintext, user);
+	s = password_scheme_lookup(scheme, &encoding);
+	if (s == NULL)
+		return 0;
+
+	len = strlen(password);
+	if (encoding != PW_ENCODING_NONE && s->raw_password_len != 0 &&
+	    strchr(scheme, '.') == NULL) {
+		/* encoding not specified. we can autodetect between
+		   base64 and hex encodings. */
+		encoding = len == s->raw_password_len * 2 ? PW_ENCODING_HEX :
+			PW_ENCODING_BASE64;
 	}
 
-	return NULL;
+	switch (encoding) {
+	case PW_ENCODING_NONE:
+		*raw_password_r = (const unsigned char *)password;
+		*size_r = len;
+		break;
+	case PW_ENCODING_BASE64:
+		buf = buffer_create_static_hard(pool_datastack_create(),
+						MAX_BASE64_DECODED_SIZE(len));
+		if (base64_decode(password, len, NULL, buf) < 0)
+			return -1;
+
+		*raw_password_r = buf->data;
+		*size_r = buf->used;
+		break;
+	case PW_ENCODING_HEX:
+		buf = buffer_create_static_hard(pool_datastack_create(),
+						len / 2 + 1);
+		if (hex_to_binary(password, buf) < 0)
+			return -1;
+
+		*raw_password_r = buf->data;
+		*size_r = buf->used;
+		break;
+	}
+	if (s->raw_password_len != *size_r && s->raw_password_len != 0) {
+		/* password has invalid length */
+		return -1;
+	}
+	return 1;
 }
 
-static bool crypt_verify(const char *plaintext, const char *password,
-			 const char *user __attr_unused__)
+bool password_generate(const char *plaintext, const char *user,
+		       const char *scheme,
+		       const unsigned char **raw_password_r, size_t *size_r)
 {
-	return strcmp(mycrypt(plaintext, password), password) == 0;
+	const struct password_scheme *s;
+	enum password_encoding encoding;
+
+	s = password_scheme_lookup(scheme, &encoding);
+	if (s == NULL)
+		return FALSE;
+
+	s->password_generate(plaintext, user, raw_password_r, size_r);
+	return TRUE;
 }
 
-static const char *crypt_generate(const char *plaintext,
-				  const char *user __attr_unused__)
+bool password_generate_encoded(const char *plaintext, const char *user,
+			       const char *scheme, const char **password_r)
 {
-	char salt[9];
+	const struct password_scheme *s;
+	const unsigned char *raw_password;
+	enum password_encoding encoding;
+	string_t *str;
+	size_t size;
 
-	random_fill(salt, 2);
-	salt[0] = salt_chars[salt[0] % (sizeof(salt_chars)-1)];
-	salt[1] = salt_chars[salt[1] % (sizeof(salt_chars)-1)];
-	salt[2] = '\0';
-	return t_strdup(mycrypt(plaintext, salt));
+	s = password_scheme_lookup(scheme, &encoding);
+	if (s == NULL)
+		return FALSE;
+
+	s->password_generate(plaintext, user, &raw_password, &size);
+	switch (encoding) {
+	case PW_ENCODING_NONE:
+		*password_r = t_strndup(raw_password, size);
+		break;
+	case PW_ENCODING_BASE64:
+		str = t_str_new(MAX_BASE64_ENCODED_SIZE(size) + 1);
+		base64_encode(raw_password, size, str);
+		*password_r = str_c(str);
+		break;
+	case PW_ENCODING_HEX:
+		*password_r = binary_to_hex(raw_password, size);
+		break;
+	}
+	return TRUE;
 }
 
-static bool md5_crypt_verify(const char *plaintext, const char *password,
-			     const char *user __attr_unused__)
+bool password_scheme_is_alias(const char *scheme1, const char *scheme2)
 {
-	const char *str;
+	const struct password_scheme *s, *s1 = NULL, *s2 = NULL;
 
-	str = password_generate_md5_crypt(plaintext, password);
-	return strcmp(str, password) == 0;
+	scheme1 = t_strcut(scheme1, '.');
+	scheme2 = t_strcut(scheme2, '.');
+
+	if (strcasecmp(scheme1, scheme2) == 0)
+		return TRUE;
+
+	for (s = schemes; s->name != NULL; s++) {
+		if (strcasecmp(s->name, scheme1) == 0)
+			s1 = s;
+		else if (strcasecmp(s->name, scheme2) == 0)
+			s2 = s;
+	}
+
+	/* if they've the same generate function, they're equivalent */
+	return s1 != NULL && s2 != NULL &&
+		s1->password_generate == s2->password_generate;
 }
 
-static const char *md5_crypt_generate(const char *plaintext,
-				      const char *user __attr_unused__)
+static bool
+crypt_verify(const char *plaintext, const char *user __attr_unused__,
+	     const unsigned char *raw_password, size_t size)
 {
-	char salt[9];
-	int i;
+	const char *password;
 
-	random_fill(salt, 8);
-	for (i = 0; i < 8; i++)
-		salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)];
-	salt[8] = '\0';
-	return password_generate_md5_crypt(plaintext, salt);
+	password = t_strndup(raw_password, size);
+	return strcmp(mycrypt(plaintext, password), password) == 0;
 }
 
-static const char *sha1_generate(const char *plaintext,
-				const char *user __attr_unused__)
+static void
+crypt_generate(const char *plaintext, const char *user __attr_unused__,
+	       const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char digest[SHA1_RESULTLEN];
-	string_t *str;
+	char salt[3];
+	const char *password;
 
-	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);
+	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';
+
+	password = t_strdup(mycrypt(plaintext, salt));
+	*raw_password_r = (const unsigned char *)password;
+	*size_r = strlen(password);
 }
 
-static const void *
-password_decode(const char *password, unsigned int result_len)
+static bool
+md5_crypt_verify(const char *plaintext, const char *user __attr_unused__,
+		 const unsigned char *raw_password, size_t size)
 {
-	buffer_t *buf;
-	size_t len;
+	const char *password, *str;
 
-	len = strlen(password);
-	if (len == result_len*2) {
-		/* hex-encoded */
-		buf = buffer_create_static_hard(pool_datastack_create(),
-						result_len);
+	password = t_strndup(raw_password, size);
+	str = password_generate_md5_crypt(plaintext, password);
+	return strcmp(str, password) == 0;
+}
 
-		if (hex_to_binary(password, buf) < 0)
-			return NULL;
-	} else {
-		/* base64-encoded */
-		buf = buffer_create_static_hard(pool_datastack_create(),
-						MAX_BASE64_DECODED_SIZE(len));
+static void
+md5_crypt_generate(const char *plaintext, const char *user __attr_unused__,
+		   const unsigned char **raw_password_r, size_t *size_r)
+{
+	const char *password;
+	char salt[9];
+	unsigned int i;
 
-		if (base64_decode(password, len, NULL, buf) < 0)
-			return NULL;
-	}
+	random_fill(salt, sizeof(salt)-1);
+	for (i = 0; i < sizeof(salt)-1; i++)
+		salt[i] = salt_chars[salt[i] % (sizeof(salt_chars)-1)];
+	salt[sizeof(salt)-1] = '\0';
 
-	return buf->used != result_len ? NULL : buf->data;
+	password = password_generate_md5_crypt(plaintext, salt);
+	*raw_password_r = (const unsigned char *)password;
+	*size_r = strlen(password);
 }
 
-static bool sha1_verify(const char *plaintext, const char *password,
-			const char *user)
+static void
+sha1_generate(const char *plaintext, const char *user __attr_unused__,
+	      const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char sha1_digest[SHA1_RESULTLEN];
-	const char *data;
-
-	sha1_get_digest(plaintext, strlen(plaintext), sha1_digest);
+	unsigned char *digest;
 
-	data = password_decode(password, SHA1_RESULTLEN);
-	if (data == NULL) {
-		i_error("sha1_verify(%s): Invalid password encoding", user);
-		return 0;
-	}
+	digest = t_malloc(SHA1_RESULTLEN);
+	sha1_get_digest(plaintext, strlen(plaintext), digest);
 
-	return memcmp(sha1_digest, data, SHA1_RESULTLEN) == 0;
+	*raw_password_r = digest;
+	*size_r = SHA1_RESULTLEN;
 }
 
-static const char *ssha_generate(const char *plaintext,
-				 const char *user __attr_unused__)
+static void
+ssha_generate(const char *plaintext, const char *user __attr_unused__,
+	      const unsigned char **raw_password_r, size_t *size_r)
 {
 #define SSHA_SALT_LEN 4
-	unsigned char ssha_digest[SHA1_RESULTLEN + SSHA_SALT_LEN];
-	unsigned char *salt = &ssha_digest[SHA1_RESULTLEN];
+	unsigned char *digest, *salt;
 	struct sha1_ctxt ctx;
-	string_t *str;
 
+	digest = t_malloc(SHA1_RESULTLEN + SSHA_SALT_LEN);
+	salt = digest + SHA1_RESULTLEN;
 	random_fill(salt, SSHA_SALT_LEN);
 
 	sha1_init(&ctx);
 	sha1_loop(&ctx, plaintext, strlen(plaintext));
 	sha1_loop(&ctx, salt, SSHA_SALT_LEN);
-	sha1_result(&ctx, ssha_digest);
+	sha1_result(&ctx, 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);
+	*raw_password_r = digest;
+	*size_r = SHA1_RESULTLEN + SSHA_SALT_LEN;
 }
 
-static bool ssha_verify(const char *plaintext, const char *password,
-			const char *user __attr_unused__)
+static bool ssha_verify(const char *plaintext, const char *user,
+			const unsigned char *raw_password, size_t size)
 {
 	unsigned char sha1_digest[SHA1_RESULTLEN];
-	buffer_t *buf;
-	const char *data;
-	size_t size, password_len;
 	struct sha1_ctxt ctx;
 
-	/* format: base64-encoded MD5 hash and salt */
-	password_len = strlen(password);
-	buf = buffer_create_static_hard(pool_datastack_create(),
-					MAX_BASE64_DECODED_SIZE(password_len));
-
-	if (base64_decode(password, password_len, NULL, buf) < 0) {
-		i_error("ssha_verify(%s): failed decoding SSHA base64", user);
-		return 0;
-	}
-
-	data = buffer_get_data(buf, &size);
+	/* format: <SHA1 hash><salt> */
 	if (size <= SHA1_RESULTLEN) {
-		i_error("ssha_verify(%s): invalid SSHA base64 decode", user);
-		return 0;
+		i_error("ssha_verify(%s): SSHA password too short", user);
+		return FALSE;
 	}
 
 	sha1_init(&ctx);
 	sha1_loop(&ctx, plaintext, strlen(plaintext));
-	sha1_loop(&ctx, &data[SHA1_RESULTLEN], size-SHA1_RESULTLEN);
+	sha1_loop(&ctx, raw_password + SHA1_RESULTLEN, size - SHA1_RESULTLEN);
 	sha1_result(&ctx, sha1_digest);
-	return memcmp(sha1_digest, data, SHA1_RESULTLEN) == 0;
+	return memcmp(sha1_digest, raw_password, SHA1_RESULTLEN) == 0;
 }
 
-static const char *smd5_generate(const char *plaintext,
-				 const char *user __attr_unused__)
+static void
+smd5_generate(const char *plaintext, const char *user __attr_unused__,
+	      const unsigned char **raw_password_r, size_t *size_r)
 {
 #define SMD5_SALT_LEN 4
-	unsigned char smd5_digest[MD5_RESULTLEN + SMD5_SALT_LEN];
-	unsigned char *salt = &smd5_digest[MD5_RESULTLEN];
+	unsigned char *digest, *salt;
 	struct md5_context ctx;
-	string_t *str;
 
+	digest = t_malloc(SHA1_RESULTLEN + SSHA_SALT_LEN);
+	salt = digest + SHA1_RESULTLEN;
 	random_fill(salt, SMD5_SALT_LEN);
 
 	md5_init(&ctx);
 	md5_update(&ctx, plaintext, strlen(plaintext));
 	md5_update(&ctx, salt, SMD5_SALT_LEN);
-	md5_final(&ctx, smd5_digest);
+	md5_final(&ctx, 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);
+	*raw_password_r = digest;
+	*size_r = SHA1_RESULTLEN + SSHA_SALT_LEN;
 }
 
-static bool smd5_verify(const char *plaintext, const char *password,
-			const char *user __attr_unused__)
+static bool smd5_verify(const char *plaintext, const char *user,
+			const unsigned char *raw_password, size_t size)
 {
 	unsigned char md5_digest[MD5_RESULTLEN];
-	buffer_t *buf;
-	const char *data;
-	size_t size, password_len;
 	struct md5_context ctx;
 
-	/* format: base64-encoded MD5 hash and salt */
-	password_len = strlen(password);
-	buf = buffer_create_static_hard(pool_datastack_create(),
-					MAX_BASE64_DECODED_SIZE(password_len));
-
-	if (base64_decode(password, password_len, NULL, buf) < 0) {
-		i_error("smd5_verify(%s): failed decoding SMD5 base64", user);
-		return 0;
-	}
-
-	data = buffer_get_data(buf, &size);
+	/* format: <MD5 hash><salt> */
 	if (size <= MD5_RESULTLEN) {
-		i_error("smd5_verify(%s): invalid SMD5 base64 decode", user);
-		return 0;
+		i_error("smd5_verify(%s): SMD5 password too short", user);
+		return FALSE;
 	}
 
 	md5_init(&ctx);
 	md5_update(&ctx, plaintext, strlen(plaintext));
-	md5_update(&ctx, &data[MD5_RESULTLEN], size-MD5_RESULTLEN);
+	md5_update(&ctx, raw_password + MD5_RESULTLEN, size - MD5_RESULTLEN);
 	md5_final(&ctx, md5_digest);
-	return memcmp(md5_digest, data, MD5_RESULTLEN) == 0;
-}
-
-static bool plain_verify(const char *plaintext, const char *password,
-			 const char *user __attr_unused__)
-{
-	return strcmp(password, plaintext) == 0;
-}
-
-static const char *plain_generate(const char *plaintext,
-				  const char *user __attr_unused__)
-{
-	return plaintext;
-}
-
-static bool cram_md5_verify(const char *plaintext, const char *password,
-			    const char *user __attr_unused__)
-{
-	return strcmp(password_generate_cram_md5(plaintext), password) == 0;
+	return memcmp(md5_digest, raw_password, MD5_RESULTLEN) == 0;
 }
 
-static const char *cram_md5_generate(const char *plaintext,
-				     const char *user __attr_unused__)
+static void
+plain_generate(const char *plaintext, const char *user __attr_unused__,
+	       const unsigned char **raw_password_r, size_t *size_r)
 {
-	return password_generate_cram_md5(plaintext);
+	*raw_password_r = (const unsigned char *)plaintext,
+	*size_r = strlen(plaintext);
 }
 
-static bool digest_md5_verify(const char *plaintext, const char *password,
-			      const char *user)
+static void
+cram_md5_generate(const char *plaintext, const char *user __attr_unused__,
+		  const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char digest[MD5_RESULTLEN];
-	const char *realm, *str;
-
-	/* user:realm:passwd */
-	realm = strchr(user, '@');
-	if (realm != NULL) realm++; else realm = "";
+	struct hmac_md5_context ctx;
+	unsigned char *context_digest;
 
-	str = t_strconcat(t_strcut(user, '@'), ":", realm,  ":",
-			  plaintext, NULL);
-	md5_get_digest(str, strlen(str), digest);
-	str = binary_to_hex(digest, sizeof(digest));
+	context_digest = t_malloc(CRAM_MD5_CONTEXTLEN);
+	hmac_md5_init(&ctx, (const unsigned char *)plaintext,
+		      strlen(plaintext));
+	hmac_md5_get_cram_context(&ctx, context_digest);
 
-	return strcasecmp(str, password) == 0;
+	*raw_password_r = context_digest;
+	*size_r = CRAM_MD5_CONTEXTLEN;
 }
 
-static const char *digest_md5_generate(const char *plaintext, const char *user)
+static void
+digest_md5_generate(const char *plaintext, const char *user,
+		    const unsigned char **raw_password_r, size_t *size_r)
 {
 	const char *realm, *str;
-	unsigned char digest[MD5_RESULTLEN];
+	unsigned char *digest;
 
 	if (user == NULL)
-		i_fatal("digest_md5_generate(): username not given");
+		i_panic("digest_md5_generate(): username not given");
 
 	/* user:realm:passwd */
 	realm = strchr(user, '@');
 	if (realm != NULL) realm++; else realm = "";
 
-	str = t_strconcat(t_strcut(user, '@'), ":", realm,  ":",
-			  plaintext, NULL);
+	digest = t_malloc(MD5_RESULTLEN);
+	str = t_strdup_printf("%s:%s:%s", t_strcut(user, '@'),
+			      realm, plaintext);
 	md5_get_digest(str, strlen(str), digest);
-	return binary_to_hex(digest, sizeof(digest));
-}
 
-static bool plain_md4_verify(const char *plaintext, const char *password,
-			     const char *user)
-{
-	unsigned char digest[MD4_RESULTLEN];
-	const void *data;
-
-	md4_get_digest(plaintext, strlen(plaintext), digest);
-
-	data = password_decode(password, MD4_RESULTLEN);
-	if (data == NULL) {
-		i_error("plain_md4_verify(%s): Invalid password encoding",
-			user);
-		return 0;
-	}
-	return memcmp(digest, data, MD4_RESULTLEN) == 0;
+	*raw_password_r = digest;
+	*size_r = MD5_RESULTLEN;
 }
 
-static const char *plain_md4_generate(const char *plaintext,
-				      const char *user __attr_unused__)
+static void
+plain_md4_generate(const char *plaintext, const char *user __attr_unused__,
+		   const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char digest[MD4_RESULTLEN];
+	unsigned char *digest;
 
+	digest = t_malloc(MD4_RESULTLEN);
 	md4_get_digest(plaintext, strlen(plaintext), digest);
-	return binary_to_hex(digest, sizeof(digest));
+
+	*raw_password_r = digest;
+	*size_r = MD4_RESULTLEN;
 }
 
-static bool plain_md5_verify(const char *plaintext, const char *password,
-			     const char *user)
+static void
+plain_md5_generate(const char *plaintext, const char *user __attr_unused__,
+		   const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char digest[MD5_RESULTLEN];
-	const void *data;
+	unsigned char *digest;
 
+	digest = t_malloc(MD5_RESULTLEN);
 	md5_get_digest(plaintext, strlen(plaintext), digest);
 
-	data = password_decode(password, MD5_RESULTLEN);
-	if (data == NULL) {
-		i_error("plain_md5_verify(%s): Invalid password encoding",
-			user);
-		return 0;
-	}
-	return memcmp(digest, data, MD5_RESULTLEN) == 0;
+	*raw_password_r = digest;
+	*size_r = MD5_RESULTLEN;
 }
 
-static const char *plain_md5_generate(const char *plaintext,
-				      const char *user __attr_unused__)
+static void
+lm_generate(const char *plaintext, const char *user __attr_unused__,
+	    const unsigned char **raw_password_r, size_t *size_r)
 {
-	unsigned char digest[MD5_RESULTLEN];
-
-	md5_get_digest(plaintext, strlen(plaintext), digest);
-	return binary_to_hex(digest, sizeof(digest));
-}
+	unsigned char *digest;
 
-static const char *ldap_md5_generate(const char *plaintext,
-				     const char *user __attr_unused__)
-{
-	unsigned char digest[MD5_RESULTLEN];
-	string_t *str;
+	digest = t_malloc(LM_HASH_SIZE);
+	lm_hash(plaintext, digest);
 
-	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);
+	*raw_password_r = digest;
+	*size_r = LM_HASH_SIZE;
 }
 
-static bool lm_verify(const char *plaintext, const char *password,
-		      const char *user __attr_unused__)
+static void
+ntlm_generate(const char *plaintext, const char *user __attr_unused__,
+	      const unsigned char **raw_password_r, size_t *size_r)
 {
-	return strcasecmp(password, password_generate_lm(plaintext)) == 0;
-}
+	unsigned char *digest;
 
-static const char *lm_generate(const char *plaintext,
-				 const char *user __attr_unused__)
-{
-	return password_generate_lm(plaintext);
-}
+	digest = t_malloc(NTLMSSP_HASH_SIZE);
+	ntlm_v1_hash(plaintext, digest);
 
-static bool ntlm_verify(const char *plaintext, const char *password,
-			const char *user __attr_unused__)
-{
-	return strcasecmp(password, password_generate_ntlm(plaintext)) == 0;
+	*raw_password_r = digest;
+	*size_r = NTLMSSP_HASH_SIZE;
 }
 
-static const char *ntlm_generate(const char *plaintext,
-				 const char *user __attr_unused__)
+static bool otp_verify(const char *plaintext, const char *user __attr_unused__,
+		       const unsigned char *raw_password, size_t size)
 {
-	return password_generate_ntlm(plaintext);
-}
+	const char *password;
 
-static bool otp_verify(const char *plaintext, const char *password,
-		       const char *user __attr_unused__)
-{
+	password = t_strndup(raw_password, size);
 	return strcasecmp(password,
 		password_generate_otp(plaintext, password, -1)) == 0;
 }
 
-static const char *otp_generate(const char *plaintext,
-				const char *user __attr_unused__)
+static void
+otp_generate(const char *plaintext, const char *user __attr_unused__,
+	     const unsigned char **raw_password_r, size_t *size_r)
 {
-	return password_generate_otp(plaintext, NULL, OTP_HASH_SHA1);
-}
+	const char *password;
 
-static const char *skey_generate(const char *plaintext,
-				 const char *user __attr_unused__)
-{
-	return password_generate_otp(plaintext, NULL, OTP_HASH_MD4);
+	password = password_generate_otp(plaintext, NULL, OTP_HASH_SHA1);
+	*raw_password_r = (const unsigned char *)password;
+	*size_r = strlen(password);
 }
 
-static bool rpa_verify(const char *plaintext, const char *password,
-		       const char *user __attr_unused__)
+static void
+skey_generate(const char *plaintext, const char *user __attr_unused__,
+	      const unsigned char **raw_password_r, size_t *size_r)
 {
-	return strcasecmp(password, password_generate_rpa(plaintext)) == 0;
+	const char *password;
+
+	password = password_generate_otp(plaintext, NULL, OTP_HASH_MD4);
+	*raw_password_r = (const unsigned char *)password;
+	*size_r = strlen(password);
 }
 
-static const char *rpa_generate(const char *plaintext,
-				const char *user __attr_unused__)
+static void
+rpa_generate(const char *plaintext, const char *user __attr_unused__,
+	     const unsigned char **raw_password_r, size_t *size_r)
 {
-	return password_generate_rpa(plaintext);
+	unsigned char *digest;
+
+	digest = t_malloc(MD5_RESULTLEN);
+	password_generate_rpa(plaintext, digest);
+
+	*raw_password_r = digest;
+	*size_r = MD5_RESULTLEN;
 }
 
 static const struct password_scheme default_schemes[] = {
-	{ "CRYPT", crypt_verify, crypt_generate },
-	{ "MD5", md5_crypt_verify, md5_crypt_generate },
-	{ "MD5-CRYPT", md5_crypt_verify, md5_crypt_generate },
- 	{ "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 },
-	{ "CRAM-MD5", cram_md5_verify, cram_md5_generate },
-	{ "HMAC-MD5", cram_md5_verify, cram_md5_generate },
-	{ "DIGEST-MD5", digest_md5_verify, digest_md5_generate },
-	{ "PLAIN-MD4", plain_md4_verify, plain_md4_generate },
-	{ "PLAIN-MD5", plain_md5_verify, plain_md5_generate },
-	{ "LDAP-MD5", plain_md5_verify, ldap_md5_generate },
-	{ "LANMAN", lm_verify, lm_generate },
-	{ "NTLM", ntlm_verify, ntlm_generate },
-	{ "OTP", otp_verify, otp_generate },
-	{ "SKEY", otp_verify, skey_generate },
-	{ "RPA", rpa_verify, rpa_generate },
-	{ NULL, NULL, NULL }
+	{ "CRYPT", PW_ENCODING_NONE, 0, crypt_verify, crypt_generate },
+	{ "MD5", PW_ENCODING_NONE, 0, md5_crypt_verify, md5_crypt_generate },
+	{ "MD5-CRYPT", PW_ENCODING_NONE, 0,
+	  md5_crypt_verify, md5_crypt_generate },
+ 	{ "SHA", PW_ENCODING_BASE64, SHA1_RESULTLEN, NULL, sha1_generate },
+ 	{ "SHA1", PW_ENCODING_BASE64, SHA1_RESULTLEN, NULL, sha1_generate },
+	{ "SMD5", PW_ENCODING_BASE64, 0, smd5_verify, smd5_generate },
+	{ "SSHA", PW_ENCODING_BASE64, 0, ssha_verify, ssha_generate },
+	{ "PLAIN", PW_ENCODING_NONE, 0, NULL, plain_generate },
+	{ "CLEARTEXT", PW_ENCODING_NONE, 0, NULL, plain_generate },
+	{ "CRAM-MD5", PW_ENCODING_HEX, 0, NULL, cram_md5_generate },
+	{ "HMAC-MD5", PW_ENCODING_HEX, CRAM_MD5_CONTEXTLEN,
+	  NULL, cram_md5_generate },
+	{ "DIGEST-MD5", PW_ENCODING_HEX, MD5_RESULTLEN,
+	  NULL, digest_md5_generate },
+	{ "PLAIN-MD4", PW_ENCODING_HEX, MD4_RESULTLEN,
+	  NULL, plain_md4_generate },
+	{ "PLAIN-MD5", PW_ENCODING_HEX, MD5_RESULTLEN,
+	  NULL, plain_md5_generate },
+	{ "LDAP-MD5", PW_ENCODING_BASE64, MD5_RESULTLEN,
+	  NULL, plain_md5_generate },
+	{ "LANMAN", PW_ENCODING_HEX, LM_HASH_SIZE, NULL, lm_generate },
+	{ "NTLM", PW_ENCODING_HEX, NTLMSSP_HASH_SIZE, NULL, ntlm_generate },
+	{ "OTP", PW_ENCODING_NONE, 0, otp_verify, otp_generate },
+	{ "SKEY", PW_ENCODING_NONE, 0, otp_verify, skey_generate },
+	{ "RPA", PW_ENCODING_HEX, MD5_RESULTLEN, NULL, rpa_generate },
+
+	{ NULL, 0, 0, NULL, NULL }
 };
 
 void password_schemes_init(void)

Index: password-scheme.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/password-scheme.h,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- password-scheme.h	13 May 2007 08:24:06 -0000	1.11
+++ password-scheme.h	13 May 2007 12:17:09 -0000	1.12
@@ -1,43 +1,65 @@
 #ifndef __PASSWORD_SCHEME_H
 #define __PASSWORD_SCHEME_H
 
+enum password_encoding {
+	PW_ENCODING_NONE,
+	PW_ENCODING_BASE64,
+	PW_ENCODING_HEX
+};
+
 struct password_scheme {
 	const char *name;
+	enum password_encoding default_encoding;
+	/* If non-zero, this is the expected raw password length.
+	   It can be used to automatically detect encoding between
+	   hex and base64 encoded passwords. */
+	unsigned int raw_password_len;
 
-	bool (*password_verify)(const char *plaintext, const char *password,
-				const char *user);
-	const char *(*password_generate)(const char *plaintext,
-					 const char *user);
+	bool (*password_verify)(const char *plaintext, const char *user,
+				const unsigned char *raw_password, size_t size);
+	void (*password_generate)(const char *plaintext, const char *user,
+				  const unsigned char **raw_password_r,
+				  size_t *size_r);
 };
 
 /* Returns 1 = matched, 0 = didn't match, -1 = unknown scheme */
-int password_verify(const char *plaintext, const char *password,
-		    const char *scheme, const char *user);
+int password_verify(const char *plaintext, const char *user, const char *scheme,
+		    const unsigned char *raw_password, size_t size);
 
 /* Extracts scheme from password, or returns NULL if it isn't found.
    If auth_request is given, it's used for debug logging. */
 const char *password_get_scheme(const char **password);
 
-/* Create wanted password scheme out of plaintext password and username. */
-const char *password_generate(const char *plaintext, const char *user,
-			      const char *scheme);
+/* Decode encoded (base64/hex) password to raw form. Returns 1 if ok,
+   0 if scheme is unknown, -1 if password is invalid. */
+int password_decode(const char *password, const char *scheme,
+		    const unsigned char **raw_password_r, size_t *size_r);
 
-/* Iterate through the list of password schemes, returning names */
-const char *password_list_schemes(const struct password_scheme **listptr);
+/* Create password with wanted scheme out of plaintext password and username.
+   Potential base64/hex directives are ignored in scheme. Returns FALSE if
+   the scheme is unknown. */
+bool password_generate(const char *plaintext, const char *user,
+		       const char *scheme,
+		       const unsigned char **raw_password_r, size_t *size_r);
+/* Like above, but generate encoded passwords. If hex/base64 directive isn't
+   specified in the scheme, the default encoding for the scheme is used.
+   Returns FALSE if the scheme is unknown. */
+bool password_generate_encoded(const char *plaintext, const char *user,
+			       const char *scheme, const char **password_r);
 
 /* Returns TRUE if schemes are equivalent. */
 bool password_scheme_is_alias(const char *scheme1, const char *scheme2);
 
+/* 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);
 
 /* INTERNAL: */
 const char *password_generate_md5_crypt(const char *pw, const char *salt);
-const char *password_generate_cram_md5(const char *pw);
-const char *password_generate_lm(const char *pw);
-const char *password_generate_ntlm(const char *pw);
 const char *password_generate_otp(const char *pw, const char *state,
 				  unsigned int algo);
-const char *password_generate_rpa(const char *pw);
+void password_generate_rpa(const char *pw, unsigned char result[]);
 
 #endif

Index: userdb-static.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/auth/userdb-static.c,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -d -r1.26 -r1.27
--- userdb-static.c	13 May 2007 08:24:06 -0000	1.26
+++ userdb-static.c	13 May 2007 12:17:09 -0000	1.27
@@ -62,7 +62,8 @@
 
 static void
 static_credentials_callback(enum passdb_result result,
-			    const char *password __attr_unused__,
+			    const unsigned char *credentials __attr_unused__,
+			    size_t size __attr_unused__,
 			    struct auth_request *auth_request)
 {
 	struct static_context *ctx = auth_request->context;
@@ -116,7 +117,7 @@
 		auth_request->state = AUTH_REQUEST_STATE_MECH_CONTINUE;
 
 		auth_request->context = ctx;
-		auth_request_lookup_credentials(auth_request, "CRYPT",
+		auth_request_lookup_credentials(auth_request, "",
 						static_credentials_callback);
 	} else {
 		static_lookup_real(auth_request, callback);



More information about the dovecot-cvs mailing list