Index: Makefile
===================================================================
--- Makefile	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ Makefile	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -10,5 +10,5 @@
 
 # Program variables
-objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o conf.o log.o
+objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) auth.o $(AUTH_OBJS) unix.o conf.o log.o
 headers = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h)
 subdirs = lib protocols
Index: auth.c
===================================================================
--- auth.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
+++ auth.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -0,0 +1,46 @@
+#define BITLBEE_CORE
+#include "bitlbee.h"
+
+GList *auth_init(const char *backend)
+{
+	GList *gl = NULL;
+	int ok = backend ? 0 : 1;
+
+	return ok ? gl : NULL;
+}
+
+storage_status_t auth_check_pass(irc_t *irc, const char *nick, const char *password)
+{
+	GList *gl;
+	storage_status_t status = storage_check_pass(irc, nick, password);
+
+	if (status == STORAGE_CHECK_BACKEND) {
+		for (gl = global.auth; gl; gl = gl->next) {
+			auth_backend_t *be = gl->data;
+			if (!strcmp(be->name, irc->auth_backend)) {
+				status = be->check_pass(nick, password);
+				break;
+			}
+		}
+	} else if (status == STORAGE_NO_SUCH_USER && global.conf->auth_backend) {
+		for (gl = global.auth; gl; gl = gl->next) {
+			auth_backend_t *be = gl->data;
+			if (!strcmp(be->name, global.conf->auth_backend)) {
+				status = be->check_pass(nick, password);
+				/* Save the user so storage_load will pick them up, similar to
+				 * what the register command would do */
+				if (status == STORAGE_OK) {
+					irc->auth_backend = g_strdup(global.conf->auth_backend);
+					storage_save(irc, (char *)password, 0);
+				}
+				break;
+			}
+		}
+	}
+
+	if (status == STORAGE_OK) {
+		irc_setpass(irc, password);
+	}
+
+	return status;
+}
Index: auth.h
===================================================================
--- auth.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
+++ auth.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -0,0 +1,13 @@
+#ifndef __BITLBEE_AUTH_H__
+#define __BITLBEE_AUTH_H__
+
+#include "storage.h"
+
+typedef struct {
+	const char *name;
+	storage_status_t (*check_pass)(const char *nick, const char *password);
+} auth_backend_t;
+
+GList *auth_init(const char *backend);
+storage_status_t auth_check_pass(irc_t *irc, const char *nick, const char *password);
+#endif
Index: bitlbee.conf
===================================================================
--- bitlbee.conf	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ bitlbee.conf	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -51,4 +51,17 @@
 ##
 # AuthMode = Open
+
+## AuthBackend
+##
+## By default, the authentication data for a user is stored in the storage
+## backend. If you want to authenticate against another authentication system
+## (e.g. ldap), you can specify that here.
+##
+## Beware that this disables password changes and causes passwords for the
+## accounts people create to be stored in plain text instead of encrypted with
+## their bitlbee password.
+#
+# AuthBackend = storage
+#
 
 ## AuthPassword
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ bitlbee.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -133,4 +133,5 @@
 #include "irc.h"
 #include "storage.h"
+#include "auth.h"
 #include "set.h"
 #include "nogaim.h"
@@ -154,4 +155,5 @@
 	conf_t *conf;
 	GList *storage; /* The first backend in the list will be used for saving */
+	GList *auth;    /* Authentication backends */
 	char *helpfile;
 	int restart;
Index: conf.c
===================================================================
--- conf.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ conf.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -55,4 +55,5 @@
 	conf->runmode = RUNMODE_INETD;
 	conf->authmode = AUTHMODE_OPEN;
+	conf->auth_backend = NULL;
 	conf->auth_pass = NULL;
 	conf->oper_pass = NULL;
@@ -241,4 +242,11 @@
 					conf->authmode = AUTHMODE_OPEN;
 				}
