changeset 10561:1db4f49de0c6

[gaim-migrate @ 11939] sf patch #1009209, from Malcolm Smith CHAP support for SOCKS5 Please test this. Normally I try to avoid commiting things that aren't bug fixes to oldstatus, but someone said (I think it was Felipe), "oldstatus is also good for testing things." Uh, or something like that. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Tue, 01 Feb 2005 04:29:44 +0000
parents 33746d45bd0d
children e06da39b467c
files COPYRIGHT ChangeLog src/proxy.c
diffstat 3 files changed, 172 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Feb 01 03:52:46 2005 +0000
+++ b/COPYRIGHT	Tue Feb 01 04:29:44 2005 +0000
@@ -168,6 +168,7 @@
 John Silvestri
 Craig Slusher
 Alex Smith
+Malcolm Smith
 David Smock
 Phil Snowberger
 Eddie Sohn (tr1sk)
--- a/ChangeLog	Tue Feb 01 03:52:46 2005 +0000
+++ b/ChangeLog	Tue Feb 01 04:29:44 2005 +0000
@@ -23,6 +23,7 @@
 	* Custom smileys for MSN (Irving Cordova)
 	* Entries in the text replacement plugin are now sorted alphabetically
 	  (Richard Laager)
+	* CHAP authentication support for SOCKS5 proxies (Malcolm Smith)
 
 	Bug fixes:
 	* Some memory leaks plugged (Miah Gregory, Felipe Contreras)
--- a/src/proxy.c	Tue Feb 01 03:52:46 2005 +0000
+++ b/src/proxy.c	Tue Feb 01 04:29:44 2005 +0000
@@ -35,6 +35,7 @@
 #include "prefs.h"
 #include "proxy.h"
 #include "util.h"
+#include "md5.h"
 
 static GaimProxyInfo *global_proxy_info = NULL;
 
@@ -1132,7 +1133,14 @@
 	}
 	fcntl(source, F_SETFL, 0);
 
