changeset 3058:052f3a5743af HEAD

Make FAIL reply contain "temp" parameter if the authentication failed because of temporary internal error. Also cleaned up the auth code a bit.
author Timo Sirainen <tss@iki.fi>
date Fri, 07 Jan 2005 19:27:20 +0200
parents 3e5faae6f10b
children 08c640bdf749
files doc/auth-protocol.txt src/auth/auth-client-connection.c src/auth/mech-anonymous.c src/auth/mech-apop.c src/auth/mech-cram-md5.c src/auth/mech-digest-md5.c src/auth/mech-login.c src/auth/mech-ntlm.c src/auth/mech-plain.c src/auth/mech-rpa.c src/auth/mech.c src/auth/mech.h src/auth/passdb-ldap.c src/auth/passdb-passwd-file.c src/auth/passdb-sql.c src/auth/passdb.c src/auth/passdb.h
diffstat 17 files changed, 330 insertions(+), 191 deletions(-) [+]
line wrap: on
line diff
--- a/doc/auth-protocol.txt	Fri Jan 07 19:24:12 2005 +0200
+++ b/doc/auth-protocol.txt	Fri Jan 07 19:27:20 2005 +0200
@@ -111,9 +111,10 @@
 
 FAIL parameters may contain "reason=.." parameter which should be sent to
 remote user instead of a standard "Authentication failed" message. For
-example "invalid base64 data" or "temporary internal failure". It should
-NOT be used to give exact reason for authentication failure (ie. "user not
-found" vs. "password mismatch").
+example "invalid base64 data". It should NOT be used to give exact reason
+for authentication failure (ie. "user not found" vs. "password mismatch").
+Sending "temp" parameter indicates that the error was a temporary internal
+failure, eg. connection was lost to SQL database.
 
 CONT command means that the authentication continues, and more data is
 expected from client to finish the authentication. Given base64 data should
--- a/src/auth/auth-client-connection.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/auth-client-connection.c	Fri Jan 07 19:27:20 2005 +0200
@@ -89,6 +89,8 @@
 			str_append_c(str, '\t');
 			str_append(str, request->extra_fields);
 		}
+		if (request->internal_failure)
+			str_append(str, "\ttemp");
 		break;
 	}
 
--- a/src/auth/mech-anonymous.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-anonymous.c	Fri Jan 07 19:27:20 2005 +0200
@@ -22,7 +22,7 @@
 	request->callback = callback;
 	request->user = p_strdup(request->pool, anonymous_username);
 
-	mech_auth_finish(request, NULL, 0, TRUE);
+	mech_auth_success(request, NULL, 0);
 }
 
 static void
--- a/src/auth/mech-apop.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-apop.c	Fri Jan 07 19:27:20 2005 +0200
@@ -31,27 +31,42 @@
 	unsigned char digest[16];
 };
 
+static int verify_credentials(struct apop_auth_request *request,
+			      const char *credentials)
+{
+	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_final(&ctx, digest);
+
+	return memcmp(digest, request->digest, 16) == 0;
+}
+
 static void
