diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | auth.c | 46 | ||||
-rw-r--r-- | auth.h | 13 | ||||
-rw-r--r-- | bitlbee.conf | 13 | ||||
-rw-r--r-- | bitlbee.h | 2 | ||||
-rw-r--r-- | conf.c | 8 | ||||
-rw-r--r-- | conf.h | 1 | ||||
-rwxr-xr-x | configure | 5 | ||||
-rw-r--r-- | irc.h | 1 | ||||
-rw-r--r-- | irc_commands.c | 2 | ||||
-rw-r--r-- | root_commands.c | 12 | ||||
-rw-r--r-- | storage.c | 8 | ||||
-rw-r--r-- | storage.h | 9 | ||||
-rw-r--r-- | storage_xml.c | 136 | ||||
-rw-r--r-- | tests/Makefile | 2 | ||||
-rw-r--r-- | unix.c | 6 |
16 files changed, 188 insertions, 78 deletions
@@ -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 @@ -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; +} @@ -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. @@ -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; @@ -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); @@ -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; @@ -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 @@ -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"); @@ -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; @@ -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 @@ -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); |