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... */ + switch (serv->loginmethod) { - irc_nickserv (serv, "", serv->nick, pass, ""); - } - else - { - 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 };