-apop_credentials_callback(const char *credentials,
+apop_credentials_callback(enum passdb_result result,
+			  const char *credentials,
 			  struct auth_request *auth_request)
 {
 	struct apop_auth_request *request =
 		(struct apop_auth_request *)auth_request;
-	unsigned char digest[16];
-	struct md5_context ctx;
-	int ret = FALSE;
 
-	if (credentials != NULL) {
-		md5_init(&ctx);
-		md5_update(&ctx, request->challenge,
-			   strlen(request->challenge));
-		md5_update(&ctx, credentials, strlen(credentials));
-		md5_final(&ctx, digest);
-
-		ret = memcmp(digest, request->digest, 16) == 0;
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		if (verify_credentials(request, credentials))
+			mech_auth_success(auth_request, NULL, 0);
+		else
+			mech_auth_fail(auth_request);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		break;
+	default:
+		mech_auth_fail(auth_request);
+		break;
 	}
-
-	mech_auth_finish(auth_request, NULL, 0, ret);
 }
 
 static void
@@ -72,7 +87,7 @@
 			i_info("apop(%s): no initial respone",
 			       get_log_prefix(auth_request));
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 
@@ -92,7 +107,7 @@
 			i_info("apop(%s): invalid challenge",
 			       get_log_prefix(auth_request));
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 	request->challenge = p_strdup(request->pool, (const char *)data);
@@ -109,7 +124,7 @@
 			i_info("apop(%s): malformed data",
 			       get_log_prefix(auth_request));
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 	tmp++;
@@ -120,7 +135,7 @@
 			i_info("apop(%s): %s",
 			       get_log_prefix(auth_request), error);
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 
--- a/src/auth/mech-cram-md5.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-cram-md5.c	Fri Jan 07 19:27:20 2005 +0200
@@ -55,15 +55,15 @@
 	buffer_t *context_digest_buf;
 	const char *response_hex;
 
-	if (credentials == NULL)
-		return FALSE;
-
 	context_digest_buf =
 		buffer_create_data(pool_datastack_create(),
 				   context_digest, sizeof(context_digest));
 
-	if (hex_to_binary(credentials, context_digest_buf) < 0)
+	if (hex_to_binary(credentials, context_digest_buf) < 0) {
+		i_error("cram-md5(%s): passdb credentials are not in hex",
+			get_log_prefix(&request->auth_request));
 		return FALSE;
+	}
 
 	hmac_md5_set_cram_context(&ctx, context_digest);
 	hmac_md5_update(&ctx, request->challenge, strlen(request->challenge));
@@ -109,20 +109,26 @@
 	return TRUE;
 }
 
-static void credentials_callback(const char *result,
+static void credentials_callback(enum passdb_result result,
+				 const char *credentials,
 				 struct auth_request *auth_request)
 {
 	struct cram_auth_request *request =
 		(struct cram_auth_request *)auth_request;
 
-	if (verify_credentials(request, result))
-		mech_auth_finish(auth_request, NULL, 0, TRUE);
-	else {
-		if (verbose) {
-			i_info("cram-md5(%s): authentication failed",
-			       get_log_prefix(auth_request));
-		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		if (verify_credentials(request, credentials))
+			mech_auth_success(auth_request, NULL, 0);
+		else
+			mech_auth_fail(auth_request);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		break;
+	default:
+		mech_auth_fail(auth_request);
+		break;
 	}
 }
 
@@ -154,9 +160,7 @@
 
 	if (verbose)
 		i_info("cram-md5(%s): %s", get_log_prefix(auth_request), error);
-
-	/* failed */
-	mech_auth_finish(auth_request, NULL, 0, FALSE);
+	mech_auth_fail(auth_request);
 }
 
 static void
--- a/src/auth/mech-digest-md5.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-digest-md5.c	Fri Jan 07 19:27:20 2005 +0200
@@ -118,13 +118,19 @@
 	int i;
 
 	/* get the MD5 password */
-	if (credentials == NULL || strlen(credentials) != sizeof(digest)*2)
+	if (strlen(credentials) != sizeof(digest)*2) {
+		i_error("digest-md5(%s): passdb credentials' length is wrong",
+			get_log_prefix(&request->auth_request));
 		return FALSE;
+	}
 
 	digest_buf = buffer_create_data(pool_datastack_create(),
 					digest, sizeof(digest));
-	if (hex_to_binary(credentials, digest_buf) < 0)
+	if (hex_to_binary(credentials, digest_buf) < 0) {
+		i_error("digest-md5(%s): passdb credentials are not in hex",
+			get_log_prefix(&request->auth_request));
 		return FALSE;
+	}
 
 	/*
 	   response =
@@ -508,20 +514,31 @@
 	return !failed;
 }
 
-static void credentials_callback(const char *result,
+static void credentials_callback(enum passdb_result result,
+				 const char *credentials,
 				 struct auth_request *auth_request)
 {
 	struct digest_auth_request *request =
 		(struct digest_auth_request *)auth_request;
 
-	if (!verify_credentials(request, result)) {
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return;
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		if (!verify_credentials(request, credentials)) {
+			mech_auth_fail(auth_request);
+			return;
+		}
+
+		request->authenticated = TRUE;
+		auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
+				       request->rspauth, strlen(request->rspauth));
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		break;
+	default:
+		mech_auth_fail(auth_request);
+		break;
 	}
-
-	request->authenticated = TRUE;
-	auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
-			       request->rspauth, strlen(request->rspauth));
 }
 
 static void
@@ -536,7 +553,7 @@
 	if (request->authenticated) {
 		/* authentication is done, we were just waiting the last
 		   word from client */
-		mech_auth_finish(auth_request, NULL, 0, TRUE);
+		mech_auth_success(auth_request, NULL, 0);
 		return;
 	}
 
