From 1541299fa17a1baae54a3c836e7cdd5dada290c4 Mon Sep 17 00:00:00 2001 From: Kevin Easton Date: Sun, 30 Dec 2012 02:22:56 +0000 Subject: [PATCH] Merge infrastructure for SASL authentication support from flashback. This includes all of the underlying support, but doesn't hook it up to the /SERVER command yet, so it's not useable at this point. git-svn-id: svn://svn.code.sf.net/p/bitchx/code/trunk@216 13b04d17-f746-0410-82c6-800466cd88b0 --- include/ircaux.h | 1 + include/server.h | 6 ++++ source/ircaux.c | 46 +++++++++++++++++++++++++++++++ source/numbers.c | 11 ++++++++ source/parse.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ source/server.c | 43 ++++++++++++++++++++++++++++- 6 files changed, 177 insertions(+), 1 deletion(-) diff --git a/include/ircaux.h b/include/ircaux.h index e52bcd6..23062e6 100644 --- a/include/ircaux.h +++ b/include/ircaux.h @@ -134,6 +134,7 @@ char * get_userhost (void); char * urlencode (const char *); char * urldecode (char *); +char * base64_encode (const void *data, size_t size); /* From words.c */ #define SOS -32767 diff --git a/include/server.h b/include/server.h index a1012fd..4bff4e8 100644 --- a/include/server.h +++ b/include/server.h @@ -165,6 +165,8 @@ typedef struct int ssl_error; SSL* ssl_fd; #endif + char *sasl_nick; + char *sasl_pass; /* recv_nick: the nickname of the last person to send you a privmsg */ char *recv_nick; @@ -395,6 +397,10 @@ NotifyItem *get_server_notify_list (int); void clean_split_server_list (int, time_t); void write_server_list(char *); void write_server_file (char *); +// void set_server_sasl_nick(int, const char *); + char *get_server_sasl_nick(int); +// void set_server_sasl_pass(int, const char *); + char *get_server_sasl_pass(int); #define USER_MODE 0x0001 #define USER_MODE_A USER_MODE << 0 diff --git a/source/ircaux.c b/source/ircaux.c index 6c19363..e725884 100644 --- a/source/ircaux.c +++ b/source/ircaux.c @@ -3242,3 +3242,49 @@ unsigned long BX_random_number (unsigned long l) } } +/* + * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * This is licensed under the 3-clause BSD license, which is found above. + */ +/* + * Return a malloced, base64 string representation of the first 'size' bytes + * starting at 'data'. + */ +char *base64_encode(const void *data, size_t size) +{ + static const char base64_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + char *s, *p; + size_t i; + unsigned c; + const unsigned char * const q = data; + + p = s = new_malloc((size + 2) / 3 * 4 + 1); + + for (i = 0; i < size;) { + c = q[i++]; + c *= 256; + if (i < size) + c += q[i]; + i++; + c *= 256; + if (i < size) + c += q[i]; + i++; + p[0] = base64_chars[(c & 0x00fc0000) >> 18]; + p[1] = base64_chars[(c & 0x0003f000) >> 12]; + p[2] = base64_chars[(c & 0x00000fc0) >> 6]; + p[3] = base64_chars[(c & 0x0000003f) >> 0]; + if (i > size) + p[3] = '='; + if (i > size + 1) + p[2] = '='; + p += 4; + } + *p = 0; + + return s; +} diff --git a/source/numbers.c b/source/numbers.c index 6f30432..5efe62f 100644 --- a/source/numbers.c +++ b/source/numbers.c @@ -1220,6 +1220,17 @@ void numbered_command(char *from, int comm, char **ArgList) set_server_flag(from_server, USER_MODE_R, 1); break; } + case 903: /* SASL authentication successful */ + case 904: /* SASL authentication failed */ + case 905: /* SASL message too long */ + case 906: /* SASL authentication aborted */ + case 907: /* You have already completed SASL authentication */ + { + my_send_to_server(from_server, "CAP END"); + if (do_hook(current_numeric, "%s %s", from, *ArgList)) + display_msg(from, ArgList); + break; + } case 367: { number_of_bans++; diff --git a/source/parse.c b/source/parse.c index ef51eef..0516ce6 100644 --- a/source/parse.c +++ b/source/parse.c @@ -887,6 +887,75 @@ static void p_error(char *from, char **ArgList) say("%s", ArgList[0]); } +/* + * This only handles negotiating the SASL capability with the PLAIN method. It would + * be good to add DH-BLOWFISH, and later, full capability support. + */ +static void p_cap(char *from, char **ArgList) +{ + char *caps, *p; + + if (!strcmp(ArgList[1], "ACK")) + { + caps = LOCAL_COPY(ArgList[2]); + while ((p = next_arg(caps, &caps)) != NULL) + { + /* Only AUTHENTICATE before registration */ + if (!strcmp(p, "sasl") && !is_server_connected(from_server)) + { + my_send_to_server(from_server, "AUTHENTICATE PLAIN"); + break; + } + } + } + else if (!strcmp(ArgList[1], "NAK")) + { + caps = LOCAL_COPY(ArgList[2]); + while ((p = next_arg(caps, &caps)) != NULL) + { + /* End capability negotiation to continue registration */ + if (!strcmp(p, "sasl") && !is_server_connected(from_server)) + { + my_send_to_server(from_server, "CAP END"); + break; + } + } + } +} + +static void p_authenticate(char *from, char **ArgList) +{ + /* "AUTHENTICATE command MUST be used before registration is complete" */ + if (is_server_connected(from_server)) + return; + + if (!strcmp(ArgList[0], "+")) + { + /* Message is BASE64(nick\0nick\0pass) */ + char buf[IRCD_BUFFER_SIZE]; + char *output = NULL; + char *nick = get_server_sasl_nick(from_server); + char *pass = get_server_sasl_pass(from_server); + size_t nick_len = nick ? strlen(nick) + 1 : 0; /* nick_len includes \0 */ + size_t pass_len = pass ? strlen(pass) : 0; + + /* "The client can abort an authentication by sending an asterisk as the data" */ + if (!nick || !pass || nick_len * 2 + pass_len > sizeof buf) + { + my_send_to_server(from_server, "AUTHENTICATE *"); + return; + } + + memcpy(buf, nick, nick_len); + memcpy(buf + nick_len, nick, nick_len); + memcpy(buf + nick_len * 2, pass, pass_len); + + output = base64_encode(buf, nick_len * 2 + pass_len); + my_send_to_server(from_server, "AUTHENTICATE %s", output); + new_free(&output); + } +} + void add_user_who (WhoEntry *w, char *from, char **ArgList) { char *userhost; @@ -1758,7 +1827,9 @@ static void p_rpong (char *from, char **ArgList) protocol_command rfc1459[] = { { "ADMIN", NULL, NULL, 0, 0, 0}, +{ "AUTHENTICATE", p_authenticate, NULL, 0, 0, 0}, { "AWAY", NULL, NULL, 0, 0, 0}, +{ "CAP", p_cap, NULL, 0, 0, 0}, { "CONNECT", NULL, NULL, 0, 0, 0}, { "ERROR", p_error, NULL, 0, 0, 0}, { "ERROR:", p_error, NULL, 0, 0, 0}, diff --git a/source/server.c b/source/server.c index 8192e6f..8f0d2fe 100644 --- a/source/server.c +++ b/source/server.c @@ -1634,7 +1634,7 @@ BUILT_IN_COMMAND(servercmd) { if (!(server=new_next_arg(args,&args))) { - say("Not enough paramters - supply server name"); + say("Not enough parameters - supply server name"); return; } say("Trying to establish ssl connection with server: %s",server); @@ -2279,6 +2279,9 @@ void register_server (int ssn_index, char *nick) int old_from_server = from_server; if (server_list[ssn_index].password) my_send_to_server(ssn_index, "PASS %s", server_list[ssn_index].password); + + if (server_list[ssn_index].sasl_nick && server_list[ssn_index].sasl_pass) + my_send_to_server(ssn_index, "CAP REQ :sasl"); my_send_to_server(ssn_index, "USER %s %s %s :%s", username, (send_umode && *send_umode) ? send_umode : @@ -3790,3 +3793,41 @@ int save_servers (FILE *fp) } return i; } + +#if 0 +void set_server_sasl_nick(int server, const char *nick) +{ + if (server <= -1 || server >= number_of_servers) + return; + if (nick) + malloc_strcpy(&server_list[server].sasl_nick, nick); + else + new_free(&server_list[server].sasl_nick); +} +#endif + +char *get_server_sasl_nick(int server) +{ + if (server <= -1 || server >= number_of_servers) + return NULL; + return server_list[server].sasl_nick; +} + +#if 0 +void set_server_sasl_pass(int server, const char *pass) +{ + if (server <= -1 || server >= number_of_servers) + return; + if (pass) + malloc_strcpy(&server_list[server].sasl_pass, pass); + else + new_free(&server_list[server].sasl_pass); +} +#endif + +char *get_server_sasl_pass(int server) +{ + if (server <= -1 || server >= number_of_servers) + return NULL; + return server_list[server].sasl_pass; +}