aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml6
-rw-r--r--Makefile2
-rw-r--r--auth.c65
-rw-r--r--auth.h13
-rw-r--r--auth_ldap.c77
-rw-r--r--auth_pam.c62
-rw-r--r--bitlbee.conf26
-rw-r--r--bitlbee.h2
-rw-r--r--conf.c19
-rw-r--r--conf.h2
-rwxr-xr-xconfigure33
-rw-r--r--irc.h1
-rw-r--r--irc_cap.c3
-rw-r--r--irc_commands.c2
-rw-r--r--protocols/account.c4
-rw-r--r--protocols/account.h2
-rw-r--r--root_commands.c29
-rw-r--r--set.h1
-rw-r--r--storage.c8
-rw-r--r--storage.h9
-rw-r--r--storage_xml.c150
-rw-r--r--tests/Makefile2
-rw-r--r--unix.c6
23 files changed, 443 insertions, 81 deletions
diff --git a/.travis.yml b/.travis.yml
index 6a0da07f..9162ca4c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ sudo: false
language: c
script:
- - ./configure
+ - ./configure --pam=1 --ldap=1
- make check
- BITLBEE_SKYPE=plugin dpkg-buildpackage -uc -us -d
@@ -28,12 +28,14 @@ addons:
- libevent-dev
- libpurple-dev
- check
+ - libpam0g-dev
+ - libldap2-dev
coverity_scan:
project:
name: "bitlbee/bitlbee"
description: "An IRC to other chat networks gateway"
notification_email: dx@dxzone.com.ar
- build_command_prepend: ./configure --otr=1 --debug=1
+ build_command_prepend: ./configure --otr=1 --debug=1 --pam=1 --ldap=1
build_command: make
branch_pattern: coverity_scan
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..5a9d8bb8
--- /dev/null
+++ b/auth.c
@@ -0,0 +1,65 @@
+#define BITLBEE_CORE
+#include "bitlbee.h"
+
+#ifdef WITH_PAM
+extern auth_backend_t auth_pam;
+#endif
+#ifdef WITH_LDAP
+extern auth_backend_t auth_ldap;
+#endif
+
+GList *auth_init(const char *backend)
+{
+ GList *gl = NULL;
+ int ok = backend ? 0 : 1;
+#ifdef WITH_PAM
+ gl = g_list_append(gl, &auth_pam);
+ if (backend && !strcmp(backend, "pam")) {
+ ok = 1;
+ }
+#endif
+#ifdef WITH_LDAP
+ gl = g_list_append(gl, &auth_ldap);
+ if (backend && !strcmp(backend, "ldap")) {
+ ok = 1;
+ }
+#endif
+
+ 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/auth_ldap.c b/auth_ldap.c
new file mode 100644
index 00000000..e2cff8f7
--- /dev/null
+++ b/auth_ldap.c
@@ -0,0 +1,77 @@
+#define BITLBEE_CORE
+#define LDAP_DEPRECATED 1
+#include "bitlbee.h"
+#include <ldap.h>
+
+static storage_status_t ldap_check_pass(const char *nick, const char *password)
+{
+ LDAP *ldap;
+ LDAPMessage *msg, *entry;
+ char *dn = NULL;
+ char *filter;
+ char *attrs[1] = { NULL };
+ int ret, count;
+
+ if((ret = ldap_initialize(&ldap, NULL)) != LDAP_SUCCESS) {
+ log_message(LOGLVL_WARNING, "ldap_initialize failed: %s", ldap_err2string(ret));
+ return STORAGE_OTHER_ERROR;
+ }
+
+ /* First we do an anonymous bind to map uid=$nick to a DN*/
+ if((ret = ldap_simple_bind_s(ldap, NULL, NULL)) != LDAP_SUCCESS) {
+ ldap_unbind_s(ldap);
+ log_message(LOGLVL_WARNING, "Anonymous bind failed: %s", ldap_err2string(ret));
+ return STORAGE_OTHER_ERROR;
+ }
+
+
+ /* We search and process the result */
+ filter = g_strdup_printf("(uid=%s)", nick);
+ ret = ldap_search_ext_s(ldap, NULL, LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL, NULL, 1, &msg);
+ g_free(filter);
+
+ if(ret != LDAP_SUCCESS) {
+ ldap_unbind_s(ldap);
+ log_message(LOGLVL_WARNING, "uid search failed: %s", ldap_err2string(ret));
+ return STORAGE_OTHER_ERROR;
+ }
+
+ count = ldap_count_entries(ldap, msg);
+ if (count == -1) {
+ ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &ret);
+ ldap_msgfree(msg);
+ ldap_unbind_s(ldap);
+ log_message(LOGLVL_WARNING, "uid search failed: %s", ldap_err2string(ret));
+ return STORAGE_OTHER_ERROR;
+ }
+
+ if (!count) {
+ ldap_msgfree(msg);
+ ldap_unbind_s(ldap);
+ return STORAGE_NO_SUCH_USER;
+ }
+
+ entry = ldap_first_entry(ldap, msg);
+ dn = ldap_get_dn(ldap, entry);
+ ldap_msgfree(msg);
+
+ /* And now we bind as the user to authenticate */
+ ret = ldap_simple_bind_s(ldap, dn, password);
+ g_free(dn);
+ ldap_unbind_s(ldap);
+
+ switch (ret) {
+ case LDAP_SUCCESS:
+ return STORAGE_OK;
+ case LDAP_INVALID_CREDENTIALS:
+ return STORAGE_INVALID_PASSWORD;
+ default:
+ log_message(LOGLVL_WARNING, "Authenticated bind failed: %s", ldap_err2string(ret));
+ return STORAGE_OTHER_ERROR;
+ }
+}
+
+auth_backend_t auth_ldap = {
+ .name = "ldap",
+ .check_pass = ldap_check_pass,
+};
diff --git a/auth_pam.c b/auth_pam.c
new file mode 100644
index 00000000..1a8f4344
--- /dev/null
+++ b/auth_pam.c
@@ -0,0 +1,62 @@
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include <security/pam_appl.h>
+
+#define PAM_CHECK(x) do { \
+ ret = (x); \
+ if(ret != PAM_SUCCESS) { \
+ pam_func = #x; \
+ goto pam_error; \
+ } \
+} while(0)
+
+/* This function fills in the password when PAM asks for it */
+int pamconv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
+ int i;
+ struct pam_response *rsp = g_new0(struct pam_response, num_msg);
+
+ for (i = 0; i < num_msg; i++) {
+ rsp[i].resp = NULL;
+ rsp[i].resp_retcode = 0;
+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
+ rsp[i].resp = g_strdup((char *)appdata_ptr);
+ }
+ }
+ *resp = rsp;
+ return PAM_SUCCESS;
+}
+
+static storage_status_t pam_check_pass(const char *nick, const char *password)
+{
+ int ret;
+ const struct pam_conv pamc = { pamconv, (void*) password };
+ pam_handle_t *pamh = NULL;
+ char *pam_func;
+
+ PAM_CHECK(pam_start("bitlbee", nick, &pamc, &pamh));
+ PAM_CHECK(pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK));
+ PAM_CHECK(pam_acct_mgmt(pamh, 0));
+
+ pam_end(pamh, ret);
+ return STORAGE_OK;
+
+pam_error:
+ switch (ret) {
+ case PAM_AUTH_ERR:
+ pam_end(pamh, ret);
+ return STORAGE_INVALID_PASSWORD;
+ case PAM_USER_UNKNOWN:
+ case PAM_PERM_DENIED:
+ pam_end(pamh, ret);
+ return STORAGE_NO_SUCH_USER;
+ default:
+ log_message(LOGLVL_WARNING, "%s failed: %s", pam_func, pam_strerror(pamh, ret));
+ pam_end(pamh, ret);
+ return STORAGE_OTHER_ERROR;
+ }
+}
+
+auth_backend_t auth_pam = {
+ .name = "pam",
+ .check_pass = pam_check_pass,
+};
diff --git a/bitlbee.conf b/bitlbee.conf
index 51b5777a..b6544378 100644
--- a/bitlbee.conf
+++ b/bitlbee.conf
@@ -51,6 +51,25 @@
##
# 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.
+##
+## Currently available backends:
+##
+## - storage (internal storage)
+## - pam (Linux PAM authentication)
+## - ldap (LDAP server configured in the openldap settings)
+#
+# AuthBackend = storage
+#
+
## AuthPassword
##
## Password the user should enter when logging into a closed BitlBee server.
@@ -69,6 +88,13 @@
## or
# OperPassword = md5:I0mnZbn1t4R731zzRdDN2/pK7lRX
+## AllowAccountAdd
+##
+## Whether to allow registered and identified users to add new accounts using
+## 'account add'
+##
+# AllowAccountAdd 1
+
## HostName
##
## Normally, BitlBee gets a hostname using getsockname(). If you have a nicer
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 b249a11b..8c2439e7 100644
--- a/conf.c
+++ b/conf.c
@@ -54,8 +54,10 @@ 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;
conf->configdir = g_strdup(CONFIG);
conf->plugindir = g_strdup(PLUGINDIR);
conf->pidfile = g_strdup(PIDFILE);
@@ -239,12 +241,29 @@ 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 if (g_strcasecmp(ini->value, "pam") == 0 ||
+ g_strcasecmp(ini->value, "ldap") == 0) {
+ g_free(conf->auth_backend);
+ conf->auth_backend = g_strdup(ini->value);
+ } 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);
} else if (g_strcasecmp(ini->key, "operpassword") == 0) {
g_free(conf->oper_pass);
conf->oper_pass = g_strdup(ini->value);
+ } else if (g_strcasecmp(ini->key, "allowaccountadd") == 0) {
+ if (!is_bool(ini->value)) {
+ fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
+ return 0;
+ }
+ conf->allow_account_add = bool2int(ini->value);
} else if (g_strcasecmp(ini->key, "hostname") == 0) {
g_free(conf->hostname);
conf->hostname = g_strdup(ini->value);
diff --git a/conf.h b/conf.h
index 1ee311bd..cd600775 100644
--- a/conf.h
+++ b/conf.h
@@ -36,8 +36,10 @@ typedef struct conf {
int verbose;
runmode_t runmode;
authmode_t authmode;
+ char *auth_backend;
char *auth_pass;
char *oper_pass;
+ int allow_account_add;
char *hostname;
char *configdir;
char *plugindir;
diff --git a/configure b/configure
index f2b913c6..9cc81794 100755
--- a/configure
+++ b/configure
@@ -51,6 +51,9 @@ skype=0
events=glib
ssl=auto
+pam=0
+ldap=0
+
pie=1
arch=$(uname -s)
@@ -133,6 +136,9 @@ Option Description Default
--purple=0/1 Disable/enable libpurple support $purple
(automatically disables other protocol modules)
+--pam=0/1 Disable/enable PAM authentication $pam
+--ldap=0/1 Disable/enable LDAP authentication $ldap
+
--doc=0/1 Disable/enable help.txt generation $doc
--debug=0/1 Disable/enable debugging $debug
--strip=0/1 Disable/enable binary stripping $strip
@@ -628,6 +634,33 @@ for i in $STORAGES; do
done
echo "STORAGE_OBJS="$STORAGE_OBJS >> Makefile.settings
+authobjs=
+authlibs=
+if [ "$pam" = 0 ]; then
+ echo '#undef WITH_PAM' >> config.h
+else
+ if ! echo '#include <security/pam_appl.h>' | $CC -E - >/dev/null 2>/dev/null; then
+ echo 'Cannot find libpam development libraries, aborting. (Install libpam0g-dev?)'
+ exit 1
+ fi
+ echo '#define WITH_PAM' >> config.h
+ authobjs=$authobjs'auth_pam.o '
+ authlibs=$authlibs'-lpam '
+fi
+if [ "$ldap" = 0 ]; then
+ echo '#undef WITH_LDAP' >> config.h
+else
+ if ! echo '#include <ldap.h>' | $CC -E - >/dev/null 2>/dev/null; then
+ echo 'Cannot find libldap development libraries, aborting. (Install libldap2-dev?)'
+ exit 1
+ fi
+ echo '#define WITH_LDAP' >> config.h
+ authobjs=$authobjs'auth_ldap.o '
+ authlibs=$authlibs'-lldap '
+fi
+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_cap.c b/irc_cap.c
index 42f70529..af1215e0 100644
--- a/irc_cap.c
+++ b/irc_cap.c
@@ -176,6 +176,9 @@ void irc_cmd_cap(irc_t *irc, char **cmd)
irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : "");
} else if (g_strcasecmp(cmd[1], "END") == 0) {
+ if (!(irc->status & USTATUS_CAP_PENDING)) {
+ return;
+ }
irc->status &= ~USTATUS_CAP_PENDING;
if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
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/protocols/account.c b/protocols/account.c
index fcafe215..e25e40c7 100644
--- a/protocols/account.c
+++ b/protocols/account.c
@@ -66,13 +66,13 @@ account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass)
s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
s = set_add(&a->set, "password", NULL, set_eval_account, a);
- s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
+ s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
s = set_add(&a->set, "tag", NULL, set_eval_account, a);
s->flags |= SET_NOSAVE;
s = set_add(&a->set, "username", NULL, set_eval_account, a);
- s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+ s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
set_setstr(&a->set, "username", user);
/* Hardcode some more clever tag guesses. */
diff --git a/protocols/account.h b/protocols/account.h
index 0e118680..bea8ca9f 100644
--- a/protocols/account.h
+++ b/protocols/account.h
@@ -62,6 +62,7 @@ int protocol_account_islocal(const char* protocol);
typedef enum {
ACC_SET_OFFLINE_ONLY = 0x02, /* Allow changes only if the acct is offline. */
ACC_SET_ONLINE_ONLY = 0x04, /* Allow changes only if the acct is online. */
+ ACC_SET_LOCKABLE = 0x08 /* Setting cannot be changed if the account is locked down */
} account_set_flag_t;
typedef enum {
@@ -69,6 +70,7 @@ typedef enum {
ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
ACC_FLAG_LOCAL = 0x08, /* Contact list is local. */
+ ACC_FLAG_LOCKED = 0x10, /* Account is locked (cannot be deleted, certain settings can't changed) */
} account_flag_t;
#endif
diff --git a/root_commands.c b/root_commands.c
index 4ce964ae..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");
@@ -339,6 +341,10 @@ static int cmd_set_real(irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags
set_t *s = set_find(head, set_name);
int st;
+ if (s && s->flags & SET_LOCKED) {
+ irc_rootmsg(irc, "This setting can not be changed");
+ return 0;
+ }
if (s && checkflags && checkflags(irc, s) == 0) {
return 0;
}
@@ -387,6 +393,9 @@ static int cmd_account_set_checkflags(irc_t *irc, set_t *s)
} else if (!a->ic && s && s->flags & ACC_SET_ONLINE_ONLY) {
irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "on");
return 0;
+ } else if (a->flags & ACC_FLAG_LOCKED && s && s->flags & ACC_SET_LOCKABLE) {
+ irc_rootmsg(irc, "This setting can not be changed for locked accounts");
+ return 0;
}
return 1;
@@ -409,6 +418,11 @@ static void cmd_account(irc_t *irc, char **cmd)
MIN_ARGS(3);
+ if (!global.conf->allow_account_add) {
+ irc_rootmsg(irc, "This server does not allow adding new accounts");
+ return;
+ }
+
if (cmd[4] == NULL) {
for (a = irc->b->accounts; a; a = a->next) {
if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
@@ -546,7 +560,10 @@ static void cmd_account(irc_t *irc, char **cmd)
}
if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
- if (a->ic) {
+ if (a->flags & ACC_FLAG_LOCKED) {
+ irc_rootmsg(irc, "Account is locked, can't delete");
+ }
+ else if (a->ic) {
irc_rootmsg(irc, "Account is still logged in, can't delete");
} else {
account_del(irc->b, a);
diff --git a/set.h b/set.h
index 3735d797..f0a51136 100644
--- a/set.h
+++ b/set.h
@@ -48,6 +48,7 @@ typedef enum {
SET_HIDDEN = 0x0200, /* Don't show up in setting lists. Mostly for internal storage. */
SET_PASSWORD = 0x0400, /* Value shows up in settings list as "********". */
SET_HIDDEN_DEFAULT = 0x0800, /* Hide unless changed from default. */
+ SET_LOCKED = 0x1000 /* Setting is locked, don't allow changing it */
} set_flags_t;
typedef struct set {
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 4237e10e..20f3fe3c 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"
@@ -64,9 +62,11 @@ static void xml_init(void)
static void handle_settings(struct xt_node *node, set_t **head)
{
struct xt_node *c;
+ struct set *s;
for (c = node->children; (c = xt_find_node(c, "setting")); c = c->next) {
char *name = xt_find_attr(c, "name");
+ char *locked = xt_find_attr(c, "locked");
if (!name) {
continue;
@@ -79,13 +79,19 @@ static void handle_settings(struct xt_node *node, set_t **head)
}
}
set_setstr(head, name, c->text);
+ if (locked && !g_strcasecmp(locked, "true")) {
+ s = set_find(head, name);
+ if (s) {
+ s->flags |= SET_LOCKED;
+ }
+ }
}
}
static xt_status handle_account(struct xt_node *node, gpointer data)
{
struct xml_parsedata *xd = data;
- char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;
+ char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag, *locked;
char *pass_b64 = NULL;
unsigned char *pass_cr = NULL;
int pass_len, local = 0;
@@ -98,6 +104,7 @@ static xt_status handle_account(struct xt_node *node, gpointer data)
server = xt_find_attr(node, "server");
autoconnect = xt_find_attr(node, "autoconnect");
tag = xt_find_attr(node, "tag");
+ locked = xt_find_attr(node, "locked");
protocol = xt_find_attr(node, "protocol");
if (protocol) {
@@ -111,25 +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;
- }
+ }
+
+ pass_len = 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);
@@ -185,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];
@@ -227,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;
}
@@ -259,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);
}
@@ -279,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) {
@@ -306,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);
@@ -319,6 +349,9 @@ struct xt_node *xml_generate(irc_t *irc)
if (acc->server && acc->server[0]) {
xt_add_attr(cur, "server", acc->server);
}
+ if (acc->flags & ACC_FLAG_LOCKED) {
+ xt_add_attr(cur, "locked", "true");
+ }
g_free(pass_b64);
@@ -363,6 +396,9 @@ static void xml_generate_settings(struct xt_node *cur, set_t **head)
struct xt_node *xset;
xt_add_child(cur, xset = xt_new_node("setting", set->value, NULL));
xt_add_attr(xset, "name", set->key);
+ if (set->flags & SET_LOCKED) {
+ xt_add_attr(xset, "locked", "true");
+ }
}
}
}
@@ -421,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..7756c17f 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 auth_pam.o auth_ldap.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);