@@ -566,7 +583,7 @@
 		       get_log_prefix(auth_request), error);
 	}
 
-	mech_auth_finish(auth_request, NULL, 0, FALSE);
+	mech_auth_fail(auth_request);
 }
 
 static void
--- a/src/auth/mech-login.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-login.c	Fri Jan 07 19:27:20 2005 +0200
@@ -17,7 +17,17 @@
 static void verify_callback(enum passdb_result result,
 			    struct auth_request *request)
 {
-	mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK);
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		mech_auth_success(request, NULL, 0);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(request);
+		break;
+	default:
+		mech_auth_fail(request);
+		break;
+	}
 }
 
 static void
@@ -38,7 +48,7 @@
 				i_info("login(%s): %s",
 				       get_log_prefix(request), error);
 			}
-			mech_auth_finish(request, NULL, 0, FALSE);
+			mech_auth_fail(request);
 			return;
 		}
 
--- a/src/auth/mech-ntlm.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-ntlm.c	Fri Jan 07 19:27:20 2005 +0200
@@ -33,64 +33,77 @@
 	struct ntlmssp_response *response;
 };
 
-static void
-lm_credentials_callback(const char *credentials,
-			struct auth_request *auth_request)
+static int lm_verify_credentials(struct ntlm_auth_request *request,
+				 const char *credentials)
 {
-	struct ntlm_auth_request *request =
-		(struct ntlm_auth_request *)auth_request;
 	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;
-	int ret;
 
 	response_length =
 		ntlmssp_buffer_length(request->response, lm_response);
 	client_response = ntlmssp_buffer_data(request->response, lm_response);
 
-	if (credentials == NULL || response_length < LM_RESPONSE_SIZE) {
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return;
+	if (response_length < LM_RESPONSE_SIZE) {
+		i_error("ntlm(%s): passdb credentials' length is too small",
+			get_log_prefix(&request->auth_request));
+		return FALSE;
 	}
 
-	hash_buffer = buffer_create_data(auth_request->pool,
+	hash_buffer = buffer_create_data(request->auth_request.pool,
 					 hash, sizeof(hash));
-	hex_to_binary(credentials, hash_buffer);
+	if (hex_to_binary(credentials, hash_buffer) < 0) {
+		i_error("ntlm(%s): passdb credentials are not in hex",
+			get_log_prefix(&request->auth_request));
+		return FALSE;
+	}
 
 	ntlmssp_v1_response(hash, request->challenge, lm_response);
-
-	ret = memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0;
-
-	mech_auth_finish(auth_request, NULL, 0, ret);
+	return memcmp(lm_response, client_response, LM_RESPONSE_SIZE) == 0;
 }
 
 static void
-ntlm_credentials_callback(const char *credentials,
-			  struct auth_request *auth_request)
+lm_credentials_callback(enum passdb_result result,
+			const char *credentials,
+			struct auth_request *auth_request)
 {
 	struct ntlm_auth_request *request =
 		(struct ntlm_auth_request *)auth_request;
+
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		if (lm_verify_credentials(request, credentials))
+			mech_auth_success(auth_request, NULL, 0);
+		else
+			mech_auth_fail(auth_request);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		break;
+	default:
+		mech_auth_fail(auth_request);
+		break;
+	}
+}
+
+static int ntlm_verify_credentials(struct ntlm_auth_request *request,
+				   const char *credentials)
+{
+        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;
-	int ret;
 
 	response_length =
 		ntlmssp_buffer_length(request->response, ntlm_response);
 	client_response = ntlmssp_buffer_data(request->response, ntlm_response);
 
-	if (credentials == NULL || response_length == 0) {
-		/* We can't use LM authentication if NTLM2 was negotiated */
-		if (request->ntlm2_negotiated)
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
-		else
-			passdb->lookup_credentials(auth_request,
-						   PASSDB_CREDENTIALS_LANMAN,
-						   lm_credentials_callback);
-		return;
+	if (response_length == 0) {
+		/* try LM authentication unless NTLM2 was negotiated */
+		return request->ntlm2_negotiated ? -1 : 0;
 	}
 
 	hash_buffer = buffer_create_data(auth_request->pool,
@@ -112,8 +125,8 @@
 				    response_length - NTLMSSP_V2_RESPONSE_SIZE,
 				    ntlm_v2_response);
 
-		ret = memcmp(ntlm_v2_response, client_response,
-			     NTLMSSP_V2_RESPONSE_SIZE) == 0;
+		return memcmp(ntlm_v2_response, client_response,
+			      NTLMSSP_V2_RESPONSE_SIZE) == 0 ? 1 : -1;
 	} else {
 		unsigned char ntlm_response[NTLMSSP_RESPONSE_SIZE];
 		const unsigned char *client_lm_response =
@@ -127,11 +140,43 @@
 			ntlmssp_v1_response(hash, request->challenge,
 					    ntlm_response);
 
-		ret = memcmp(ntlm_response, client_response,
-			     NTLMSSP_RESPONSE_SIZE) == 0;
+		return memcmp(ntlm_response, client_response,
+			      NTLMSSP_RESPONSE_SIZE) == 0 ? 1 : -1;
+	}
+}
+
+static void
+ntlm_credentials_callback(enum passdb_result result,
+			  const char *credentials,
+			  struct auth_request *auth_request)
+{
+	struct ntlm_auth_request *request =
+		(struct ntlm_auth_request *)auth_request;
+	int ret;
+
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		ret = ntlm_verify_credentials(request, credentials);
+		if (ret > 0) {
+			mech_auth_success(auth_request, NULL, 0);
+			return;
+		}
+		if (ret < 0) {
+			mech_auth_fail(auth_request);
+			return;
+		}
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		return;
+	default:
+		break;
 	}
 
-	mech_auth_finish(auth_request, NULL, 0, ret);
+	/* NTLM credentials not found or didn't want to use them,
+	   try with LM credentials */
+	passdb->lookup_credentials(auth_request, PASSDB_CREDENTIALS_LANMAN,
+				   lm_credentials_callback);
 }
 
 static void
