From 7dfe47c67006a9a4817e088ea43686a91f47f9d8 Mon Sep 17 00:00:00 2001 From: Jack Allnutt Date: Fri, 2 Nov 2012 18:52:04 +0000 Subject: [PATCH] SASL support Falls back to `PASS` command if `CAP` is not supported, the `sasl` capability is not supported or SASL authentication fails. Issue #110 --- server/irc/commands.js | 67 +++++++++++++++++++++++++++++++++++++--- server/irc/connection.js | 25 +++++++++++---- 2 files changed, 81 insertions(+), 11 deletions(-) diff --git a/server/irc/commands.js b/server/irc/commands.js index a47cb19..c5207eb 100644 --- a/server/irc/commands.js +++ b/server/irc/commands.js @@ -38,7 +38,14 @@ var irc_numerics = { ERR_BANNEDFROMCHAN: '474', ERR_BADCHANNELKEY: '475', ERR_CHANOPRIVSNEEDED: '482', - RPL_STARTTLS: '670' + RPL_STARTTLS: '670', + RPL_SASLAUTHENTICATED: '900', + RPL_SASLLOGGEDIN: '903', + ERR_SASLNOTAUTHORISED: '904', + ERR_SASLNOTAUTHORISED: '905', + ERR_SASLABORTED: '906', + ERR_SASLALREADYAUTHED: '907' + }; @@ -74,7 +81,6 @@ var listeners = { var nick = command.params[0]; this.irc_connection.registered = true; this.cap_negotation = false; - console.log(this.irc_connection.cap.enabled); this.client.sendIrcCommand('connect', {server: this.con_num, nick: nick}); }, 'RPL_ISUPPORT': function (command) { @@ -436,8 +442,12 @@ var listeners = { // TODO: capability modifiers // i.e. - for disable, ~ for requires ACK, = for sticky var capabilities = command.trailing.replace(/[\-~=]/, '').split(' '); - var want = ['multi-prefix']; var request; + var want = ['multi-prefix']; + + if (this.irc_connection.password) { + want.push('sasl'); + } switch (command.params[1]) { case 'LS': @@ -448,6 +458,7 @@ var listeners = { } else { this.irc_connection.write('CAP END'); this.irc_connection.cap_negotation = false; + this.irc_connection.register(); } break; case 'ACK': @@ -456,8 +467,14 @@ var listeners = { this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities); } if (this.irc_connection.cap.requested.length > 0) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotation = false; + if (_.contains(this.irc_connection.cap.enabled, 'sasl')) { + this.irc_connection.sasl = true; + this.irc_connection.write('AUTHENTICATE PLAIN'); + } else { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotation = false; + this.irc_connection.register(); + } } break; case 'NAK': @@ -467,6 +484,7 @@ var listeners = { if (this.irc_connection.cap.requested.length > 0) { this.irc_connection.write('CAP END'); this.irc_connection.cap_negotation = false; + this.irc_connection.register(); } break; case 'LIST': @@ -474,6 +492,45 @@ var listeners = { break; } }, + 'AUTHENTICATE': function (command) { + var b = new Buffer(this.irc_connection.nick + "\0" + this.irc_connection.nick + "\0" + this.irc_connection.password, 'utf8'); + var b64 = b.toString('base64'); + if (command.params[0] === '+') { + while (b64.length >= 400) { + this.irc_connection.write('AUTHENTICATE ' + b64.slice(0, 399)); + b64 = b64.slice(399); + } + if (b64.length > 0) { + this.irc_connection.write('AUTHENTICATE ' + b64); + } else { + this.irc_connection.write('AUTHENTICATE +'); + } + } else { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotation = false; + this.irc_connection.register(); + } + }, + 'RPL_SASLAUTHENTICATED':function (command) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotation = false; + this.irc_connection.register(); + }, + 'RPL_SASLLOGGEDIN': function (command) { + // noop + }, + 'ERR_SASLNOTAUTHORISED':function (command) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotation = false; + this.irc_connection.cap.enabled = _.without(this.irc_connection.cap.enabled, 'sasl'); + this.irc_connection.register(); + }, + 'ERR_SASLABORTED': function (command) { + this.irc_connection.cap.enabled = _.without(this.irc_connection.cap.enabled, 'sasl'); + }, + 'ERR_SASLALREADYAUTHED':function (command) { + // noop + }, 'ERROR': function (command) { /*command.server = this.con_num; command.command = 'ERROR'; diff --git a/server/irc/connection.js b/server/irc/connection.js index 916d7cd..28e6519 100644 --- a/server/irc/connection.js +++ b/server/irc/connection.js @@ -40,6 +40,7 @@ var IrcConnection = function (hostname, port, ssl, nick, user, pass) { this.ssl = !(!ssl); this.options = Object.create(null); this.cap = {requested: [], enabled: []}; + this.sasl = false; this.password = pass; this.hold_last = false; @@ -95,18 +96,30 @@ var connect_handler = function () { }); } - if (this.password) { - this.write('PASS ' + this.password); - } - this.write('CAP LS'); - this.write('NICK ' + connect_data.nick); - this.write('USER ' + connect_data.username + ' 0 0 :' + connect_data.realname); + + this.registration_timeout = setTimeout(function () { + that.register.call(that); + }, 1000); this.connected = true; this.emit('connected'); }; +IrcConnection.prototype.register = function () { + if (this.registration_timeout !== null) { + clearTimeout(this.registeration_timeout); + this.registration_timeout = null; + } + if ((this.password) && (!_.contains(this.cap.enabled, 'sasl'))) { + this.write('PASS ' + this.password); + } + this.write('NICK ' + this.nick); + this.write('USER ' + this.nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + '[www.kiwiirc.com] ' + this.nick); + if (this.cap_negotation) { + this.write('CAP END'); + } +}; -- 2.25.1