+			} else if (g_strcasecmp(ini->key, "authbackend") == 0) {
+				if (g_strcasecmp(ini->value, "storage") == 0) {
+					conf->auth_backend = NULL;
+				} else {
+					fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
+					return 0;
+				}
 			} else if (g_strcasecmp(ini->key, "authpassword") == 0) {
 				g_free(conf->auth_pass);
Index: conf.h
===================================================================
--- conf.h	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ conf.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -37,4 +37,5 @@
 	runmode_t runmode;
 	authmode_t authmode;
+	char *auth_backend;
 	char *auth_pass;
 	char *oper_pass;
Index: configure
===================================================================
--- configure	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ configure	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -628,4 +628,9 @@
 done
 echo "STORAGE_OBJS="$STORAGE_OBJS >> Makefile.settings
+
+authobjs=
+authlibs=
+echo AUTH_OBJS=$authobjs >> Makefile.settings
+echo EFLAGS+=$authlibs >> Makefile.settings
 
 if [ "$strip" = 0 ]; then
Index: irc.h
===================================================================
--- irc.h	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ irc.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -92,4 +92,5 @@
 	                   logging in, this may contain a password we should
 	                   send to identify after USER/NICK are received. */
+	char *auth_backend;
 
 	char umode[8];
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ irc_commands.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -97,5 +97,5 @@
 	/* just check the password here to be able to reply with useful numerics
 	 * the actual identification will be handled later */
-	status = storage_check_pass(user, pass);
+	status = auth_check_pass(irc, user, pass);
 
 	if (status == STORAGE_OK) {
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ root_commands.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -143,8 +143,7 @@
 	}
 
-	if (load) {
+	status = auth_check_pass(irc, irc->user->nick, password);
+	if (load && (status == STORAGE_OK)) {
 		status = storage_load(irc, password);
-	} else {
-		status = storage_check_pass(irc->user->nick, password);
 	}
 
@@ -159,5 +158,4 @@
 		irc_rootmsg(irc, "Password accepted%s",
 		            load ? ", settings and accounts loaded" : "");
-		irc_setpass(irc, password);
 		irc->status |= USTATUS_IDENTIFIED;
 		irc_umode_set(irc, "+R", 1);
@@ -268,5 +266,9 @@
 	storage_status_t status;
 
-	status = storage_remove(irc->user->nick, cmd[1]);
+	status = auth_check_pass(irc, irc->user->nick, cmd[1]);
+	if (status == STORAGE_OK) {
+		status = storage_remove(irc->user->nick);
+	}
+
 	switch (status) {
 	case STORAGE_NO_SUCH_USER:
Index: storage.c
===================================================================
--- storage.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ storage.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -87,5 +87,5 @@
 }
 
-storage_status_t storage_check_pass(const char *nick, const char *password)
+storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password)
 {
 	GList *gl;
@@ -97,5 +97,5 @@
 		storage_status_t status;
 
-		status = st->check_pass(nick, password);
+		status = st->check_pass(irc, nick, password);
 		if (status != STORAGE_NO_SUCH_USER) {
 			return status;
@@ -171,5 +171,5 @@
 }
 
-storage_status_t storage_remove(const char *nick, const char *password)
+storage_status_t storage_remove(const char *nick)
 {
 	GList *gl;
@@ -185,5 +185,5 @@
 		storage_status_t status;
 
-		status = st->remove(nick, password);
+		status = st->remove(nick);
 		ok |= status == STORAGE_OK;
 		if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK) {
Index: storage.h
===================================================================
--- storage.h	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ storage.h	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -31,4 +31,5 @@
 	STORAGE_NO_SUCH_USER,
 	STORAGE_INVALID_PASSWORD,
+	STORAGE_CHECK_BACKEND,
 	STORAGE_ALREADY_EXISTS,
 	STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as
@@ -43,9 +44,9 @@
 	void (*init)(void);
 
-	storage_status_t (*check_pass)(const char *nick, const char *password);
+	storage_status_t (*check_pass)(irc_t *irc, const char *nick, const char *password);
 
 	storage_status_t (*load)(irc_t *irc, const char *password);
 	storage_status_t (*save)(irc_t *irc, int overwrite);
-	storage_status_t (*remove)(const char *nick, const char *password);
+	storage_status_t (*remove)(const char *nick);
 
 	/* May be NULL if not supported by backend */
@@ -53,9 +54,9 @@
 } storage_t;
 
-storage_status_t storage_check_pass(const char *nick, const char *password);
+storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password);
 
 storage_status_t storage_load(irc_t * irc, const char *password);
 storage_status_t storage_save(irc_t *irc, char *password, int overwrite);
-storage_status_t storage_remove(const char *nick, const char *password);
+storage_status_t storage_remove(const char *nick);
 
 void register_storage_backend(storage_t *);
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ storage_xml.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -34,9 +34,7 @@
 
 typedef enum {
-	XML_PASS_CHECK_ONLY = -1,
-	XML_PASS_UNKNOWN = 0,
-	XML_PASS_WRONG,
-	XML_PASS_OK
-} xml_pass_st;
+	XML_PASS_CHECK = 0,
+	XML_LOAD
+} xml_action;
 
 /* To make it easier later when extending the format: */