@@ -158,7 +203,7 @@
 				       get_log_prefix(auth_request),
 				       error);
 			}
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			mech_auth_fail(auth_request);
 			return;
 		}
 
@@ -183,7 +228,7 @@
 				       get_log_prefix(auth_request),
 				       error);
 			}
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			mech_auth_fail(auth_request);
 			return;
 		}
 
@@ -199,7 +244,7 @@
 				i_info("ntlm(%s): %s",
 				       get_log_prefix(auth_request), error);
 			}
-			mech_auth_finish(auth_request, NULL, 0, FALSE);
+			mech_auth_fail(auth_request);
 			return;
 		}
 
--- a/src/auth/mech-plain.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-plain.c	Fri Jan 07 19:27:20 2005 +0200
@@ -8,7 +8,17 @@
 static void verify_callback(enum passdb_result result,
 			    struct auth_request *request)
 {
-	mech_auth_finish(request, NULL, 0, result == PASSDB_RESULT_OK);
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		mech_auth_success(request, NULL, 0);
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(request);
+		break;
+	default:
+		mech_auth_fail(request);
+		break;
+	}
 }
 
 static void
@@ -48,7 +58,7 @@
 			i_info("plain(%s): invalid input",
 			       get_log_prefix(request));
 		}
-		mech_auth_finish(request, NULL, 0, FALSE);
+		mech_auth_fail(request);
 	} else {
 		/* split and save user/realm */
 		if (strchr(authenid, '@') == NULL && default_realm != NULL) {
@@ -65,7 +75,7 @@
 				i_info("plain(%s): %s",
 				       get_log_prefix(request), error);
 			}
-			mech_auth_finish(request, NULL, 0, FALSE);
+			mech_auth_fail(request);
 		} else {
 			passdb->verify_plain(request, pass, verify_callback);
 		}
--- a/src/auth/mech-rpa.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech-rpa.c	Fri Jan 07 19:27:20 2005 +0200
@@ -29,7 +29,7 @@
 	int phase;
 
 	/* cached: */
