diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 46 | ||||
-rw-r--r-- | lib/arc.c | 223 | ||||
-rw-r--r-- | lib/arc.h | 40 | ||||
-rw-r--r-- | lib/base64.c | 153 | ||||
-rw-r--r-- | lib/base64.h | 33 | ||||
-rw-r--r-- | lib/des.c | 646 | ||||
-rw-r--r-- | lib/des.h | 51 | ||||
-rw-r--r-- | lib/events.h | 87 | ||||
-rw-r--r-- | lib/events_glib.c | 153 | ||||
-rw-r--r-- | lib/events_libevent.c | 294 | ||||
-rw-r--r-- | lib/ftutil.c | 144 | ||||
-rw-r--r-- | lib/ftutil.h | 40 | ||||
-rw-r--r-- | lib/http_client.c | 456 | ||||
-rw-r--r-- | lib/http_client.h | 84 | ||||
-rw-r--r-- | lib/ini.c | 145 | ||||
-rw-r--r-- | lib/ini.h | 45 | ||||
-rw-r--r-- | lib/md5.c | 262 | ||||
-rw-r--r-- | lib/md5.h | 46 | ||||
-rw-r--r-- | lib/misc.c | 730 | ||||
-rw-r--r-- | lib/misc.h | 74 | ||||
-rw-r--r-- | lib/oauth.c | 456 | ||||
-rw-r--r-- | lib/oauth.h | 94 | ||||
-rw-r--r-- | lib/proxy.c | 586 | ||||
-rw-r--r-- | lib/proxy.h | 53 | ||||
-rw-r--r-- | lib/sha1.c | 422 | ||||
-rw-r--r-- | lib/sha1.h | 71 | ||||
-rw-r--r-- | lib/ssl_bogus.c | 71 | ||||
-rw-r--r-- | lib/ssl_client.h | 84 | ||||
-rw-r--r-- | lib/ssl_gnutls.c | 280 | ||||
-rw-r--r-- | lib/ssl_nss.c | 240 | ||||
-rw-r--r-- | lib/ssl_openssl.c | 305 | ||||
-rw-r--r-- | lib/ssl_sspi.c | 278 | ||||
-rw-r--r-- | lib/url.c | 109 | ||||
-rw-r--r-- | lib/url.h | 44 | ||||
-rw-r--r-- | lib/xmltree.c | 698 | ||||
-rw-r--r-- | lib/xmltree.h | 100 |
36 files changed, 7643 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..3ae43935 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,46 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)lib/ +endif + +# [SH] Program variables +objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o + +LFLAGS += -r + +# [SH] Phony targets +all: lib.o +check: all +lcov: check +gcov: + gcov *.c + +.PHONY: all clean distclean + +clean: $(subdirs) + rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + rm -rf .depend + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) + @echo '*' Linking lib.o + @$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: $(SRCDIR)%.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +-include .depend/*.d diff --git a/lib/arc.c b/lib/arc.c new file mode 100644 index 00000000..fd498454 --- /dev/null +++ b/lib/arc.c @@ -0,0 +1,223 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) ArcFour implementation for safer password storage. * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library 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 * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +\***************************************************************************/ + +/* + This file implements ArcFour-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 picked ArcFour is because it's pretty simple but effective, + so it will work without adding several KBs or an extra library dependency. + + (ArcFour is an RC4-compatible cipher. See for details: + http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt) +*/ + + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "misc.h" +#include "arc.h" + +/* Add some seed to the password, to make sure we *never* use the same key. + This defines how many bytes we use as a seed. */ +#define ARC_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 ARC_CYCLES 1024 + +struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ) +{ + struct arc_state *st; + int i, j, tmp; + unsigned char S2[256]; + + st = g_malloc( sizeof( struct arc_state ) ); + st->i = st->j = 0; + if( kl <= 0 ) + kl = strlen( (char*) key ); + + for( i = 0; i < 256; i ++ ) + { + st->S[i] = i; + S2[i] = key[i%kl]; + } + + for( i = j = 0; i < 256; i ++ ) + { + j = ( j + st->S[i] + S2[i] ) & 0xff; + tmp = st->S[i]; + st->S[i] = st->S[j]; + st->S[j] = tmp; + } + + memset( S2, 0, 256 ); + i = j = 0; + + for( i = 0; i < cycles; i ++ ) + arc_getbyte( st ); + + return st; +} + +/* + For those who don't know, ArcFour 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 byte generator, the next function can + be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char arc_getbyte( struct arc_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; + tmp = (st->S[st->i] + st->S[st->j]) & 0xff; + + return st->S[tmp]; +} + +/* + 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 state + structures. These 6 bytes are also saved in the results, because of + course we'll need them in arc_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. + + Note that if you use the pad_to argument, you will need zero-termi- + nation to find back the original string length after decryption. So + it shouldn't be used if your string contains \0s by itself! +*/ + +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ) +{ + struct arc_state *st; + unsigned char *key; + char *padded = NULL; + int key_len, i, padded_len; + + key_len = strlen( password ) + ARC_IV_LEN; + if( clear_len <= 0 ) + clear_len = strlen( clear ); + + /* Pad the string to the closest multiple of pad_to. This makes it + impossible to see the exact length of the password. */ + if( pad_to > 0 && ( clear_len % pad_to ) > 0 ) + { + padded_len = clear_len + pad_to - ( clear_len % pad_to ); + padded = g_malloc( padded_len ); + memcpy( padded, clear, clear_len ); + + /* First a \0 and then random data, so we don't have to do + anything special when decrypting. */ + padded[clear_len] = 0; + random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 ); + + clear = padded; + clear_len = padded_len; + } + + /* Prepare buffers and the key + IV */ + *crypt = g_malloc( clear_len + ARC_IV_LEN ); + key = g_malloc( key_len ); + strcpy( (char*) key, password ); + + /* Add the salt. Save it for later (when decrypting) and, of course, + add it to the encryption key. */ + random_bytes( crypt[0], ARC_IV_LEN ); + memcpy( key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN ); + + /* Generate the initial S[] from the IVed key. */ + st = arc_keymaker( key, key_len, ARC_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st ); + + g_free( st ); + g_free( padded ); + + return clear_len + ARC_IV_LEN; +} + +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ) +{ + struct arc_state *st; + unsigned char *key; + int key_len, clear_len, i; + + key_len = strlen( password ) + ARC_IV_LEN; + clear_len = crypt_len - ARC_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 < ARC_IV_LEN; i ++ ) + key[key_len-ARC_IV_LEN+i] = crypt[i]; + + /* Generate the initial S[] from the IVed key. */ + st = arc_keymaker( key, key_len, ARC_CYCLES ); + g_free( key ); + + for( i = 0; i < clear_len; i ++ ) + clear[0][i] = crypt[i+ARC_IV_LEN] ^ arc_getbyte( st ); + clear[0][i] = 0; /* Nice to have for plaintexts. */ + + g_free( st ); + + return clear_len; +} diff --git a/lib/arc.h b/lib/arc.h new file mode 100644 index 00000000..816fa612 --- /dev/null +++ b/lib/arc.h @@ -0,0 +1,40 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple (but secure) ArcFour implementation for safer password storage. * +* * +* Copyright 2007 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. * +* * +\***************************************************************************/ + + +/* See arc.c for more information. */ + +struct arc_state +{ + unsigned char S[256]; + unsigned char i, j; +}; + +#ifndef G_GNUC_MALLOC +#define G_GNUC_MALLOC +#endif + +G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char arc_getbyte( struct arc_state *st ); +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ); +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ); diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 00000000..ea0db6b9 --- /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((const unsigned char *)text, strlen(text)); +} + +char *base64_encode(const unsigned 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] && in[i+1] && in[i+2] && in[i+3]; 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..ebd74bf1 --- /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 unsigned 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/lib/des.c b/lib/des.c new file mode 100644 index 00000000..3b9cc8d5 --- /dev/null +++ b/lib/des.c @@ -0,0 +1,646 @@ +/* + * FIPS-46-3 compliant 3DES implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Modified for BitlBee: Added a function compatible with the existing + * function in ssl_openssl.c, fairly specialised for MSN auth (since that's + * all this is used for at least for now). + * + * Added some consts to the tables at the top, and disabled some 64-bit + * and 128-bit key code that I don't need. + * + * *Many* thanks to Christophe for this compact and easy to import code. + */ + +#include <string.h> +#include <glib.h> +#include "des.h" + +/* the eight DES S-boxes */ + +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* PC1: left and right halves bit-swap */ + +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* platform-independant 32-bit integer manipulation macros */ + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +/* Initial Permutation macro */ + +#define DES_IP(X,Y) \ +{ \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \ + X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \ +} + +/* Final Permutation macro */ + +#define DES_FP(X,Y) \ +{ \ + X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \ + T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \ + Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \ + T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \ + T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \ + T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \ + T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \ +} + +/* DES round macro */ + +#define DES_ROUND(X,Y) \ +{ \ + T = *SK++ ^ X; \ + Y ^= SB8[ (T ) & 0x3F ] ^ \ + SB6[ (T >> 8) & 0x3F ] ^ \ + SB4[ (T >> 16) & 0x3F ] ^ \ + SB2[ (T >> 24) & 0x3F ]; \ + \ + T = *SK++ ^ ((X << 28) | (X >> 4)); \ + Y ^= SB7[ (T ) & 0x3F ] ^ \ + SB5[ (T >> 8) & 0x3F ] ^ \ + SB3[ (T >> 16) & 0x3F ] ^ \ + SB1[ (T >> 24) & 0x3F ]; \ +} + +/* DES key schedule */ + +int des_main_ks( uint32_t SK[32], const uint8_t key[8] ) +{ + int i; + uint32_t X, Y, T; + + GET_UINT32( X, key, 0 ); + GET_UINT32( Y, key, 4 ); + + /* Permuted Choice 1 */ + + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T ); + + X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2) + | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] ) + | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6) + | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4); + + Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2) + | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] ) + | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6) + | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* calculate subkeys */ + + for( i = 0; i < 16; i++ ) + { + if( i < 2 || i == 8 || i == 15 ) + { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } + else + { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } + + return( 0 ); +} + +#if TEST +int des_set_key( des_context *ctx, uint8_t key[8] ) +{ + int i; + + /* setup encryption subkeys */ + + des_main_ks( ctx->esk, key ); + + /* setup decryption subkeys */ + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[30 - i]; + ctx->dsk[i + 1] = ctx->esk[31 - i]; + } + + return( 0 ); +} + +/* DES 64-bit block encryption/decryption */ + +void des_crypt( uint32_t SK[32], uint8_t input[8], uint8_t output[8] ) +{ + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des_crypt( ctx->esk, input, output ); +} + +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des_crypt( ctx->dsk, input, output ); +} + +/* Triple-DES key schedule */ + +int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ) +{ + int i; + + des_main_ks( ctx->esk , key1 ); + des_main_ks( ctx->dsk + 32, key2 ); + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[30 - i]; + ctx->dsk[i + 1] = ctx->esk[31 - i]; + + ctx->esk[i + 32] = ctx->dsk[62 - i]; + ctx->esk[i + 33] = ctx->dsk[63 - i]; + + ctx->esk[i + 64] = ctx->esk[ i]; + ctx->esk[i + 65] = ctx->esk[ 1 + i]; + + ctx->dsk[i + 64] = ctx->dsk[ i]; + ctx->dsk[i + 65] = ctx->dsk[ 1 + i]; + } + + return( 0 ); +} +#endif + +int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], + const uint8_t key3[8] ) +{ + int i; + + des_main_ks( ctx->esk , key1 ); + des_main_ks( ctx->dsk + 32, key2 ); + des_main_ks( ctx->esk + 64, key3 ); + + for( i = 0; i < 32; i += 2 ) + { + ctx->dsk[i ] = ctx->esk[94 - i]; + ctx->dsk[i + 1] = ctx->esk[95 - i]; + + ctx->esk[i + 32] = ctx->dsk[62 - i]; + ctx->esk[i + 33] = ctx->dsk[63 - i]; + + ctx->dsk[i + 64] = ctx->esk[30 - i]; + ctx->dsk[i + 65] = ctx->esk[31 - i]; + } + + return( 0 ); +} + +/* Triple-DES 64-bit block encryption/decryption */ + +void des3_crypt( uint32_t SK[96], uint8_t input[8], uint8_t output[8] ) +{ + uint32_t X, Y, T; + + GET_UINT32( X, input, 0 ); + GET_UINT32( Y, input, 4 ); + + DES_IP( X, Y ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + DES_ROUND( X, Y ); DES_ROUND( Y, X ); + + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + DES_ROUND( Y, X ); DES_ROUND( X, Y ); + + DES_FP( Y, X ); + + PUT_UINT32( Y, output, 0 ); + PUT_UINT32( X, output, 4 ); +} + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des3_crypt( ctx->esk, input, output ); +} + +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ) +{ + des3_crypt( ctx->dsk, input, output ); +} + +size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input, + size_t input_len, const unsigned char *iv, unsigned char **res ) +{ + des3_context ctx3; + size_t off; + uint8_t buf[8]; + + /* Keep it simple, for as long as this is just used for MSN auth anyway. */ + if( key_len != 24 || ( input_len % 8 ) != 0 ) + return 0; + + *res = g_malloc( input_len ); + des3_set_3keys( &ctx3, key, key + 8, key + 16 ); + + /* This loop does CBC 3DES. */ + memcpy( buf, iv, 8 ); + for( off = 0; off < input_len; off += 8 ) + { + int i; + + for( i = 0; i < 8; i ++ ) + buf[i] ^= input[off+i]; + des3_encrypt( &ctx3, buf, buf ); + memcpy( *res + off, buf, 8 ); + } + + return input_len; +} + +#ifdef TEST + +#include <string.h> +#include <stdio.h> + +/* + * Triple-DES Monte Carlo Test: ECB mode + * source: NIST - tripledes-vectors.zip + */ + +static const unsigned char DES3_keys[3][8] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }, + { 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 }, + { 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 } +}; + +static const unsigned char DES3_init[8] = +{ + 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 +}; + +static const unsigned char DES3_enc_test[3][8] = +{ + { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B }, + { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 }, + { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 } +}; + +static const unsigned char DES3_dec_test[3][8] = +{ + { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D }, + { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB }, + { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A } +}; + +int main( void ) +{ + int m, n, i; + des_context ctx; + des3_context ctx3; + unsigned char buf[8]; + + for( m = 0; m < 2; m++ ) + { + printf( "\n Triple-DES Monte Carlo Test (ECB mode) - " ); + + if( m == 0 ) printf( "encryption\n\n" ); + if( m == 1 ) printf( "decryption\n\n" ); + + for( n = 0; n < 3; n++ ) + { + printf( " Test %d, key size = %3d bits: ", + n + 1, 64 + n * 64 ); + + fflush( stdout ); + + memcpy( buf, DES3_init, 8 ); + + switch( n ) + { + case 0: + des_set_key( &ctx, DES3_keys[0] ); + break; + + case 1: + des3_set_2keys( &ctx3, DES3_keys[0], + DES3_keys[1] ); + break; + + case 2: + des3_set_3keys( &ctx3, DES3_keys[0], + DES3_keys[1], + DES3_keys[2] ); + break; + } + + for( i = 0; i < 10000; i++ ) + { + if( n == 0 ) + { + if( m == 0 ) des_encrypt( &ctx, buf, buf ); + if( m == 1 ) des_decrypt( &ctx, buf, buf ); + } + else + { + if( m == 0 ) des3_encrypt( &ctx3, buf, buf ); + if( m == 1 ) des3_decrypt( &ctx3, buf, buf ); + } + } + + if( ( m == 0 && memcmp( buf, DES3_enc_test[n], 8 ) ) || + ( m == 1 && memcmp( buf, DES3_dec_test[n], 8 ) ) ) + { + printf( "failed!\n" ); + return( 1 ); + } + + printf( "passed.\n" ); + } + } + + printf( "\n" ); + + return( 0 ); +} + +#endif diff --git a/lib/des.h b/lib/des.h new file mode 100644 index 00000000..92fbfd22 --- /dev/null +++ b/lib/des.h @@ -0,0 +1,51 @@ +/* + * FIPS-46-3 compliant 3DES implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DES_H +#define _DES_H + +#include <stdint.h> + +typedef struct +{ + uint32_t esk[32]; /* DES encryption subkeys */ + uint32_t dsk[32]; /* DES decryption subkeys */ +} +des_context; + +typedef struct +{ + uint32_t esk[96]; /* Triple-DES encryption subkeys */ + uint32_t dsk[96]; /* Triple-DES decryption subkeys */ +} +des3_context; + +int des_set_key( des_context *ctx, uint8_t key[8] ); +void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] ); + +int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] ); +int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8], + const uint8_t key3[8] ); + +void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); +void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] ); + +#endif /* des.h */ diff --git a/lib/events.h b/lib/events.h new file mode 100644 index 00000000..66c4c6b4 --- /dev/null +++ b/lib/events.h @@ -0,0 +1,87 @@ +/* + * nogaim + * + * Copyright (C) 2006 Wilmer van der Gaast and others + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* This stuff used to be in proxy.c too, but I split it off so BitlBee can + use other libraries (like libevent) to handle events. proxy.c is one very + nice piece of work from Gaim. It connects to a TCP server in the back- + ground and calls a callback function once the connection is ready to use. + This function (proxy_connect()) can be found in proxy.c. (It also + transparently handles HTTP/SOCKS proxies, when necessary.) + + This file offers some extra event handling toys, which will be handled + by GLib or libevent. The advantage of using libevent is that it can use + more advanced I/O polling functions like epoll() in recent Linux + kernels. This should improve BitlBee's scalability. */ + + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +/* The conditions you can pass to b_input_add()/that will be passed to + the given callback function. */ +typedef enum { + B_EV_IO_READ = 1 << 0, + B_EV_IO_WRITE = 1 << 1, + B_EV_FLAG_FORCE_ONCE = 1 << 16, + B_EV_FLAG_FORCE_REPEAT = 1 << 17, +} b_input_condition; +typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); + +/* For internal use. */ +#define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) +#define GAIM_ERR_COND (G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +/* #define event_debug( x... ) printf( x ) */ +#define event_debug( x... ) + +/* Call this once when the program starts. It'll initialize the event handler + library (if necessary) and then return immediately. */ +G_MODULE_EXPORT void b_main_init(); + +/* This one enters the event loop. It shouldn't return until one of the event + handlers calls b_main_quit(). */ +G_MODULE_EXPORT void b_main_run(); +G_MODULE_EXPORT void b_main_quit(); + + +/* Add event handlers (for I/O or a timeout). The event handler will be called + every time the event "happens", until your event handler returns FALSE (or + until you remove it using b_event_remove(). As usual, the data argument + can be used to pass your own data to the event handler. */ +G_MODULE_EXPORT gint b_input_add(int fd, b_input_condition cond, b_event_handler func, gpointer data); +G_MODULE_EXPORT gint b_timeout_add(gint timeout, b_event_handler func, gpointer data); +G_MODULE_EXPORT void b_event_remove(gint id); + +/* With libevent, this one also cleans up event handlers if that wasn't already + done (the caller is expected to do so but may miss it sometimes). */ +G_MODULE_EXPORT void closesocket(int fd); + +#endif /* _EVENTS_H_ */ diff --git a/lib/events_glib.c b/lib/events_glib.c new file mode 100644 index 00000000..3fafc872 --- /dev/null +++ b/lib/events_glib.c @@ -0,0 +1,153 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * Event handling (using GLib) + */ + +/* + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "proxy.h" + +typedef struct _GaimIOClosure { + b_event_handler function; + gpointer data; + guint flags; +} GaimIOClosure; + +static GMainLoop *loop = NULL; + +void b_main_init() +{ + if( loop == NULL ) + loop = g_main_new( FALSE ); +} + +void b_main_run() +{ + g_main_run( loop ); +} + +void b_main_quit() +{ + g_main_quit( loop ); +} + +static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + GaimIOClosure *closure = data; + b_input_condition gaim_cond = 0; + gboolean st; + + if (condition & GAIM_READ_COND) + gaim_cond |= B_EV_IO_READ; + if (condition & GAIM_WRITE_COND) + gaim_cond |= B_EV_IO_WRITE; + + event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data ); + + st = closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond); + + if( !st ) + event_debug( "Returned FALSE, cancelling.\n" ); + + if (closure->flags & B_EV_FLAG_FORCE_ONCE) + return FALSE; + else if (closure->flags & B_EV_FLAG_FORCE_REPEAT) + return TRUE; + else + return st; +} + +static void gaim_io_destroy(gpointer data) +{ + event_debug( "gaim_io_destroy( 0x%x )\n", data ); + g_free(data); +} + +gint b_input_add(gint source, b_input_condition condition, b_event_handler function, gpointer data) +{ + GaimIOClosure *closure = g_new0(GaimIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + int st; + + closure->function = function; + closure->data = data; + closure->flags = condition; + + if (condition & B_EV_IO_READ) + cond |= GAIM_READ_COND; + if (condition & B_EV_IO_WRITE) + cond |= GAIM_WRITE_COND; + + channel = g_io_channel_unix_new(source); + st = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + gaim_io_invoke, closure, gaim_io_destroy); + + event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) = %d (%p)\n", source, condition, function, data, st, closure ); + + g_io_channel_unref(channel); + return st; +} + +gint b_timeout_add(gint timeout, b_event_handler func, gpointer data) +{ + /* GSourceFunc and the BitlBee event handler function aren't + really the same, but they're "compatible". ;-) It will do + for now, BitlBee only looks at the "data" argument. */ + gint st = g_timeout_add(timeout, (GSourceFunc) func, data); + + event_debug( "b_timeout_add( %d, %d, %d ) = %d\n", timeout, func, data, st ); + + return st; +} + +void b_event_remove(gint tag) +{ + event_debug( "b_event_remove( %d )\n", tag ); + + if (tag > 0) + g_source_remove(tag); +} + +void closesocket( int fd ) +{ + close( fd ); +} diff --git a/lib/events_libevent.c b/lib/events_libevent.c new file mode 100644 index 00000000..43d770ea --- /dev/null +++ b/lib/events_libevent.c @@ -0,0 +1,294 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * Event handling (using libevent) + */ + +/* + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <event.h> +#include "proxy.h" + +static void b_main_restart(); +static guint id_next = 1; /* Next ID to be allocated to an event handler. */ +static guint id_cur = 0; /* Event ID that we're currently handling. */ +static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */ +static GHashTable *id_hash; +static int quitting = 0; /* Prepare to quit, stop handling events. */ + +/* Since libevent doesn't handle two event handlers for one fd-condition + very well (which happens sometimes when BitlBee changes event handlers + for a combination), let's buid some indexes so we can delete them here + already, just in time. */ +static GHashTable *read_hash; +static GHashTable *write_hash; + +struct event_base *leh; +struct event_base *old_leh; + +struct b_event_data +{ + guint id; + struct event evinfo; + gint timeout; + b_event_handler function; + void *data; + guint flags; +}; + +void b_main_init() +{ + if( leh != NULL ) + { + /* Clean up the hash tables? */ + + b_main_restart(); + old_leh = leh; + } + + leh = event_init(); + + id_hash = g_hash_table_new( g_int_hash, g_int_equal ); + read_hash = g_hash_table_new( g_int_hash, g_int_equal ); + write_hash = g_hash_table_new( g_int_hash, g_int_equal ); +} + +void b_main_run() +{ + /* This while loop is necessary to exit the event loop and start a + different one (necessary for ForkDaemon mode). */ + while( event_base_dispatch( leh ) == 0 && !quitting ) + { + if( old_leh != NULL ) + { + /* For some reason this just isn't allowed... + Possibly a bug in older versions, will see later. + event_base_free( old_leh ); */ + old_leh = NULL; + } + + event_debug( "New event loop.\n" ); + } +} + +static void b_main_restart() +{ + struct timeval tv; + + memset( &tv, 0, sizeof( struct timeval ) ); + event_base_loopexit( leh, &tv ); + + event_debug( "b_main_restart()\n" ); +} + +void b_main_quit() +{ + /* Tell b_main_run() that it shouldn't restart the loop. Also, + libevent sometimes generates events before really quitting, + we want to stop them. */ + quitting = 1; + + b_main_restart(); +} + +static void b_event_passthrough( int fd, short event, void *data ) +{ + struct b_event_data *b_ev = data; + b_input_condition cond = 0; + gboolean st; + + if( fd >= 0 ) + { + if( event & EV_READ ) + cond |= B_EV_IO_READ; + if( event & EV_WRITE ) + cond |= B_EV_IO_WRITE; + } + + event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id ); + + /* Since the called function might cancel this handler already + (which free()s b_ev), we have to remember the ID here. */ + id_cur = b_ev->id; + id_dead = 0; + + if( quitting ) + { + b_event_remove( id_cur ); + return; + } + + st = b_ev->function( b_ev->data, fd, cond ); + if( id_dead ) + { + /* This event was killed already, don't touch it! */ + return; + } + else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) ) + { + event_debug( "Handler returned FALSE: " ); + b_event_remove( id_cur ); + } + else if( fd == -1 ) + { + /* fd == -1 means it was a timer. These can't be auto-repeated + so it has to be recreated every time. */ + struct timeval tv; + + tv.tv_sec = b_ev->timeout / 1000; + tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000; + + evtimer_add( &b_ev->evinfo, &tv ); + } +} + +gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data ) +{ + struct b_event_data *b_ev; + + event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data ); + + if( ( condition & B_EV_IO_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) || + ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) + { + /* We'll stick with this libevent entry, but give it a new BitlBee id. */ + g_hash_table_remove( id_hash, &b_ev->id ); + + event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next ); + + b_ev->id = id_next++; + b_ev->function = function; + b_ev->data = data; + } + else + { + GIOCondition out_cond; + + event_debug( "(new) = %d\n", id_next ); + + b_ev = g_new0( struct b_event_data, 1 ); + b_ev->id = id_next++; + b_ev->function = function; + b_ev->data = data; + + out_cond = EV_PERSIST; + if( condition & B_EV_IO_READ ) + out_cond |= EV_READ; + if( condition & B_EV_IO_WRITE ) + out_cond |= EV_WRITE; + + event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev ); + event_add( &b_ev->evinfo, NULL ); + + if( out_cond & EV_READ ) + g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev ); + if( out_cond & EV_WRITE ) + g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev ); + } + + b_ev->flags = condition; + g_hash_table_insert( id_hash, &b_ev->id, b_ev ); + return b_ev->id; +} + +/* TODO: Persistence for timers! */ +gint b_timeout_add( gint timeout, b_event_handler function, gpointer data ) +{ + struct b_event_data *b_ev = g_new0( struct b_event_data, 1 ); + struct timeval tv; + + b_ev->id = id_next++; + b_ev->timeout = timeout; + b_ev->function = function; + b_ev->data = data; + + tv.tv_sec = timeout / 1000; + tv.tv_usec = ( timeout % 1000 ) * 1000; + + evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev ); + evtimer_add( &b_ev->evinfo, &tv ); + + event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id ); + + g_hash_table_insert( id_hash, &b_ev->id, b_ev ); + + return b_ev->id; +} + +void b_event_remove( gint id ) +{ + struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id ); + + event_debug( "b_event_remove( %d )\n", id ); + if( b_ev ) + { + if( id == id_cur ) + id_dead = TRUE; + + g_hash_table_remove( id_hash, &b_ev->id ); + if( b_ev->evinfo.ev_fd >= 0 ) + { + if( b_ev->evinfo.ev_events & EV_READ ) + g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd ); + if( b_ev->evinfo.ev_events & EV_WRITE ) + g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd ); + } + + event_del( &b_ev->evinfo ); + g_free( b_ev ); + } + else + { + event_debug( "Already removed?\n" ); + } +} + +void closesocket( int fd ) +{ + struct b_event_data *b_ev; + + /* Since epoll() (the main reason we use libevent) automatically removes sockets from + the epoll() list when a socket gets closed and some modules have a habit of + closing sockets before removing event handlers, our and libevent's administration + get a little bit messed up. So this little function will remove the handlers + properly before closing a socket. */ + + if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) + { + event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd ); + b_event_remove( b_ev->id ); + } + if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) + { + event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd ); + b_event_remove( b_ev->id ); + } + + close( fd ); +} diff --git a/lib/ftutil.c b/lib/ftutil.c new file mode 100644 index 00000000..71c09b50 --- /dev/null +++ b/lib/ftutil.c @@ -0,0 +1,144 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Utility functions for file transfer * +* * +* Copyright 2008 Uli Meis <a.sporto+bee@gmail.com> * +* * +* 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. * +* * +\***************************************************************************/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include <poll.h> +#include <netinet/tcp.h> +#include "lib/ftutil.h" + +#define ASSERTSOCKOP(op, msg) \ + if( (op) == -1 ) {\ + g_snprintf( errmsg, sizeof( errmsg ), msg ": %s", strerror( errno ) ); \ + return -1; } + +/* + * Creates a listening socket and returns it in saddr_ptr. + */ +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr ) +{ + int fd, gret, saddrlen; + struct addrinfo hints, *rp; + socklen_t ssize = sizeof( struct sockaddr_storage ); + struct sockaddr_storage saddrs, *saddr = &saddrs; + static char errmsg[1024]; + char *ftlisten = global.conf->ft_listen; + + if( errptr ) + *errptr = errmsg; + + strcpy( port, "0" ); + + /* Format is <IP-A>[:<Port-A>];<IP-B>[:<Port-B>] where + * A is for connections with the bitlbee client (DCC) + * and B is for connections with IM peers. + */ + if( ftlisten ) + { + char *scolon = strchr( ftlisten, ';' ); + char *colon; + + if( scolon ) + { + if( for_bitlbee_client ) + { + *scolon = '\0'; + strncpy( host, ftlisten, HOST_NAME_MAX ); + *scolon = ';'; + } + else + { + strncpy( host, scolon + 1, HOST_NAME_MAX ); + } + } + else + { + strncpy( host, ftlisten, HOST_NAME_MAX ); + } + + if( ( colon = strchr( host, ':' ) ) ) + { + *colon = '\0'; + strncpy( port, colon + 1, 5 ); + } + } + else if( copy_fd >= 0 && getsockname( copy_fd, (struct sockaddr*) &saddrs, &ssize ) == 0 && + ( saddrs.ss_family == AF_INET || saddrs.ss_family == AF_INET6 ) && + getnameinfo( (struct sockaddr*) &saddrs, ssize, host, HOST_NAME_MAX, + NULL, 0, NI_NUMERICHOST ) == 0 ) + { + /* We just took our local address on copy_fd, which is likely to be a + sensible address from which we can do a file transfer now - the + most sensible we can get easily. */ + } + else + { + ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" ); + } + + memset( &hints, 0, sizeof( struct addrinfo ) ); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_NUMERICSERV; + + if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 ) + { + sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) ); + return -1; + } + + saddrlen = rp->ai_addrlen; + + memcpy( saddr, rp->ai_addr, saddrlen ); + + freeaddrinfo( rp ); + + ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" ); + ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" ); + ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" ); + + if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ? + ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr : + ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr, + host, HOST_NAME_MAX ) ) + { + strcpy( errmsg, "inet_ntop failed on listening socket" ); + return -1; + } + + ssize = sizeof( struct sockaddr_storage ); + ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" ); + + if( saddr->ss_family == AF_INET ) + g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in *) saddr )->sin_port ) ); + else + g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in6 *) saddr )->sin6_port ) ); + + if( saddr_ptr ) + memcpy( saddr_ptr, saddr, saddrlen ); + + /* I hate static-length strings.. */ + host[HOST_NAME_MAX] = '\0'; + port[5] = '\0'; + + return fd; +} diff --git a/lib/ftutil.h b/lib/ftutil.h new file mode 100644 index 00000000..09c1104e --- /dev/null +++ b/lib/ftutil.h @@ -0,0 +1,40 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Utility functions for file transfer * +* * +* Copyright 2008 Uli Meis <a.sporto+bee@gmail.com> * +* * +* 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. * +* * +\***************************************************************************/ + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */ +#endif + +/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */ +#ifndef HOST_NAME_MAX +#include <sys/param.h> +#ifdef MAXHOSTNAMELEN +#define HOST_NAME_MAX MAXHOSTNAMELEN +#else +#define HOST_NAME_MAX 255 +#endif +#endif + +/* This function should be used with care. host should be AT LEAST a + char[HOST_NAME_MAX+1] and port AT LEAST a char[6]. */ +int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr ); diff --git a/lib/http_client.c b/lib/http_client.c new file mode 100644 index 00000000..69f06ec5 --- /dev/null +++ b/lib/http_client.c @@ -0,0 +1,456 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* HTTP(S) module */ + +/* + 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 +*/ + +#include <string.h> +#include <stdio.h> + +#include "http_client.h" +#include "url.h" +#include "sock.h" + + +static gboolean http_connected( gpointer data, int source, b_input_condition cond ); +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ); +static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ); +static void http_free( struct http_request *req ); + + +struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ) +{ + struct http_request *req; + int error = 0; + + req = g_new0( struct http_request, 1 ); + + if( ssl ) + { + req->ssl = ssl_connect( host, port, http_ssl_connected, req ); + if( req->ssl == NULL ) + error = 1; + } + else + { + req->fd = proxy_connect( host, port, http_connected, req ); + if( req->fd < 0 ) + error = 1; + } + + if( error ) + { + http_free( req ); + return NULL; + } + + req->func = func; + req->data = data; + req->request = g_strdup( request ); + req->request_length = strlen( request ); + req->redir_ttl = 3; + + return( req ); +} + +struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data ) +{ + url_t *url = g_new0( url_t, 1 ); + char *request; + void *ret; + + if( !url_set( url, url_string ) ) + { + g_free( url ); + return NULL; + } + + if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS ) + { + g_free( url ); + return NULL; + } + + request = g_strdup_printf( "GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" + "\r\n", url->file, url->host ); + + ret = http_dorequest( url->host, url->port, + url->proto == PROTO_HTTPS, request, func, data ); + + g_free( url ); + g_free( request ); + return ret; +} + +/* This one is actually pretty simple... Might get more calls if we can't write + the whole request at once. */ +static gboolean http_connected( gpointer data, int source, b_input_condition cond ) +{ + struct http_request *req = data; + int st; + + if( source < 0 ) + goto error; + + if( req->inpa > 0 ) + b_event_remove( req->inpa ); + + sock_make_nonblocking( req->fd ); + + if( req->ssl ) + { + st = ssl_write( req->ssl, req->request + req->bytes_written, + req->request_length - req->bytes_written ); + if( st < 0 ) + { + if( ssl_errno != SSL_AGAIN ) + { + ssl_disconnect( req->ssl ); + goto error; + } + } + } + else + { + st = write( source, req->request + req->bytes_written, + req->request_length - req->bytes_written ); + if( st < 0 ) + { + if( !sockerr_again() ) + { + closesocket( req->fd ); + goto error; + } + } + } + + if( st > 0 ) + req->bytes_written += st; + + if( req->bytes_written < req->request_length ) + req->inpa = b_input_add( source, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE, + http_connected, req ); + else + req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req ); + + return FALSE; + +error: + req->status_string = g_strdup( "Error while writing HTTP request" ); + + req->func( req ); + http_free( req ); + return FALSE; +} + +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ) +{ + struct http_request *req = data; + + if( source == NULL ) + return http_connected( data, -1, cond ); + + req->fd = ssl_getfd( source ); + + return http_connected( data, req->fd, cond ); +} + +static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) +{ + struct http_request *req = data; + int evil_server = 0; + char buffer[2048]; + char *end1, *end2; + int st; + + if( req->inpa > 0 ) + b_event_remove( req->inpa ); + + if( req->ssl ) + { + st = ssl_read( req->ssl, buffer, sizeof( buffer ) ); + if( st < 0 ) + { + if( ssl_errno != SSL_AGAIN ) + { + /* goto cleanup; */ + + /* YAY! We have to deal with crappy Microsoft + servers that LOVE to send invalid TLS + packets that abort connections! \o/ */ + + goto got_reply; + } + } + else if( st == 0 ) + { + goto got_reply; + } + } + else + { + st = read( req->fd, buffer, sizeof( buffer ) ); + if( st < 0 ) + { + if( !sockerr_again() ) + { + req->status_string = g_strdup( strerror( errno ) ); + goto cleanup; + } + } + else if( st == 0 ) + { + goto got_reply; + } + } + + if( st > 0 ) + { + req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); + memcpy( req->reply_headers + req->bytes_read, buffer, st ); + req->bytes_read += st; + } + + /* There will be more! */ + req->inpa = b_input_add( req->fd, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ, + http_incoming_data, req ); + + return FALSE; + +got_reply: + /* Maybe if the webserver is overloaded, or when there's bad SSL + support... */ + if( req->bytes_read == 0 ) + { + req->status_string = g_strdup( "Empty HTTP reply" ); + goto cleanup; + } + + /* Zero termination is very convenient. */ + req->reply_headers[req->bytes_read] = 0; + + /* Find the separation between headers and body, and keep stupid + webservers in mind. */ + end1 = strstr( req->reply_headers, "\r\n\r\n" ); + end2 = strstr( req->reply_headers, "\n\n" ); + + if( end2 && end2 < end1 ) + { + end1 = end2 + 1; + evil_server = 1; + } + else if( end1 ) + { + end1 += 2; + } + else + { + req->status_string = g_strdup( "Malformed HTTP reply" ); + goto cleanup; + } + + *end1 = 0; + + if( evil_server ) + req->reply_body = end1 + 1; + else + req->reply_body = end1 + 2; + + req->body_size = req->reply_headers + req->bytes_read - req->reply_body; + + if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) + { + if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 ) + { + req->status_string = g_strdup( "Can't parse status code" ); + req->status_code = -1; + } + else + { + char *eol; + + if( evil_server ) + eol = strchr( end1, '\n' ); + else + eol = strchr( end1, '\r' ); + + req->status_string = g_strndup( end1 + 1, eol - end1 - 1 ); + + /* Just to be sure... */ + if( ( eol = strchr( req->status_string, '\r' ) ) ) + *eol = 0; + if( ( eol = strchr( req->status_string, '\n' ) ) ) + *eol = 0; + } + } + else + { + req->status_string = g_strdup( "Can't locate status code" ); + req->status_code = -1; + } + + if( ( req->status_code == 301 || req->status_code == 302 ) && req->redir_ttl-- > 0 ) + { + char *loc, *new_request, *new_host; + int error = 0, new_port, new_proto; + + /* We might fill it again, so let's not leak any memory. */ + g_free( req->status_string ); + req->status_string = NULL; + + loc = strstr( req->reply_headers, "\nLocation: " ); + if( loc == NULL ) /* We can't handle this redirect... */ + { + req->status_string = g_strdup( "Can't locate Location: header" ); + goto cleanup; + } + + loc += 11; + while( *loc == ' ' ) + loc ++; + + /* TODO/FIXME: Possibly have to handle relative redirections, + and rewrite Host: headers. Not necessary for now, it's + enough for passport authentication like this. */ + + if( *loc == '/' ) + { + /* Just a different pathname... */ + + /* Since we don't cache the servername, and since we + don't need this yet anyway, I won't implement it. */ + + req->status_string = g_strdup( "Can't handle recursive redirects" ); + + goto cleanup; + } + else + { + /* A whole URL */ + url_t *url; + char *s; + + s = strstr( loc, "\r\n" ); + if( s == NULL ) + goto cleanup; + + url = g_new0( url_t, 1 ); + *s = 0; + + if( !url_set( url, loc ) ) + { + req->status_string = g_strdup( "Malformed redirect URL" ); + g_free( url ); + goto cleanup; + } + + /* Okay, this isn't fun! We have to rebuild the request... :-( */ + new_request = g_malloc( req->request_length + strlen( url->file ) ); + + /* So, now I just allocated enough memory, so I'm + going to use strcat(), whether you like it or not. :-) */ + + sprintf( new_request, "GET %s HTTP/1.0", url->file ); + + s = strstr( req->request, "\r\n" ); + if( s == NULL ) + { + req->status_string = g_strdup( "Error while rebuilding request string" ); + g_free( new_request ); + g_free( url ); + goto cleanup; + } + + strcat( new_request, s ); + new_host = g_strdup( url->host ); + new_port = url->port; + new_proto = url->proto; + + g_free( url ); + } + + if( req->ssl ) + ssl_disconnect( req->ssl ); + else + closesocket( req->fd ); + + req->fd = -1; + req->ssl = NULL; + + if( new_proto == PROTO_HTTPS ) + { + req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req ); + if( req->ssl == NULL ) + error = 1; + } + else + { + req->fd = proxy_connect( new_host, new_port, http_connected, req ); + if( req->fd < 0 ) + error = 1; + } + g_free( new_host ); + + if( error ) + { + req->status_string = g_strdup( "Connection problem during redirect" ); + g_free( new_request ); + goto cleanup; + } + + g_free( req->request ); + g_free( req->reply_headers ); + req->request = new_request; + req->request_length = strlen( new_request ); + req->bytes_read = req->bytes_written = req->inpa = 0; + req->reply_headers = req->reply_body = NULL; + + return FALSE; + } + + /* Assume that a closed connection means we're finished, this indeed + breaks with keep-alive connections and faulty connections. */ + req->finished = 1; + +cleanup: + if( req->ssl ) + ssl_disconnect( req->ssl ); + else + closesocket( req->fd ); + + req->func( req ); + http_free( req ); + return FALSE; +} + +static void http_free( struct http_request *req ) +{ + g_free( req->request ); + g_free( req->reply_headers ); + g_free( req->status_string ); + g_free( req ); +} + diff --git a/lib/http_client.h b/lib/http_client.h new file mode 100644 index 00000000..27c484ff --- /dev/null +++ b/lib/http_client.h @@ -0,0 +1,84 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* HTTP(S) module */ + +/* + 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 +*/ + +/* http_client allows you to talk (asynchronously, again) to HTTP servers. + In the "background" it will send the whole query and wait for a complete + response to come back. Right now it's only used by the MSN Passport + authentication code, but it might be useful for other things too (for + example the AIM usericon patch uses this so icons can be stored on + webservers instead of the local filesystem). + + Didn't test this too much, but it seems to work well. Just don't look + at the code that handles HTTP 30x redirects. ;-) The function is + probably not very useful for downloading lots of data since it keeps + everything in a memory buffer until the download is completed (and + can't pass any data or whatever before then). It's very useful for + doing quick requests without blocking the whole program, though. */ + +#include <glib.h> +#include "ssl_client.h" + +struct http_request; + +/* Your callback function should look like this: */ +typedef void (*http_input_function)( struct http_request * ); + +/* This structure will be filled in by the http_dorequest* functions, and + it will be passed to the callback function. Use the data field to add + your own data. */ +struct http_request +{ + char *request; /* The request to send to the server. */ + int request_length; /* Its size. */ + int status_code; /* The numeric HTTP status code. (Or -1 + if something really went wrong) */ + char *status_string; /* The error text. */ + char *reply_headers; + char *reply_body; + int body_size; /* The number of bytes in reply_body. */ + int finished; /* Set to non-0 if the request was completed + successfully. */ + int redir_ttl; /* You can set it to 0 if you don't want + http_client to follow them. */ + + http_input_function func; + gpointer data; + + /* Please don't touch the things down here, you shouldn't need them. */ + + void *ssl; + int fd; + + int inpa; + int bytes_written; + int bytes_read; +}; + +/* The _url variant is probably more useful than the raw version. The raw + version is probably only useful if you want to do POST requests or if + you want to add some extra headers. As you can see, HTTPS connections + are also supported (using ssl_client). */ +struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); +struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); diff --git a/lib/ini.c b/lib/ini.c new file mode 100644 index 00000000..aa291bb2 --- /dev/null +++ b/lib/ini.c @@ -0,0 +1,145 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* INI file reading code */ + +/* + 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" + +ini_t *ini_open( char *file ) +{ + int fd; + ini_t *ini = NULL; + struct stat fi; + + if( ( fd = open( file, O_RDONLY ) ) != -1 && + fstat( fd, &fi ) == 0 && + fi.st_size <= 16384 && + ( ini = g_malloc( sizeof( ini_t ) + fi.st_size + 1 ) ) && + read( fd, ini->file, fi.st_size ) == fi.st_size ) + { + memset( ini, 0, sizeof( ini_t ) ); + ini->size = fi.st_size; + ini->file[ini->size] = 0; + ini->cur = ini->file; + ini->c_section = ""; + + close( fd ); + + return ini; + } + + if( fd >= 0 ) + close( fd ); + + ini_close( ini ); + + return NULL; +} + +/* Strips leading and trailing whitespace and returns a pointer to the first + non-ws character of the given string. */ +static char *ini_strip_whitespace( char *in ) +{ + char *e; + + while( isspace( *in ) ) + in++; + + e = in + strlen( in ) - 1; + while( e > in && isspace( *e ) ) + e--; + e[1] = 0; + + return in; +} + +int ini_read( ini_t *file ) +{ + char *s; + + while( file->cur && file->cur < file->file + file->size ) + { + char *e, *next; + + file->line++; + + /* Find the end of line */ + if( ( e = strchr( file->cur, '\n' ) ) != NULL ) + { + *e = 0; + next = e + 1; + } + else + { + /* No more lines. */ + e = file->cur + strlen( file->cur ); + next = NULL; + } + + /* Comment? */ + if( ( s = strchr( file->cur, '#' ) ) != NULL ) + *s = 0; + + file->cur = ini_strip_whitespace( file->cur ); + + if( *file->cur == '[' ) + { + file->cur++; + if( ( s = strchr( file->cur, ']' ) ) != NULL ) + { + *s = 0; + file->c_section = file->cur; + } + } + else if( ( s = strchr( file->cur, '=' ) ) != NULL ) + { + *s = 0; + file->key = ini_strip_whitespace( file->cur ); + file->value = ini_strip_whitespace( s + 1 ); + + if( ( s = strchr( file->key, '.' ) ) != NULL ) + { + *s = 0; + file->section = file->key; + file->key = s + 1; + } + else + { + file->section = file->c_section; + } + + file->cur = next; + return 1; + } + /* else: noise/comment/etc, let's just ignore it. */ + + file->cur = next; + } + + return 0; +} + +void ini_close( ini_t *file ) +{ + g_free( file ); +} diff --git a/lib/ini.h b/lib/ini.h new file mode 100644 index 00000000..6ae0bde5 --- /dev/null +++ b/lib/ini.h @@ -0,0 +1,45 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* INI file reading code */ + +/* + 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 +*/ + +#ifndef _INI_H +#define _INI_H + +typedef struct +{ + int line; + char *c_section; + char *section; + char *key; + char *value; + int size; + char *cur, *tok; + char file[]; +} ini_t; + +ini_t *ini_open( char *file ); +int ini_read( ini_t *file ); +void ini_close( ini_t *file ); + +#endif diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 00000000..3c39eccd --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,262 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include <sys/types.h> +#include <string.h> /* for memcpy() */ +#include "md5.h" + +static void md5_transform(uint32_t buf[4], uint32_t const in[16]); + +/* + * Wrapper function for all-in-one MD5 + * + * Bernardo Reino, aka Lepton. + * 20021120 + */ + +/* Turns out MD5 was designed for little-endian machines. If we're running + on a big-endian machines, we have to swap some bytes. Since detecting + endianness at compile time reliably seems pretty hard, let's do it at + run-time. It's not like we're going to checksum megabytes of data... */ +static uint32_t cvt32(uint32_t val) +{ + static int little_endian = -1; + + if (little_endian == -1) + { + little_endian = 1; + little_endian = *((char*) &little_endian); + } + + if (little_endian) + return val; + else + return (val >> 24) | + ((val >> 8) & 0xff00) | + ((val << 8) & 0xff0000) | + (val << 24); +} + +void md5_init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void md5_append(struct MD5Context *ctx, const md5_byte_t *buf, + unsigned int len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + md5_transform(ctx->buf, (uint32_t *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16]) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + md5_transform(ctx->buf, (uint32_t *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + + /* Append length in bits and transform */ + ((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]); + ((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]); + + md5_transform(ctx->buf, (uint32_t *) ctx->in); + ctx->buf[0] = cvt32(ctx->buf[0]); + ctx->buf[1] = cvt32(ctx->buf[1]); + ctx->buf[2] = cvt32(ctx->buf[2]); + ctx->buf[3] = cvt32(ctx->buf[3]); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void md5_transform(uint32_t buf[4], uint32_t const in[16]) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 00000000..3ba28586 --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,46 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef _MD5_H +#define _MD5_H + +#include <sys/types.h> +#include <gmodule.h> +#if(__sun) +#include <inttypes.h> +#else +#include <stdint.h> +#endif + +typedef uint8_t md5_byte_t; +typedef struct MD5Context { + uint32_t buf[4]; + uint32_t bits[2]; + unsigned char in[64]; +} md5_state_t; + +G_MODULE_EXPORT void md5_init(struct MD5Context *context); +G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len); +G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]); + +#endif diff --git a/lib/misc.c b/lib/misc.c new file mode 100644 index 00000000..05192d9c --- /dev/null +++ b/lib/misc.c @@ -0,0 +1,730 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2006 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * Various utility functions. Some are copied from Gaim to support the + * IM-modules, most are from BitlBee. + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * (and possibly other members of the Gaim team) + * Copyright 2002-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 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 "nogaim.h" +#include "base64.h" +#include "md5.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <glib.h> +#include <time.h> + +#ifdef HAVE_RESOLV_A +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#include "md5.h" +#include "ssl_client.h" + +void strip_linefeed(gchar *text) +{ + int i, j; + gchar *text2 = g_malloc(strlen(text) + 1); + + for (i = 0, j = 0; text[i]; i++) + if (text[i] != '\r') + text2[j++] = text[i]; + text2[j] = '\0'; + + strcpy(text, text2); + g_free(text2); +} + +time_t get_time(int year, int month, int day, int hour, int min, int sec) +{ + struct tm tm; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; + + return mktime(&tm); +} + +time_t mktime_utc( struct tm *tp ) +{ + struct tm utc; + time_t res, tres; + + tp->tm_isdst = -1; + res = mktime( tp ); + /* Problem is, mktime() just gave us the GMT timestamp for the + given local time... While the given time WAS NOT local. So + we should fix this now. + + Now I could choose between messing with environment variables + (kludgy) or using timegm() (not portable)... Or doing the + following, which I actually prefer... + + tzset() may also work but in other places I actually want to + use local time. + + FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */ + gmtime_r( &res, &utc ); + utc.tm_isdst = -1; + if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min ) + /* Sweet! We're in UTC right now... */ + return res; + + tres = mktime( &utc ); + res += res - tres; + + /* Yes, this is a hack. And it will go wrong around DST changes. + BUT this is more likely to be threadsafe than messing with + environment variables, and possibly more portable... */ + + return res; +} + +typedef struct htmlentity +{ + char code[7]; + char is[3]; +} htmlentity_t; + +static const htmlentity_t ent[] = +{ + { "lt", "<" }, + { "gt", ">" }, + { "amp", "&" }, + { "apos", "'" }, + { "quot", "\"" }, + { "aacute", "á" }, + { "eacute", "é" }, + { "iacute", "é" }, + { "oacute", "ó" }, + { "uacute", "ú" }, + { "agrave", "à " }, + { "egrave", "è" }, + { "igrave", "ì" }, + { "ograve", "ò" }, + { "ugrave", "ù" }, + { "acirc", "â" }, + { "ecirc", "ê" }, + { "icirc", "î" }, + { "ocirc", "ô" }, + { "ucirc", "û" }, + { "auml", "ä" }, + { "euml", "ë" }, + { "iuml", "ï" }, + { "ouml", "ö" }, + { "uuml", "ü" }, + { "nbsp", " " }, + { "", "" } +}; + +void strip_html( char *in ) +{ + char *start = in; + char out[strlen(in)+1]; + char *s = out, *cs; + int i, matched; + int taglen; + + memset( out, 0, sizeof( out ) ); + + while( *in ) + { + if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) ) + { + /* If in points at a < and in+1 points at a letter or a slash, this is probably + a HTML-tag. Try to find a closing > and continue there. If the > can't be + found, assume that it wasn't a HTML-tag after all. */ + + cs = in; + + while( *in && *in != '>' ) + in ++; + + taglen = in - cs - 1; /* not <0 because the above loop runs at least once */ + if( *in ) + { + if( g_strncasecmp( cs+1, "b", taglen) == 0 ) + *(s++) = '\x02'; + else if( g_strncasecmp( cs+1, "/b", taglen) == 0 ) + *(s++) = '\x02'; + else if( g_strncasecmp( cs+1, "i", taglen) == 0 ) + *(s++) = '\x1f'; + else if( g_strncasecmp( cs+1, "/i", taglen) == 0 ) + *(s++) = '\x1f'; + else if( g_strncasecmp( cs+1, "br", taglen) == 0 ) + *(s++) = '\n'; + in ++; + } + else + { + in = cs; + *(s++) = *(in++); + } + } + else if( *in == '&' ) + { + cs = ++in; + while( *in && isalpha( *in ) ) + in ++; + + if( *in == ';' ) in ++; + matched = 0; + + for( i = 0; *ent[i].code; i ++ ) + if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 ) + { + int j; + + for( j = 0; ent[i].is[j]; j ++ ) + *(s++) = ent[i].is[j]; + + matched = 1; + break; + } + + /* None of the entities were matched, so return the string */ + if( !matched ) + { + in = cs - 1; + *(s++) = *(in++); + } + } + else + { + *(s++) = *(in++); + } + } + + strcpy( start, out ); +} + +char *escape_html( const char *html ) +{ + const char *c = html; + GString *ret; + char *str; + + if( html == NULL ) + return( NULL ); + + ret = g_string_new( "" ); + + while( *c ) + { + switch( *c ) + { + case '&': + ret = g_string_append( ret, "&" ); + break; + case '<': + ret = g_string_append( ret, "<" ); + break; + case '>': + ret = g_string_append( ret, ">" ); + break; + case '"': + ret = g_string_append( ret, """ ); + break; + default: + ret = g_string_append_c( ret, *c ); + } + c ++; + } + + str = ret->str; + g_string_free( ret, FALSE ); + return( str ); +} + +/* Decode%20a%20file%20name */ +void http_decode( char *s ) +{ + char *t; + int i, j, k; + + t = g_new( char, strlen( s ) + 1 ); + + for( i = j = 0; s[i]; i ++, j ++ ) + { + if( s[i] == '%' ) + { + if( sscanf( s + i + 1, "%2x", &k ) ) + { + t[j] = k; + i += 2; + } + else + { + *t = 0; + break; + } + } + else + { + t[j] = s[i]; + } + } + t[j] = 0; + + strcpy( s, t ); + g_free( t ); +} + +/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ +/* This fuction is safe, but make sure you call it safely as well! */ +void http_encode( char *s ) +{ + char t[strlen(s)+1]; + int i, j; + + strcpy( t, s ); + for( i = j = 0; t[i]; i ++, j ++ ) + { + /* Warning: isalnum() is locale-aware, so don't use it here! */ + if( ( t[i] >= 'A' && t[i] <= 'Z' ) || + ( t[i] >= 'a' && t[i] <= 'z' ) || + ( t[i] >= '0' && t[i] <= '9' ) || + strchr( "._-~", t[i] ) ) + { + s[j] = t[i]; + } + else + { + sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); + j += 2; + } + } + s[j] = 0; +} + +/* Strip newlines from a string. Modifies the string passed to it. */ +char *strip_newlines( char *source ) +{ + int i; + + for( i = 0; source[i] != '\0'; i ++ ) + if( source[i] == '\n' || source[i] == '\r' ) + source[i] = ' '; + + return source; +} + +/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */ +char *ipv6_wrap( char *src ) +{ + static char dst[64]; + int i; + + for( i = 0; src[i]; i ++ ) + if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) + break; + + /* Hmm, it's not even an IP... */ + if( src[i] ) + return src; + + g_snprintf( dst, sizeof( dst ), "::ffff:%s", src ); + + return dst; +} + +/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */ +char *ipv6_unwrap( char *src ) +{ + int i; + + if( g_strncasecmp( src, "::ffff:", 7 ) != 0 ) + return src; + + for( i = 7; src[i]; i ++ ) + if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) + break; + + /* Hmm, it's not even an IP... */ + if( src[i] ) + return src; + + return ( src + 7 ); +} + +/* Convert from one charset to another. + + from_cs, to_cs: Source and destination charsets + src, dst: Source and destination strings + size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though. + maxbuf: Maximum number of bytes to write to dst + + Returns the number of bytes written to maxbuf or -1 on an error. +*/ +signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ) +{ + GIConv cd; + size_t res; + size_t inbytesleft, outbytesleft; + char *inbuf = src; + char *outbuf = dst; + + cd = g_iconv_open( to_cs, from_cs ); + if( cd == (GIConv) -1 ) + return( -1 ); + + inbytesleft = size ? size : strlen( src ); + outbytesleft = maxbuf - 1; + res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); + *outbuf = '\0'; + g_iconv_close( cd ); + + if( res == (size_t) -1 ) + return( -1 ); + else + return( outbuf - dst ); +} + +/* A pretty reliable random number generator. Tries to use the /dev/random + devices first, and falls back to the random number generator from libc + when it fails. Opens randomizer devices with O_NONBLOCK to make sure a + lack of entropy won't halt BitlBee. */ +void random_bytes( unsigned char *buf, int count ) +{ +#ifndef _WIN32 + static int use_dev = -1; + + /* Actually this probing code isn't really necessary, is it? */ + if( use_dev == -1 ) + { + if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 ) + use_dev = 1; + else + { + use_dev = 0; + srand( ( getpid() << 16 ) ^ time( NULL ) ); + } + } + + if( use_dev ) + { + int fd; + + /* At least on Linux, /dev/random can block if there's not + enough entropy. We really don't want that, so if it can't + give anything, use /dev/urandom instead. */ + if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 ) + if( read( fd, buf, count ) == count ) + { + close( fd ); + return; + } + close( fd ); + + /* urandom isn't supposed to block at all, but just to be + sure. If it blocks, we'll disable use_dev and use the libc + randomizer instead. */ + if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 ) + if( read( fd, buf, count ) == count ) + { + close( fd ); + return; + } + close( fd ); + + /* If /dev/random blocks once, we'll still try to use it + again next time. If /dev/urandom also fails for some + reason, stick with libc during this session. */ + + use_dev = 0; + srand( ( getpid() << 16 ) ^ time( NULL ) ); + } + + if( !use_dev ) +#endif + { + int i; + + /* Possibly the LSB of rand() isn't very random on some + platforms. Seems okay on at least Linux and OSX though. */ + for( i = 0; i < count; i ++ ) + buf[i] = rand() & 0xff; + } +} + +int is_bool( char *value ) +{ + if( *value == 0 ) + return 0; + + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + return 1; + if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + return 1; + + while( *value ) + if( !isdigit( *value ) ) + return 0; + else + value ++; + + return 1; +} + +int bool2int( char *value ) +{ + int i; + + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + return 1; + if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + return 0; + + if( sscanf( value, "%d", &i ) == 1 ) + return i; + + return 0; +} + +struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain ) +{ + struct ns_srv_reply **replies = NULL; +#ifdef HAVE_RESOLV_A + struct ns_srv_reply *reply = NULL; + char name[1024]; + unsigned char querybuf[1024]; + const unsigned char *buf; + ns_msg nsh; + ns_rr rr; + int i, n, len, size; + + g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); + + if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) + return NULL; + + if( ns_initparse( querybuf, size, &nsh ) != 0 ) + return NULL; + + n = 0; + while( ns_parserr( &nsh, ns_s_an, n, &rr ) == 0 ) + { + size = ns_rr_rdlen( rr ); + buf = ns_rr_rdata( rr ); + + len = 0; + for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) + len += buf[i] + 1; + + if( i > size ) + break; + + reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); + memcpy( reply->name, buf + 7, len ); + + for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) + reply->name[i] = '.'; + + if( i > len ) + { + g_free( reply ); + break; + } + + reply->prio = ( buf[0] << 8 ) | buf[1]; + reply->weight = ( buf[2] << 8 ) | buf[3]; + reply->port = ( buf[4] << 8 ) | buf[5]; + + n ++; + replies = g_renew( struct ns_srv_reply *, replies, n + 1 ); + replies[n-1] = reply; + } + if( replies ) + replies[n] = NULL; +#endif + + return replies; +} + +void srv_free( struct ns_srv_reply **srv ) +{ + int i; + + if( srv == NULL ) + return; + + for( i = 0; srv[i]; i ++ ) + g_free( srv[i] ); + g_free( srv ); +} + +/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */ +char *word_wrap( const char *msg, int line_len ) +{ + GString *ret = g_string_sized_new( strlen( msg ) + 16 ); + + while( strlen( msg ) > line_len ) + { + int i; + + /* First try to find out if there's a newline already. Don't + want to add more splits than necessary. */ + for( i = line_len; i > 0 && msg[i] != '\n'; i -- ); + if( msg[i] == '\n' ) + { + g_string_append_len( ret, msg, i + 1 ); + msg += i + 1; + continue; + } + + for( i = line_len; i > 0; i -- ) + { + if( msg[i] == '-' ) + { + g_string_append_len( ret, msg, i + 1 ); + g_string_append_c( ret, '\n' ); + msg += i + 1; + break; + } + else if( msg[i] == ' ' ) + { + g_string_append_len( ret, msg, i ); + g_string_append_c( ret, '\n' ); + msg += i + 1; + break; + } + } + if( i == 0 ) + { + g_string_append_len( ret, msg, line_len ); + g_string_append_c( ret, '\n' ); + msg += line_len; + } + } + g_string_append( ret, msg ); + + return g_string_free( ret, FALSE ); +} + +gboolean ssl_sockerr_again( void *ssl ) +{ + if( ssl ) + return ssl_errno == SSL_AGAIN; + else + return sockerr_again(); +} + +/* Returns values: -1 == Failure (base64-decoded to something unexpected) + 0 == Okay + 1 == Password doesn't match the hash. */ +int md5_verify_password( char *password, char *hash ) +{ + md5_byte_t *pass_dec = NULL; + md5_byte_t pass_md5[16]; + md5_state_t md5_state; + int ret = -1, i; + + if( base64_decode( hash, &pass_dec ) == 21 ) + { + md5_init( &md5_state ); + md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) ); + 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] ) + { + ret = 1; + break; + } + } + + /* If we reached the end of the loop, it was a match! */ + if( i == 16 ) + ret = 0; + } + + g_free( pass_dec ); + + return ret; +} + +/* Split commands (root-style, *not* IRC-style). Handles "quoting of" + white\ space in 'various ways'. Returns a NULL-terminated static + char** so watch out with nested use! Definitely not thread-safe. */ +char **split_command_parts( char *command ) +{ + static char *cmd[IRC_MAX_ARGS+1]; + char *s, q = 0; + int k; + + memset( cmd, 0, sizeof( cmd ) ); + cmd[0] = command; + k = 1; + for( s = command; *s && k < IRC_MAX_ARGS; s ++ ) + if( *s == ' ' && !q ) + { + *s = 0; + while( *++s == ' ' ); + if( *s == '"' || *s == '\'' ) + { + q = *s; + s ++; + } + if( *s ) + { + cmd[k++] = s; + s --; + } + else + { + break; + } + } + else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) ) + { + char *cpy; + + for( cpy = s; *cpy; cpy ++ ) + cpy[0] = cpy[1]; + } + else if( *s == q ) + { + q = *s = 0; + } + + /* Full zero-padding for easier argc checking. */ + while( k <= IRC_MAX_ARGS ) + cmd[k++] = NULL; + + return cmd; +} diff --git a/lib/misc.h b/lib/misc.h new file mode 100644 index 00000000..83ba9e67 --- /dev/null +++ b/lib/misc.h @@ -0,0 +1,74 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Misc. functions */ + +/* + 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 +*/ + +#ifndef _MISC_H +#define _MISC_H + +#include <gmodule.h> +#include <time.h> + +struct ns_srv_reply +{ + int prio; + int weight; + int port; + char name[]; +}; + +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 time_t get_time( int year, int month, int day, int hour, int min, int sec ); +G_MODULE_EXPORT time_t mktime_utc( struct tm *tp ); +double gettime( void ); + +G_MODULE_EXPORT void strip_html( char *msg ); +G_MODULE_EXPORT char *escape_html( const char *html ); +G_MODULE_EXPORT void http_decode( char *s ); +G_MODULE_EXPORT void http_encode( char *s ); + +G_MODULE_EXPORT char *ipv6_wrap( char *src ); +G_MODULE_EXPORT char *ipv6_unwrap( char *src ); + +G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); + +G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count ); + +G_MODULE_EXPORT int is_bool( char *value ); +G_MODULE_EXPORT int bool2int( char *value ); + +G_MODULE_EXPORT struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain ); +G_MODULE_EXPORT void srv_free( struct ns_srv_reply **srv ); + +G_MODULE_EXPORT char *word_wrap( const char *msg, int line_len ); + +G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); + +G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); + +G_MODULE_EXPORT char **split_command_parts( char *command ); + +#endif diff --git a/lib/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..9c67363a --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,456 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library 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 * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "http_client.h" +#include "base64.h" +#include "misc.h" +#include "sha1.h" +#include "url.h" +#include "oauth.h" + +#define HMAC_BLOCK_SIZE 64 + +static char *oauth_sign( const char *method, const char *url, + const char *params, struct oauth_info *oi ) +{ + sha1_state_t sha1; + uint8_t hash[sha1_hash_size]; + uint8_t key[HMAC_BLOCK_SIZE+1]; + char *s; + int i; + + /* Create K. If our current key is >64 chars we have to hash it, + otherwise just pad. */ + memset( key, 0, HMAC_BLOCK_SIZE ); + i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 ); + if( i > HMAC_BLOCK_SIZE ) + { + sha1_init( &sha1 ); + sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); + sha1_append( &sha1, (uint8_t*) "&", 1 ); + if( oi->token_secret ) + sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) ); + sha1_finish( &sha1, key ); + } + else + { + g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", + oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + + /* OAuth: text = method&url¶ms, all http_encoded. */ + sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + + s = g_new0( char, strlen( url ) * 3 + 1 ); + strcpy( s, url ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + g_free( s ); + + s = g_new0( char, strlen( params ) * 3 + 1 ); + strcpy( s, params ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + g_free( s ); + + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, hash ); + + /* base64_encode + HTTP escape it (both consumers + need it that away) and we're done. */ + s = base64_encode( hash, sha1_hash_size ); + s = g_realloc( s, strlen( s ) * 3 + 1 ); + http_encode( s ); + + return s; +} + +static char *oauth_nonce() +{ + unsigned char bytes[21]; + char *ret = g_new0( char, sizeof( bytes) / 3 * 4 + 1 ); + + random_bytes( bytes, sizeof( bytes ) ); + base64_encode_real( bytes, sizeof( bytes), (unsigned char*) ret, "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0A" ); + + return ret; +} + +void oauth_params_add( GSList **params, const char *key, const char *value ) +{ + char *item; + + item = g_strdup_printf( "%s=%s", key, value ); + *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); +} + +void oauth_params_del( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l, *n; + + for( l = *params; l; l = n ) + { + n = l->next; + + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + { + g_free( l->data ); + *params = g_slist_remove( *params, l->data ); + } + } +} + +void oauth_params_set( GSList **params, const char *key, const char *value ) +{ + oauth_params_del( params, key ); + oauth_params_add( params, key, value ); +} + +const char *oauth_params_get( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l; + + for( l = *params; l; l = l->next ) + { + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + return (const char*) l->data + key_len + 1; + } + + return NULL; +} + +static void oauth_params_parse( GSList **params, char *in ) +{ + char *amp, *eq, *s; + + while( in && *in ) + { + eq = strchr( in, '=' ); + if( !eq ) + break; + + *eq = '\0'; + if( ( amp = strchr( eq + 1, '&' ) ) ) + *amp = '\0'; + + s = g_strdup( eq + 1 ); + http_decode( s ); + oauth_params_add( params, in, s ); + g_free( s ); + + *eq = '='; + if( amp == NULL ) + break; + + *amp = '&'; + in = amp + 1; + } +} + +void oauth_params_free( GSList **params ) +{ + while( params && *params ) + { + g_free( (*params)->data ); + *params = g_slist_remove( *params, (*params)->data ); + } +} + +char *oauth_params_string( GSList *params ) +{ + GSList *l; + GString *str = g_string_new( "" ); + + for( l = params; l; l = l->next ) + { + char *s, *eq; + + s = g_malloc( strlen( l->data ) * 3 + 1 ); + strcpy( s, l->data ); + if( ( eq = strchr( s, '=' ) ) ) + http_encode( eq + 1 ); + g_string_append( str, s ); + g_free( s ); + + if( l->next ) + g_string_append_c( str, '&' ); + } + + return g_string_free( str, FALSE ); +} + +void oauth_info_free( struct oauth_info *info ) +{ + if( info ) + { + g_free( info->auth_url ); + g_free( info->request_token ); + g_free( info->token ); + g_free( info->token_secret ); + oauth_params_free( &info->params ); + g_free( info ); + } +} + +static void oauth_add_default_params( GSList **params, const struct oauth_service *sp ) +{ + char *s; + + oauth_params_set( params, "oauth_consumer_key", sp->consumer_key ); + oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); + + s = g_strdup_printf( "%d", (int) time( NULL ) ); + oauth_params_set( params, "oauth_timestamp", s ); + g_free( s ); + + s = oauth_nonce(); + oauth_params_set( params, "oauth_nonce", s ); + g_free( s ); + + oauth_params_set( params, "oauth_version", "1.0" ); +} + +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi ) +{ + GSList *params = NULL; + char *s, *params_s, *post; + void *req; + url_t url_p; + + if( !url_set( &url_p, url ) ) + { + oauth_params_free( params_ ); + return NULL; + } + + if( params_ ) + params = *params_; + + oauth_add_default_params( ¶ms, oi->sp ); + + params_s = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + s = oauth_sign( "POST", url, params_s, oi ); + post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); + g_free( params_s ); + g_free( s ); + + s = g_strdup_printf( "POST %s HTTP/1.0\r\n" + "Host: %s\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: %zd\r\n" + "\r\n" + "%s", url_p.file, url_p.host, strlen( post ), post ); + g_free( post ); + + req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, + s, func, oi ); + g_free( s ); + + return req; +} + +static void oauth_request_token_done( struct http_request *req ); + +struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ) +{ + struct oauth_info *st = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + st->func = func; + st->data = data; + st->sp = sp; + + oauth_params_add( ¶ms, "oauth_callback", "oob" ); + + if( !oauth_post_request( sp->url_request_token, ¶ms, oauth_request_token_done, st ) ) + { + oauth_info_free( st ); + return NULL; + } + + return st; +} + +static void oauth_request_token_done( struct http_request *req ) +{ + struct oauth_info *st = req->data; + + st->http = req; + + if( req->status_code == 200 ) + { + GSList *params = NULL; + + st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body ); + oauth_params_parse( ¶ms, req->reply_body ); + st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oauth_params_free( ¶ms ); + } + + st->stage = OAUTH_REQUEST_TOKEN; + st->func( st ); +} + +static void oauth_access_token_done( struct http_request *req ); + +gboolean oauth_access_token( const char *pin, struct oauth_info *st ) +{ + GSList *params = NULL; + + oauth_params_add( ¶ms, "oauth_token", st->request_token ); + oauth_params_add( ¶ms, "oauth_verifier", pin ); + + return oauth_post_request( st->sp->url_access_token, ¶ms, oauth_access_token_done, st ) != NULL; +} + +static void oauth_access_token_done( struct http_request *req ) +{ + struct oauth_info *st = req->data; + + st->http = req; + + if( req->status_code == 200 ) + { + oauth_params_parse( &st->params, req->reply_body ); + st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) ); + st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) ); + } + + st->stage = OAUTH_ACCESS_TOKEN; + if( st->func( st ) ) + { + /* Don't need these anymore, but keep the rest. */ + g_free( st->auth_url ); + st->auth_url = NULL; + g_free( st->request_token ); + st->request_token = NULL; + oauth_params_free( &st->params ); + } +} + +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ) +{ + GSList *params = NULL, *l; + char *sig = NULL, *params_s, *s; + GString *ret = NULL; + + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_add_default_params( ¶ms, oi->sp ); + + /* Start building the OAuth header. 'key="value", '... */ + ret = g_string_new( "OAuth " ); + for( l = params; l; l = l->next ) + { + char *kv = l->data; + char *eq = strchr( kv, '=' ); + char esc[strlen(kv)*3+1]; + + if( eq == NULL ) + break; /* WTF */ + + strcpy( esc, eq + 1 ); + http_encode( esc ); + + g_string_append_len( ret, kv, eq - kv + 1 ); + g_string_append_c( ret, '"' ); + g_string_append( ret, esc ); + g_string_append( ret, "\", " ); + } + + /* Now, before generating the signature, add GET/POST arguments to params + since they should be included in the base signature string (but not in + the HTTP header). */ + if( args ) + oauth_params_parse( ¶ms, args ); + if( ( s = strchr( url, '?' ) ) ) + { + s = g_strdup( s + 1 ); + oauth_params_parse( ¶ms, s ); + g_free( s ); + } + + /* Append the signature and we're done! */ + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, oi ); + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + g_free( params_s ); + + oauth_params_free( ¶ms ); + g_free( sig ); + + return ret ? g_string_free( ret, FALSE ) : NULL; +} + +char *oauth_to_string( struct oauth_info *oi ) +{ + GSList *params = NULL; + char *ret; + + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_params_add( ¶ms, "oauth_token_secret", oi->token_secret ); + ret = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + return ret; +} + +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ) +{ + struct oauth_info *oi = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + oauth_params_parse( ¶ms, in ); + oi->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oi->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); + oauth_params_free( ¶ms ); + oi->sp = sp; + + return oi; +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..8270a545 --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,94 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 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. * +* * +\***************************************************************************/ + +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; + +/* Callback function called twice during the access token request process. + Return FALSE if something broke and the process must be aborted. */ +typedef gboolean (*oauth_cb)( struct oauth_info * ); + +typedef enum +{ + OAUTH_INIT, + OAUTH_REQUEST_TOKEN, + OAUTH_ACCESS_TOKEN, +} oauth_stage_t; + +struct oauth_info +{ + oauth_stage_t stage; + const struct oauth_service *sp; + + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_url; + char *request_token; + + char *token; + char *token_secret; + GSList *params; +}; + +struct oauth_service +{ + char *url_request_token; + char *url_access_token; + char *url_authorize; + + char *consumer_key; + char *consumer_secret; +}; + +/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1) + Request an initial anonymous token which can be used to construct an + authorization URL for the user. This is passed to the callback function + in a struct oauth_info. */ +struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ); + +/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) + The user gets a PIN or so which we now exchange for the final access + token. This is passed to the callback function in the same + struct oauth_info. */ +gboolean oauth_access_token( const char *pin, struct oauth_info *st ); + +/* http://oauth.net/core/1.0a/#anchor12 (section 7) + Generate an OAuth Authorization: HTTP header. access_token should be + saved/fetched using the functions above. args can be a string with + whatever's going to be in the POST body of the request. GET args will + automatically be grabbed from url. */ +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ); + +/* Shouldn't normally be required unless the process is aborted by the user. */ +void oauth_info_free( struct oauth_info *info ); + +/* Convert to and back from strings, for easier saving. */ +char *oauth_to_string( struct oauth_info *oi ); +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ); + +/* For reading misc. data. */ +const char *oauth_params_get( GSList **params, const char *key ); diff --git a/lib/proxy.c b/lib/proxy.c new file mode 100644 index 00000000..b79afea4 --- /dev/null +++ b/lib/proxy.c @@ -0,0 +1,586 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "nogaim.h" +#include "proxy.h" +#include "base64.h" + +char proxyhost[128] = ""; +int proxyport = 0; +int proxytype = PROXY_NONE; +char proxyuser[128] = ""; +char proxypass[128] = ""; + +/* Some systems don't know this one. It's not essential, so set it to 0 then. */ +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + +struct PHB { + b_event_handler func, proxy_func; + gpointer data, proxy_data; + char *host; + int port; + int fd; + gint inpa; + struct addrinfo *gai, *gai_cur; +}; + +static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb); + +static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond) +{ + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + len = sizeof(error); + +#ifndef _WIN32 + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) { + if ((phb->gai_cur = phb->gai_cur->ai_next)) { + int new_fd; + b_event_remove(phb->inpa); + if ((new_fd = proxy_connect_none(NULL, 0, phb))) { + b_event_remove(phb->inpa); + closesocket(source); + dup2(new_fd, source); + phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb); + return FALSE; + } + } + freeaddrinfo(phb->gai); + closesocket(source); + b_event_remove(phb->inpa); + if( phb->proxy_func ) + phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ); + else { + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb); + } + return FALSE; + } +#endif + freeaddrinfo(phb->gai); + sock_make_blocking(source); + b_event_remove(phb->inpa); + if( phb->proxy_func ) + phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ); + else { + phb->func(phb->data, source, B_EV_IO_READ); + g_free(phb); + } + + return FALSE; +} + +static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb) +{ + struct sockaddr_in me; + int fd = -1; + + if (phb->gai_cur == NULL) + { + int ret; + char port[6]; + struct addrinfo hints; + + g_snprintf(port, sizeof(port), "%d", port_); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + + if (!(ret = getaddrinfo(host, port, &hints, &phb->gai))) + phb->gai_cur = phb->gai; + else + event_debug("gai(): %s\n", gai_strerror(ret)); + } + + for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next) + { + if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) { + event_debug( "socket failed: %d\n", errno); + continue; + } + + sock_make_nonblocking(fd); + + if (global.conf->iface_out) + { + me.sin_family = AF_INET; + me.sin_port = 0; + me.sin_addr.s_addr = inet_addr( global.conf->iface_out ); + + if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0) + event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out); + } + + event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); + + if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) { + event_debug( "connect failed: %s\n", strerror(errno)); + closesocket(fd); + fd = -1; + continue; + } else { + phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); + phb->fd = fd; + + break; + } + } + + if(fd < 0 && host) + g_free(phb); + + return fd; +} + + +/* Connecting to HTTP proxies */ + +#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established" +#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established" + +static gboolean http_canread(gpointer data, gint source, b_input_condition cond) +{ + int nlc = 0; + int pos = 0; + struct PHB *phb = data; + char inputline[8192]; + + b_event_remove(phb->inpa); + + while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { + if (inputline[pos - 1] == '\n') + nlc++; + else if (inputline[pos - 1] != '\r') + nlc = 0; + } + inputline[pos] = '\0'; + + if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || + (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { + phb->func(phb->data, source, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + + return FALSE; +} + +static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond) +{ + char cmd[384]; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + b_event_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + sock_make_blocking(source); + + g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, + phb->host, phb->port); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + if (strlen(proxyuser) > 0) { + char *t1, *t2; + t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); + t2 = tobase64(t1); + g_free(t1); + g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); + g_free(t2); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + } + + g_snprintf(cmd, sizeof(cmd), "\r\n"); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb); + + return FALSE; +} + +static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = http_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS4 proxies */ + +static gboolean s4_canread(gpointer data, gint source, b_input_condition cond) +{ + unsigned char packet[12]; + struct PHB *phb = data; + + b_event_remove(phb->inpa); + + memset(packet, 0, sizeof(packet)); + if (read(source, packet, 9) >= 4 && packet[1] == 90) { + phb->func(phb->data, source, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + + return FALSE; +} + +static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) +{ + unsigned char packet[12]; + struct hostent *hp; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + b_event_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + sock_make_blocking(source); + + /* XXX does socks4 not support host name lookups by the proxy? */ + if (!(hp = gethostbyname(phb->host))) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + packet[0] = 4; + packet[1] = 1; + packet[2] = phb->port >> 8; + packet[3] = phb->port & 0xff; + packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; + packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; + packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; + packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; + packet[8] = 0; + if (write(source, packet, 9) != 9) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb); + + return FALSE; +} + +static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = s4_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS5 proxies */ + +static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + b_event_remove(phb->inpa); + + if (read(source, buf, 10) < 10) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + phb->func(phb->data, source, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + + return FALSE; +} + +static void s5_sendconnect(gpointer data, gint source) +{ + unsigned char buf[512]; + struct PHB *phb = data; + int hlen = strlen(phb->host); + + buf[0] = 0x05; + buf[1] = 0x01; /* CONNECT */ + buf[2] = 0x00; /* reserved */ + buf[3] = 0x03; /* address type -- host name */ + buf[4] = hlen; + memcpy(buf + 5, phb->host, hlen); + buf[5 + strlen(phb->host)] = phb->port >> 8; + buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; + + if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb); +} + +static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + b_event_remove(phb->inpa); + + if (read(source, buf, 2) < 2) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + if ((buf[0] != 0x01) || (buf[1] != 0x00)) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + s5_sendconnect(phb, source); + + return FALSE; +} + +static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + b_event_remove(phb->inpa); + + if (read(source, buf, 2) < 2) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + if ((buf[0] != 0x05) || (buf[1] == 0xff)) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + if (buf[1] == 0x02) { + unsigned int i = strlen(proxyuser), j = strlen(proxypass); + buf[0] = 0x01; /* version 1 */ + buf[1] = i; + memcpy(buf + 2, proxyuser, i); + buf[2 + i] = j; + memcpy(buf + 2 + i + 1, proxypass, j); + if (write(source, buf, 3 + i + j) < 3 + i + j) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb); + } else { + s5_sendconnect(phb, source); + } + + return FALSE; +} + +static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) +{ + unsigned char buf[512]; + int i; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + b_event_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + sock_make_blocking(source); + + i = 0; + buf[0] = 0x05; /* SOCKS version 5 */ + if (proxyuser[0]) { + buf[1] = 0x02; /* two methods */ + buf[2] = 0x00; /* no authentication */ + buf[3] = 0x02; /* username/password authentication */ + i = 4; + } else { + buf[1] = 0x01; + buf[2] = 0x00; + i = 3; + } + + if (write(source, buf, i) < i) { + close(source); + phb->func(phb->data, -1, B_EV_IO_READ); + g_free(phb->host); + g_free(phb); + return FALSE; + } + + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb); + + return FALSE; +} + +static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = s5_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Export functions */ + +int proxy_connect(const char *host, int port, b_event_handler func, gpointer data) +{ + struct PHB *phb; + + if (!host || port <= 0 || !func || strlen(host) > 128) { + return -1; + } + + phb = g_new0(struct PHB, 1); + phb->func = func; + phb->data = data; + + if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0) + return proxy_connect_none(host, port, phb); + else if (proxytype == PROXY_HTTP) + return proxy_connect_http(host, port, phb); + else if (proxytype == PROXY_SOCKS4) + return proxy_connect_socks4(host, port, phb); + else if (proxytype == PROXY_SOCKS5) + return proxy_connect_socks5(host, port, phb); + + g_free(phb); + return -1; +} diff --git a/lib/proxy.h b/lib/proxy.h new file mode 100644 index 00000000..680790a5 --- /dev/null +++ b/lib/proxy.h @@ -0,0 +1,53 @@ +/* + * nogaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* this is the export part of the proxy.c file. it does a little + prototype-ing stuff and redefine some net function to mask them + with some kind of transparent layer */ + +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +#include "events.h" + +#define PROXY_NONE 0 +#define PROXY_HTTP 1 +#define PROXY_SOCKS4 2 +#define PROXY_SOCKS5 3 + +extern char proxyhost[128]; +extern int proxyport; +extern int proxytype; +extern char proxyuser[128]; +extern char proxypass[128]; + +G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data); + +#endif /* _PROXY_H_ */ diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..7ee90640 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,422 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses <stdint.h> (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include <string.h> +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +static void sha1_pad(sha1_state_t *); +static void sha1_process_block(sha1_state_t *); + +/* + * sha1_init + * + * Description: + * This function will initialize the sha1_state_t in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int sha1_init(sha1_state_t * context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * sha1_finish + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int sha1_finish(sha1_state_t * context, uint8_t Message_Digest[sha1_hash_size]) +{ + int i; + + if (!context || !Message_Digest) { + return shaNull; + } + + if (context->Corrupted) { + return context->Corrupted; + } + + if (!context->Computed) { + sha1_pad(context); + for (i = 0; i < 64; ++i) { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + + } + + for (i = 0; i < sha1_hash_size; ++i) { + Message_Digest[i] = context->Intermediate_Hash[i >> 2] + >> 8 * (3 - (i & 0x03)); + } + + return shaSuccess; +} + +/* + * sha1_append + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int +sha1_append(sha1_state_t * context, + const uint8_t * message_array, unsigned length) +{ + if (!length) { + return shaSuccess; + } + + if (!context || !message_array) { + return shaNull; + } + + if (context->Computed) { + context->Corrupted = shaStateError; + + return shaStateError; + } + + if (context->Corrupted) { + return context->Corrupted; + } + while (length-- && !context->Corrupted) { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) { + context->Length_High++; + if (context->Length_High == 0) { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) { + sha1_process_block(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * sha1_process_block + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +static void sha1_process_block(sha1_state_t * context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for (t = 16; t < 80; t++) { + W[t] = + SHA1CircularShift(1, + W[t - 3] ^ W[t - 8] ^ W[t - + 14] ^ W[t - + 16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) { + temp = SHA1CircularShift(5, A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30, B); + + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) { + temp = + SHA1CircularShift(5, + A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30, B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) { + temp = SHA1CircularShift(5, A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30, B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) { + temp = + SHA1CircularShift(5, + A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30, B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +/* + * sha1_pad + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +static void sha1_pad(sha1_state_t * context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) { + context->Message_Block[context->Message_Block_Index++] = + 0x80; + while (context->Message_Block_Index < 64) { + context->Message_Block[context-> + Message_Block_Index++] = 0; + } + + sha1_process_block(context); + + while (context->Message_Block_Index < 56) { + context->Message_Block[context-> + Message_Block_Index++] = 0; + } + } else { + context->Message_Block[context->Message_Block_Index++] = + 0x80; + while (context->Message_Block_Index < 56) { + + context->Message_Block[context-> + Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + sha1_process_block(context); +} + +#define HMAC_BLOCK_SIZE 64 + +/* BitlBee addition: */ +void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]) +{ + sha1_state_t sha1; + uint8_t hash[sha1_hash_size]; + uint8_t key[HMAC_BLOCK_SIZE+1]; + int i; + + if( key_len == 0 ) + key_len = strlen( key_ ); + if( payload_len == 0 ) + payload_len = strlen( payload ); + + /* Create K. If our current key is >64 chars we have to hash it, + otherwise just pad. */ + memset( key, 0, HMAC_BLOCK_SIZE + 1 ); + if( key_len > HMAC_BLOCK_SIZE ) + { + sha1_init( &sha1 ); + sha1_append( &sha1, (uint8_t*) key_, key_len ); + sha1_finish( &sha1, key ); + } + else + { + memcpy( key, key_, key_len ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, (const uint8_t*) payload, payload_len ); + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, Message_Digest ); +} diff --git a/lib/sha1.h b/lib/sha1.h new file mode 100644 index 00000000..a87410eb --- /dev/null +++ b/lib/sha1.h @@ -0,0 +1,71 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#if(__sun) +#include <inttypes.h> +#else +#include <stdint.h> +#endif +#include <gmodule.h> + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum { + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define sha1_hash_size 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context { + uint32_t Intermediate_Hash[sha1_hash_size/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} sha1_state_t; + +/* + * Function Prototypes + */ + +G_MODULE_EXPORT int sha1_init(sha1_state_t *); +G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int); +G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]); +G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]); + +#endif diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c new file mode 100644 index 00000000..f4ce5d4d --- /dev/null +++ b/lib/ssl_bogus.c @@ -0,0 +1,71 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - dummy version */ + +/* + 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 +*/ + +#include "ssl_client.h" + +int ssl_errno; + +void ssl_init( void ) +{ +} + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ + return( NULL ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ + return( -1 ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + return( -1 ); +} + +void ssl_disconnect( void *conn_ ) +{ +} + +int ssl_getfd( void *conn ) +{ + return( -1 ); +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + return NULL; +} + +b_input_condition ssl_getdirection( void *conn ) +{ + return B_EV_IO_READ; +} + +int ssl_pending( void *conn ) +{ + return 0; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h new file mode 100644 index 00000000..d0340840 --- /dev/null +++ b/lib/ssl_client.h @@ -0,0 +1,84 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module */ + +/* + 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 +*/ + +/* ssl_client makes it easier to open SSL connections to servers. (It + doesn't offer SSL server functionality yet, but it could be useful + to add it later.) Different ssl_client modules are available, and + ssl_client tries to make them all behave the same. It's very simple + and basic, it just imitates the proxy_connect() function from the + Gaim libs and passes the socket to the program once the handshake + is completed. */ + +#include <glib.h> +#include "proxy.h" + +/* Some generic error codes. Especially SSL_AGAIN is important if you + want to do asynchronous I/O. */ +#define SSL_OK 0 +#define SSL_NOHANDSHAKE 1 +#define SSL_AGAIN 2 + +extern int ssl_errno; + +/* This is what your callback function should look like. */ +typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); + + +/* Perform any global initialization the SSL library might need. */ +G_MODULE_EXPORT void ssl_init( void ); + +/* Connect to host:port, call the given function when the connection is + ready to be used for SSL traffic. This is all done asynchronously, no + blocking I/O! (Except for the DNS lookups, for now...) */ +G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); + +/* Start an SSL session on an existing fd. Useful for STARTTLS functionality, + for example in Jabber. */ +G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); + +/* Obviously you need special read/write functions to read data. */ +G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); +G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); + +/* See ssl_openssl.c for an explanation. */ +G_MODULE_EXPORT int ssl_pending( void *conn ); + +/* Abort the SSL connection and disconnect the socket. Do not use close() + directly, both the SSL library and the peer will be unhappy! */ +G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); + +/* Get the fd for this connection, you will usually need it for event + handling. */ +G_MODULE_EXPORT int ssl_getfd( void *conn ); + +/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's + possible that something has to be read while actually were trying to + write something (think about key exchange/refresh/etc). So when an + SSL operation returned SSL_AGAIN, *always* use this function when + adding an event handler to the queue. (And it should perform exactly + the same action as the handler that just received the SSL_AGAIN.) */ +G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); + +G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c new file mode 100644 index 00000000..cdc7c498 --- /dev/null +++ b/lib/ssl_gnutls.c @@ -0,0 +1,280 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - GnuTLS version */ + +/* + 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 +*/ + +#include <gnutls/gnutls.h> +#include <gcrypt.h> +#include <fcntl.h> +#include <unistd.h> +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include "stdlib.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +#include <limits.h> + +#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL +#define GNUTLS_STUPID_CAST (long) +#else +#define GNUTLS_STUPID_CAST (int) +#endif + +struct scd +{ + ssl_input_function func; + gpointer data; + int fd; + gboolean established; + int inpa; + + gnutls_session session; + gnutls_certificate_credentials xcred; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void ssl_init( void ) +{ + if( initialized ) + return; + + gnutls_global_init(); + initialized = TRUE; + atexit( gnutls_global_deinit ); +} + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + conn->func = func; + conn->data = data; + conn->inpa = -1; + + if( conn->fd < 0 ) + { + g_free( conn ); + return NULL; + } + + return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = fd; + conn->func = func; + conn->data = data; + conn->inpa = -1; + + /* This function should be called via a (short) timeout instead of + directly from here, because these SSL calls are *supposed* to be + *completely* asynchronous and not ready yet when this function + (or *_connect, for examle) returns. Also, errors are reported via + the callback function, not via this function's return value. + + In short, doing things like this makes the rest of the code a lot + simpler. */ + + b_timeout_add( 1, ssl_starttls_real, conn ); + + return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + if( source == -1 ) + { + conn->func( conn->data, NULL, cond ); + g_free( conn ); + return FALSE; + } + + ssl_init(); + + gnutls_certificate_allocate_credentials( &conn->xcred ); + gnutls_init( &conn->session, GNUTLS_CLIENT ); + gnutls_set_default_priority( conn->session ); + gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); + + sock_make_nonblocking( conn->fd ); + gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd ); + + return ssl_handshake( data, source, cond ); +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + int st; + + if( ( st = gnutls_handshake( conn->session ) ) < 0 ) + { + if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) + { + conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), + ssl_handshake, data ); + } + else + { + conn->func( conn->data, NULL, cond ); + + gnutls_deinit( conn->session ); + gnutls_certificate_free_credentials( conn->xcred ); + closesocket( conn->fd ); + + g_free( conn ); + } + } + else + { + /* For now we can't handle non-blocking perfectly everywhere... */ + sock_make_blocking( conn->fd ); + + conn->established = TRUE; + conn->func( conn->data, conn, cond ); + } + + return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ + int st; + + if( !((struct scd*)conn)->established ) + { + ssl_errno = SSL_NOHANDSHAKE; + return( -1 ); + } + + st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len ); + + ssl_errno = SSL_OK; + if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) + ssl_errno = SSL_AGAIN; + + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st ); + + return st; +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + int st; + + if( !((struct scd*)conn)->established ) + { + ssl_errno = SSL_NOHANDSHAKE; + return( -1 ); + } + + st = gnutls_record_send( ((struct scd*)conn)->session, buf, len ); + + ssl_errno = SSL_OK; + if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) + ssl_errno = SSL_AGAIN; + + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st ); + + return st; +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ + return 0; +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + if( conn->inpa != -1 ) + b_event_remove( conn->inpa ); + + if( conn->established ) + gnutls_bye( conn->session, GNUTLS_SHUT_WR ); + + closesocket( conn->fd ); + + if( conn->session ) + gnutls_deinit( conn->session ); + if( conn->xcred ) + gnutls_certificate_free_credentials( conn->xcred ); + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ + return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? + B_EV_IO_WRITE : B_EV_IO_READ ); +} + +size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input, + size_t input_len, const unsigned char *iv, unsigned char **res ) +{ + gcry_cipher_hd_t gcr; + gcry_error_t st; + + ssl_init(); + + *res = g_malloc( input_len ); + st = gcry_cipher_open( &gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0 ) || + gcry_cipher_setkey( gcr, key, key_len ) || + gcry_cipher_setiv( gcr, iv, 8 ) || + gcry_cipher_encrypt( gcr, *res, input_len, input, input_len ); + + gcry_cipher_close( gcr ); + + if( st == 0 ) + return input_len; + + g_free( *res ); + return 0; +} diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c new file mode 100644 index 00000000..512c7655 --- /dev/null +++ b/lib/ssl_nss.c @@ -0,0 +1,240 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - NSS version */ + +/* Copyright 2005 Jelmer Vernooij */ + +/* + 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 +*/ + +#include "bitlbee.h" +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include <nspr.h> +#include <prio.h> +#include <sslproto.h> +#include <nss.h> +#include <pk11pub.h> +#include <private/pprio.h> +#include <ssl.h> +#include <seccomon.h> +#include <secerr.h> +#include <sslerr.h> + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ + ssl_input_function func; + gpointer data; + int fd; + PRFileDesc *prfd; + gboolean established; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); + + +static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver) +{ + return SECSuccess; +} + +static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) +{ + PRErrorCode err; + + if(!arg) return SECFailure; + + *(PRErrorCode *)arg = err = PORT_GetError(); + + switch(err) { + case SEC_ERROR_INVALID_AVA: + case SEC_ERROR_INVALID_TIME: + case SEC_ERROR_BAD_SIGNATURE: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_CERT_VALID: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + case SEC_ERROR_CRL_EXPIRED: + case SEC_ERROR_CRL_BAD_SIGNATURE: + case SEC_ERROR_EXTENSION_VALUE_INVALID: + case SEC_ERROR_CA_CERT_INVALID: + case SEC_ERROR_CERT_USAGES_INVALID: + case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: + return SECSuccess; + + default: + return SECFailure; + } +} + + +void ssl_init( void ) +{ + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + NSS_NoDB_Init(NULL); + NSS_SetDomesticPolicy(); + initialized = TRUE; +} + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + conn->func = func; + conn->data = data; + + if( conn->fd < 0 ) + { + g_free( conn ); + return( NULL ); + } + + if( !initialized ) + { + ssl_init(); + } + + + return( conn ); +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = fd; + conn->func = func; + conn->data = data; + + /* This function should be called via a (short) timeout instead of + directly from here, because these SSL calls are *supposed* to be + *completely* asynchronous and not ready yet when this function + (or *_connect, for examle) returns. Also, errors are reported via + the callback function, not via this function's return value. + + In short, doing things like this makes the rest of the code a lot + simpler. */ + + b_timeout_add( 1, ssl_starttls_real, conn ); + + return conn; +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + if( source == -1 ) + goto ssl_connected_failure; + + /* Until we find out how to handle non-blocking I/O with NSS... */ + sock_make_blocking( conn->fd ); + + conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); + SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); + SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); + SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); + SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); + SSL_ResetHandshake(conn->prfd, PR_FALSE); + + if (SSL_ForceHandshake(conn->prfd)) { + goto ssl_connected_failure; + } + + + conn->established = TRUE; + conn->func( conn->data, conn, cond ); + return FALSE; + + ssl_connected_failure: + + conn->func( conn->data, NULL, cond ); + + PR_Close( conn -> prfd ); + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) ); +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ + struct scd *c = (struct scd *) conn; + + if( c == NULL ) { + return 0; + } + + return ( c->established && SSL_DataPending( c->prfd ) > 0 ); +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + PR_Close( conn->prfd ); + closesocket( conn->fd ); + + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ + /* Just in case someone calls us, let's return the most likely case: */ + return B_EV_IO_READ; +} diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c new file mode 100644 index 00000000..64bc9257 --- /dev/null +++ b/lib/ssl_openssl.c @@ -0,0 +1,305 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - OpenSSL version */ + +/* + 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 +*/ + +#include <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ + ssl_input_function func; + gpointer data; + int fd; + gboolean established; + + int inpa; + int lasterr; /* Necessary for SSL_get_error */ + SSL *ssl; + SSL_CTX *ssl_ctx; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void ssl_init( void ) +{ + initialized = TRUE; + SSL_library_init(); + // SSLeay_add_ssl_algorithms(); +} + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + if( conn->fd < 0 ) + { + g_free( conn ); + return NULL; + } + + conn->func = func; + conn->data = data; + conn->inpa = -1; + + return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = fd; + conn->func = func; + conn->data = data; + conn->inpa = -1; + + /* This function should be called via a (short) timeout instead of + directly from here, because these SSL calls are *supposed* to be + *completely* asynchronous and not ready yet when this function + (or *_connect, for examle) returns. Also, errors are reported via + the callback function, not via this function's return value. + + In short, doing things like this makes the rest of the code a lot + simpler. */ + + b_timeout_add( 1, ssl_starttls_real, conn ); + + return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + SSL_METHOD *meth; + + if( source == -1 ) + goto ssl_connected_failure; + + if( !initialized ) + { + ssl_init(); + } + + meth = TLSv1_client_method(); + conn->ssl_ctx = SSL_CTX_new( meth ); + if( conn->ssl_ctx == NULL ) + goto ssl_connected_failure; + + conn->ssl = SSL_new( conn->ssl_ctx ); + if( conn->ssl == NULL ) + goto ssl_connected_failure; + + /* We can do at least the handshake with non-blocking I/O */ + sock_make_nonblocking( conn->fd ); + SSL_set_fd( conn->ssl, conn->fd ); + + return ssl_handshake( data, source, cond ); + +ssl_connected_failure: + conn->func( conn->data, NULL, cond ); + + if( conn->ssl ) + { + SSL_shutdown( conn->ssl ); + SSL_free( conn->ssl ); + } + if( conn->ssl_ctx ) + { + SSL_CTX_free( conn->ssl_ctx ); + } + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ + struct scd *conn = data; + int st; + + if( ( st = SSL_connect( conn->ssl ) ) < 0 ) + { + conn->lasterr = SSL_get_error( conn->ssl, st ); + if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) + { + conn->func( conn->data, NULL, cond ); + + SSL_shutdown( conn->ssl ); + SSL_free( conn->ssl ); + SSL_CTX_free( conn->ssl_ctx ); + + if( source >= 0 ) closesocket( source ); + g_free( conn ); + + return FALSE; + } + + conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); + return FALSE; + } + + conn->established = TRUE; + sock_make_blocking( conn->fd ); /* For now... */ + conn->func( conn->data, conn, cond ); + return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ + int st; + + if( !((struct scd*)conn)->established ) + { + ssl_errno = SSL_NOHANDSHAKE; + return -1; + } + + st = SSL_read( ((struct scd*)conn)->ssl, buf, len ); + + ssl_errno = SSL_OK; + if( st <= 0 ) + { + ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); + if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) + ssl_errno = SSL_AGAIN; + } + + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + + return st; +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + int st; + + if( !((struct scd*)conn)->established ) + { + ssl_errno = SSL_NOHANDSHAKE; + return -1; + } + + st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); + + if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st ); + + ssl_errno = SSL_OK; + if( st <= 0 ) + { + ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); + if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) + ssl_errno = SSL_AGAIN; + } + + return st; +} + +/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info: + http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209 + http://www.openssl.org/docs/ssl/SSL_pending.html + + Required because OpenSSL empties the TCP buffer completely but doesn't + necessarily give us all the unencrypted data. + + Returns 0 if there's nothing left or if we don't have to care (GnuTLS), + 1 if there's more data. */ +int ssl_pending( void *conn ) +{ + return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ? + SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0; +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + if( conn->inpa != -1 ) + b_event_remove( conn->inpa ); + + if( conn->established ) + SSL_shutdown( conn->ssl ); + + closesocket( conn->fd ); + + SSL_free( conn->ssl ); + SSL_CTX_free( conn->ssl_ctx ); + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ + return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); +} + +size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res) +{ + int output_length = 0; + EVP_CIPHER_CTX ctx; + + *res = g_new0(unsigned char, 72); + + /* Don't set key or IV because we will modify the parameters */ + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1); + EVP_CIPHER_CTX_set_key_length(&ctx, key_len); + EVP_CIPHER_CTX_set_padding(&ctx, 0); + /* We finished modifying parameters so now we can set key and IV */ + EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1); + EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len); + EVP_CipherFinal_ex(&ctx, *res, &output_length); + EVP_CIPHER_CTX_cleanup(&ctx); + //EVP_cleanup(); + + return output_length; +} diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c new file mode 100644 index 00000000..e14c451e --- /dev/null +++ b/lib/ssl_sspi.c @@ -0,0 +1,278 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - SSPI backend */ + +/* 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 + 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 +*/ + +#include "ssl_client.h" +#include <windows.h> +#define SECURITY_WIN32 +#include <security.h> +#include <sspi.h> +#include <schannel.h> +#include "sock.h" + +static gboolean initialized = FALSE; +int ssl_errno; + +struct scd +{ + int fd; + ssl_input_function func; + gpointer data; + gboolean established; + CredHandle cred; /* SSL credentials */ + CtxtHandle context; /* SSL context */ + SecPkgContext_StreamSizes sizes; + + char *host; + + char *pending_raw_data; + gsize pending_raw_data_len; + char *pending_data; + gsize pending_data_len; +}; + +static void ssl_connected(gpointer, gint, GaimInputCondition); + +void sspi_global_init(void) +{ + /* FIXME */ +} + +void sspi_global_deinit(void) +{ + /* FIXME */ +} + +void *ssl_connect(char *host, int port, ssl_input_function func, gpointer data) +{ + struct scd *conn = g_new0(struct scd, 1); + + conn->fd = proxy_connect(host, port, ssl_connected, conn); + sock_make_nonblocking(conn->fd); + conn->func = func; + conn->data = data; + conn->host = g_strdup(host); + + if (conn->fd < 0) + { + g_free(conn); + return NULL; + } + + if (!initialized) + { + sspi_global_init(); + initialized = TRUE; + atexit(sspi_global_deinit); + } + + return conn; +} + +static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond) +{ + struct scd *conn = _conn; + SCHANNEL_CRED ssl_cred; + TimeStamp timestamp; + SecBuffer ibuf[2],obuf[1]; + SecBufferDesc ibufs,obufs; + ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | + ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | + ISC_REQ_MANUAL_CRED_VALIDATION; + ULONG a; + gsize size = 0; + gchar *data = NULL; + + memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED)); + ssl_cred.dwVersion = SCHANNEL_CRED_VERSION; + ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; + + SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, ×tamp); + + if (st != SEC_E_OK) { + conn->func(conn->data, NULL, cond); + return; + } + + do { + /* initialize buffers */ + ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data; + ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL; + obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL; + ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; + ibuf[1].BufferType = SECBUFFER_EMPTY; + + /* initialize buffer descriptors */ + ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; + ibufs.cBuffers = 2; obufs.cBuffers = 1; + ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; + + st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, ×tamp); + if (obuf[0].pvBuffer && obuf[0].cbBuffer) { + /* FIXME: Check return value */ + send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0); + } + + switch (st) { + case SEC_I_INCOMPLETE_CREDENTIALS: + break; + case SEC_I_CONTINUE_NEEDED: + break; + case SEC_E_INCOMPLETE_MESSAGE: + break; + case SEC_E_OK: + break; + } + + QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes); + } while (1); + + conn->func(conn->data, conn, cond); +} + +int ssl_read(void *conn, char *retdata, int len) +{ + struct scd *scd = conn; + SecBufferDesc msg; + SecBuffer buf[4]; + int ret = -1, i; + char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); + + /* FIXME: Try to read some data */ + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 4; + msg.pBuffers = buf; + + buf[0].BufferType = SECBUFFER_DATA; + buf[0].cbBuffer = len; + buf[0].pvBuffer = data; + + buf[1].BufferType = SECBUFFER_EMPTY; + buf[2].BufferType = SECBUFFER_EMPTY; + buf[3].BufferType = SECBUFFER_EMPTY; + + SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL); + + if (st != SEC_E_OK) { + /* FIXME */ + return -1; + } + + for (i = 0; i < 4; i++) { + if (buf[i].BufferType == SECBUFFER_DATA) { + memcpy(retdata, buf[i].pvBuffer, len); + ret = len; + } + } + + g_free(data); + return -1; +} + +int ssl_write(void *conn, const char *userdata, int len) +{ + struct scd *scd = conn; + SecBuffer buf[4]; + SecBufferDesc msg; + char *data; + int ret; + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 4; + msg.pBuffers = buf; + + data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); + memcpy(data + scd->sizes.cbHeader, userdata, len); + + buf[0].BufferType = SECBUFFER_STREAM_HEADER; + buf[0].cbBuffer = scd->sizes.cbHeader; + buf[0].pvBuffer = data; + + buf[1].BufferType = SECBUFFER_DATA; + buf[1].cbBuffer = len; + buf[1].pvBuffer = data + scd->sizes.cbHeader; + + buf[2].BufferType = SECBUFFER_STREAM_TRAILER; + buf[2].cbBuffer = scd->sizes.cbTrailer; + buf[2].pvBuffer = data + scd->sizes.cbHeader + len; + buf[3].BufferType = SECBUFFER_EMPTY; + + SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0); + + ret = send(scd->fd, data, + buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0); + + g_free(data); + + return ret; +} + +void ssl_disconnect(void *conn) +{ + struct scd *scd = conn; + + SecBufferDesc msg; + SecBuffer buf; + DWORD dw; + + dw = SCHANNEL_SHUTDOWN; + buf.cbBuffer = sizeof(dw); + buf.BufferType = SECBUFFER_TOKEN; + buf.pvBuffer = &dw; + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 1; + msg.pBuffers = &buf; + + SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg); + + if (st != SEC_E_OK) { + /* FIXME */ + } + + /* FIXME: call InitializeSecurityContext(Schannel), passing + * in empty buffers*/ + + DeleteSecurityContext(&scd->context); + + FreeCredentialsHandle(&scd->cred); + + closesocket(scd->fd); + g_free(scd->host); + g_free(scd); +} + +int ssl_getfd(void *conn) +{ + return ((struct scd*)conn)->fd; +} + +GaimInputCondition ssl_getdirection( void *conn ) +{ + return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */ +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 00000000..9e330f8c --- /dev/null +++ b/lib/url.c @@ -0,0 +1,109 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2001-2005 Wilmer van der Gaast and others * + \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel */ + +/* + 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 +*/ + +#include "url.h" + +/* Convert an URL to a url_t structure */ +int url_set( url_t *url, const char *set_url ) +{ + char s[MAX_STRING+1]; + char *i; + + memset( url, 0, sizeof( url_t ) ); + memset( s, 0, sizeof( s ) ); + + /* protocol:// */ + if( ( i = strstr( set_url, "://" ) ) == NULL ) + { + url->proto = PROTO_DEFAULT; + strncpy( s, set_url, MAX_STRING ); + } + else + { + if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) + url->proto = PROTO_HTTP; + else if( g_strncasecmp( set_url, "https", i - set_url ) == 0 ) + url->proto = PROTO_HTTPS; + else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 ) + url->proto = PROTO_SOCKS4; + else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) + url->proto = PROTO_SOCKS5; + else + return 0; + + strncpy( s, i + 3, MAX_STRING ); + } + + /* Split */ + if( ( i = strchr( s, '/' ) ) == NULL ) + { + strcpy( url->file, "/" ); + } + else + { + strncpy( url->file, i, MAX_STRING ); + *i = 0; + } + strncpy( url->host, s, MAX_STRING ); + + /* Check for username in host field */ + if( strrchr( url->host, '@' ) != NULL ) + { + strncpy( url->user, url->host, MAX_STRING ); + i = strrchr( url->user, '@' ); + *i = 0; + strcpy( url->host, i + 1 ); + *url->pass = 0; + } + /* If not: Fill in defaults */ + else + { + *url->user = *url->pass = 0; + } + + /* Password? */ + if( ( i = strchr( url->user, ':' ) ) != NULL ) + { + *i = 0; + strcpy( url->pass, i + 1 ); + } + /* Port number? */ + if( ( i = strchr( url->host, ':' ) ) != NULL ) + { + *i = 0; + sscanf( i + 1, "%d", &url->port ); + } + else + { + if( url->proto == PROTO_HTTP ) + url->port = 80; + else if( url->proto == PROTO_HTTPS ) + url->port = 443; + else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS5 ) + url->port = 1080; + } + + return( url->port > 0 ); +} diff --git a/lib/url.h b/lib/url.h new file mode 100644 index 00000000..55107ad2 --- /dev/null +++ b/lib/url.h @@ -0,0 +1,44 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2001-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel */ + +/* + 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 +*/ + +#include "bitlbee.h" + +#define PROTO_HTTP 2 +#define PROTO_HTTPS 5 +#define PROTO_SOCKS4 3 +#define PROTO_SOCKS5 4 +#define PROTO_DEFAULT PROTO_HTTP + +typedef struct url +{ + int proto; + int port; + char host[MAX_STRING+1]; + char file[MAX_STRING+1]; + char user[MAX_STRING+1]; + char pass[MAX_STRING+1]; +} url_t; + +int url_set( url_t *url, const char *set_url ); diff --git a/lib/xmltree.c b/lib/xmltree.c new file mode 100644 index 00000000..54a7dd13 --- /dev/null +++ b/lib/xmltree.c @@ -0,0 +1,698 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly) * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library 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 * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#include <glib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> + +#include "xmltree.h" + +static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ + struct xt_parser *xt = data; + struct xt_node *node = g_new0( struct xt_node, 1 ), *nt; + int i; + + node->parent = xt->cur; + node->name = g_strdup( element_name ); + + /* First count the number of attributes */ + for( i = 0; attr_names[i]; i ++ ); + + /* Then allocate a NULL-terminated array. */ + node->attr = g_new0( struct xt_attr, i + 1 ); + + /* And fill it, saving one variable by starting at the end. */ + for( i --; i >= 0; i -- ) + { + node->attr[i].key = g_strdup( attr_names[i] ); + node->attr[i].value = g_strdup( attr_values[i] ); + } + + /* Add it to the linked list of children nodes, if we have a current + node yet. */ + if( xt->cur ) + { + if( xt->cur->children ) + { + for( nt = xt->cur->children; nt->next; nt = nt->next ); + nt->next = node; + } + else + { + xt->cur->children = node; + } + } + else if( xt->root ) + { + /* ERROR situation: A second root-element??? */ + } + + /* Now this node will be the new current node. */ + xt->cur = node; + /* And maybe this is the root? */ + if( xt->root == NULL ) + xt->root = node; +} + +static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ + struct xt_parser *xt = data; + struct xt_node *node = xt->cur; + + if( node == NULL ) + return; + + /* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */ + node->text = g_renew( char, node->text, node->text_len + text_len + 1 ); + memcpy( node->text + node->text_len, text, text_len ); + node->text_len += text_len; + /* Zero termination is always nice to have. */ + node->text[node->text_len] = 0; +} + +static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ + struct xt_parser *xt = data; + + xt->cur->flags |= XT_COMPLETE; + xt->cur = xt->cur->parent; +} + +GMarkupParser xt_parser_funcs = +{ + xt_start_element, + xt_end_element, + xt_text, + NULL, + NULL +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ) +{ + struct xt_parser *xt = g_new0( struct xt_parser, 1 ); + + xt->data = data; + xt->handlers = handlers; + xt_reset( xt ); + + return xt; +} + +/* Reset the parser, flush everything we have so far. For example, we need + this for XMPP when doing TLS/SASL to restart the stream. */ +void xt_reset( struct xt_parser *xt ) +{ + if( xt->parser ) + g_markup_parse_context_free( xt->parser ); + + xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL ); + + if( xt->root ) + { + xt_free_node( xt->root ); + xt->root = NULL; + xt->cur = NULL; + } +} + +/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on + end-of-stream and 1 otherwise. */ +int xt_feed( struct xt_parser *xt, const char *text, int text_len ) +{ + if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) + { + return -1; + } + + return !( xt->root && xt->root->flags & XT_COMPLETE ); +} + +/* Find completed nodes and see if a handler has to be called. Passing + a node isn't necessary if you want to start at the root, just pass + NULL. This second argument is needed for recursive calls. */ +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) +{ + struct xt_node *c; + xt_status st; + int i; + + /* Just in case someone likes infinite loops... */ + if( xt->root == NULL ) + return 0; + + if( node == NULL ) + return xt_handle( xt, xt->root, depth ); + + if( depth != 0 ) + for( c = node->children; c; c = c->next ) + if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) ) + return 0; + + if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) + { + if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ ) + { + /* This one is fun! \o/ */ + + /* If handler.name == NULL it means it should always match. */ + if( ( xt->handlers[i].name == NULL || + /* If it's not, compare. There should always be a name. */ + g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && + /* If handler.parent == NULL, it's a match. */ + ( xt->handlers[i].parent == NULL || + /* If there's a parent node, see if the name matches. */ + ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : + /* If there's no parent, the handler should mention <root> as a parent. */ + strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) + { + st = xt->handlers[i].func( node, xt->data ); + + if( st == XT_ABORT ) + return 0; + else if( st != XT_NEXT ) + break; + } + } + + node->flags |= XT_SEEN; + } + + return 1; +} + +/* Garbage collection: Cleans up all nodes that are handled. Useful for + streams because there's no reason to keep a complete packet history + in memory. */ +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) +{ + struct xt_node *c, *prev; + + if( !xt || !xt->root ) + return; + + if( node == NULL ) + { + xt_cleanup( xt, xt->root, depth ); + return; + } + + if( node->flags & XT_SEEN && node == xt->root ) + { + xt_free_node( xt->root ); + xt->root = xt->cur = NULL; + /* xt->cur should be NULL already, BTW... */ + + return; + } + + /* c contains the current node, prev the previous node (or NULL). + I admit, this one's pretty horrible. */ + for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children ) + { + if( c->flags & XT_SEEN ) + { + /* Remove the node from the linked list. */ + if( prev ) + prev->next = c->next; + else + node->children = c->next; + + xt_free_node( c ); + + /* Since the for loop wants to get c->next, make sure + c points at something that exists (and that c->next + will actually be the next item we should check). c + can be NULL now, if we just removed the first item. + That explains the ? thing in for(). */ + c = prev; + } + else + { + /* This node can't be cleaned up yet, but maybe a + subnode can. */ + if( depth != 0 ) + xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth ); + } + } +} + +struct xt_node *xt_from_string( const char *in ) +{ + struct xt_parser *parser; + struct xt_node *ret; + + parser = xt_new( NULL, NULL ); + xt_feed( parser, in, strlen( in ) ); + ret = parser->root; + parser->root = NULL; + xt_free( parser ); + + return ret; +} + +static void xt_to_string_real( struct xt_node *node, GString *str ) +{ + char *buf; + struct xt_node *c; + int i; + + g_string_append_printf( str, "<%s", node->name ); + + for( i = 0; node->attr[i].key; i ++ ) + { + buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value ); + g_string_append( str, buf ); + g_free( buf ); + } + + if( node->text == NULL && node->children == NULL ) + { + g_string_append( str, "/>" ); + return; + } + + g_string_append( str, ">" ); + if( node->text_len > 0 ) + { + buf = g_markup_escape_text( node->text, node->text_len ); + g_string_append( str, buf ); + g_free( buf ); + } + + for( c = node->children; c; c = c->next ) + xt_to_string_real( c, str ); + + g_string_append_printf( str, "</%s>", node->name ); +} + +char *xt_to_string( struct xt_node *node ) +{ + GString *ret; + char *real; + + ret = g_string_new( "" ); + xt_to_string_real( node, ret ); + + real = ret->str; + g_string_free( ret, FALSE ); + + return real; +} + +#ifdef DEBUG +void xt_print( struct xt_node *node ) +{ + int i; + struct xt_node *c; + + /* Indentation */ + for( c = node; c->parent; c = c->parent ) + printf( " " ); + + /* Start the tag */ + printf( "<%s", node->name ); + + /* Print the attributes */ + for( i = 0; node->attr[i].key; i ++ ) + { + char *v = g_markup_escape_text( node->attr[i].value, -1 ); + printf( " %s=\"%s\"", node->attr[i].key, v ); + g_free( v ); + } + + /* /> in case there's really *nothing* inside this tag, otherwise + just >. */ + /* If this tag doesn't have any content at all... */ + if( node->text == NULL && node->children == NULL ) + { + printf( "/>\n" ); + return; + /* Then we're finished! */ + } + + /* Otherwise... */ + printf( ">" ); + + /* Only print the text if it contains more than whitespace (TEST). */ + if( node->text_len > 0 ) + { + for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ ); + if( node->text[i] ) + { + char *v = g_markup_escape_text( node->text, -1 ); + printf( "%s", v ); + g_free( v ); + } + } + + if( node->children ) + printf( "\n" ); + + for( c = node->children; c; c = c->next ) + xt_print( c ); + + if( node->children ) + for( c = node; c->parent; c = c->parent ) + printf( " " ); + + /* Non-empty tag is now finished. */ + printf( "</%s>\n", node->name ); +} +#endif + +struct xt_node *xt_dup( struct xt_node *node ) +{ + struct xt_node *dup = g_new0( struct xt_node, 1 ); + struct xt_node *c, *dc = NULL; + int i; + + /* Let's NOT copy the parent element here BTW! Only do it for children. */ + + dup->name = g_strdup( node->name ); + dup->flags = node->flags; + if( node->text ) + { + dup->text = g_memdup( node->text, node->text_len + 1 ); + dup->text_len = node->text_len; + } + + /* Count the number of attributes and allocate the new array. */ + for( i = 0; node->attr[i].key; i ++ ); + dup->attr = g_new0( struct xt_attr, i + 1 ); + + /* Copy them all! */ + for( i --; i >= 0; i -- ) + { + dup->attr[i].key = g_strdup( node->attr[i].key ); + dup->attr[i].value = g_strdup( node->attr[i].value ); + } + + /* This nice mysterious loop takes care of the children. */ + for( c = node->children; c; c = c->next ) + { + if( dc == NULL ) + dc = dup->children = xt_dup( c ); + else + dc = ( dc->next = xt_dup( c ) ); + + dc->parent = dup; + } + + return dup; +} + +/* Frees a node. This doesn't clean up references to itself from parents! */ +void xt_free_node( struct xt_node *node ) +{ + int i; + + if( !node ) + return; + + g_free( node->name ); + g_free( node->text ); + + for( i = 0; node->attr[i].key; i ++ ) + { + g_free( node->attr[i].key ); + g_free( node->attr[i].value ); + } + g_free( node->attr ); + + while( node->children ) + { + struct xt_node *next = node->children->next; + + xt_free_node( node->children ); + node->children = next; + } + + g_free( node ); +} + +void xt_free( struct xt_parser *xt ) +{ + if( !xt ) + return; + + if( xt->root ) + xt_free_node( xt->root ); + + g_markup_parse_context_free( xt->parser ); + + g_free( xt ); +} + +/* To find a node's child with a specific name, pass the node's children + list, not the node itself! The reason you have to do this by hand: So + that you can also use this function as a find-next. */ +struct xt_node *xt_find_node( struct xt_node *node, const char *name ) +{ + while( node ) + { + char *colon; + + if( g_strcasecmp( node->name, name ) == 0 || + ( ( colon = strchr( node->name, ':' ) ) && + g_strcasecmp( colon + 1, name ) == 0 ) ) + break; + + node = node->next; + } + + return node; +} + +/* More advanced than the one above, understands something like + ../foo/bar to find a subnode bar of a node foo which is a child + of node's parent. Pass the node directly, not its list of children. */ +struct xt_node *xt_find_path( struct xt_node *node, const char *name ) +{ + while( name && *name && node ) + { + char *colon, *slash; + int n; + + if( ( slash = strchr( name, '/' ) ) ) + n = slash - name; + else + n = strlen( name ); + + if( strncmp( name, "..", n ) == 0 ) + { + node = node->parent; + } + else + { + node = node->children; + + while( node ) + { + if( g_strncasecmp( node->name, name, n ) == 0 || + ( ( colon = strchr( node->name, ':' ) ) && + g_strncasecmp( colon + 1, name, n ) == 0 ) ) + break; + + node = node->next; + } + } + + name = slash ? slash + 1 : NULL; + } + + return node; +} + +char *xt_find_attr( struct xt_node *node, const char *key ) +{ + int i; + char *colon; + + if( !node ) + return NULL; + + for( i = 0; node->attr[i].key; i ++ ) + if( g_strcasecmp( node->attr[i].key, key ) == 0 ) + break; + + /* This is an awful hack that only takes care of namespace prefixes + inside a tag. Since IMHO excessive namespace usage in XMPP is + massive overkill anyway (this code exists for almost four years + now and never really missed it): Meh. */ + if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 && + ( colon = strchr( node->name, ':' ) ) ) + { + *colon = '\0'; + for( i = 0; node->attr[i].key; i ++ ) + if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 && + strcmp( node->attr[i].key + 6, node->name ) == 0 ) + break; + *colon = ':'; + } + + return node->attr[i].value; +} + +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ) +{ + struct xt_node *node, *c; + + node = g_new0( struct xt_node, 1 ); + node->name = g_strdup( name ); + node->children = children; + node->attr = g_new0( struct xt_attr, 1 ); + + if( text ) + { + node->text_len = strlen( text ); + node->text = g_memdup( text, node->text_len + 1 ); + } + + for( c = children; c; c = c->next ) + { + if( c->parent != NULL ) + { + /* ERROR CONDITION: They seem to have a parent already??? */ + } + + c->parent = node; + } + + return node; +} + +void xt_add_child( struct xt_node *parent, struct xt_node *child ) +{ + struct xt_node *node; + + /* This function can actually be used to add more than one child, so + do handle this properly. */ + for( node = child; node; node = node->next ) + { + if( node->parent != NULL ) + { + /* ERROR CONDITION: They seem to have a parent already??? */ + } + + node->parent = parent; + } + + if( parent->children == NULL ) + { + parent->children = child; + } + else + { + for( node = parent->children; node->next; node = node->next ); + node->next = child; + } +} + +/* Same, but at the beginning. */ +void xt_insert_child( struct xt_node *parent, struct xt_node *child ) +{ + struct xt_node *node, *last = NULL; + + if( child == NULL ) + return; /* BUG */ + + for( node = child; node; node = node->next ) + { + if( node->parent != NULL ) + { + /* ERROR CONDITION: They seem to have a parent already??? */ + } + + node->parent = parent; + last = node; + } + + last->next = parent->children; + parent->children = child; +} + +void xt_add_attr( struct xt_node *node, const char *key, const char *value ) +{ + int i; + + /* Now actually it'd be nice if we can also change existing attributes + (which actually means this function doesn't have the right name). + So let's find out if we have this attribute already... */ + for( i = 0; node->attr[i].key; i ++ ) + if( strcmp( node->attr[i].key, key ) == 0 ) + break; + + if( node->attr[i].key == NULL ) + { + /* If not, allocate space for a new attribute. */ + node->attr = g_renew( struct xt_attr, node->attr, i + 2 ); + node->attr[i].key = g_strdup( key ); + node->attr[i+1].key = NULL; + } + else + { + /* Otherwise, free the old value before setting the new one. */ + g_free( node->attr[i].value ); + } + + node->attr[i].value = g_strdup( value ); +} + +int xt_remove_attr( struct xt_node *node, const char *key ) +{ + int i, last; + + for( i = 0; node->attr[i].key; i ++ ) + if( strcmp( node->attr[i].key, key ) == 0 ) + break; + + /* If we didn't find the attribute... */ + if( node->attr[i].key == NULL ) + return 0; + + g_free( node->attr[i].key ); + g_free( node->attr[i].value ); + + /* If it's the last, this is easy: */ + if( node->attr[i+1].key == NULL ) + { + node->attr[i].key = node->attr[i].value = NULL; + } + else /* It's also pretty easy, actually. */ + { + /* Find the last item. */ + for( last = i + 1; node->attr[last+1].key; last ++ ); + + node->attr[i] = node->attr[last]; + node->attr[last].key = NULL; + node->attr[last].value = NULL; + } + + /* Let's not bother with reallocating memory here. It takes time and + most packets don't stay in memory for long anyway. */ + + return 1; +} diff --git a/lib/xmltree.h b/lib/xmltree.h new file mode 100644 index 00000000..5a0dbc8e --- /dev/null +++ b/lib/xmltree.h @@ -0,0 +1,100 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly) * +* * +* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library 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 * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#ifndef _XMLTREE_H +#define _XMLTREE_H + +typedef enum +{ + XT_COMPLETE = 1, /* </tag> reached */ + XT_SEEN = 2, /* Handler called (or not defined) */ +} xt_flags; + +typedef enum +{ + XT_ABORT, /* Abort, don't handle the rest anymore */ + XT_HANDLED, /* Handled this tag properly, go to the next one */ + XT_NEXT /* Try if there's another matching handler */ +} xt_status; + +struct xt_attr +{ + char *key, *value; +}; + +struct xt_node +{ + struct xt_node *parent; + struct xt_node *children; + + char *name; + struct xt_attr *attr; + char *text; + int text_len; + + struct xt_node *next; + xt_flags flags; +}; + +typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data ); + +struct xt_handler_entry +{ + char *name, *parent; + xt_handler_func func; +}; + +struct xt_parser +{ + GMarkupParseContext *parser; + struct xt_node *root; + struct xt_node *cur; + + const struct xt_handler_entry *handlers; + gpointer data; + + GError *gerr; +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); +void xt_reset( struct xt_parser *xt ); +int xt_feed( struct xt_parser *xt, const char *text, int text_len ); +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +struct xt_node *xt_from_string( const char *in ); +char *xt_to_string( struct xt_node *node ); +void xt_print( struct xt_node *node ); +struct xt_node *xt_dup( struct xt_node *node ); +void xt_free_node( struct xt_node *node ); +void xt_free( struct xt_parser *xt ); +struct xt_node *xt_find_node( struct xt_node *node, const char *name ); +struct xt_node *xt_find_path( struct xt_node *node, const char *name ); +char *xt_find_attr( struct xt_node *node, const char *key ); + +struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children ); +void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_insert_child( struct xt_node *parent, struct xt_node *child ); +void xt_add_attr( struct xt_node *node, const char *key, const char *value ); +int xt_remove_attr( struct xt_node *node, const char *key ); + +#endif |