@@ -121,26 +119,33 @@
 	if (!handle || !pass_b64 || !protocol || !prpl) {
 		return XT_ABORT;
-	} else if ((pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr)) &&
-	           arc_decode(pass_cr, pass_len, &password, xd->given_pass) >= 0) {
-		acc = account_add(xd->irc->b, prpl, handle, password);
-		if (server) {
-			set_setstr(&acc->set, "server", server);
-		}
-		if (autoconnect) {
-			set_setstr(&acc->set, "auto_connect", autoconnect);
-		}
-		if (tag) {
-			set_setstr(&acc->set, "tag", tag);
-		}
-		if (local) {
-			acc->flags |= ACC_FLAG_LOCAL;
-		}
-		if (locked && !g_strcasecmp(locked, "true")) {
-			acc->flags |= ACC_FLAG_LOCKED;
-		}
+	}
+
+	base64_decode(pass_b64, (unsigned char **) &pass_cr);
+	if (xd->irc->auth_backend) {
+		password = g_strdup((char *)pass_cr);
 	} else {
-		g_free(pass_cr);
-		g_free(password);
-		return XT_ABORT;
+		pass_len = arc_decode(pass_cr, pass_len, &password, xd->given_pass);
+		if (pass_len < 0) {
+			g_free(pass_cr);
+			g_free(password);
+			return XT_ABORT;
+		}
+	}
+
+	acc = account_add(xd->irc->b, prpl, handle, password);
+	if (server) {
+		set_setstr(&acc->set, "server", server);
+	}
+	if (autoconnect) {
+		set_setstr(&acc->set, "auto_connect", autoconnect);
+	}
+	if (tag) {
+		set_setstr(&acc->set, "tag", tag);
+	}
+	if (local) {
+		acc->flags |= ACC_FLAG_LOCAL;
+	}
+	if (locked && !g_strcasecmp(locked, "true")) {
+		acc->flags |= ACC_FLAG_LOCKED;
 	}
 
@@ -198,5 +203,5 @@
 };
 
-static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_pass_st action)
+static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_action action)
 {
 	struct xml_parsedata xd[1];
@@ -240,22 +245,25 @@
 	}
 
