Revision control
Copy as Markdown
Other Tools
/* 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,
/**
* Tests that AUTH NTLM for SMTP fails the login, rather than stalling, when the
* server sends a challenge the NTLM auth module rejects, when the auth module
* can't be initialized, or when the user cancels the password prompt.
*/
var { MailServices } = ChromeUtils.importESModule(
);
var { MockRegistrar } = ChromeUtils.importESModule(
);
var { PromiseTestUtils } = ChromeUtils.importESModule(
);
/* import-globals-from ../../../test/resources/alertTestUtils.js */
load("../../../resources/alertTestUtils.js");
// The challenge the server sends in response to AUTH NTLM.
var gChallenge;
var gAuthFailedPrompts = 0;
var gPasswordPrompts = 0;
var gServer;
var gSmtpServer;
var gIdentity;
/* exported alert, confirmExPS, promptPasswordPS */
// for alertTestUtils.js
function alert() {}
function confirmExPS() {
gAuthFailedPrompts++;
// Cancel.
return 1;
}
function promptPasswordPS() {
gPasswordPrompts++;
// Cancel.
return false;
}
add_setup(function () {
gServer = setupServerDaemon(daemon => {
const handler = new SMTP_RFC2821_handler(daemon);
handler.kAuthRequired = true;
handler.kAuthSchemes = ["NTLM"];
handler._kAuthSchemeStartFunction.NTLM = function () {
this._nextAuthFunction = () => "535 Unexpected NTLM response";
this._multiline = true;
return `334 ${gChallenge}`;
};
handler.resetTest();
return handler;
});
gServer.start();
registerAlertTestUtils();
localAccountUtils.loadLocalMailAccount();
gSmtpServer = getBasicSmtpServer(gServer.port);
gSmtpServer.authMethod = Ci.nsMsgAuthMethod.NTLM;
gSmtpServer.username = "testsmtp";
gSmtpServer.password = "smtptest";
gIdentity = getSmtpIdentity("identity@foo.invalid", gSmtpServer);
registerCleanupFunction(() => gServer.stop());
});
/**
* Sends a message, which should fail.
*
* @param {string} description - What the test is about.
* @returns {string[]} The commands the server received.
*/
async function sendMessageAndFail(description) {
gAuthFailedPrompts = 0;
gServer.resetTest();
const messageId = Cc["@mozilla.org/messengercompose/computils;1"]
.createInstance(Ci.nsIMsgCompUtils)
.msgGenerateMessageId(gIdentity, null);
const listener = new PromiseTestUtils.PromiseMsgOutgoingListener();
gSmtpServer.sendMailMessage(
do_get_file("data/message1.eml"),
MailServices.headerParser.parseEncodedHeaderW("to@foo.invalid"),
[],
gIdentity,
"from@foo.invalid",
null,
null,
false,
messageId,
listener
);
await Assert.rejects(
listener.promise,
/./,
`sending should fail ${description}`
);
gSmtpServer.closeCachedConnections();
let transaction = gServer.playTransaction();
if (Array.isArray(transaction)) {
transaction = transaction.at(-1);
}
return transaction.them;
}
/**
* Sends a message and checks that the login failed.
*
* @param {string} challenge - The challenge the server should send.
*/
async function checkLoginFails(challenge) {
gChallenge = challenge;
await sendMessageAndFail(`for challenge ${challenge}`);
Assert.equal(
gAuthFailedPrompts,
1,
`the user should be asked once what to do for challenge ${challenge}`
);
}
add_task(async function testPaddingOnlyChallenge() {
await checkLoginFails("====");
});
add_task(async function testInvalidChallenge() {
await checkLoginFails("!!!!");
});
add_task(async function testInitFailure() {
const mockCid = MockRegistrar.register("@mozilla.org/mail/auth-module;1", {
QueryInterface: ChromeUtils.generateQI(["nsIMailAuthModule"]),
init() {
throw Components.Exception("", Cr.NS_ERROR_FAILURE);
},
});
try {
const them = await sendMessageAndFail("when the auth module fails");
Assert.equal(
gAuthFailedPrompts,
1,
"the user should be asked once what to do when the auth module fails"
);
Assert.ok(
!them.some(command => command.startsWith("AUTH NTLM")),
"AUTH NTLM should not be sent when the auth module fails"
);
} finally {
MockRegistrar.unregister(mockCid);
}
});
add_task(async function testPasswordPromptCancelled() {
gSmtpServer.password = "";
gPasswordPrompts = 0;
try {
const them = await sendMessageAndFail(
"when the password prompt is cancelled"
);
Assert.equal(gPasswordPrompts, 1, "the password should be asked for");
Assert.equal(
gAuthFailedPrompts,
0,
"the user should not be asked what to do after cancelling"
);
Assert.ok(
!them.some(command => command.startsWith("AUTH NTLM")),
"AUTH NTLM should not be sent after cancelling"
);
} finally {
gSmtpServer.password = "smtptest";
}
});