-	/* XXX does socks4 not support host name lookups by the proxy? */
+	/*
+	 * The socks4 spec doesn't include support for doing host name
+	 * lookups by the proxy.  Some socks4 servers do this via
+	 * extensions to the protocol.  Since we don't know if a
+	 * server supports this, it would need to be implemented
+	 * with an option, or some detection mechanism - in the
+	 * meantime, stick with plain old SOCKS4.
+	 */
 	if (!(hp = gethostbyname(phb->host))) {
 		close(source);
 
@@ -1279,10 +1287,10 @@
 	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;
+	buf[5 + hlen] = phb->port >> 8;
+	buf[5 + hlen + 1] = phb->port & 0xff;
 
-	if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
+	if (write(source, buf, (5 + hlen + 2)) < (5 + hlen + 2)) {
 		close(source);
 
 		try_connect(phb);
@@ -1318,6 +1326,141 @@
 	s5_sendconnect(phb, source);
 }
 
+static void hmacmd5_chap(const unsigned char * challenge, int challen, const char * passwd, unsigned char * response)
+{
+	int i;
+	unsigned char Kxoripad[65];
+	unsigned char Kxoropad[65];
+	md5_state_t ctx;
+	int pwlen;
+	char * pwinput;
+	char md5buf[16];
+
+	pwinput=(char *)passwd;
+	pwlen=strlen(passwd);
+	if (pwlen>64) {
+		md5_init(&ctx);
+		md5_append(&ctx, (unsigned char *)passwd, strlen(passwd));
+		md5_finish(&ctx, (unsigned char *)md5buf);
+		pwinput=(char *)md5buf;
+		pwlen=16;
+	}
+
+	memset(Kxoripad,0,sizeof(Kxoripad));
+	memset(Kxoropad,0,sizeof(Kxoropad));
+	memcpy(Kxoripad,pwinput,pwlen);
+	memcpy(Kxoropad,pwinput,pwlen);
+	for (i=0;i<64;i++) {
+		Kxoripad[i]^=0x36;
+		Kxoropad[i]^=0x5c;
+	}
+	md5_init(&ctx);
+	md5_append(&ctx, Kxoripad, 64);
+	md5_append(&ctx, challenge, challen);
+	md5_finish(&ctx, (unsigned char *)Kxoripad);
+
+	md5_init(&ctx);
+	md5_append(&ctx, Kxoropad, 64);
+	md5_append(&ctx, Kxoripad, 16);
+	md5_finish(&ctx, response);
+}
+
+static void
+s5_readchap(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char buf[260];
+	unsigned char cmdbuf[20];
+	struct PHB *phb = data;
+
+	int navas, currentav;
+
+	gaim_input_remove(phb->inpa);
+	gaim_debug(GAIM_DEBUG_INFO, "socks5 proxy", "Got CHAP response.\n");
+
+	if (read(source, cmdbuf, 2) < 2) {
+		close(source);
+
+		try_connect(phb);
+		return;
+	}
+
+	if (cmdbuf[0] != 0x01) {
+		close(source);
+
+		try_connect(phb);
+		return;
+	}
+
+	navas = cmdbuf[1];
+
+	for (currentav = 0; currentav < navas; currentav++) {
+		if (read(source, cmdbuf, 2) < 2) {
+			close(source);
+
+			try_connect(phb);
+			return;
+		}
+		if (read(source, buf, cmdbuf[1]) < cmdbuf[1]) {
+			close(source);
+
+			try_connect(phb);
+			return;
+		}
+		switch (cmdbuf[0]) {
+			case 0x00:
+				/* Did auth work? */
+				if (buf[0] == 0x00) {
+					/* Success */
+					return s5_sendconnect(phb, source);
+				} else {
+					/* Failure */
+					gaim_debug_warning("proxy", "socks5 CHAP authentication "
+									   "failed.  Disconnecting...");
+					close(source);
+
+					try_connect(phb);
+					return;
+				}
+				break;
+			case 0x03:
+				/* Server wants our credentials */
+				hmacmd5_chap(buf, cmdbuf[1],
+					gaim_proxy_info_get_password(phb->gpi),
+					buf + 4);
+				buf[0] = 0x01;
+				buf[1] = 0x01;
+				buf[2] = 0x04;
+				buf[3] = 0x10;
+				if (write(source, buf, 20) < 20) {
+					close(source);
+
+					try_connect(phb);
+					return;
+				}
+				break;
+			case 0x11:
+				/* Server wants to select an algorithm */
+				if (buf[0] != 0x85) {
+					/* Only currently support HMAC-MD5 */
+					gaim_debug_warning("proxy", "Server tried to select an "
+									   "algorithm that we did not advertise "
+									   "as supporting.  This is a violation "
+									   "of the socks5 CHAP specification.  "
+									   "Disconnecting...");
+					close(source);
+
+					try_connect(phb);
+					return;
+				}
+				break;
+		}
+	}
+	/* Fell through.  We ran out of CHAP events to process, but haven't
+	 * succeeded or failed authentication - there may be more to come.
+	 * If this is the case, come straight back here. */
+	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb);
+}
+
 static void
 s5_canread(gpointer data, gint source, GaimInputCondition cond)
 {
@@ -1361,6 +1504,25 @@
 		}
 
 		phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
+	} else if (buf[1] == 0x03) {
+		unsigned int userlen;
+		userlen = strlen(gaim_proxy_info_get_username(phb->gpi));
+		buf[0] = 0x01;
+		buf[1] = 0x02;
+		buf[2] = 0x11;
+		buf[3] = 0x01;
+		buf[4] = 0x85;
+		buf[5] = 0x02;
+		buf[6] = userlen;
+		memcpy(buf + 7, gaim_proxy_info_get_username(phb->gpi), userlen);
+		if (write(source, buf, 7 + userlen) < 7 + userlen) {
+			close(source);
+
+			try_connect(phb);
+			return;
+		}
+
+		phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readchap, phb);
 	}
 	else {
 		s5_sendconnect(phb, source);
@@ -1394,10 +1556,11 @@
 	buf[0] = 0x05;		/* SOCKS version 5 */
 
 	if (gaim_proxy_info_get_username(phb->gpi) != NULL) {
-		buf[1] = 0x02;	/* two methods */
+		buf[1] = 0x03;	/* three methods */
 		buf[2] = 0x00;	/* no authentication */
-		buf[3] = 0x02;	/* username/password authentication */
-		i = 4;
+		buf[3] = 0x03;	/* CHAP authentication */
+		buf[4] = 0x02;	/* username/password authentication */
+		i = 5;
 	}
 	else {
 		buf[1] = 0x01;