aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--auth.c46
-rw-r--r--auth.h13
-rw-r--r--bitlbee.conf13
-rw-r--r--bitlbee.h2
-rw-r--r--conf.c8
-rw-r--r--conf.h1
-rwxr-xr-xconfigure5
-rw-r--r--irc.h1
-rw-r--r--irc_commands.c2
-rw-r--r--root_commands.c12
-rw-r--r--storage.c8
-rw-r--r--storage.h9
-rw-r--r--storage_xml.c136
-rw-r--r--tests/Makefile2
-rw-r--r--unix.c6
16 files changed, 188 insertions, 78 deletions
diff --git a/Makefile b/Makefile
index 102af35f..3d930f97 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@
-include Makefile.settings
# 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
diff --git a/auth.c b/auth.c
new file mode 100644
index 00000000..e83a683f
--- /dev/null
+++ b/auth.c
@@ -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;
+}
diff --git a/auth.h b/auth.h
new file mode 100644
index 00000000..a38ef37b
--- /dev/null
+++ b/auth.h
@@ -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
diff --git a/bitlbee.conf b/bitlbee.conf
index a79a4483..60c5bdf7 100644
--- a/bitlbee.conf
+++ b/bitlbee.conf
@@ -51,6 +51,19 @@
##
# 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
##
## Password the user should enter when logging into a closed BitlBee server.
diff --git a/bitlbee.h b/bitlbee.h
index 0b708f13..26a1c982 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -132,6 +132,7 @@ extern "C" {
#include "bee.h"
#include "irc.h"
#include "storage.h"
+#include "auth.h"
#include "set.h"
#include "nogaim.h"
#include "commands.h"
@@ -153,6 +154,7 @@ typedef struct global {
char *conf_file;
conf_t *conf;
GList *storage; /* The first backend in the list will be used for saving */
+ GList *auth; /* Authentication backends */
char *helpfile;
int restart;
} global_t;
diff --git a/conf.c b/conf.c
index 6da77d59..24e71b91 100644
--- a/conf.c
+++ b/conf.c
@@ -54,6 +54,7 @@ conf_t *conf_load(int argc, char *argv[])
conf->migrate_storage = g_strsplit("text", ",", -1);
conf->runmode = RUNMODE_INETD;
conf->authmode = AUTHMODE_OPEN;
+ conf->auth_backend = NULL;
conf->auth_pass = NULL;
conf->oper_pass = NULL;
conf->allow_account_add = 1;
@@ -240,6 +241,13 @@ static int conf_loadini(conf_t *conf, char *file)
} else {
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);
conf->auth_pass = g_strdup(ini->value);
diff --git a/conf.h b/conf.h
index 12b4d369..cd600775 100644
--- a/conf.h
+++ b/conf.h
@@ -36,6 +36,7 @@ typedef struct conf {
int verbose;
runmode_t runmode;
authmode_t authmode;
+ char *auth_backend;
char *auth_pass;
char *oper_pass;
int allow_account_add;
diff --git a/configure b/configure
index f2b913c6..8089d1a2 100755
--- a/configure
+++ b/configure
@@ -628,6 +628,11 @@ for i in $STORAGES; do
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
echo "STRIP=\# skip strip" >> Makefile.settings;
else
diff --git a/irc.h b/irc.h
index 350719f5..2e0cc3d5 100644
--- a/irc.h
+++ b/irc.h
@@ -91,6 +91,7 @@ typedef struct irc {
char *password; /* HACK: Used to save the user's password, but before
logging in, this may contain a password we should
send to identify after USER/NICK are received. */
+ char *auth_backend;
char umode[8];
diff --git a/irc_commands.c b/irc_commands.c
index 694fe35c..d3971df9 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -96,7 +96,7 @@ static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass)
/* 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) {
if (!irc->user->nick) {
diff --git a/root_commands.c b/root_commands.c
index 0f024345..dcf7a7ed 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -142,10 +142,9 @@ static void cmd_identify(irc_t *irc, char **cmd)
return;
}
- 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);
}
switch (status) {
@@ -158,7 +157,6 @@ static void cmd_identify(irc_t *irc, char **cmd)
case STORAGE_OK:
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);
@@ -267,7 +265,11 @@ static void cmd_drop(irc_t *irc, char **cmd)
{
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:
irc_rootmsg(irc, "That account does not exist");
diff --git a/storage.c b/storage.c
index 510def72..7b684ac7 100644
--- a/storage.c
+++ b/storage.c
@@ -86,7 +86,7 @@ GList *storage_init(const char *primary, char **migrate)
return ret;
}
-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;
@@ -96,7 +96,7 @@ storage_status_t storage_check_pass(const char *nick, const char *password)
storage_t *st = gl->data;
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;
}
@@ -170,7 +170,7 @@ storage_status_t storage_save(irc_t *irc, char *password, int overwrite)
return st;
}
-storage_status_t storage_remove(const char *nick, const char *password)
+storage_status_t storage_remove(const char *nick)
{
GList *gl;
storage_status_t ret = STORAGE_OK;
@@ -184,7 +184,7 @@ storage_status_t storage_remove(const char *nick, const char *password)
storage_t *st = gl->data;
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) {
ret = status;
diff --git a/storage.h b/storage.h
index 829f8454..6e6387ed 100644
--- a/storage.h
+++ b/storage.h
@@ -30,6 +30,7 @@ typedef enum {
STORAGE_OK = 0,
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
a database that is unreachable. log() will be
@@ -42,21 +43,21 @@ typedef struct {
/* May be set to NULL if not required */
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 */
storage_status_t (*rename)(const char *onick, const char *nnick, const char *password);
} 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 *);
G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
diff --git a/storage_xml.c b/storage_xml.c
index dbdd151d..7e8555ef 100644
--- a/storage_xml.c
+++ b/storage_xml.c
@@ -33,11 +33,9 @@
#include <glib/gstdio.h>
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: */
#define XML_FORMAT_VERSION "1"
@@ -120,28 +118,35 @@ static xt_status handle_account(struct xt_node *node, gpointer data)
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;
}
g_free(pass_cr);
@@ -197,7 +202,7 @@ static const struct xt_handler_entry handlers[] = {
{ NULL, NULL, NULL, },
};
-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];
char *fn, buf[2048];
@@ -239,24 +244,27 @@ static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const cha
goto error;
}
- {
+ if (action == XML_PASS_CHECK) {
char *nick = xt_find_attr(node, "nick");
char *pass = xt_find_attr(node, "password");
+ char *backend = xt_find_attr(node, "auth_backend");
- if (!nick || !pass) {
+ 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;
+ } else {
+ ret = STORAGE_OK;
}
- }
-
- if (action == XML_PASS_CHECK_ONLY) {
- ret = STORAGE_OK;
goto error;
}
- /* DO NOT call xt_handle() before verifying the password! */
if (xt_handle(xp, NULL, 1) == XT_HANDLED) {
ret = STORAGE_OK;
}
@@ -271,12 +279,12 @@ error:
static storage_status_t xml_load(irc_t *irc, const char *password)
{
- return xml_load_real(irc, irc->user->nick, password, XML_PASS_UNKNOWN);
+ return xml_load_real(irc, irc->user->nick, password, XML_LOAD);
}
-static storage_status_t xml_check_pass(const char *my_nick, const char *password)
+static storage_status_t xml_check_pass(irc_t *irc, const char *my_nick, const char *password)
{
- return xml_load_real(NULL, my_nick, password, XML_PASS_CHECK_ONLY);
+ return xml_load_real(irc, my_nick, password, XML_PASS_CHECK);
}
@@ -291,24 +299,27 @@ struct xt_node *xml_generate(irc_t *irc)
GSList *l;
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);
for (acc = irc->b->accounts; acc; acc = acc->next) {
@@ -318,9 +329,16 @@ struct xt_node *xml_generate(irc_t *irc)
char *pass_b64;
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);
xt_add_attr(cur, "protocol", acc->prpl->name);
@@ -439,15 +457,9 @@ finish:
}
-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);
nick_lc(NULL, lc);
diff --git a/tests/Makefile b/tests/Makefile
index efca9bff..02cac9eb 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -14,7 +14,7 @@ clean:
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
diff --git a/unix.c b/unix.c
index 8794a904..da4711d7 100644
--- a/unix.c
+++ b/unix.c
@@ -103,6 +103,12 @@ int main(int argc, char *argv[])
return(1);
}
+ 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);
log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);