Index: doc/dovecot-ldap.conf
===================================================================
RCS file: /home/cvs/dovecot/doc/dovecot-ldap.conf,v
retrieving revision 1.10
diff -u -r1.10 dovecot-ldap.conf
--- doc/dovecot-ldap.conf	12 Jul 2005 18:14:44 -0000	1.10
+++ doc/dovecot-ldap.conf	25 Nov 2005 15:51:34 -0000
@@ -1,5 +1,5 @@
-# NOTE: We don't support "authentication binds", so you'll have to give
-# dovecot-auth read access to userPassword field in LDAP server. With OpenLDAP
+# NOTE: you'll have to give dovecot-auth read access to userPassword 
+# field in LDAP server when not using "authendicated binds". With OpenLDAP
 # this is done by modifying /etc/ldap/slapd.conf. There should already be
 # something like this:
 
@@ -18,6 +18,11 @@
 # Password for LDAP server
 #dnpass = 
 
+# Set "auth_bind" to "yes" if you want to use "authenticated binds"
+# as a login validation mechanism. NOTE: the pass_attrs option
+# will (naturally) be ignored if you enable this
+#auth_bind = yes
+
 # LDAP protocol version to use. Likely 2 or 3.
 #ldap_version = 2
 
Index: src/auth/db-ldap.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/db-ldap.c,v
retrieving revision 1.32
diff -u -r1.32 db-ldap.c
--- src/auth/db-ldap.c	16 Oct 2005 14:06:59 -0000	1.32
+++ src/auth/db-ldap.c	25 Nov 2005 15:51:34 -0000
@@ -33,6 +33,7 @@
 	DEF(SET_STR, uris),
 	DEF(SET_STR, dn),
 	DEF(SET_STR, dnpass),
+	DEF(SET_BOOL, auth_bind),
 	DEF(SET_STR, deref),
 	DEF(SET_STR, scope),
 	DEF(SET_STR, base),
@@ -51,6 +52,7 @@
 	MEMBER(uris) NULL,
 	MEMBER(dn) NULL,
 	MEMBER(dnpass) NULL,
+	MEMBER(auth_bind) FALSE,
 	MEMBER(deref) "never",
 	MEMBER(scope) "subtree",
 	MEMBER(base) NULL,
Index: src/auth/db-ldap.h
===================================================================
RCS file: /home/cvs/dovecot/src/auth/db-ldap.h,v
retrieving revision 1.15
diff -u -r1.15 db-ldap.h
--- src/auth/db-ldap.h	22 Jul 2005 11:32:57 -0000	1.15
+++ src/auth/db-ldap.h	25 Nov 2005 15:51:34 -0000
@@ -15,6 +15,7 @@
 	const char *uris;
 	const char *dn;
 	const char *dnpass;
+	int auth_bind;
 	const char *deref;
 	const char *scope;
 	const char *base;
Index: src/auth/passdb-ldap.c
===================================================================
RCS file: /home/cvs/dovecot/src/auth/passdb-ldap.c,v
retrieving revision 1.36
diff -u -r1.36 passdb-ldap.c
--- src/auth/passdb-ldap.c	28 Oct 2005 12:02:36 -0000	1.36
+++ src/auth/passdb-ldap.c	25 Nov 2005 15:51:34 -0000
@@ -170,6 +170,77 @@
 					    auth_request);
 }
 