-	unsigned char *pwd_md5;
+	unsigned char pwd_md5[16];
 	size_t service_len;
 	const unsigned char *service_ucs2be;
 	size_t username_len;
@@ -399,36 +399,48 @@
 	return buffer_free_without_data(buf);
 }
 
+static int verify_credentials(struct rpa_auth_request *request,
+			      const char *credentials)
+{
+	unsigned char response[16];
+	buffer_t *hash_buffer;
+
+	hash_buffer = buffer_create_data(request->pool, request->pwd_md5, 16);
+	hex_to_binary(credentials, hash_buffer);
+
+	rpa_user_response(request, response);
+	return memcmp(response, request->user_response, 16) == 0;
+}
+
 static void
-rpa_credentials_callback(const char *credentials,
+rpa_credentials_callback(enum passdb_result result,
+			 const char *credentials,
 			 struct auth_request *auth_request)
 {
 	struct rpa_auth_request *request =
 		(struct rpa_auth_request *)auth_request;
-	unsigned char response[16];
-	buffer_t *hash_buffer;
 	const unsigned char *token4;
 	size_t token4_size;
 
-	if (credentials == NULL) {
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return;
+	switch (result) {
+	case PASSDB_RESULT_OK:
+		if (!verify_credentials(request, credentials))
+			mech_auth_fail(auth_request);
+		else {
+			token4 = mech_rpa_build_token4(request, &token4_size);
+			auth_request->callback(auth_request,
+					       AUTH_CLIENT_RESULT_CONTINUE,
+					       token4, token4_size);
+			request->phase = 2;
+		}
+		break;
+	case PASSDB_RESULT_INTERNAL_FAILURE:
+		mech_auth_internal_failure(auth_request);
+		break;
+	default:
+		mech_auth_fail(auth_request);
+		break;
 	}
-
-	request->pwd_md5 = p_malloc(request->pool, 16);
-	hash_buffer = buffer_create_data(request->pool, request->pwd_md5, 16);
-	hex_to_binary(credentials, hash_buffer);
-
-	rpa_user_response(request, response);
-	if (memcmp(response, request->user_response, 16) != 0) {
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
-		return;
-	}
-
-	token4 = mech_rpa_build_token4(request, &token4_size);
-	auth_request->callback(auth_request, AUTH_CLIENT_RESULT_CONTINUE,
-			       token4, token4_size);
-	request->phase = 2;
 }
 
 static void
@@ -446,7 +458,7 @@
 			i_info("rpa(%s): invalid token 1, %s",
 			       get_log_prefix(auth_request), error);
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 
@@ -478,7 +490,7 @@
 			i_info("rpa(%s): invalid token 3, %s",
 			       get_log_prefix(auth_request), error);
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 
@@ -487,7 +499,7 @@
 			i_info("rpa(%s): %s",
 			       get_log_prefix(auth_request), error);
 		}
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		return;
 	}
 
@@ -500,7 +512,6 @@
 		     const unsigned char *data, size_t data_size)
 {
 	static const unsigned char client_ack[3] = { 0x60, 0x01, 0x00 };
-	int ret = TRUE;
 
 	if ((data_size != sizeof(client_ack)) ||
 	    (memcmp(data, client_ack, sizeof(client_ack)) != 0)) {
@@ -508,10 +519,10 @@
 			i_info("rpa(%s): invalid token 5 or client rejects us",
 			       get_log_prefix(auth_request));
 		}
-		ret = FALSE;
+		mech_auth_fail(auth_request);
+	} else {
+		mech_auth_success(auth_request, NULL, 0);
 	}
-
-	mech_auth_finish(auth_request, NULL, 0, ret);
 }
 
 static void
@@ -535,7 +546,7 @@
 		mech_rpa_auth_phase3(auth_request, data, data_size);
 		break;
 	default:
-		mech_auth_finish(auth_request, NULL, 0, FALSE);
+		mech_auth_fail(auth_request);
 		break;
 	}
 }
--- a/src/auth/mech.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech.c	Fri Jan 07 19:27:20 2005 +0200
@@ -127,37 +127,9 @@
 	auth_request_unref(request);
 }
 
