summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBerke Viktor <bviktor@hexchat.org>2013-05-17 01:39:28 +0200
committerBerke Viktor <bviktor@hexchat.org>2013-05-17 01:40:07 +0200
commita1a94ed31973c0d8a262c82c3cb3d10c760d0ad7 (patch)
treee709bd015bb009b58259f3aae5e31fb963fb6550
parent096d0660e2cc707e13d7d55a9f3039418e21e883 (diff)
Add support for QuakeNet /CHALLENGEAUTH, remove legacy /AUTH
-rw-r--r--src/common/inbound.c3
-rw-r--r--src/common/proto-irc.c62
-rw-r--r--src/common/servlist.c2
-rw-r--r--src/common/servlist.h4
-rw-r--r--src/common/util.c61
-rw-r--r--src/common/util.h1
-rw-r--r--src/fe-gtk/servlistgui.c10
7 files changed, 115 insertions, 28 deletions
diff --git a/src/common/inbound.c b/src/common/inbound.c
index 07539a63..6c85feec 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -1380,7 +1380,10 @@ inbound_nickserv_login (server *serv)
 		case LOGIN_NICKSERV:
 		case LOGIN_NS:
 		case LOGIN_MSG_NS:
+		case LOGIN_CHALLENGEAUTH:
+#if 0
 		case LOGIN_AUTH:
+#endif
 			return 1;
 		default:
 			return 0;
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 8556f9e9..b5c68e82 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -67,43 +67,49 @@ irc_nickserv (server *serv, char *cmd, char *arg1, char *arg2, char *arg3)
 	/* are all ircd authors idiots? */
 	switch (serv->loginmethod)
 	{
-	case LOGIN_MSG_NICKSERV:
-		tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case LOGIN_NICKSERV:
-		tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
+		case LOGIN_MSG_NICKSERV:
+			tcp_sendf (serv, "PRIVMSG NICKSERV :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_NICKSERV:
+			tcp_sendf (serv, "NICKSERV %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_MSG_NS:
+			tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
 #if 0
-	case LOGIN_NS:
-		tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
+		case LOGIN_NS:
+			tcp_sendf (serv, "NS %s %s%s%s\r\n", cmd, arg1, arg2, arg3);
+			break;
+		case LOGIN_AUTH:
+			/* why couldn't QuakeNet implement one of the existing ones? */
+			tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
+			break;
 #endif
-	case LOGIN_MSG_NS:
-		tcp_sendf (serv, "PRIVMSG NS :%s %s%s%s\r\n", cmd, arg1, arg2, arg3);
-		break;
-	case LOGIN_AUTH:
-		/* why couldn't QuakeNet implement one of the existing ones? */
-		tcp_sendf (serv, "AUTH %s %s\r\n", arg1, arg2);
 	}
 }
 
 static void
 irc_ns_identify (server *serv, char *pass)
 {
-	if (serv->loginmethod == LOGIN_AUTH)	/* QuakeNet needs to do everything in its own ways... */
-	{
-		irc_nickserv (serv, "", serv->nick, pass, "");
-	}
-	else
+	switch (serv->loginmethod)
 	{
-		irc_nickserv (serv, "IDENTIFY", pass, "", "");
+		case LOGIN_CHALLENGEAUTH:
+			tcp_sendf (serv, "PRIVMSG %s :CHALLENGE\r\n", CHALLENGEAUTH_NICK);	/* request a challenge from Q */
+			break;
+#if 0
+		case LOGIN_AUTH:
+			irc_nickserv (serv, "", serv->nick, pass, "");
+			break;
+#endif
+		default:
+			irc_nickserv (serv, "IDENTIFY", pass, "", "");
 	}
 }
 
 static void
 irc_ns_ghost (server *serv, char *usname, char *pass)
 {
-	if (serv->loginmethod != LOGIN_AUTH)
+	if (serv->loginmethod != LOGIN_AUTH && serv->loginmethod != LOGIN_CHALLENGEAUTH)
 	{
 		irc_nickserv (serv, "GHOST", usname, " ", pass);
 	}
@@ -1080,11 +1086,21 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 
 		case WORDL('N','O','T','I'):
 			{
-				int id = FALSE;	/* identified */
+				int id = FALSE;							/* identified */
+				char *response;
 
 				text = word_eol[4];
 				if (*text == ':')
+				{
 					text++;
+				}
+
+				if (!strncmp (text, "CHALLENGE ", 10))		/* QuakeNet CHALLENGEAUTH upon our request */
+				{
+					response = challengeauth_response (((ircnet *)serv->network)->user, serv->password, word[5]);
+					tcp_sendf (serv, "PRIVMSG %s :CHALLENGEAUTH %s %s %s\r\n", CHALLENGEAUTH_NICK, ((ircnet *)serv->network)->user, response, CHALLENGEAUTH_ALGO);
+					g_free (response);
+				}
 
 				if (serv->have_idmsg)
 				{
diff --git a/src/common/servlist.c b/src/common/servlist.c
index da0b746d..d2d87c6a 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -426,7 +426,7 @@ static const struct defaultserver def[] =
 	{0,			"nfsi.ptnet.org"},
 	{0,			"fctunl.ptnet.org"},
 
-	{"QuakeNet", 0, 0, 0, LOGIN_AUTH},
+	{"QuakeNet", 0, 0, 0, LOGIN_CHALLENGEAUTH},
 	{0,			"irc.quakenet.org"},
 	{0,			"irc.se.quakenet.org"},
 	{0,			"irc.dk.quakenet.org"},
diff --git a/src/common/servlist.h b/src/common/servlist.h
index 92100b9b..717e153c 100644
--- a/src/common/servlist.h
+++ b/src/common/servlist.h
@@ -75,6 +75,10 @@ extern GSList *network_list;
 #define LOGIN_AUTH				5
 #define LOGIN_SASL				6
 #define LOGIN_PASS				7
+#define LOGIN_CHALLENGEAUTH		8
+
+#define CHALLENGEAUTH_ALGO		"HMAC-SHA-256"
+#define CHALLENGEAUTH_NICK		"Q@CServe.quakenet.org"
 
 /* DEFAULT_CHARSET is already defined in wingdi.h */
 #define IRC_DEFAULT_CHARSET		"UTF-8 (Unicode)"
diff --git a/src/common/util.c b/src/common/util.c
index a6c9fe21..5c09992b 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -1978,3 +1978,64 @@ find_font (const char *fontname)
 	return 0;
 }
 #endif
+
+static char *
+str_sha256hash (char *string)
+{
+	int i;
+	unsigned char hash[SHA256_DIGEST_LENGTH];
+	char buf[SHA256_DIGEST_LENGTH * 2 + 1];		/* 64 digit hash + '\0' */
+	SHA256_CTX sha256;
+
+	SHA256_Init (&sha256);
+	SHA256_Update (&sha256, string, strlen (string));
+	SHA256_Final (hash, &sha256);
+
+	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+	{
+		sprintf (buf + (i * 2), "%02x", hash[i]);
+	}
+
+	buf[SHA256_DIGEST_LENGTH * 2] = 0;
+
+	return g_strdup (buf);
+}
+
+char *
+challengeauth_response (char *username, char *password, char *challenge)
+{
+	int i;
+	char *user;
+	char *pass;
+	char *passhash;
+	char *key;
+	char *keyhash;
+	unsigned char *digest;
+	GString *buf = g_string_new_len (NULL, SHA256_DIGEST_LENGTH * 2);
+
+	user = g_strdup (username);
+	*user = rfc_tolower (*username);			/* convert username to lowercase as per the RFC*/
+
+	pass = g_strndup (password, 10);			/* truncate to 10 characters */
+	passhash = str_sha256hash (pass);
+	g_free (pass);
+
+	key = g_strdup_printf ("%s:%s", user, passhash);
+	g_free (user);
+	g_free (passhash);
+
+	keyhash = str_sha256hash (key);
+	g_free (key);
+
+	digest = HMAC (EVP_sha256 (), keyhash, strlen (keyhash), (unsigned char *) challenge, strlen (challenge), NULL, NULL);
+	g_free (keyhash);
+
+	for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+	{
+		g_string_append_printf (buf, "%02x", (unsigned int) digest[i]);
+	}
+
+	digest = (unsigned char *) g_string_free (buf, FALSE);
+
+	return (char *) digest;
+}
diff --git a/src/common/util.h b/src/common/util.h
index 9e2d9f52..0ebd89d4 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -80,5 +80,6 @@ int portable_mode ();
 int unity_mode ();
 GSList *get_subdirs (const char *path);
 char *encode_sasl_pass (char *user, char *pass);
+char *challengeauth_response (char *username, char *password, char *challenge);
 
 #endif
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index 53aba691..97c0062f 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -121,11 +121,12 @@ static int login_types_conf[] =
 	LOGIN_PASS,
 	LOGIN_MSG_NICKSERV,
 	LOGIN_NICKSERV,
+	LOGIN_MSG_NS,
+	LOGIN_CHALLENGEAUTH
 #if 0
 	LOGIN_NS,
-#endif
-	LOGIN_MSG_NS,
 	LOGIN_AUTH,
+#endif
 };
 
 static const char *login_types[]=
@@ -135,11 +136,12 @@ static const char *login_types[]=
 	"Server Password (/PASS password)",
 	"NickServ (/MSG NickServ + password)",
 	"NickServ (/NICKSERV + password)",
+	"NickServ (/MSG NS + password)",
+	"Challenge Auth (username + password)",
 #if 0
 	"NickServ (/NS + password)",
-#endif
-	"NickServ (/MSG NS + password)",
 	"AUTH (/AUTH nickname password)",
+#endif
 	NULL
 };