diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | account.c | 3 | ||||
-rw-r--r-- | account.h | 1 | ||||
-rw-r--r-- | bitlbee.c | 4 | ||||
-rw-r--r-- | bitlbee.h | 2 | ||||
-rw-r--r-- | conf.c | 5 | ||||
-rwxr-xr-x | configure | 71 | ||||
-rw-r--r-- | doc/bitlbee.schema | 62 | ||||
-rw-r--r-- | irc.c | 10 | ||||
-rw-r--r-- | lib/Makefile | 37 | ||||
-rw-r--r-- | lib/base64.c | 153 | ||||
-rw-r--r-- | lib/base64.h | 33 | ||||
-rw-r--r-- | lib/events.h (renamed from protocols/events.h) | 0 | ||||
-rw-r--r-- | lib/events_glib.c (renamed from protocols/events_glib.c) | 0 | ||||
-rw-r--r-- | lib/events_libevent.c (renamed from protocols/events_libevent.c) | 0 | ||||
-rw-r--r-- | lib/http_client.c (renamed from protocols/http_client.c) | 0 | ||||
-rw-r--r-- | lib/http_client.h (renamed from protocols/http_client.h) | 0 | ||||
-rw-r--r-- | lib/ini.c (renamed from ini.c) | 0 | ||||
-rw-r--r-- | lib/ini.h (renamed from ini.h) | 0 | ||||
-rw-r--r-- | lib/md5.c (renamed from protocols/md5.c) | 0 | ||||
-rw-r--r-- | lib/md5.h (renamed from protocols/md5.h) | 0 | ||||
-rw-r--r-- | lib/misc.c (renamed from util.c) | 57 | ||||
-rw-r--r-- | lib/misc.h (renamed from util.h) | 1 | ||||
-rw-r--r-- | lib/proxy.c (renamed from protocols/proxy.c) | 1 | ||||
-rw-r--r-- | lib/proxy.h (renamed from protocols/proxy.h) | 0 | ||||
-rw-r--r-- | lib/rc4.c | 184 | ||||
-rw-r--r-- | lib/rc4.h | 34 | ||||
-rw-r--r-- | lib/sha.c (renamed from protocols/sha.c) | 0 | ||||
-rw-r--r-- | lib/sha.h (renamed from protocols/sha.h) | 0 | ||||
-rw-r--r-- | lib/ssl_bogus.c (renamed from protocols/ssl_bogus.c) | 0 | ||||
-rw-r--r-- | lib/ssl_client.h (renamed from protocols/ssl_client.h) | 0 | ||||
-rw-r--r-- | lib/ssl_gnutls.c (renamed from protocols/ssl_gnutls.c) | 0 | ||||
-rw-r--r-- | lib/ssl_nss.c (renamed from protocols/ssl_nss.c) | 0 | ||||
-rw-r--r-- | lib/ssl_openssl.c (renamed from protocols/ssl_openssl.c) | 0 | ||||
-rw-r--r-- | lib/url.c (renamed from url.c) | 0 | ||||
-rw-r--r-- | lib/url.h (renamed from url.h) | 0 | ||||
-rw-r--r-- | protocols/Makefile | 2 | ||||
-rw-r--r-- | protocols/nogaim.c | 1 | ||||
-rw-r--r-- | protocols/yahoo/libyahoo2.c | 30 | ||||
-rw-r--r-- | root_commands.c | 7 | ||||
-rw-r--r-- | storage.c | 11 | ||||
-rw-r--r-- | storage.h | 4 | ||||
-rw-r--r-- | storage_ldap.c | 177 | ||||
-rw-r--r-- | storage_xml.c | 506 | ||||
-rw-r--r-- | unix.c | 14 |
45 files changed, 1300 insertions, 116 deletions
@@ -9,9 +9,11 @@ -include Makefile.settings # Program variables -objects = account.o bitlbee.o conf.o crypting.o help.o ini.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_text.o unix.o url.o user.o util.o +objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_text.o storage_xml.o unix.o user.o headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ini.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h url.h user.h protocols/http_client.h protocols/md5.h protocols/nogaim.h protocols/proxy.h protocols/sha.h protocols/ssl_client.h -subdirs = protocols +subdirs = protocols lib + +objects += $(LDAP_OBJ) # Expansion of variables subdirobjs = $(foreach dir,$(subdirs),$(dir)/$(dir).o) @@ -34,7 +34,7 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) if( irc->accounts ) { for( a = irc->accounts; a->next; a = a->next ); - a = a->next = g_new0 ( account_t, 1 ); + a = a->next = g_new0( account_t, 1 ); } else { @@ -44,6 +44,7 @@ account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass ) a->prpl = prpl; a->user = g_strdup( user ); a->pass = g_strdup( pass ); + a->auto_connect = 1; a->irc = irc; return( a ); @@ -33,6 +33,7 @@ typedef struct account char *pass; char *server; + int auto_connect; int reconnect; struct irc *irc; @@ -290,6 +290,10 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition { irc_t *irc; + /* Since we're fork()ing here, let's make sure we won't + get the same random numbers as the parent/siblings. */ + srand( time( NULL ) ^ getpid() ); + /* Close the listening socket, we're a client. */ close( global.listen_socket ); b_event_remove( global.listen_watch_source_id ); @@ -129,7 +129,7 @@ extern char *CONF_FILE; #include "help.h" #include "query.h" #include "sock.h" -#include "util.h" +#include "misc.h" #include "proxy.h" typedef struct global { @@ -33,7 +33,7 @@ #include "url.h" #include "ipc.h" -#include "protocols/proxy.h" +#include "proxy.h" char *CONF_FILE; @@ -54,7 +54,8 @@ conf_t *conf_load( int argc, char *argv[] ) conf->port = 6667; conf->nofork = 0; conf->verbose = 0; - conf->primary_storage = "text"; + conf->primary_storage = "xml"; + conf->migrate_storage = g_strsplit( "text", ",", -1 ); conf->runmode = RUNMODE_INETD; conf->authmode = AUTHMODE_OPEN; conf->auth_pass = NULL; @@ -30,6 +30,7 @@ strip=1 ipv6=1 events=glib +ldap=auto ssl=auto arch=`uname -s` @@ -66,6 +67,8 @@ Option Description Default --ipv6=0/1 IPv6 socket support $ipv6 +--ldap=0/1/auto LDAP support $ldap + --events=... Event handler (glib, libevent) $events --ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) $ssl @@ -140,21 +143,23 @@ else echo 'CFLAGS=-O3' >> Makefile.settings fi -echo CFLAGS+=-I`pwd` -I`pwd`/protocols -I. >> Makefile.settings +echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings if [ -n "$CC" ]; then - echo "CC=$CC" >> Makefile.settings; + CC=$CC elif type gcc > /dev/null 2> /dev/null; then - echo "CC=gcc" >> Makefile.settings; + CC=gcc elif type cc > /dev/null 2> /dev/null; then - echo "CC=cc" >> Makefile.settings; + CC=cc else echo 'Cannot find a C compiler, aborting.' exit 1; fi +echo "CC=$CC" >> Makefile.settings; + if [ -n "$LD" ]; then echo "LD=$LD" >> Makefile.settings; elif type ld > /dev/null 2> /dev/null; then @@ -201,6 +206,29 @@ else fi echo 'EVENT_HANDLER=events_'$events'.o' >> Makefile.settings +if [ "$events" = "libevent" ]; then + if ! [ -e "${libevent}include/event.h" ]; then + echo + echo 'Warning: Could not find event.h, you might have to install it and/or specify' + echo 'its location using the --libevent= argument. (Example: If event.h is in' + echo '/usr/local/include and binaries are in /usr/local/lib: --libevent=/usr/local)' + fi + + echo '#define EVENTS_LIBEVENT' >> config.h + cat <<EOF>>Makefile.settings +EFLAGS+=-levent -L${libevent}lib +CFLAGS+=-I${libevent}include +EOF +elif [ "$events" = "glib" ]; then + ## We already use glib anyway, so this is all we need (and in fact not even this, but just to be sure...): + echo '#define EVENTS_GLIB' >> config.h +else + echo + echo 'ERROR: Unknown event handler specified.' + exit 1 +fi +echo 'EVENT_HANDLER=events_'$events'.o' >> Makefile.settings + detect_gnutls() { if libgnutls-config --version > /dev/null 2> /dev/null; then @@ -231,6 +259,23 @@ EOF fi; } +detect_ldap() +{ + TMPFILE=`mktemp` + if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then + cat<<EOF>>Makefile.settings +EFLAGS+=-lldap +CFLAGS+= +EOF + ldap=1 + rm -f $TMPFILE + ret=1 + else + ldap=0 + ret=0 + fi +} + if [ "$msn" = 1 -o "$jabber" = 1 ]; then if [ "$ssl" = "auto" ]; then detect_gnutls @@ -291,6 +336,18 @@ if [ "$msn" = 1 -o "$jabber" = 1 ]; then echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings fi +if [ "$ldap" = "auto" ]; then + detect_ldap +fi + +if [ "$ldap" = 0 ]; then + echo "LDAP_OBJ=" >> Makefile.settings + echo "#undef WITH_LDAP" >> config.h +elif [ "$ldap" = 1 ]; then + echo "#define WITH_LDAP 1" >> config.h + echo "LDAP_OBJ=storage_ldap.o" >> Makefile.settings +fi + if [ "$strip" = 0 ]; then echo "STRIP=\# skip strip" >> Makefile.settings; else @@ -443,3 +500,9 @@ if [ -n "$protocols" ]; then else echo ' Building without IM-protocol support. We wish you a lot of fun...'; fi + +if [ "$ldap" = "0" ]; then + echo " LDAP storage backend disabled." +else + echo " LDAP storage backend enabled." +fi diff --git a/doc/bitlbee.schema b/doc/bitlbee.schema new file mode 100644 index 00000000..3322e057 --- /dev/null +++ b/doc/bitlbee.schema @@ -0,0 +1,62 @@ +## LDAP Schema file for BitlBee +## Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> +## +## We need the following object classes and related attributes: +## +## bitlBeeBuddy: +## - nick +## - handle + +## each bitlBeeNick has zero or more bitlBeeAccount subentries +## and bitlBeeAccount entries contain zero or more bitlBeeBuddy entries + +## The admin needs to setup the LDAP server to: +## - allow anonymous users to auth against bitlBeeNick objects on the +## password field +## - allow anonymous users to create new objects that start with nick= +## - allow read/write for a user that is authenticated only to his/her own +## object and subentries + +## - userid +## - userPassword +## - setting (multiple values) +## depends: top, account + +attributetype ( 1.3.6.1.4.1.25873.2.1.1 NAME 'bitlBeeAutoConnect' + DESC 'Autoconnect setting' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.4.1.25873.2.1.2 NAME 'bitlBeeAccountNo' + DESC 'Account number' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeAccount' SUP account STRUCTURAL + DESC 'BitlBee User Account ' + MUST ( userid, userPassword ) + MAY ( ) ) + +## bitlBeeAccount: +## - accountNo 1.3.6.1.4.1.1466.115.121.1.27 +## - protocol (msn, oscar, jabber, yahoo, ...) +## - username +## - password +## - server name +## - autoconnect (true/false) 1.3.6.1.4.1.1466.115.121.1.7 +## depends: top + +objectclass ( 1.3.6.1.4.1.25873.2.2.1 NAME 'bitlBeeIMAccount' SUP account STRUCTURAL + DESC 'BitlBee IM Account ' + MUST ( bitlBeeAccountNo, userid, userPassword ) + MAY ( host, bitlBeeAutoconnect ) ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.2 NAME 'bitlBeeSetting' SUP top STRUCTURAL + DESC 'BitlBee Configuration Setting' + MUST ( bitlBeeSettingName ) + MAY ( bitlBeeSettingValue ) ) + +objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeBuddy' SUP top STRUCTURAL + DESC 'BitlBee Nick Mapping' + MUST ( bitlBeeBuddyHandle ) + MAY ( ircNick ) ) @@ -32,10 +32,11 @@ static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond ); GSList *irc_connection_list = NULL; -static char *passchange (irc_t *irc, void *set, char *value) +static char *passchange( irc_t *irc, void *set, char *value ) { - irc_setpass (irc, value); - return (NULL); + irc_setpass( irc, value ); + irc_usermsg( irc, "Password successfully changed" ); + return NULL; } irc_t *irc_new( int fd ) @@ -328,11 +329,10 @@ void irc_free(irc_t * irc) Sets pass without checking */ void irc_setpass (irc_t *irc, const char *pass) { - if (irc->password) g_free (irc->password); + g_free (irc->password); if (pass) { irc->password = g_strdup (pass); - irc_usermsg (irc, "Password successfully changed"); } else { irc->password = NULL; } diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..6408c5ba --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings + +# [SH] Program variables +objects = base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o rc4.o sha.o $(SSL_CLIENT) url.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: lib.o + +.PHONY: all clean distclean + +clean: $(subdirs) + rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) + @echo '*' Linking lib.o + @$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 00000000..69069dae --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,153 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Base64 handling functions. encode_real() is mostly based on the y64 en- * +* coder from libyahoo2. Moving it to a new file because it's getting big. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include <glib.h> +#include <string.h> +#include "base64.h" + +static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +char *tobase64(const char *text) +{ + return base64_encode(text, strlen(text)); +} + +char *base64_encode(const char *in, int len) +{ + char *out; + + out = g_malloc((len + 2) /* the == padding */ + / 3 /* every 3-byte block */ + * 4 /* becomes a 4-byte one */ + + 1); /* and of course, ASCIIZ! */ + + base64_encode_real((unsigned char*) in, len, (unsigned char*) out, real_b64); + + return out; +} + +int base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, const char *b64digits) +{ + int outlen = 0; + + for (; inlen >= 3; inlen -= 3) + { + out[outlen++] = b64digits[in[0] >> 2]; + out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; + out[outlen++] = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; + out[outlen++] = b64digits[in[2] & 0x3f]; + in += 3; + } + if (inlen > 0) + { + out[outlen++] = b64digits[in[0] >> 2]; + if (inlen > 1) + { + out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; + out[outlen++] = b64digits[((in[1]<<2) & 0x3c)]; + } + else + { + out[outlen++] = b64digits[((in[0]<<4) & 0x30)]; + out[outlen++] = b64digits[64]; + } + out[outlen++] = b64digits[64]; + } + out[outlen] = 0; + + return outlen; +} + +/* Just a simple wrapper, but usually not very convenient because of zero + termination. */ +char *frombase64(const char *in) +{ + unsigned char *out; + + base64_decode(in, &out); + + return (char*) out; +} + +/* FIXME: Lookup table stuff is not threadsafe! (But for now BitlBee is not threaded.) */ +int base64_decode(const char *in, unsigned char **out) +{ + static char b64rev[256] = { 0 }; + int len, i; + + /* Create a reverse-lookup for the Base64 sequence. */ + if( b64rev[0] == 0 ) + { + memset( b64rev, 0xff, 256 ); + for( i = 0; i <= 64; i ++ ) + b64rev[(int)real_b64[i]] = i; + } + + len = strlen( in ); + *out = g_malloc( ( len + 6 ) / 4 * 3 ); + len = base64_decode_real( (unsigned char*) in, *out, b64rev ); + *out = g_realloc( *out, len + 1 ); + out[0][len] = 0; /* Zero termination can't hurt. */ + + return len; +} + +int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev) +{ + int i, outlen = 0; + + for( i = 0; in[i]; i += 4 ) + { + int sx; + + sx = b64rev[(int)in[i+0]]; + if( sx >= 64 ) + break; + out[outlen] = ( sx << 2 ) & 0xfc; + + sx = b64rev[(int)in[i+1]]; + if( sx >= 64 ) + break; + out[outlen] |= ( sx >> 4 ) & 0x03; + outlen ++; + out[outlen] = ( sx << 4 ) & 0xf0; + + sx = b64rev[(int)in[i+2]]; + if( sx >= 64 ) + break; + out[outlen] |= ( sx >> 2 ) & 0x0f; + outlen ++; + out[outlen] = ( sx << 6 ) & 0xc0; + + sx = b64rev[(int)in[i+3]]; + if( sx >= 64 ) + break; + out[outlen] |= sx; + outlen ++; + } + + /* If sx > 64 the base64 string was damaged. Should we ignore this? */ + + return outlen; +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 00000000..570f2b14 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,33 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Base64 handling functions. encode_real() is mostly based on the y64 en- * +* coder from libyahoo2. Moving it to a new file because it's getting big. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> + +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT char *base64_encode( const char *in, int len ); +G_MODULE_EXPORT int base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, const char *b64digits ); +G_MODULE_EXPORT char *frombase64( const char *in ); +G_MODULE_EXPORT int base64_decode( const char *in, unsigned char **out ); +G_MODULE_EXPORT int base64_decode_real( const unsigned char *in, unsigned char *out, char *b64reverse ); diff --git a/protocols/events.h b/lib/events.h index 781fca6a..781fca6a 100644 --- a/protocols/events.h +++ b/lib/events.h diff --git a/protocols/events_glib.c b/lib/events_glib.c index 620720cd..620720cd 100644 --- a/protocols/events_glib.c +++ b/lib/events_glib.c diff --git a/protocols/events_libevent.c b/lib/events_libevent.c index 1119c2ab..1119c2ab 100644 --- a/protocols/events_libevent.c +++ b/lib/events_libevent.c diff --git a/protocols/http_client.c b/lib/http_client.c index b00fcf98..b00fcf98 100644 --- a/protocols/http_client.c +++ b/lib/http_client.c diff --git a/protocols/http_client.h b/lib/http_client.h index 50ee80cf..50ee80cf 100644 --- a/protocols/http_client.h +++ b/lib/http_client.h diff --git a/protocols/md5.c b/lib/md5.c index e6273585..e6273585 100644 --- a/protocols/md5.c +++ b/lib/md5.c diff --git a/protocols/md5.h b/lib/md5.h index f24f2ff1..f24f2ff1 100644 --- a/protocols/md5.h +++ b/lib/md5.h @@ -83,63 +83,6 @@ char *add_cr(char *text) return ret; } -static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/"; - -/* XXX Find bug */ -char *tobase64(const char *text) -{ - char *out = NULL; - const char *c; - unsigned int tmp = 0; - int len = 0, n = 0; - - c = text; - - while (*c) { - tmp = tmp << 8; - tmp += *c; - n++; - - if (n == 3) { - out = g_realloc(out, len + 4); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; - out[len + 3] = alphabet[tmp & 0x3f]; - len += 4; - tmp = 0; - n = 0; - } - c++; - } - switch (n) { - - case 2: - tmp <<= 8; - out = g_realloc(out, len + 5); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; - out[len + 3] = '='; - out[len + 4] = 0; - break; - case 1: - tmp <<= 16; - out = g_realloc(out, len + 5); - out[len] = alphabet[(tmp >> 18) & 0x3f]; - out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; - out[len + 2] = '='; - out[len + 3] = '='; - out[len + 4] = 0; - break; - case 0: - out = g_realloc(out, len + 1); - out[len] = 0; - break; - } - return out; -} - char *normalize(const char *s) { static char buf[BUF_LEN]; @@ -29,7 +29,6 @@ G_MODULE_EXPORT void strip_linefeed( gchar *text ); G_MODULE_EXPORT char *add_cr( char *text ); G_MODULE_EXPORT char *strip_newlines(char *source); -G_MODULE_EXPORT char *tobase64( const char *text ); G_MODULE_EXPORT char *normalize( const char *s ); G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value ); diff --git a/protocols/proxy.c b/lib/proxy.c index 70a2158d..7911b06f 100644 --- a/protocols/proxy.c +++ b/lib/proxy.c @@ -40,6 +40,7 @@ #include <errno.h> #include "nogaim.h" #include "proxy.h" +#include "base64.h" char proxyhost[128] = ""; int proxyport = 0; diff --git a/protocols/proxy.h b/lib/proxy.h index 680790a5..680790a5 100644 --- a/protocols/proxy.h +++ b/lib/proxy.h diff --git a/lib/rc4.c b/lib/rc4.c new file mode 100644 index 00000000..cbe0e2c0 --- /dev/null +++ b/lib/rc4.c @@ -0,0 +1,184 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) RC4 implementation for safer password storage. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +/* + This file implements RC4-encryption, which will mainly be used to save IM + passwords safely in the new XML-format. Possibly other uses will come up + later. It's supposed to be quite reliable (thanks to the use of a 6-byte + IV/seed), certainly compared to the old format. The only realistic way to + crack BitlBee passwords now is to use a sniffer to get your hands on the + user's password. + + If you see that something's wrong in this implementation (I asked a + couple of people to look at it already, but who knows), please tell me. + + The reason I chose for RC4 is because it's pretty simple but effective, + so it will work without adding several KBs or an extra library dependency. +*/ + + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include "rc4.h" + +/* Add some seed to the password, to make sure we *never* use the same key. + This defines how many byes we use as a seed. */ +#define RC4_IV_LEN 6 + +/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended + to shuffle S[] just a bit more before you start to use it. This defines how + many bytes we'll request before we'll really use them for encryption. */ +#define RC4_CYCLES 1024 + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ) +{ + struct rc4_state *st; + int i, j, tmp; + + st = g_malloc( sizeof( struct rc4_state ) ); + st->i = st->j = 0; + for( i = 0; i < 256; i ++ ) + st->S[i] = i; + + if( kl <= 0 ) + kl = strlen( (char*) key ); + + for( i = j = 0; i < 256; i ++ ) + { + j = ( j + st->S[i] + key[i%kl] ) & 0xff; + tmp = st->S[i]; + st->S[i] = st->S[j]; + st->S[j] = tmp; + } + + for( i = 0; i < cycles; i ++ ) + rc4_getbyte( st ); + + return st; +} + +/* + For those who don't know, RC4 is basically an algorithm that generates a + stream of bytes after you give it a key. Just get a byte from it and xor + it with your cleartext. To decrypt, just give it the same key again and + start xorring. + + The function above initializes the RC4 byte generator, the next function + can be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char rc4_getbyte( struct rc4_state *st ) +{ + unsigned char tmp; + + /* Unfortunately the st-> stuff doesn't really improve readability here... */ + st->i ++; + st->j += st->S[st->i]; + tmp = st->S[st->i]; + st->S[st->i] = st->S[st->j]; + st->S[st->j] = tmp; + + return st->S[(st->S[st->i] + st->S[st->j]) & 0xff]; +} + +/* + The following two functions can be used for reliable encryption and + decryption. Known plaintext attacks are prevented by adding some (6, + by default) random bytes to the password before setting up the RC4 + structures. These 6 bytes are also saved in the results, because of + course we'll need them in rc4_decode(). + + Because the length of the resulting string is unknown to the caller, + it should pass a char**. Since the encode/decode functions allocate + memory for the string, make sure the char** points at a NULL-pointer + (or at least to something you already free()d), or you'll leak + memory. And of course, don't forget to free() the result when you + don't need it anymore. + + Both functions return the number of bytes in the result string. +*/ + +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ) +{ + struct rc4_state *st; + unsigned char *key; + int key_len, i; + + key_len = strlen( password ) + RC4_IV_LEN; + if( clear_len <= 0 ) + clear_len = strlen( (char*) clear ); + + /* Prepare buffers and the key + IV */ + *crypt = g_malloc( clear_len + RC4_IV_LEN ); + key = g_malloc( key_len ); + strcpy( (char*) key, password ); + for( i = 0; i < RC4_IV_LEN; i ++ ) + key[key_len-RC4_IV_LEN+i] = crypt[0][i] = rand() & 0xff; + + /* Generate the initial S[] from the IVed key. */ + st = rc4_keymaker( key, key_len, RC4_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + crypt[0][i+RC4_IV_LEN] = clear[i] ^ rc4_getbyte( st ); + + g_free( st ); + + return clear_len + RC4_IV_LEN; +} + +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ) +{ + struct rc4_state *st; + unsigned char *key; + int key_len, clear_len, i; + + key_len = strlen( password ) + RC4_IV_LEN; + clear_len = crypt_len - RC4_IV_LEN; + + if( clear_len < 0 ) + { + *clear = g_strdup( "" ); + return 0; + } + + /* Prepare buffers and the key + IV */ + *clear = g_malloc( clear_len + 1 ); + key = g_malloc( key_len ); + strcpy( (char*) key, password ); + for( i = 0; i < RC4_IV_LEN; i ++ ) + key[key_len-RC4_IV_LEN+i] = crypt[i]; + + /* Generate the initial S[] from the IVed key. */ + st = rc4_keymaker( key, key_len, RC4_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + clear[0][i] = crypt[i+RC4_IV_LEN] ^ rc4_getbyte( st ); + clear[0][i] = 0; /* Nice to have for plaintexts. */ + + g_free( st ); + + return clear_len; +} diff --git a/lib/rc4.h b/lib/rc4.h new file mode 100644 index 00000000..6c9ea6b9 --- /dev/null +++ b/lib/rc4.h @@ -0,0 +1,34 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) RC4 implementation for safer password storage. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + + +struct rc4_state +{ + unsigned char S[256]; + unsigned char i, j; +}; + +struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char rc4_getbyte( struct rc4_state *st ); +int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password ); +int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password ); diff --git a/protocols/sha.c b/lib/sha.c index 895505a1..895505a1 100644 --- a/protocols/sha.c +++ b/lib/sha.c diff --git a/protocols/sha.h b/lib/sha.h index e8152b1b..e8152b1b 100644 --- a/protocols/sha.h +++ b/lib/sha.h diff --git a/protocols/ssl_bogus.c b/lib/ssl_bogus.c index 00aaa7c4..00aaa7c4 100644 --- a/protocols/ssl_bogus.c +++ b/lib/ssl_bogus.c diff --git a/protocols/ssl_client.h b/lib/ssl_client.h index 1a9c79e9..1a9c79e9 100644 --- a/protocols/ssl_client.h +++ b/lib/ssl_client.h diff --git a/protocols/ssl_gnutls.c b/lib/ssl_gnutls.c index 3ebe1756..3ebe1756 100644 --- a/protocols/ssl_gnutls.c +++ b/lib/ssl_gnutls.c diff --git a/protocols/ssl_nss.c b/lib/ssl_nss.c index 218b3a80..218b3a80 100644 --- a/protocols/ssl_nss.c +++ b/lib/ssl_nss.c diff --git a/protocols/ssl_openssl.c b/lib/ssl_openssl.c index b6f6c520..b6f6c520 100644 --- a/protocols/ssl_openssl.c +++ b/lib/ssl_openssl.c diff --git a/protocols/Makefile b/protocols/Makefile index b74212f4..cc45fb09 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = $(EVENT_HANDLER) http_client.o md5.o nogaim.o proxy.o sha.o $(SSL_CLIENT) +objects = nogaim.o # [SH] The next two lines should contain the directory name (in $(subdirs)) # and the name of the object file, which should be linked into diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 78b51b53..04d1ee3e 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -296,6 +296,7 @@ void signoff( struct gaim_connection *gc ) b_event_remove( gc->keepalive ); gc->flags |= OPT_LOGGING_OUT; + gc->keepalive = 0; gc->prpl->close( gc ); b_event_remove( gc->inpa ); diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index c691f18b..69b63baa 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -89,6 +89,8 @@ char *strchr (), *strrchr (); #define vsnprintf _vsnprintf #endif +#include "base64.h" + #ifdef USE_STRUCT_CALLBACKS struct yahoo_callbacks *yc=NULL; @@ -694,34 +696,10 @@ static void yahoo_packet_dump(unsigned char *data, int len) } } -static char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789._"; -static void to_y64(unsigned char *out, const unsigned char *in, int inlen) /* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +static void to_y64(unsigned char *out, const unsigned char *in, int inlen) { - for (; inlen >= 3; inlen -= 3) - { - *out++ = base64digits[in[0] >> 2]; - *out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; - *out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; - *out++ = base64digits[in[2] & 0x3f]; - in += 3; - } - if (inlen > 0) - { - unsigned char fragment; - - *out++ = base64digits[in[0] >> 2]; - fragment = (in[0] << 4) & 0x30; - if (inlen > 1) - fragment |= in[1] >> 4; - *out++ = base64digits[fragment]; - *out++ = (inlen < 2) ? '-' - : base64digits[(in[1] << 2) & 0x3c]; - *out++ = '-'; - } - *out = '\0'; + base64_encode_real(in, inlen, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"); } static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length) diff --git a/root_commands.c b/root_commands.c index 3d3584b3..e69b9981 100644 --- a/root_commands.c +++ b/root_commands.c @@ -138,11 +138,12 @@ static void cmd_identify( irc_t *irc, char **cmd ) irc_usermsg( irc, "The nick is (probably) not registered" ); break; case STORAGE_OK: - irc_usermsg( irc, "Password accepted" ); + irc_usermsg( irc, "Password accepted, settings and accounts loaded" ); irc_umode_set( irc, "+R", 1 ); break; + case STORAGE_OTHER_ERROR: default: - irc_usermsg( irc, "Something very weird happened" ); + irc_usermsg( irc, "Unknown error while loading configuration" ); break; } } @@ -305,7 +306,7 @@ static void cmd_account( irc_t *irc, char **cmd ) irc_usermsg( irc, "Trying to get all accounts connected..." ); for( a = irc->accounts; a; a = a->next ) - if( !a->gc ) + if( !a->gc && a->auto_connect ) account_on( irc, a ); } else @@ -6,6 +6,8 @@ /* Support for multiple storage backends */ +/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */ + /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,9 +30,9 @@ #include "crypting.h" extern storage_t storage_text; +extern storage_t storage_xml; -static GList text_entry = { &storage_text, NULL, NULL }; -static GList *storage_backends = &text_entry; +static GList *storage_backends = NULL; void register_storage_backend(storage_t *backend) { @@ -62,7 +64,10 @@ GList *storage_init(const char *primary, char **migrate) GList *ret = NULL; int i; storage_t *storage; - + + register_storage_backend(&storage_text); + register_storage_backend(&storage_xml); + storage = storage_init_single(primary); if (storage == NULL) return NULL; @@ -32,8 +32,8 @@ typedef enum { STORAGE_INVALID_PASSWORD, STORAGE_ALREADY_EXISTS, STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as - a database that is unreachable. log() will be - used for the exact error message */ + a database that is unreachable. log() will be + used for the exact error message */ } storage_status_t; typedef struct { diff --git a/storage_ldap.c b/storage_ldap.c new file mode 100644 index 00000000..4bc99de5 --- /dev/null +++ b/storage_ldap.c @@ -0,0 +1,177 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Storage backend that uses a LDAP database */ + +/* Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include <ldap.h> + +#define BB_LDAP_HOST "localhost" +#define BB_LDAP_BASE "" + +static char *nick_dn(const char *nick) +{ + return g_strdup_printf("bitlBeeNick=%s%s%s", nick, BB_LDAP_BASE?",":"", BB_LDAP_BASE?BB_LDAP_BASE:""); +} + +static storage_status_t nick_connect(const char *nick, const char *password, LDAP **ld) +{ + char *mydn; + int ret; + storage_status_t status; + *ld = ldap_init(BB_LDAP_HOST, LDAP_PORT); + + if (!ld) { + log_message( LOGLVL_WARNING, "Unable to connect to LDAP server at %s", BB_LDAP_HOST ); + return STORAGE_OTHER_ERROR; + } + + mydn = nick_dn(nick); + + ret = ldap_simple_bind_s(*ld, mydn, password); + + switch (ret) { + case LDAP_SUCCESS: status = STORAGE_OK; break; + case LDAP_INVALID_CREDENTIALS: status = STORAGE_INVALID_PASSWORD; break; + default: + log_message( LOGLVL_WARNING, "Unable to authenticate %s: %s", mydn, ldap_err2string(ret) ); + status = STORAGE_OTHER_ERROR; + break; + } + + g_free(mydn); + + return status; +} + +static storage_status_t sldap_load ( const char *my_nick, const char* password, irc_t *irc ) +{ + LDAPMessage *res, *msg; + LDAP *ld; + int ret, i; + storage_status_t status; + char *mydn; + + status = nick_connect(my_nick, password, &ld); + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(my_nick); + + ret = ldap_search_s(ld, mydn, LDAP_SCOPE_BASE, "(objectClass=*)", NULL, 0, &res); + + if (ret != LDAP_SUCCESS) { + log_message( LOGLVL_WARNING, "Unable to search for %s: %s", mydn, ldap_err2string(ret) ); + ldap_unbind_s(ld); + return STORAGE_OTHER_ERROR; + } + + g_free(mydn); + + for (msg = ldap_first_entry(ld, res); msg; msg = ldap_next_entry(ld, msg)) { + } + + /* FIXME: Store in irc_t */ + + ldap_unbind_s(ld); + + return STORAGE_OK; +} + +static storage_status_t sldap_check_pass( const char *nick, const char *password ) +{ + LDAP *ld; + storage_status_t status; + + status = nick_connect(nick, password, &ld); + + ldap_unbind_s(ld); + + return status; +} + +static storage_status_t sldap_remove( const char *nick, const char *password ) +{ + storage_status_t status; + LDAP *ld; + char *mydn; + int ret; + + status = nick_connect(nick, password, &ld); + + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(nick); + + ret = ldap_delete(ld, mydn); + + if (ret != LDAP_SUCCESS) { + log_message( LOGLVL_WARNING, "Error removing %s: %s", mydn, ldap_err2string(ret) ); + ldap_unbind_s(ld); + return STORAGE_OTHER_ERROR; + } + + ldap_unbind_s(ld); + + g_free(mydn); + return STORAGE_OK; +} + +static storage_status_t sldap_save( irc_t *irc, int overwrite ) +{ + LDAP *ld; + char *mydn; + storage_status_t status; + LDAPMessage *msg; + + status = nick_connect(irc->nick, irc->password, &ld); + if (status != STORAGE_OK) + return status; + + mydn = nick_dn(irc->nick); + + /* FIXME: Make this a bit more atomic? What if we crash after + * removing the old account but before adding the new one ? */ + if (overwrite) + sldap_remove(irc->nick, irc->password); + + g_free(mydn); + + ldap_unbind_s(ld); + + return STORAGE_OK; +} + + + +storage_t storage_ldap = { + .name = "ldap", + .check_pass = sldap_check_pass, + .remove = sldap_remove, + .load = sldap_load, + .save = sldap_save +}; diff --git a/storage_xml.c b/storage_xml.c new file mode 100644 index 00000000..7d72c781 --- /dev/null +++ b/storage_xml.c @@ -0,0 +1,506 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Storage backend that uses an XMLish format for all data. */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "base64.h" +#include "rc4.h" +#include "md5.h" + +typedef enum +{ + XML_PASS_CHECK_ONLY = -1, + XML_PASS_UNKNOWN = 0, + XML_PASS_OK +} xml_pass_st; + +/* This isn't very clean, probably making a separate error class + code for + BitlBee would be a better solution. But this will work for now... */ +#define XML_PASS_ERRORMSG "Wrong username or password" +#define XML_FORMAT_VERSION 1 + +struct xml_parsedata +{ + irc_t *irc; + char *current_setting; + account_t *current_account; + char *given_nick; + char *given_pass; + xml_pass_st pass_st; +}; + +static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key ) +{ + int i; + + for( i = 0; attr_names[i]; i ++ ) + if( g_strcasecmp( attr_names[i], key ) == 0 ) + return (char*) attr_values[i]; + + return NULL; +} + +static void xml_destroy_xd( gpointer data ) +{ + struct xml_parsedata *xd = data; + + g_free( xd->given_nick ); + g_free( xd->given_pass ); + g_free( xd ); +} + +static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + irc_t *irc = xd->irc; + + if( g_strcasecmp( element_name, "user" ) == 0 ) + { + char *nick = xml_attr( attr_names, attr_values, "nick" ); + char *pass = xml_attr( attr_names, attr_values, "password" ); + md5_byte_t *pass_dec = NULL; + + if( !nick || !pass ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + } + else if( base64_decode( pass, &pass_dec ) != 21 ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Error while decoding password attribute" ); + } + else + { + md5_byte_t pass_md5[16]; + md5_state_t md5_state; + int i; + + md5_init( &md5_state ); + md5_append( &md5_state, (md5_byte_t*) xd->given_pass, strlen( xd->given_pass ) ); + md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ + md5_finish( &md5_state, pass_md5 ); + + for( i = 0; i < 16; i ++ ) + { + if( pass_dec[i] != pass_md5[i] ) + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + XML_PASS_ERRORMSG ); + break; + } + } + + /* If we reached the end of the loop, it was a match! */ + if( i == 16 ) + { + if( xd->pass_st != XML_PASS_CHECK_ONLY ) + xd->pass_st = XML_PASS_OK; + } + } + + g_free( pass_dec ); + } + else if( xd->pass_st < XML_PASS_OK ) + { + /* Let's not parse anything else if we only have to check + the password. */ + } + else if( g_strcasecmp( element_name, "account" ) == 0 ) + { + char *protocol, *handle, *server, *password = NULL, *autoconnect; + char *pass_b64 = NULL, *pass_rc4 = NULL; + int pass_len; + struct prpl *prpl = NULL; + + handle = xml_attr( attr_names, attr_values, "handle" ); + pass_b64 = xml_attr( attr_names, attr_values, "password" ); + server = xml_attr( attr_names, attr_values, "server" ); + autoconnect = xml_attr( attr_names, attr_values, "autoconnect" ); + + protocol = xml_attr( attr_names, attr_values, "protocol" ); + if( protocol ) + prpl = find_protocol( protocol ); + + if( !handle || !pass_b64 || !protocol ) + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + else if( !prpl ) + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Unknown protocol: %s", protocol ); + else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_rc4 ) ) && + rc4_decode( (unsigned char*) pass_rc4, pass_len, + (unsigned char**) &password, xd->given_pass ) ) + { + xd->current_account = account_add( irc, prpl, handle, password ); + if( server ) + xd->current_account->server = g_strdup( server ); + if( autoconnect ) + /* Return value doesn't matter, since account_add() already sets + a default! */ + sscanf( autoconnect, "%d", &xd->current_account->auto_connect ); + } + else + { + /* Actually the _decode functions don't even return error codes, + but maybe they will later... */ + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Error while decrypting account password" ); + } + + g_free( pass_rc4 ); + g_free( password ); + } + else if( g_strcasecmp( element_name, "setting" ) == 0 ) + { + if( xd->current_account == NULL ) + { + char *setting; + + if( xd->current_setting ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + } + + if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) ) + xd->current_setting = g_strdup( setting ); + else + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + } + } + else if( g_strcasecmp( element_name, "buddy" ) == 0 ) + { + char *handle, *nick; + + handle = xml_attr( attr_names, attr_values, "handle" ); + nick = xml_attr( attr_names, attr_values, "nick" ); + + if( xd->current_account && handle && nick ) + { + nick_set( irc, handle, xd->current_account->prpl, nick ); + } + else + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, + "Missing attributes for %s element", element_name ); + } + } + else + { + g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, + "Unkown element: %s", element_name ); + } +} + +static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + + if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting ) + { + g_free( xd->current_setting ); + xd->current_setting = NULL; + } + else if( g_strcasecmp( element_name, "account" ) == 0 ) + { + xd->current_account = NULL; + } +} + +static void xml_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ + struct xml_parsedata *xd = data; + irc_t *irc = xd->irc; + + if( xd->pass_st < XML_PASS_OK ) + { + /* Let's not parse anything else if we only have to check + the password, or if we didn't get the chance to check it + yet. */ + } + else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && + xd->current_setting && xd->current_account == NULL ) + { + set_setstr( irc, xd->current_setting, (char*) text ); + g_free( xd->current_setting ); + xd->current_setting = NULL; + } +} + +GMarkupParser xml_parser = +{ + xml_start_element, + xml_end_element, + xml_text, + NULL, + NULL +}; + +static void xml_init( void ) +{ + if( access( global.conf->configdir, F_OK ) != 0 ) + log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG ); + else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 ) + log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir ); +} + +static storage_status_t xml_load_real( const char *my_nick, const char *password, irc_t *irc, xml_pass_st action ) +{ + GMarkupParseContext *ctx; + struct xml_parsedata *xd; + char *fn, buf[512]; + GError *gerr = NULL; + int fd, st; + + if( irc && irc->status & USTATUS_IDENTIFIED ) + return( 1 ); + + xd = g_new0( struct xml_parsedata, 1 ); + xd->irc = irc; + xd->given_nick = g_strdup( my_nick ); + xd->given_pass = g_strdup( password ); + xd->pass_st = action; + nick_lc( xd->given_nick ); + + fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" ); + if( ( fd = open( fn, O_RDONLY ) ) < 0 ) + { + xml_destroy_xd( xd ); + g_free( fn ); + return STORAGE_NO_SUCH_USER; + } + g_free( fn ); + + ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd ); + + while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) + { + if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr ) + { + g_markup_parse_context_free( ctx ); + close( fd ); + + /* Slightly dirty... */ + if( gerr && strcmp( gerr->message, XML_PASS_ERRORMSG ) == 0 ) + { + g_clear_error( &gerr ); + return STORAGE_INVALID_PASSWORD; + } + else + { + if( gerr && irc ) + irc_usermsg( irc, "Error from XML-parser: %s", gerr->message ); + + g_clear_error( &gerr ); + return STORAGE_OTHER_ERROR; + } + } + } + /* Just to be sure... */ + g_clear_error( &gerr ); + + g_markup_parse_context_free( ctx ); + close( fd ); + + if( action == XML_PASS_CHECK_ONLY ) + return STORAGE_OK; + + irc->status |= USTATUS_IDENTIFIED; + + if( set_getint( irc, "auto_connect" ) ) + { + /* Can't do this directly because r_c_s alters the string */ + strcpy( buf, "account on" ); + root_command_string( irc, NULL, buf, 0 ); + } + + return STORAGE_OK; +} + +static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc ) +{ + return xml_load_real( my_nick, password, irc, XML_PASS_UNKNOWN ); +} + +static storage_status_t xml_check_pass( const char *my_nick, const char *password ) +{ + /* This is a little bit risky because we have to pass NULL for the + irc_t argument. This *should* be fine, if I didn't miss anything... */ + return xml_load_real( my_nick, password, NULL, XML_PASS_CHECK_ONLY ); +} + +static int xml_printf( int fd, char *fmt, ... ) +{ + va_list params; + char *out; + int len; + + va_start( params, fmt ); + out = g_markup_vprintf_escaped( fmt, params ); + va_end( params ); + + len = strlen( out ); + len -= write( fd, out, len ); + g_free( out ); + + return len == 0; +} + +static storage_status_t xml_save( irc_t *irc, int overwrite ) +{ + char path[512], *path2, *pass_buf = NULL; + set_t *set; + nick_t *nick; + account_t *acc; + int fd, i; + md5_byte_t pass_md5[21]; + md5_state_t md5_state; + + if( irc->password == NULL ) + { + irc_usermsg( irc, "Please register yourself if you want to save your settings." ); + return STORAGE_OTHER_ERROR; + } + + g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, irc->nick, ".xml" ); + + if( !overwrite && access( path, F_OK ) != -1 ) + return STORAGE_ALREADY_EXISTS; + + strcat( path, "~" ); + if( ( fd = open( path, O_WRONLY | O_CREAT, 0600 ) ) < 0 ) + { + irc_usermsg( irc, "Error while opening configuration file." ); + return STORAGE_OTHER_ERROR; + } + + /* 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. */ + for( i = 0; i < 5; i ++ ) + pass_md5[16+i] = rand() & 0xff; + 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( (char*) pass_md5, 21 ); + + if( !xml_printf( fd, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->nick, pass_buf, XML_FORMAT_VERSION ) ) + goto write_error; + + g_free( pass_buf ); + + for( set = irc->set; set; set = set->next ) + if( set->value && set->def ) + if( !xml_printf( fd, "\t<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) + goto write_error; + + for( acc = irc->accounts; acc; acc = acc->next ) + { + char *pass_rc4, *pass_b64; + int pass_len; + + pass_len = rc4_encode( (unsigned char*) acc->pass, strlen( acc->pass ), (unsigned char**) &pass_rc4, irc->password ); + pass_b64 = base64_encode( pass_rc4, pass_len ); + + if( !xml_printf( fd, "\t<account protocol=\"%s\" handle=\"%s\" password=\"%s\" autoconnect=\"%d\"", acc->prpl->name, acc->user, pass_b64, acc->auto_connect ) ) + { + g_free( pass_rc4 ); + g_free( pass_b64 ); + goto write_error; + } + g_free( pass_rc4 ); + g_free( pass_b64 ); + + if( acc->server && acc->server[0] && !xml_printf( fd, " server=\"%s\"", acc->server ) ) + goto write_error; + if( !xml_printf( fd, ">\n" ) ) + goto write_error; + + for( nick = irc->nicks; nick; nick = nick->next ) + if( nick->proto == acc->prpl ) + if( !xml_printf( fd, "\t\t<buddy handle=\"%s\" nick=\"%s\" />\n", nick->handle, nick->nick ) ) + goto write_error; + + if( !xml_printf( fd, "\t</account>\n" ) ) + goto write_error; + } + + if( !xml_printf( fd, "</user>\n" ) ) + goto write_error; + + close( fd ); + + path2 = g_strndup( path, strlen( path ) - 1 ); + if( rename( path, path2 ) != 0 ) + { + irc_usermsg( irc, "Error while renaming temporary configuration file." ); + + g_free( path2 ); + unlink( path ); + + return STORAGE_OTHER_ERROR; + } + + g_free( path2 ); + + return STORAGE_OK; + +write_error: + g_free( pass_buf ); + + irc_usermsg( irc, "Write error. Disk full?" ); + close( fd ); + + return STORAGE_OTHER_ERROR; +} + +static storage_status_t xml_remove( const char *nick, const char *password ) +{ + char s[512]; + storage_status_t status; + + status = xml_check_pass( nick, password ); + if( status != STORAGE_OK ) + return status; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".xml" ); + if( unlink( s ) == -1 ) + return STORAGE_OTHER_ERROR; + + return STORAGE_OK; +} + +storage_t storage_xml = { + .name = "xml", + .init = xml_init, + .check_pass = xml_check_pass, + .remove = xml_remove, + .load = xml_load, + .save = xml_save +}; @@ -47,20 +47,18 @@ int main( int argc, char *argv[], char **envp ) memset( &global, 0, sizeof( global_t ) ); b_main_init(); - log_init(); - nogaim_init(); - - CONF_FILE = g_strdup( CONF_FILE_DEF ); + srand( time( NULL ) ^ getpid() ); + + CONF_FILE = g_strdup( CONF_FILE_DEF ); global.helpfile = g_strdup( HELP_FILE ); - + global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); - - + if( global.conf->runmode == RUNMODE_INETD ) { i = bitlbee_inetd_init(); @@ -88,7 +86,7 @@ int main( int argc, char *argv[], char **envp ) } if( i != 0 ) return( i ); - + global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); if ( global.storage == NULL) { log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); |