184 строки
5.6 KiB
JavaScript
184 строки
5.6 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* This implements SASL for IRC.
|
|
* https://raw.github.com/atheme/atheme/master/doc/SASL
|
|
* https://ircv3.net/specs/extensions/sasl-3.2
|
|
*/
|
|
|
|
const EXPORTED_SYMBOLS = ["ircSASL", "capSASL"];
|
|
|
|
const { ircHandlers } = ChromeUtils.import(
|
|
"resource:///modules/ircHandlers.jsm"
|
|
);
|
|
|
|
var ircSASL = {
|
|
name: "SASL AUTHENTICATE",
|
|
priority: ircHandlers.DEFAULT_PRIORITY,
|
|
isEnabled() {
|
|
return this._activeCAPs.has("sasl");
|
|
},
|
|
|
|
commands: {
|
|
AUTHENTICATE(aMessage) {
|
|
// Expect an empty response, if something different is received abort.
|
|
if (aMessage.params[0] != "+") {
|
|
this.sendMessage("AUTHENTICATE", "*");
|
|
this.WARN(
|
|
"Aborting SASL authentication, unexpected message " +
|
|
"received:\n" +
|
|
aMessage.rawMessage
|
|
);
|
|
return true;
|
|
}
|
|
|
|
// An authentication identity, authorization identity and password are
|
|
// used, separated by null.
|
|
let data = [
|
|
this._requestedNickname,
|
|
this._requestedNickname,
|
|
this.imAccount.password,
|
|
].join("\0");
|
|
// btoa for Unicode, see https://developer.mozilla.org/en-US/docs/DOM/window.btoa
|
|
let base64Data = btoa(unescape(encodeURIComponent(data)));
|
|
this.sendMessage(
|
|
"AUTHENTICATE",
|
|
base64Data,
|
|
"AUTHENTICATE <base64 encoded nick, user and password not logged>"
|
|
);
|
|
return true;
|
|
},
|
|
|
|
"900": function(aMessage) {
|
|
// RPL_LOGGEDIN
|
|
// <nick>!<ident>@<host> <account> :You are now logged in as <user>
|
|
// Now logged in ("whether by SASL or otherwise").
|
|
this.isAuthenticated = true;
|
|
return true;
|
|
},
|
|
|
|
"901": function(aMessage) {
|
|
// RPL_LOGGEDOUT
|
|
// The user's account name is unset (whether by SASL or otherwise).
|
|
this.isAuthenticated = false;
|
|
return true;
|
|
},
|
|
|
|
"902": function(aMessage) {
|
|
// ERR_NICKLOCKED
|
|
// Authentication failed because the account is currently locked out,
|
|
// held, or otherwise administratively made unavailable.
|
|
this.WARN(
|
|
"You must use a nick assigned to you. SASL authentication failed."
|
|
);
|
|
this.removeCAP("sasl");
|
|
return true;
|
|
},
|
|
|
|
"903": function(aMessage) {
|
|
// RPL_SASLSUCCESS
|
|
// Authentication was successful.
|
|
this.isAuthenticated = true;
|
|
this.LOG("SASL authentication successful.");
|
|
// We may receive this again while already connected if the user manually
|
|
// identifies with Nickserv.
|
|
if (!this.connected) {
|
|
this.removeCAP("sasl");
|
|
}
|
|
return true;
|
|
},
|
|
|
|
"904": function(aMessage) {
|
|
// ERR_SASLFAIL
|
|
// Sent when the SASL authentication fails because of invalid credentials
|
|
// or other errors not explicitly mentioned by other numerics.
|
|
this.WARN("Authentication with SASL failed.");
|
|
this.removeCAP("sasl");
|
|
return true;
|
|
},
|
|
|
|
"905": function(aMessage) {
|
|
// ERR_SASLTOOLONG
|
|
// Sent when credentials are valid, but the SASL authentication fails
|
|
// because the client-sent `AUTHENTICATE` command was too long.
|
|
this.ERROR("SASL: AUTHENTICATE command was too long.");
|
|
this.removeCAP("sasl");
|
|
return true;
|
|
},
|
|
|
|
"906": function(aMessage) {
|
|
// ERR_SASLABORTED
|
|
// The client completed registration before SASL authentication completed,
|
|
// or because we sent `AUTHENTICATE` with `*` as the parameter.
|
|
//
|
|
// Freenode sends 906 in addition to 904, ignore 906 in this case.
|
|
if (this._requestedCAPs.has("sasl")) {
|
|
this.ERROR(
|
|
"Registration completed before SASL authentication completed."
|
|
);
|
|
this.removeCAP("sasl");
|
|
}
|
|
return true;
|
|
},
|
|
|
|
"907": function(aMessage) {
|
|
// ERR_SASLALREADY
|
|
// Response if client attempts to AUTHENTICATE after successful
|
|
// authentication.
|
|
this.ERROR("Attempting SASL authentication twice?!");
|
|
this.removeCAP("sasl");
|
|
return true;
|
|
},
|
|
|
|
"908": function(aMessage) {
|
|
// RPL_SASLMECHS
|
|
// <nick> <mechanisms> :are available SASL mechanisms
|
|
// List of SASL mechanisms supported by the server (or network, services).
|
|
// The numeric contains a comma-separated list of mechanisms.
|
|
return false;
|
|
},
|
|
},
|
|
};
|
|
|
|
var capSASL = {
|
|
name: "SASL CAP",
|
|
priority: ircHandlers.DEFAULT_PRIORITY,
|
|
isEnabled: () => true,
|
|
|
|
commands: {
|
|
sasl(aMessage) {
|
|
// Return early if we are already authenticated (can happen due to cap-notify)
|
|
if (this.isAuthenticated) {
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
(aMessage.cap.subcommand === "LS" ||
|
|
aMessage.cap.subcommand === "NEW") &&
|
|
this.imAccount.password
|
|
) {
|
|
if (aMessage.cap.value) {
|
|
const mechanisms = aMessage.cap.value.split(",");
|
|
// We only support the plain authentication mechanism for now, abort if it's not available.
|
|
if (!mechanisms.includes("PLAIN")) {
|
|
return true;
|
|
}
|
|
}
|
|
// If it supports SASL, let the server know we're requiring SASL.
|
|
this.addCAP("sasl");
|
|
this.sendMessage("CAP", ["REQ", "sasl"]);
|
|
} else if (aMessage.cap.subcommand === "ACK") {
|
|
// The server acknowledges our choice to use SASL, send the first
|
|
// message.
|
|
this.sendMessage("AUTHENTICATE", "PLAIN");
|
|
} else if (aMessage.cap.subcommand === "NAK") {
|
|
this.removeCAP("sasl");
|
|
}
|
|
|
|
return true;
|
|
},
|
|
},
|
|
};
|