summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorPatrick Griffis <tingping@tingping.se>2016-01-29 17:41:08 -0500
committerPatrick Griffis <tingping@tingping.se>2016-01-29 17:41:08 -0500
commit4362085847f359ed13df6f8f488a06eb52ecd767 (patch)
treef03818cefe6c641c6567f36ecd7c0a2196830f4d
parent6b62c4738d4b1f5cb1bc70bf58f6a078630e6ef5 (diff)
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
-rw-r--r--src/common/hexchat.h6
-rw-r--r--src/common/inbound.c124
-rw-r--r--src/common/inbound.h3
-rw-r--r--src/common/proto-irc.c7
-rw-r--r--src/common/server.c1
-rw-r--r--src/common/util.c223
-rw-r--r--src/common/util.h2
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 <openssl/bn.h>
 #include <openssl/rand.h>
-#include <openssl/blowfish.h>
-#include <openssl/aes.h>
 #ifndef WIN32
 #include <netinet/in.h>
 #endif
@@ -1394,227 +1392,6 @@ encode_sasl_pass_plain (char *user, char *pass)
 }
 
 #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: <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 = 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);