From 4362085847f359ed13df6f8f488a06eb52ecd767 Mon Sep 17 00:00:00 2001 From: Patrick Griffis Date: Fri, 29 Jan 2016 17:41:08 -0500 Subject: [PATCH] Remove DH-AES/DH-BLOWFISH mechanisms and misc cleanup - AES and Blowfish mechanisms are deemed insecure and servers have removed support for them - Remove attempts to retry since we only support one mech - Handle SASL 3.2's new syntax for supported mechs --- src/common/hexchat.h | 6 +- src/common/inbound.c | 124 ++++++++++------------- src/common/inbound.h | 3 +- src/common/proto-irc.c | 7 +- src/common/server.c | 1 + src/common/util.c | 223 ----------------------------------------- src/common/util.h | 2 - 7 files changed, 57 insertions(+), 309 deletions(-) diff --git a/src/common/hexchat.h b/src/common/hexchat.h index a29ea1d8..51c9b9aa 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -422,9 +422,7 @@ typedef struct session /* SASL Mechanisms */ #define MECH_PLAIN 0 -#define MECH_BLOWFISH 1 -#define MECH_AES 2 -#define MECH_EXTERNAL 3 +#define MECH_EXTERNAL 1 typedef struct server { @@ -546,7 +544,6 @@ typedef struct server unsigned int skip_next_whois:1; /* hide whois output */ unsigned int inside_whois:1; unsigned int doing_dns:1; /* /dns has been done */ - unsigned int retry_sasl:1; /* retrying another sasl mech */ unsigned int end_of_motd:1; /* end of motd reached (logged in) */ unsigned int sent_quit:1; /* sent a QUIT already? */ unsigned int use_listargs:1; /* undernet and dalnet need /list >0,<10000 */ @@ -570,7 +567,6 @@ typedef struct server unsigned int have_cert:1; /* have loaded a cert */ unsigned int use_who:1; /* whether to use WHO command to get dcc_ip */ unsigned int sasl_mech; /* mechanism for sasl auth */ - unsigned int sent_saslauth:1; /* have sent AUTHENICATE yet */ unsigned int sent_capend:1; /* have sent CAP END yet */ unsigned int waiting_on_cap:1; /* waiting on another line of CAP LS */ #ifdef USE_OPENSSL diff --git a/src/common/inbound.c b/src/common/inbound.c index 5f949822..0e962caf 100644 --- a/src/common/inbound.c +++ b/src/common/inbound.c @@ -1633,6 +1633,12 @@ inbound_identified (server *serv) /* 'MODE +e MYSELF' on freenode */ } } +static const char *sasl_mechanisms[] = +{ + "PLAIN", + "EXTERNAL" +}; + static void inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable) { @@ -1666,24 +1672,12 @@ inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable) serv->have_sasl = enable; if (enable) { - serv->sent_saslauth = FALSE; - #ifdef USE_OPENSSL if (serv->loginmethod == LOGIN_SASLEXTERNAL) - { serv->sasl_mech = MECH_EXTERNAL; - tcp_send_len (serv, "AUTHENTICATE EXTERNAL\r\n", 23); - } - else - { - /* default to most secure, it will fallback if not supported */ - serv->sasl_mech = MECH_AES; - tcp_send_len (serv, "AUTHENTICATE DH-AES\r\n", 21); - } -#else - serv->sasl_mech = MECH_PLAIN; - tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20); #endif + /* Mechanism either defaulted to PLAIN or server gave us list */ + tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]); } } } @@ -1735,6 +1729,37 @@ static const char * const supported_caps[] = { "twitch.tv/membership", }; +static int +get_supported_mech (server *serv, const char *list) +{ + char **mechs = g_strsplit (list, ",", 0); + gsize i; + int ret = -1; + + for (i = 0; mechs[i]; ++i) + { +#ifdef USE_OPENSSL + if (serv->loginmethod == LOGIN_SASLEXTERNAL) + { + if (!strcmp (mechs[i], "EXTERNAL")) + { + ret = MECH_EXTERNAL; + break; + } + } + else +#endif + if (!strcmp (mechs[i], "PLAIN")) + { + ret = MECH_PLAIN; + break; + } + } + + g_strfreev (mechs); + return ret; +} + void inbound_cap_ls (server *serv, char *nick, char *extensions_str, const message_tags_data *tags_data) @@ -1781,6 +1806,13 @@ inbound_cap_ls (server *serv, char *nick, char *extensions_str, ((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0) || (serv->loginmethod == LOGIN_SASLEXTERNAL && serv->have_cert))) { + if (value) + { + int sasl_mech = get_supported_mech (serv, value); + if (sasl_mech == -1) /* No supported mech */ + continue; + serv->sasl_mech = sasl_mech; + } want_cap = TRUE; want_sasl = TRUE; g_strlcat (buffer, "sasl ", sizeof(buffer)); @@ -1839,40 +1871,6 @@ inbound_cap_list (server *serv, char *nick, char *extensions, NULL, NULL, 0, tags_data->timestamp); } -static const char *sasl_mechanisms[] = -{ - "PLAIN", - "DH-BLOWFISH", - "DH-AES", - "EXTERNAL" -}; - -void -inbound_sasl_supportedmechs (server *serv, char *list) -{ - int i; - - if (serv->sasl_mech != MECH_EXTERNAL) - { - /* Use most secure one supported */ - for (i = MECH_AES; i >= MECH_PLAIN; i--) - { - if (strstr (list, sasl_mechanisms[i]) != NULL) - { - serv->sasl_mech = i; - serv->retry_sasl = TRUE; - tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[i]); - return; - } - } - } - - /* Abort, none supported */ - serv->sent_saslauth = TRUE; - tcp_sendf (serv, "AUTHENTICATE *\r\n"); - return; -} - void inbound_sasl_authenticate (server *serv, char *data) { @@ -1880,12 +1878,10 @@ inbound_sasl_authenticate (server *serv, char *data) char *user, *pass = NULL; const char *mech = sasl_mechanisms[serv->sasl_mech]; - /* Got a list of supported mechanisms from inspircd */ + /* Got a list of supported mechanisms from outdated inspircd + * just ignore it as it goes against spec */ if (strchr (data, ',') != NULL) - { - inbound_sasl_supportedmechs (serv, data); return; - } if (net->user && !(net->flags & FLAG_USE_GLOBAL)) user = net->user; @@ -1898,12 +1894,6 @@ inbound_sasl_authenticate (server *serv, char *data) pass = encode_sasl_pass_plain (user, serv->password); break; #ifdef USE_OPENSSL - case MECH_BLOWFISH: - pass = encode_sasl_pass_blowfish (user, serv->password, data); - break; - case MECH_AES: - pass = encode_sasl_pass_aes (user, serv->password, data); - break; case MECH_EXTERNAL: pass = g_strdup ("+"); break; @@ -1913,12 +1903,10 @@ inbound_sasl_authenticate (server *serv, char *data) if (pass == NULL) { /* something went wrong abort */ - serv->sent_saslauth = TRUE; /* prevent trying PLAIN */ tcp_sendf (serv, "AUTHENTICATE *\r\n"); return; } - serv->sent_saslauth = TRUE; tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass); g_free (pass); @@ -1927,19 +1915,9 @@ inbound_sasl_authenticate (server *serv, char *data) NULL, NULL, 0, 0); } -int +void inbound_sasl_error (server *serv) { - if (serv->retry_sasl && !serv->sent_saslauth) - return 1; - - /* If server sent 904 before we sent password, - * mech not support so fallback to next mech */ - if (!serv->sent_saslauth && serv->sasl_mech != MECH_EXTERNAL && serv->sasl_mech != MECH_PLAIN) - { - serv->sasl_mech -= 1; - tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]); - return 1; - } - return 0; + /* Just abort, not much we can do */ + tcp_sendf (serv, "AUTHENTICATE *\r\n"); } diff --git a/src/common/inbound.h b/src/common/inbound.h index c5d10445..83e78d5d 100644 --- a/src/common/inbound.h +++ b/src/common/inbound.h @@ -98,8 +98,7 @@ void inbound_cap_list (server *serv, char *nick, char *extensions, void inbound_cap_del (server *serv, char *nick, char *extensions, const message_tags_data *tags_data); void inbound_sasl_authenticate (server *serv, char *data); -int inbound_sasl_error (server *serv); -void inbound_sasl_supportedmechs (server *serv, char *list); +void inbound_sasl_error (server *serv); void do_dns (session *sess, char *nick, char *host, const message_tags_data *tags_data); gboolean alert_match_word (char *word, char *masks); diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c index 180a3e43..a565dbb6 100644 --- a/src/common/proto-irc.c +++ b/src/common/proto-irc.c @@ -946,10 +946,9 @@ process_numeric (session * sess, int n, word_eol[6]+1, word[1], word[2], NULL, 0, tags_data->timestamp); break; - case 903: /* successful SASL auth */ case 904: /* failed SASL auth */ - if (inbound_sasl_error (serv)) - break; /* might retry */ + inbound_sasl_error (serv); + case 903: /* successful SASL auth */ case 905: /* failed SASL auth */ case 906: /* aborted */ case 907: /* attempting to re-auth after a successful auth */ @@ -963,7 +962,7 @@ process_numeric (session * sess, int n, } break; case 908: /* Supported SASL Mechs */ - inbound_sasl_supportedmechs (serv, word[4]); + /* ignored for now, SASL 3.2 is a better solution and we only do PLAIN atm */ break; default: diff --git a/src/common/server.c b/src/common/server.c index 926a9f43..e4c2e377 100644 --- a/src/common/server.c +++ b/src/common/server.c @@ -1727,6 +1727,7 @@ server_set_defaults (server *serv) serv->chanmodes = g_strdup ("beI,k,l"); serv->nick_prefixes = g_strdup ("@%+"); serv->nick_modes = g_strdup ("ohv"); + serv->sasl_mech = MECH_PLAIN; server_set_encoding (serv, "UTF-8"); diff --git a/src/common/util.c b/src/common/util.c index 9ec8ef16..5c4eb8bf 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -56,8 +56,6 @@ #ifdef USE_OPENSSL #include #include -#include -#include #ifndef WIN32 #include #endif @@ -1393,227 +1391,6 @@ encode_sasl_pass_plain (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 = NULL; - 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 = g_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: - g_free (secret); - g_free (decoded_data); - - return 0; -} - -char * -encode_sasl_pass_blowfish (char *user, char *pass, char *data) -{ - DH *dh; - char *response, *ret = NULL; - 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 = g_malloc0 (pass_len); - plain_pass = g_malloc0 (pass_len); - memcpy (plain_pass, pass, strlen(pass)); - 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 = g_malloc0 (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); - - g_free (response); - - DH_free(dh); - g_free (plain_pass); - g_free (encrypted_pass); - g_free (secret); - - 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 = g_malloc0 (userpass_len); - plain_userpass = g_malloc0 (userpass_len); - - /* create message */ - /* format of: \0\0 */ - 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: */ - length = 2 + key_size + sizeof(iv) + userpass_len; - response = g_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); - g_free (plain_userpass); - g_free (encrypted_userpass); - g_free (secret); - g_free (response); - - return ret; -} -#endif - #ifdef USE_OPENSSL static char * str_sha256hash (char *string) diff --git a/src/common/util.h b/src/common/util.h index 2c9f790c..ba318585 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -76,8 +76,6 @@ void canonalize_key (char *key); int portable_mode (void); int unity_mode (void); char *encode_sasl_pass_plain (char *user, char *pass); -char *encode_sasl_pass_blowfish (char *user, char *pass, char *data); -char *encode_sasl_pass_aes (char *user, char *pass, char *data); char *challengeauth_response (char *username, char *password, char *challenge); size_t strftime_validated (char *dest, size_t destsize, const char *format, const struct tm *time); gsize strftime_utf8 (char *dest, gsize destsize, const char *format, time_t time);