-void mech_auth_finish(struct auth_request *request,
-		      const void *data, size_t data_size, int success)
+void mech_auth_success(struct auth_request *request,
+		       const void *data, size_t data_size)
 {
-	if (!success) {
-		if (request->no_failure_delay) {
-			/* passdb specifically requested to to delay the
-			   reply. */
-			request->callback(request, AUTH_CLIENT_RESULT_FAILURE,
-					  NULL, 0);
-			auth_request_destroy(request);
-			return;
-		}
-
-		/* failure. don't announce it immediately to avoid
-		   a) timing attacks, b) flooding */
-		if (auth_failures_buf->used > 0) {
-			const struct auth_request *const *requests;
-
-			requests = auth_failures_buf->data;
-			requests += auth_failures_buf->used/sizeof(*requests)-1;
-			i_assert(*requests != request);
-		}
-
-		buffer_append(auth_failures_buf, &request, sizeof(request));
-
-		/* make sure the request isn't found anymore */
-		auth_request_ref(request);
-		auth_request_destroy(request);
-		return;
-	}
-
 	request->successful = TRUE;
 	if (request->conn != NULL) {
 		request->callback(request, AUTH_CLIENT_RESULT_SUCCESS,
@@ -172,6 +144,38 @@
 	}
 }
 
+void mech_auth_fail(struct auth_request *request)
+{
+	if (request->no_failure_delay) {
+		/* passdb specifically requested to to delay the reply. */
+		request->callback(request, AUTH_CLIENT_RESULT_FAILURE, NULL, 0);
+		auth_request_destroy(request);
+		return;
+	}
+
+	/* failure. don't announce it immediately to avoid
+	   a) timing attacks, b) flooding */
+	if (auth_failures_buf->used > 0) {
+		const struct auth_request *const *requests;
+
+		requests = auth_failures_buf->data;
+		requests += auth_failures_buf->used/sizeof(*requests)-1;
+		i_assert(*requests != request);
+	}
+
+	buffer_append(auth_failures_buf, &request, sizeof(request));
+
+	/* make sure the request isn't found anymore */
+	auth_request_ref(request);
+	auth_request_destroy(request);
+}
+
+void mech_auth_internal_failure(struct auth_request *request)
+{
+	request->internal_failure = TRUE;
+	mech_auth_fail(request);
+}
+
 int mech_fix_username(char *username, const char **error_r)
 {
 	unsigned char *p;
--- a/src/auth/mech.h	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/mech.h	Fri Jan 07 19:27:20 2005 +0200
@@ -35,6 +35,7 @@
 	mech_callback_t *callback;
 
 	unsigned int successful:1;
+	unsigned int internal_failure:1;
 	unsigned int accept_input:1;
 	unsigned int no_failure_delay:1;
 	unsigned int no_login:1;
@@ -81,8 +82,10 @@
 
 const string_t *auth_mechanisms_get_list(void);
 
-void mech_auth_finish(struct auth_request *request,
-		      const void *data, size_t data_size, int success);
+void mech_auth_success(struct auth_request *request,
+		       const void *data, size_t data_size);
+void mech_auth_fail(struct auth_request *request);
+void mech_auth_internal_failure(struct auth_request *request);
 
 int mech_fix_username(char *username, const char **error_r);
 
--- a/src/auth/passdb-ldap.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/passdb-ldap.c	Fri Jan 07 19:27:20 2005 +0200
@@ -54,12 +54,14 @@
 	struct passdb_ldap_request *ldap_request =
 		(struct passdb_ldap_request *) request;
         struct auth_request *auth_request = request->context;
+	enum passdb_result result;
 	LDAPMessage *entry;
 	BerElement *ber;
 	char *attr, **vals;
 	const char *user, *password, *scheme;
 	int ret;
 
+	result = PASSDB_RESULT_USER_UNKNOWN;
 	user = auth_request->user;
 	password = NULL;
 
@@ -69,6 +71,8 @@
 			i_error("ldap(%s): ldap_search() failed: %s",
 				get_log_prefix(auth_request),
 				ldap_err2string(ret));
+
+                        result = PASSDB_RESULT_INTERNAL_FAILURE;
 			res = NULL;
 		}
 	}
@@ -105,7 +109,7 @@
 		}
 	}
 
