diff options
Diffstat (limited to 'src/common/util.c')
-rw-r--r-- | src/common/util.c | 237 |
1 files changed, 236 insertions, 1 deletions
diff --git a/src/common/util.c b/src/common/util.c index 52464621..374da6e5 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -58,6 +58,17 @@ #include <socks.h> #endif +/* SASL mechanisms */ +#ifdef USE_OPENSSL +#include <openssl/bn.h> +#include <openssl/rand.h> +#include <openssl/blowfish.h> +#include <openssl/aes.h> +#ifndef WIN32 +#include <netinet/in.h> +#endif +#endif + #ifndef HAVE_SNPRINTF #define snprintf g_snprintf #endif @@ -1929,7 +1940,7 @@ get_subdirs (const char *path) } char * -encode_sasl_pass (char *user, char *pass) +encode_sasl_pass_plain (char *user, char *pass) { int authlen; char *buffer; @@ -1944,6 +1955,230 @@ encode_sasl_pass (char *user, char *pass) return encoded; } +#ifdef USE_OPENSSL +/* Adapted from ZNC's SASL module */ + +static int +parse_dh (char *str, DH **dh_out, unsigned char **secret_out, int *keysize_out) +{ + DH *dh; + guchar *data, *decoded_data; + guchar *secret; + gsize data_len; + guint size; + guint16 size16; + BIGNUM *pubkey; + gint key_size; + + dh = DH_new(); + data = decoded_data = g_base64_decode (str, &data_len); + if (data_len < 2) + goto fail; + + /* prime number */ + memcpy (&size16, data, sizeof(size16)); + size = ntohs (size16); + data += 2; + data_len -= 2; + + if (size > data_len) + goto fail; + + dh->p = BN_bin2bn (data, size, NULL); + data += size; + + /* Generator */ + if (data_len < 2) + goto fail; + + memcpy (&size16, data, sizeof(size16)); + size = ntohs (size16); + data += 2; + data_len -= 2; + + if (size > data_len) + goto fail; + + dh->g = BN_bin2bn (data, size, NULL); + data += size; + + /* pub key */ + if (data_len < 2) + goto fail; + + memcpy (&size16, data, sizeof(size16)); + size = ntohs(size16); + data += 2; + data_len -= 2; + + pubkey = BN_bin2bn (data, size, NULL); + if (!(DH_generate_key (dh))) + goto fail; + + secret = (unsigned char*)malloc (DH_size(dh)); + key_size = DH_compute_key (secret, pubkey, dh); + if (key_size == -1) + goto fail; + + g_free (decoded_data); + + *dh_out = dh; + *secret_out = secret; + *keysize_out = key_size; + return 1; + +fail: + if (decoded_data) + g_free (decoded_data); + return 0; +} + +char * +encode_sasl_pass_blowfish (char *user, char *pass, char *data) +{ + DH *dh; + char *response, *ret; + unsigned char *secret; + unsigned char *encrypted_pass; + char *plain_pass; + BF_KEY key; + int key_size, length; + int pass_len = strlen (pass) + (8 - (strlen (pass) % 8)); + int user_len = strlen (user); + guint16 size16; + char *in_ptr, *out_ptr; + + if (!parse_dh (data, &dh, &secret, &key_size)) + return NULL; + BF_set_key (&key, key_size, secret); + + encrypted_pass = (guchar*)malloc (pass_len); + memset (encrypted_pass, 0, pass_len); + plain_pass = (char*)malloc (pass_len); + memset (plain_pass, 0, pass_len); + memcpy (plain_pass, pass, pass_len); + out_ptr = (char*)encrypted_pass; + in_ptr = (char*)plain_pass; + + for (length = pass_len; length; length -= 8, in_ptr += 8, out_ptr += 8) + BF_ecb_encrypt ((unsigned char*)in_ptr, (unsigned char*)out_ptr, &key, BF_ENCRYPT); + + /* Create response */ + length = 2 + BN_num_bytes (dh->pub_key) + pass_len + user_len + 1; + response = (char*)malloc (length); + out_ptr = response; + + /* our key */ + size16 = htons ((guint16)BN_num_bytes (dh->pub_key)); + memcpy (out_ptr, &size16, sizeof(size16)); + out_ptr += 2; + BN_bn2bin (dh->pub_key, (guchar*)out_ptr); + out_ptr += BN_num_bytes (dh->pub_key); + + /* username */ + memcpy (out_ptr, user, user_len + 1); + out_ptr += user_len + 1; + + /* pass */ + memcpy (out_ptr, encrypted_pass, pass_len); + + ret = g_base64_encode ((const guchar*)response, length); + + DH_free (dh); + free (plain_pass); + free (encrypted_pass); + free (secret); + free (response); + + return ret; +} + +char * +encode_sasl_pass_aes (char *user, char *pass, char *data) +{ + DH *dh; + AES_KEY key; + char *response = NULL; + char *out_ptr, *ret = NULL; + unsigned char *secret, *ptr; + unsigned char *encrypted_userpass, *plain_userpass; + int key_size, length; + guint16 size16; + unsigned char iv[16], iv_copy[16]; + int user_len = strlen (user) + 1; + int pass_len = strlen (pass) + 1; + int len = user_len + pass_len; + int padlen = 16 - (len % 16); + int userpass_len = len + padlen; + + if (!parse_dh (data, &dh, &secret, &key_size)) + return NULL; + + encrypted_userpass = (guchar*)malloc (userpass_len); + memset (encrypted_userpass, 0, userpass_len); + plain_userpass = (guchar*)malloc (userpass_len); + memset (plain_userpass, 0, userpass_len); + + /* create message */ + /* format of: <username>\0<password>\0<padding> */ + ptr = plain_userpass; + memcpy (ptr, user, user_len); + ptr += user_len; + memcpy (ptr, pass, pass_len); + ptr += pass_len; + if (padlen) + { + /* Padding */ + unsigned char randbytes[16]; + if (!RAND_bytes (randbytes, padlen)) + goto end; + + memcpy (ptr, randbytes, padlen); + } + + if (!RAND_bytes (iv, sizeof (iv))) + goto end; + + memcpy (iv_copy, iv, sizeof(iv)); + + /* Encrypt */ + AES_set_encrypt_key (secret, key_size * 8, &key); + AES_cbc_encrypt(plain_userpass, encrypted_userpass, userpass_len, &key, iv_copy, AES_ENCRYPT); + + /* Create response */ + /* format of: <size pubkey><pubkey><iv (always 16 bytes)><ciphertext> */ + length = 2 + key_size + sizeof(iv) + userpass_len; + response = (char*)malloc (length); + out_ptr = response; + + /* our key */ + size16 = htons ((guint16)key_size); + memcpy (out_ptr, &size16, sizeof(size16)); + out_ptr += 2; + BN_bn2bin (dh->pub_key, (guchar*)out_ptr); + out_ptr += key_size; + + /* iv */ + memcpy (out_ptr, iv, sizeof(iv)); + out_ptr += sizeof(iv); + + /* userpass */ + memcpy (out_ptr, encrypted_userpass, userpass_len); + + ret = g_base64_encode ((const guchar*)response, length); + +end: + DH_free (dh); + free (plain_userpass); + free (encrypted_userpass); + free (secret); + if (response) + free (response); + + return ret; +} +#endif + #ifdef WIN32 int find_font (const char *fontname) |