-	{
+	if (action == XML_PASS_CHECK) {
 		char *nick = xt_find_attr(node, "nick");
 		char *pass = xt_find_attr(node, "password");
-
-		if (!nick || !pass) {
+		char *backend = xt_find_attr(node, "auth_backend");
+
+		if (!nick || !(pass || backend)) {
 			goto error;
+		}
+
+		if (backend) {
+			g_free(xd->irc->auth_backend);
+			xd->irc->auth_backend = g_strdup(backend);
+			ret = STORAGE_CHECK_BACKEND;
 		} else if ((st = md5_verify_password(xd->given_pass, pass)) != 0) {
 			ret = STORAGE_INVALID_PASSWORD;
-			goto error;
-		}
-	}
-
-	if (action == XML_PASS_CHECK_ONLY) {
-		ret = STORAGE_OK;
-		goto error;
-	}
-
-	/* DO NOT call xt_handle() before verifying the password! */
+		} else {
+			ret = STORAGE_OK;
+		}
+		goto error;
+	}
+
 	if (xt_handle(xp, NULL, 1) == XT_HANDLED) {
 		ret = STORAGE_OK;
@@ -272,10 +280,10 @@
 static storage_status_t xml_load(irc_t *irc, const char *password)
 {
-	return xml_load_real(irc, irc->user->nick, password, XML_PASS_UNKNOWN);
-}
-
-static storage_status_t xml_check_pass(const char *my_nick, const char *password)
-{
-	return xml_load_real(NULL, my_nick, password, XML_PASS_CHECK_ONLY);
+	return xml_load_real(irc, irc->user->nick, password, XML_LOAD);
+}
+
+static storage_status_t xml_check_pass(irc_t *irc, const char *my_nick, const char *password)
+{
+	return xml_load_real(irc, my_nick, password, XML_PASS_CHECK);
 }
 
@@ -292,21 +300,24 @@
 	struct xt_node *root, *cur;
 
-	/* Generate a salted md5sum of the password. Use 5 bytes for the salt
-	   (to prevent dictionary lookups of passwords) to end up with a 21-
-	   byte password hash, more convenient for base64 encoding. */
-	random_bytes(pass_md5 + 16, 5);
-	md5_init(&md5_state);
-	md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
-	md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
-	md5_finish(&md5_state, pass_md5);
-	/* Save the hash in base64-encoded form. */
-	pass_buf = base64_encode(pass_md5, 21);
-
 	root = cur = xt_new_node("user", NULL, NULL);
+	if (irc->auth_backend) {
+		xt_add_attr(cur, "auth_backend", irc->auth_backend);
+	} else {
+		/* Generate a salted md5sum of the password. Use 5 bytes for the salt
+		   (to prevent dictionary lookups of passwords) to end up with a 21-
+		   byte password hash, more convenient for base64 encoding. */
+		random_bytes(pass_md5 + 16, 5);
+		md5_init(&md5_state);
+		md5_append(&md5_state, (md5_byte_t *) irc->password, strlen(irc->password));
+		md5_append(&md5_state, pass_md5 + 16, 5);   /* Add the salt. */
+		md5_finish(&md5_state, pass_md5);
+		/* Save the hash in base64-encoded form. */
+		pass_buf = base64_encode(pass_md5, 21);
+		xt_add_attr(cur, "password", pass_buf);
+		g_free(pass_buf);
+	}
+
 	xt_add_attr(cur, "nick", irc->user->nick);
-	xt_add_attr(cur, "password", pass_buf);
 	xt_add_attr(cur, "version", XML_FORMAT_VERSION);
-
-	g_free(pass_buf);
 
 	xml_generate_settings(cur, &irc->b->set);
@@ -319,7 +330,14 @@
 		int pass_len;
 
-		pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
-		pass_b64 = base64_encode(pass_cr, pass_len);
-		g_free(pass_cr);
+		if(irc->auth_backend) {
+			/* If we don't "own" the password, it may change without us
+			 * knowing, so we cannot encrypt the data, as we then may not be
+			 * able to decrypt it */
+			pass_b64 = base64_encode((unsigned char *)acc->pass, strlen(acc->pass));
+		} else {
+			pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
+			pass_b64 = base64_encode(pass_cr, pass_len);
+			g_free(pass_cr);
+		}
 
 		cur = xt_new_node("account", NULL, NULL);
@@ -440,13 +458,7 @@
 
 
-static storage_status_t xml_remove(const char *nick, const char *password)
+static storage_status_t xml_remove(const char *nick)
 {
 	char s[512], *lc;
-	storage_status_t status;
-
-	status = xml_check_pass(nick, password);
-	if (status != STORAGE_OK) {
-		return status;
-	}
 
 	lc = g_strdup(nick);
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ tests/Makefile	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -15,5 +15,5 @@
 distclean: clean
 
-main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o
+main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o auth.o
 
 test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o
Index: unix.c
===================================================================
--- unix.c	(revision 446a23ea39184c5fe43cd40706bb683b89534e2e)
+++ unix.c	(revision 8e6ecfe23ff985e57675bd00b94860edb62de9ad)
@@ -104,4 +104,10 @@
 	}
 
+	global.auth = auth_init(global.conf->auth_backend);
+	if (global.conf->auth_backend && global.auth == NULL) {
+		log_message(LOGLVL_ERROR, "Unable to load authentication backend '%s'", global.conf->auth_backend);
+		return(1);
+	}
+
 	if (global.conf->runmode == RUNMODE_INETD) {
 		log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