+static void handle_request_authbind(struct ldap_connection *conn,
+			   struct ldap_request *request, LDAPMessage *res)
+{
+	struct passdb_ldap_request *passdb_ldap_request =
+		(struct passdb_ldap_request *) request;
+	struct auth_request *auth_request = request->context;
+	int ret;
+	
+	if (res != NULL) {
+		ret = ldap_result2error(conn->ld, res, 0);
+		if (ret != LDAP_SUCCESS) {
+			auth_request_log_error(request->context, "ldap",
+					       "ldap_bind() failed: %s",
+					       ldap_err2string(ret));
+			res = NULL;
+		}
+	}
+	
+	if (!auth_request_unref(auth_request))
+		res = NULL;
+	
+	passdb_ldap_request->callback.verify_plain(res ? PASSDB_RESULT_OK :
+					    PASSDB_RESULT_PASSWORD_MISMATCH,
+					    auth_request);
+}
+
+static void handle_request_authbind_search(struct ldap_connection *conn,
+			   struct ldap_request *request, LDAPMessage *res)
+{
+	struct passdb_ldap_request *passdb_ldap_request =
+		(struct passdb_ldap_request *) request;
+	struct ldap_request *ldap_request;
+	struct auth_request *auth_request = request->context;
+	LDAPMessage *entry = NULL;
+	const char *dn = NULL;
+	int msgid;
+	int ret;
+
+	if (res != NULL) {
+		ret = ldap_result2error(conn->ld, res, 0);
+		if (ret != LDAP_SUCCESS) {
+			auth_request_log_error(request->context, "ldap",
+					       "ldap_bind() failed: %s",
+					       ldap_err2string(ret));
+			auth_request_unref(auth_request);
+			passdb_ldap_request->callback.verify_plain(PASSDB_RESULT_INTERNAL_FAILURE, auth_request);
+			return;
+		}
+	}
+
+	entry = ldap_first_entry(conn->ld, res);
+	if (entry == NULL) {
+		if (res != NULL) {
+			auth_request_log_info(auth_request, "ldap",
+					      "unknown user");
+			passdb_ldap_request->callback.verify_plain(PASSDB_RESULT_USER_UNKNOWN, auth_request);
+			auth_request_unref(auth_request);
+			return;
+		}
+	}
+	
+	dn = ldap_get_dn(conn->ld, entry);
+
+	/* switch the handler to the authenticated bind handler */
+	ldap_request = (struct ldap_request*)passdb_ldap_request;
+	ldap_request->callback = handle_request_authbind;
+	
+	msgid = ldap_bind(conn->ld, dn, auth_request->mech_password, LDAP_AUTH_SIMPLE);
+	hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request);	
+}
+
 static void ldap_lookup_pass(struct auth_request *auth_request,
 			     struct ldap_request *ldap_request)
 {
@@ -206,16 +277,64 @@
 }
 
 static void
+ldap_verify_plain_authbind(struct auth_request *request, 
+			verify_plain_callback_t *callback, 
+			struct passdb_ldap_request *passdb_ldap_request)
+{
+	struct passdb_module *_module = request->passdb->passdb;
+	struct ldap_passdb_module *module =
+		(struct ldap_passdb_module *)_module;
+	struct ldap_connection *conn = module->conn;
+	struct ldap_request *ldap_request;
+	const struct var_expand_table *vars;
+	/* we don't want any attribute in our search results; we only need the dn */
+	const char *attr_names[1] = { NULL };
+	const char *filter, *base;
+	string_t *str;
+	int msgid;
+	
+	vars = auth_request_get_var_expand_table(request, ldap_escape);
+
+	str = t_str_new(512);
+	var_expand(str, conn->set.base, vars);
+	base = t_strdup(str_c(str));
+
+	str_truncate(str, 0);
+	var_expand(str, conn->set.pass_filter, vars);
+	filter = str_c(str);
+	
+	auth_request_log_debug(request, "ldap",
+			       "base=%s scope=%s filter=%s fields=%s",
+			       base, conn->set.scope, filter, 
+			       t_strarray_join(attr_names, ","));	
+	
+	auth_request_ref(request);
+	ldap_request = (struct ldap_request*)passdb_ldap_request;
+	ldap_request->context = request;
+	ldap_request->callback = handle_request_authbind_search;
+	
+	msgid = ldap_search(conn->ld, base, LDAP_SCOPE_SUBTREE, filter,
+                        (char **)attr_names, 0);
+	hash_insert(conn->requests, POINTER_CAST(msgid), ldap_request);
+}
+
+static void
 ldap_verify_plain(struct auth_request *request,
 		  const char *password __attr_unused__,
 		  verify_plain_callback_t *callback)
 {
+	struct passdb_module *_module = request->passdb->passdb;
+	struct ldap_passdb_module *module =
+		(struct ldap_passdb_module *)_module;
+	struct ldap_connection *conn = module->conn;
 	struct passdb_ldap_request *ldap_request;
 
 	ldap_request = p_new(request->pool, struct passdb_ldap_request, 1);
 	ldap_request->callback.verify_plain = callback;
 
-        ldap_lookup_pass(request, &ldap_request->request);
+	conn->set.auth_bind ?
+		ldap_verify_plain_authbind(request, callback, ldap_request) :
+		ldap_lookup_pass(request, &ldap_request->request);
 }
 
 static void ldap_lookup_credentials(struct auth_request *request,