-	/* LDAP result is free'd now. we can check if auth_request is
+	/* LDAP result is freed now. we can check if auth_request is
 	   even needed anymore */
 	if (!auth_request_unref(auth_request))
 		return;
@@ -117,7 +121,7 @@
 	}
 
 	if (ldap_request->credentials != -1) {
-		passdb_handle_credentials(ldap_request->credentials,
+		passdb_handle_credentials(result, ldap_request->credentials,
 			password, scheme,
 			ldap_request->callback.lookup_credentials,
 			auth_request);
@@ -126,8 +130,7 @@
 
 	/* verify plain */
 	if (password == NULL) {
-		ldap_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN,
-						    auth_request);
+		ldap_request->callback.verify_plain(result, auth_request);
 		return;
 	}
 
@@ -219,7 +222,9 @@
 			scheme = passdb_ldap_conn->conn->set.
 				default_pass_scheme;
 		}
-		passdb_handle_credentials(credentials, result, scheme,
+		passdb_handle_credentials(result != NULL ? PASSDB_RESULT_OK :
+					  PASSDB_RESULT_USER_UNKNOWN,
+					  credentials, result, scheme,
 					  callback, request);
 		return;
 	}
--- a/src/auth/passdb-passwd-file.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/passdb-passwd-file.c	Fri Jan 07 19:27:20 2005 +0200
@@ -56,14 +56,14 @@
 
 	pu = db_passwd_file_lookup(passdb_pwf, request);
 	if (pu == NULL) {
-		callback(NULL, request);
+		callback(PASSDB_RESULT_USER_UNKNOWN, NULL, request);
 		return;
 	}
 
 	crypted_pass = pu->password;
 	scheme = password_get_scheme(&crypted_pass);
 
-	passdb_handle_credentials(credentials, crypted_pass,
+	passdb_handle_credentials(PASSDB_RESULT_OK, credentials, crypted_pass,
 				  scheme, callback, request);
 }
 
--- a/src/auth/passdb-sql.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/passdb-sql.c	Fri Jan 07 19:27:20 2005 +0200
@@ -57,9 +57,11 @@
 {
 	struct passdb_sql_request *sql_request = context;
 	struct auth_request *auth_request = sql_request->auth_request;
+	enum passdb_result passdb_result;
 	const char *user, *password, *scheme;
 	int ret, idx;
 
+	passdb_result = PASSDB_RESULT_USER_UNKNOWN;
 	user = auth_request->user;
 	password = NULL;
 
@@ -68,6 +70,7 @@
 		i_error("sql(%s): Password query failed: %s",
 			get_log_prefix(auth_request),
 			sql_result_get_error(result));
+		passdb_result = PASSDB_RESULT_INTERNAL_FAILURE;
 	} else if (ret == 0) {
 		if (verbose) {
 			i_info("sql(%s): Unknown user",
@@ -101,8 +104,8 @@
 	}
 
 	if (sql_request->credentials != -1) {
-		passdb_handle_credentials(sql_request->credentials,
-			password, scheme,
+		passdb_handle_credentials(passdb_result,
+			sql_request->credentials, password, scheme,
 			sql_request->callback.lookup_credentials,
 			auth_request);
 		i_free(sql_request);
@@ -111,8 +114,7 @@
 
 	/* verify plain */
 	if (password == NULL) {
-		sql_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN,
-						   auth_request);
+		sql_request->callback.verify_plain(passdb_result, auth_request);
 		i_free(sql_request);
 		return;
 	}
@@ -129,8 +131,8 @@
 	}
 
 	sql_request->callback.verify_plain(ret > 0 ? PASSDB_RESULT_OK :
-					     PASSDB_RESULT_PASSWORD_MISMATCH,
-					     auth_request);
+					   PASSDB_RESULT_PASSWORD_MISMATCH,
+					   auth_request);
 	i_free(sql_request);
 }
 
@@ -186,7 +188,9 @@
 					    &result, &scheme)) {
 		if (scheme == NULL)
 			scheme = passdb_sql_conn->set.default_pass_scheme;
-		passdb_handle_credentials(credentials, result, scheme,
+		passdb_handle_credentials(result != NULL ? PASSDB_RESULT_OK :
+					  PASSDB_RESULT_USER_UNKNOWN,
+					  credentials, result, scheme,
 					  callback, request);
 		return;
 	}
--- a/src/auth/passdb.c	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/passdb.c	Fri Jan 07 19:27:20 2005 +0200
@@ -71,44 +71,49 @@
 	return "??";
 }
 
