Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* 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/. */
// A proxy-then-web NTLM sequence like test_ntlm_proxy_and_web_auth.js, but
// without network.auth.force-generic-ntlm, so it runs against the native module
// (nsAuthSSPI) rather than the internal one.
//
// This covers what nothing else does: nsHttpNTLMAuth::mUseNative, and an
// nsAuthSSPI instance created on the main thread in ChallengeReceived whose
// Init and GetNextToken then run on a background thread -- the reason that
// class needs threadsafe refcounting.
//
// Scope note: the sequence stops after the type 1 message. Answering a type 2
// challenge makes SSPI derive a response from whoever is logged in, and on a
// machine with no credentials for the domain named in the challenge it fails
// with SEC_E_NO_CREDENTIALS. That is exactly why the other proxy tests set
// force-generic-ntlm. Everything up to and including the type 1 message is
// machine independent, so that is what is asserted here; the type 3 step stays
// covered by the generic tests.
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
);
ChromeUtils.defineLazyGetter(this, "URL", function () {
return "http://localhost:" + httpserver.identity.primaryPort;
});
// The host is trusted for automatic NTLM, so the native module uses default
// credentials and reports the identity as valid. Nothing may ask for a prompt;
// the internal module would, which is what distinguishes the two here.
function Requestor() {}
Requestor.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
getInterface(iid) {
if (iid.equals(Ci.nsIAuthPrompt2)) {
Assert.ok(false, "should not be prompted when using default credentials");
}
throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
},
};
function makeChan(url, loadingUrl) {
let principal = Services.scriptSecurityManager.createContentPrincipal(
Services.io.newURI(loadingUrl),
{}
);
return NetUtil.newChannel({
uri: url,
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
});
}
const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA";
const NTLM_PREFIX_LEN = 21;
let httpserver = null;
let requestsMade = 0;
function assertTypeOneMessage(metadata, header, description) {
Assert.ok(metadata.hasHeader(header), `${description}: ${header} present`);
let authorization = metadata.getHeader(header);
Assert.equal(
authorization.substring(0, NTLM_PREFIX_LEN),
NTLM_TYPE1_PREFIX,
`${description}: is a type 1 message`
);
// A type 1 message carries the negotiate flags and the workstation/domain of
// whoever is logged in, so it is always longer than the bare prefix.
Assert.greater(
authorization.length,
NTLM_PREFIX_LEN,
`${description}: token is not empty`
);
}
// The proxy challenges, accepts the type 1 message, and hands over to the web
// server, which does the same.
function authHandler(metadata, response) {
switch (requestsMade) {
case 0:
response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
response.setHeader("Proxy-Authenticate", "NTLM", false);
break;
case 1:
assertTypeOneMessage(metadata, "Proxy-Authorization", "proxy");
response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
response.setHeader("WWW-Authenticate", "NTLM", false);
break;
case 2:
assertTypeOneMessage(metadata, "Authorization", "web server");
response.setStatusLine(metadata.httpVersion, 200, "Successful");
break;
default:
response.setStatusLine(metadata.httpVersion, 200, "Successful");
}
requestsMade++;
}
add_setup(async function setup() {
httpserver = new HttpServer();
httpserver.start(-1);
// Trust localhost for automatic NTLM so the native module uses the logged-in
// user's credentials instead of prompting.
Services.prefs.setCharPref(
"network.automatic-ntlm-auth.trusted-uris",
"localhost"
);
Services.prefs.setBoolPref("network.automatic-ntlm-auth.allow-proxies", true);
Services.prefs.setCharPref("network.proxy.http", "localhost");
Services.prefs.setIntPref(
"network.proxy.http_port",
httpserver.identity.primaryPort
);
Services.prefs.setCharPref("network.proxy.no_proxies_on", "");
Services.prefs.setIntPref("network.proxy.type", 1);
Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
registerCleanupFunction(async () => {
Services.prefs.clearUserPref("network.automatic-ntlm-auth.trusted-uris");
Services.prefs.clearUserPref("network.automatic-ntlm-auth.allow-proxies");
Services.prefs.clearUserPref("network.proxy.http");
Services.prefs.clearUserPref("network.proxy.http_port");
Services.prefs.clearUserPref("network.proxy.no_proxies_on");
Services.prefs.clearUserPref("network.proxy.type");
Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
await httpserver.stop();
});
});
add_task(async function test_native_ntlm_proxy_and_web_auth() {
requestsMade = 0;
Cc["@mozilla.org/network/http-auth-manager;1"]
.getService(Ci.nsIHttpAuthManager)
.clearAll();
httpserver.registerPathHandler("/auth", authHandler);
let chan = makeChan(URL + "/auth", URL);
chan.notificationCallbacks = new Requestor();
let request = await new Promise(resolve => {
chan.asyncOpen(new ChannelListener(req => resolve(req)));
});
Assert.equal(
request.QueryInterface(Ci.nsIHttpChannel).responseStatus,
200,
"both the proxy and the server accepted the native credentials"
);
Assert.equal(requestsMade, 3, "expected number of requests");
});