diff options
author | dx <dx@dxzone.com.ar> | 2015-09-20 19:19:49 -0300 |
---|---|---|
committer | dx <dx@dxzone.com.ar> | 2015-09-20 19:19:49 -0300 |
commit | 0912a1105058829559529e2ae7d62daccb8337f6 (patch) | |
tree | 22e30668245180e9c76484df5a6b49ef35fdfe76 /src/irc/core | |
parent | a44c4b82de44af9aec59c542f3212795ecc52b57 (diff) | |
parent | 203c00938acad1da4a91a2c21afdac4ede08572a (diff) | |
download | irssi-0912a1105058829559529e2ae7d62daccb8337f6.zip |
Merge pull request #278 from LemonBoy/sasl
SASL support
Diffstat (limited to 'src/irc/core')
-rw-r--r-- | src/irc/core/Makefile.am | 2 | ||||
-rw-r--r-- | src/irc/core/irc-chatnets.c | 15 | ||||
-rw-r--r-- | src/irc/core/irc-chatnets.h | 9 | ||||
-rw-r--r-- | src/irc/core/irc-core.c | 5 | ||||
-rw-r--r-- | src/irc/core/irc-servers-setup.c | 24 | ||||
-rw-r--r-- | src/irc/core/irc-servers.c | 5 | ||||
-rw-r--r-- | src/irc/core/irc-servers.h | 4 | ||||
-rw-r--r-- | src/irc/core/sasl.c | 178 | ||||
-rw-r--r-- | src/irc/core/sasl.h | 34 |
9 files changed, 270 insertions, 6 deletions
diff --git a/src/irc/core/Makefile.am b/src/irc/core/Makefile.am index 7d885d20..20caaeb1 100644 --- a/src/irc/core/Makefile.am +++ b/src/irc/core/Makefile.am @@ -27,6 +27,7 @@ libirc_core_a_SOURCES = \ irc-servers-setup.c \ irc-session.c \ irc-cap.c \ + sasl.c \ lag.c \ massjoin.c \ modes.c \ @@ -50,6 +51,7 @@ pkginc_irc_core_HEADERS = \ irc-servers.h \ irc-servers-setup.h \ irc-cap.h \ + sasl.h \ modes.h \ mode-lists.h \ module.h \ diff --git a/src/irc/core/irc-chatnets.c b/src/irc/core/irc-chatnets.c index d757bf8d..b9b221b8 100644 --- a/src/irc/core/irc-chatnets.c +++ b/src/irc/core/irc-chatnets.c @@ -48,6 +48,10 @@ static void sig_chatnet_read(IRC_CHATNET_REC *rec, CONFIG_NODE *node) rec->max_msgs = config_node_get_int(node, "max_msgs", 0); rec->max_modes = config_node_get_int(node, "max_modes", 0); rec->max_whois = config_node_get_int(node, "max_whois", 0); + + rec->sasl_mechanism = config_node_get_str(node, "sasl_mechanism", NULL); + rec->sasl_username = config_node_get_str(node, "sasl_username", NULL); + rec->sasl_password = config_node_get_str(node, "sasl_password", NULL); } static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node) @@ -56,7 +60,7 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node) return; if (rec->usermode != NULL) - iconfig_node_set_str(node, "usermode", rec->usermode); + iconfig_node_set_str(node, "usermode", rec->usermode); if (rec->max_cmds_at_once > 0) iconfig_node_set_int(node, "cmdmax", rec->max_cmds_at_once); @@ -73,12 +77,19 @@ static void sig_chatnet_saved(IRC_CHATNET_REC *rec, CONFIG_NODE *node) iconfig_node_set_int(node, "max_modes", rec->max_modes); if (rec->max_whois > 0) iconfig_node_set_int(node, "max_whois", rec->max_whois); + + if (rec->sasl_mechanism != NULL) + iconfig_node_set_str(node, "sasl_mechanism", rec->sasl_mechanism); + if (rec->sasl_username != NULL) + iconfig_node_set_str(node, "sasl_username", rec->sasl_username); + if (rec->sasl_password != NULL) + iconfig_node_set_str(node, "sasl_password", rec->sasl_password); } static void sig_chatnet_destroyed(IRC_CHATNET_REC *rec) { if (IS_IRC_CHATNET(rec)) - g_free(rec->usermode); + g_free(rec->usermode); } diff --git a/src/irc/core/irc-chatnets.h b/src/irc/core/irc-chatnets.h index 22da90c5..2bb10fa9 100644 --- a/src/irc/core/irc-chatnets.h +++ b/src/irc/core/irc-chatnets.h @@ -17,12 +17,15 @@ struct _IRC_CHATNET_REC { #include "chatnet-rec.h" - char *usermode; + char *usermode; + + char *sasl_mechanism; + char *sasl_username; + char *sasl_password; int max_cmds_at_once; int cmd_queue_speed; - int max_query_chans; /* when syncing, max. number of channels to - put in one MODE/WHO command */ + int max_query_chans; /* when syncing, max. number of channels to put in one MODE/WHO command */ /* max. number of kicks/msgs/mode/whois per command */ int max_kicks, max_msgs, max_modes, max_whois; diff --git a/src/irc/core/irc-core.c b/src/irc/core/irc-core.c index e3ceeeef..a9221e02 100644 --- a/src/irc/core/irc-core.c +++ b/src/irc/core/irc-core.c @@ -27,6 +27,7 @@ #include "irc-channels.h" #include "irc-queries.h" #include "irc-cap.h" +#include "sasl.h" #include "irc-servers-setup.h" #include "channels-setup.h" @@ -119,6 +120,7 @@ void irc_core_init(void) netsplit_init(); irc_expandos_init(); cap_init(); + sasl_init(); settings_check(); module_register("core", "irc"); @@ -128,6 +130,7 @@ void irc_core_deinit(void) { signal_emit("chat protocol deinit", 1, chat_protocol_find("IRC")); + sasl_deinit(); cap_deinit(); irc_expandos_deinit(); netsplit_deinit(); @@ -140,7 +143,7 @@ void irc_core_deinit(void) irc_irc_deinit(); irc_servers_deinit(); irc_chatnets_deinit(); - irc_session_deinit(); + irc_session_deinit(); chat_protocol_unregister("IRC"); } diff --git a/src/irc/core/irc-servers-setup.c b/src/irc/core/irc-servers-setup.c index 5659991b..f5e4f8f4 100644 --- a/src/irc/core/irc-servers-setup.c +++ b/src/irc/core/irc-servers-setup.c @@ -28,6 +28,7 @@ #include "irc-chatnets.h" #include "irc-servers-setup.h" #include "irc-servers.h" +#include "sasl.h" /* Fill information to connection from server setup record */ static void sig_server_setup_fill_reconn(IRC_SERVER_CONNECT_REC *conn, @@ -79,6 +80,29 @@ static void sig_server_setup_fill_chatnet(IRC_SERVER_CONNECT_REC *conn, conn->cmd_queue_speed = ircnet->cmd_queue_speed; if (ircnet->max_query_chans > 0) conn->max_query_chans = ircnet->max_query_chans; + + /* Validate the SASL parameters filled by sig_chatnet_read() */ + conn->sasl_mechanism = SASL_MECHANISM_NONE; + + if (ircnet->sasl_mechanism != NULL) { + if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "plain")) { + /* The PLAIN method needs both the username and the password */ + if (ircnet->sasl_username != NULL && *ircnet->sasl_username && + ircnet->sasl_password != NULL && *ircnet->sasl_password) { + conn->sasl_mechanism = SASL_MECHANISM_PLAIN; + conn->sasl_username = ircnet->sasl_username; + conn->sasl_password = ircnet->sasl_password; + } else + g_warning("The fields sasl_username and sasl_password are either missing or empty"); + } + else if (!g_ascii_strcasecmp(ircnet->sasl_mechanism, "external")) { + conn->sasl_mechanism = SASL_MECHANISM_EXTERNAL; + conn->sasl_username = NULL; + conn->sasl_password = NULL; + } + else + g_warning("Unsupported SASL mechanism \"%s\" selected", ircnet->sasl_mechanism); + } } static void init_userinfo(void) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index fdc5187f..82584382 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -33,6 +33,8 @@ #include "irc-servers-setup.h" #include "irc-servers.h" #include "irc-cap.h" +#include "sasl.h" + #include "channels-setup.h" #include "channel-rejoin.h" #include "servers-idle.h" @@ -223,6 +225,9 @@ static void server_init(IRC_SERVER_REC *server) g_free(cmd); } + if (conn->sasl_mechanism != SASL_MECHANISM_NONE) + cap_toggle(server, "sasl", TRUE); + irc_send_cmd_now(server, "CAP LS"); if (conn->password != NULL && *conn->password != '\0') { diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h index f809fab5..41c4b9c2 100644 --- a/src/irc/core/irc-servers.h +++ b/src/irc/core/irc-servers.h @@ -27,6 +27,10 @@ struct _IRC_SERVER_CONNECT_REC { char *usermode; char *alternate_nick; + int sasl_mechanism; + char *sasl_username; + char *sasl_password; + int max_cmds_at_once; int cmd_queue_speed; int max_query_chans; diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c new file mode 100644 index 00000000..a04eaf45 --- /dev/null +++ b/src/irc/core/sasl.c @@ -0,0 +1,178 @@ +/* + fe-sasl.c : irssi + + Copyright (C) 2015 The Lemon Man + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include "misc.h" +#include "settings.h" + +#include "irc-cap.h" +#include "irc-servers.h" +#include "sasl.h" + +#define SASL_TIMEOUT (20 * 1000) // ms + +static gboolean sasl_timeout(IRC_SERVER_REC *server) +{ + /* The authentication timed out, we can't do much beside terminating it */ + irc_send_cmd_now(server, "AUTHENTICATE *"); + cap_finish_negotiation(server); + + server->sasl_timeout = -1; + + signal_emit("server sasl failure", 2, server, "The authentication timed out"); + + return FALSE; +} + +static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from) +{ + IRC_SERVER_CONNECT_REC *conn; + + conn = server->connrec; + + switch (conn->sasl_mechanism) { + case SASL_MECHANISM_PLAIN: + irc_send_cmd_now(server, "AUTHENTICATE PLAIN"); + break; + + case SASL_MECHANISM_EXTERNAL: + irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL"); + break; + } + server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server); +} + +static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from) +{ + char *params, *error; + + /* Stop any pending timeout, if any */ + if (server->sasl_timeout != -1) { + g_source_remove(server->sasl_timeout); + server->sasl_timeout = -1; + } + + params = event_get_params(data, 2, NULL, &error); + + signal_emit("server sasl fail", 2, server, error); + + /* Terminate the negotiation */ + cap_finish_negotiation(server); + + g_free(params); +} + +static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from) +{ + if (server->sasl_timeout != -1) { + g_source_remove(server->sasl_timeout); + server->sasl_timeout = -1; + } + + signal_emit("server sasl success", 1, server); + + /* We're already authenticated, do nothing */ + cap_finish_negotiation(server); +} + +static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from) +{ + if (server->sasl_timeout != -1) { + g_source_remove(server->sasl_timeout); + server->sasl_timeout = -1; + } + + signal_emit("server sasl success", 1, server); + + /* The authentication succeeded, time to finish the CAP negotiation */ + cap_finish_negotiation(server); +} + +static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from) +{ + IRC_SERVER_CONNECT_REC *conn; + GString *req; + char *enc_req; + + conn = server->connrec; + + /* Stop the timer */ + if (server->sasl_timeout != -1) { + g_source_remove(server->sasl_timeout); + server->sasl_timeout = -1; + } + + switch (conn->sasl_mechanism) { + case SASL_MECHANISM_PLAIN: + /* At this point we assume that conn->sasl_{username, password} are non-NULL. + * The PLAIN mechanism expects a NULL-separated string composed by the authorization identity, the + * authentication identity and the password. + * The authorization identity field is explicitly set to the user provided username. + * The whole request is then encoded in base64. */ + + req = g_string_new(NULL); + + g_string_append(req, conn->sasl_username); + g_string_append_c(req, '\0'); + g_string_append(req, conn->sasl_username); + g_string_append_c(req, '\0'); + g_string_append(req, conn->sasl_password); + + enc_req = g_base64_encode((const guchar *)req->str, req->len); + + irc_send_cmdv(server, "AUTHENTICATE %s", enc_req); + + g_free(enc_req); + g_string_free(req, TRUE); + break; + + case SASL_MECHANISM_EXTERNAL: + /* Empty response */ + irc_send_cmdv(server, "+"); + break; + } + + /* We expect a response within a reasonable time */ + server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server); +} + +void sasl_init(void) +{ + signal_add_first("server cap ack sasl", (SIGNAL_FUNC) sasl_start); + signal_add_first("event authenticate", (SIGNAL_FUNC) sasl_step); + signal_add_first("event 903", (SIGNAL_FUNC) sasl_success); + signal_add_first("event 902", (SIGNAL_FUNC) sasl_fail); + signal_add_first("event 904", (SIGNAL_FUNC) sasl_fail); + signal_add_first("event 905", (SIGNAL_FUNC) sasl_fail); + signal_add_first("event 906", (SIGNAL_FUNC) sasl_fail); + signal_add_first("event 907", (SIGNAL_FUNC) sasl_already); +} + +void sasl_deinit(void) +{ + signal_remove("server cap ack sasl", (SIGNAL_FUNC) sasl_start); + signal_remove("event authenticate", (SIGNAL_FUNC) sasl_step); + signal_remove("event 903", (SIGNAL_FUNC) sasl_success); + signal_remove("event 902", (SIGNAL_FUNC) sasl_fail); + signal_remove("event 904", (SIGNAL_FUNC) sasl_fail); + signal_remove("event 905", (SIGNAL_FUNC) sasl_fail); + signal_remove("event 906", (SIGNAL_FUNC) sasl_fail); + signal_remove("event 907", (SIGNAL_FUNC) sasl_already); +} diff --git a/src/irc/core/sasl.h b/src/irc/core/sasl.h new file mode 100644 index 00000000..0693989d --- /dev/null +++ b/src/irc/core/sasl.h @@ -0,0 +1,34 @@ +/* + fe-sasl.c : irssi + + Copyright (C) 2015 The Lemon Man + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __SASL_H +#define __SASL_H + +enum { + SASL_MECHANISM_NONE = 0, + SASL_MECHANISM_PLAIN, + SASL_MECHANISM_EXTERNAL, + SASL_MECHANISM_MAX +}; + +void sasl_init(void); +void sasl_deinit(void); + +#endif |