-void passdb_handle_credentials(enum passdb_credentials credentials,
+void passdb_handle_credentials(enum passdb_result result,
+			       enum passdb_credentials credentials,
 			       const char *password, const char *scheme,
 			       lookup_credentials_callback_t *callback,
                                struct auth_request *auth_request)
 {
 	const char *wanted_scheme;
 
+	if (result != PASSDB_RESULT_OK) {
+		callback(result, NULL, auth_request);
+		return;
+	}
+	i_assert(password != NULL);
+
 	if (credentials == PASSDB_CREDENTIALS_CRYPT) {
 		/* anything goes */
-		if (password != NULL)
-			password = t_strdup_printf("{%s}%s", scheme, password);
-		callback(password, auth_request);
+		password = t_strdup_printf("{%s}%s", scheme, password);
+		callback(result, password, auth_request);
 		return;
 	}
 
-	if (password != NULL) {
-		wanted_scheme = passdb_credentials_to_str(credentials);
-		if (strcasecmp(scheme, wanted_scheme) != 0) {
-			if (strcasecmp(scheme, "PLAIN") == 0 ||
-			    strcasecmp(scheme, "CLEARTEXT") == 0) {
-				/* we can generate anything out of plaintext
-				   passwords */
-				password = password_generate(password,
-							     auth_request->user,
-							     wanted_scheme);
-			} else {
-				if (verbose) {
-					i_info("password(%s): Requested %s "
-					       "scheme, but we have only %s",
-					       auth_request->user,
-					       wanted_scheme, scheme);
-				}
-				password = NULL;
+	wanted_scheme = passdb_credentials_to_str(credentials);
+	if (strcasecmp(scheme, wanted_scheme) != 0) {
+		if (strcasecmp(scheme, "PLAIN") != 0 &&
+		    strcasecmp(scheme, "CLEARTEXT") != 0) {
+			if (verbose) {
+				i_info("password(%s): Requested %s "
+				       "scheme, but we have only %s",
+				       auth_request->user,
+				       wanted_scheme, scheme);
 			}
+			callback(PASSDB_RESULT_SCHEME_NOT_AVAILABLE,
+				 NULL, auth_request);
+			return;
 		}
+
+		/* we can generate anything out of plaintext passwords */
+		password = password_generate(password, auth_request->user,
+					     wanted_scheme);
+		i_assert(password != NULL);
 	}
 
-	callback(password, auth_request);
+	callback(PASSDB_RESULT_OK, password, auth_request);
 }
 
 void passdb_preinit(void)
--- a/src/auth/passdb.h	Fri Jan 07 19:24:12 2005 +0200
+++ b/src/auth/passdb.h	Fri Jan 07 19:27:20 2005 +0200
@@ -22,6 +22,7 @@
 	PASSDB_RESULT_USER_UNKNOWN = -1,
 	PASSDB_RESULT_USER_DISABLED = -2,
 	PASSDB_RESULT_INTERNAL_FAILURE = -3,
+	PASSDB_RESULT_SCHEME_NOT_AVAILABLE = -4,
 
 	PASSDB_RESULT_PASSWORD_MISMATCH = 0,
 	PASSDB_RESULT_OK = 1,
@@ -29,7 +30,8 @@
 
 typedef void verify_plain_callback_t(enum passdb_result result,
 				     struct auth_request *request);
-typedef void lookup_credentials_callback_t(const char *result,
+typedef void lookup_credentials_callback_t(enum passdb_result result,
+					   const char *credentials,
 					   struct auth_request *request);
 
 struct passdb_module {
@@ -50,7 +52,8 @@
 				   lookup_credentials_callback_t *callback);
 };
 
-void passdb_handle_credentials(enum passdb_credentials credentials,
+void passdb_handle_credentials(enum passdb_result result,
+			       enum passdb_credentials credentials,
 			       const char *password, const char *scheme,
 			       lookup_credentials_callback_t *callback,
                                struct auth_request *auth_request);