summaryrefslogtreecommitdiffstats
path: root/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/test/unit/test_ntlm_proxy_and_web_auth.js')
-rw-r--r--netwerk/test/unit/test_ntlm_proxy_and_web_auth.js360
1 files changed, 360 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js
new file mode 100644
index 0000000000..0d8b2a327f
--- /dev/null
+++ b/netwerk/test/unit/test_ntlm_proxy_and_web_auth.js
@@ -0,0 +1,360 @@
+// Unit tests for a NTLM authenticated proxy, proxying for a NTLM authenticated
+// web server.
+//
+// Currently the tests do not determine whether the Authentication dialogs have
+// been displayed.
+//
+
+"use strict";
+
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+XPCOMUtils.defineLazyGetter(this, "URL", function () {
+ return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+function AuthPrompt() {}
+
+AuthPrompt.prototype = {
+ user: "guest",
+ pass: "guest",
+
+ QueryInterface: ChromeUtils.generateQI(["nsIAuthPrompt2"]),
+
+ promptAuth: function ap_promptAuth(channel, level, authInfo) {
+ authInfo.username = this.user;
+ authInfo.password = this.pass;
+
+ return true;
+ },
+
+ asyncPromptAuth: function ap_async(chan, cb, ctx, lvl, info) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
+ },
+};
+
+function Requestor() {}
+
+Requestor.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
+
+ getInterface: function requestor_gi(iid) {
+ if (iid.equals(Ci.nsIAuthPrompt2)) {
+ // Allow the prompt to store state by caching it here
+ if (!this.prompt) {
+ this.prompt = new AuthPrompt();
+ }
+ return this.prompt;
+ }
+
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+
+ prompt: null,
+};
+
+function makeChan(url, loadingUrl) {
+ var 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,
+ });
+}
+
+function TestListener(resolve) {
+ this.resolve = resolve;
+}
+TestListener.prototype.onStartRequest = function (request, context) {
+ // Need to do the instanceof to allow request.responseStatus
+ // to be read.
+ if (!(request instanceof Ci.nsIHttpChannel)) {
+ dump("Expecting an HTTP channel");
+ }
+
+ Assert.equal(expectedResponse, request.responseStatus, "HTTP Status code");
+};
+TestListener.prototype.onStopRequest = function (request, context, status) {
+ Assert.equal(expectedRequests, requestsMade, "Number of requests made ");
+
+ this.resolve();
+};
+TestListener.prototype.onDataAvaiable = function (
+ request,
+ context,
+ stream,
+ offset,
+ count
+) {
+ read_stream(stream, count);
+};
+
+// NTLM Messages, for the received type 1 and 3 messages only check that they
+// are of the expected type.
+const NTLM_TYPE1_PREFIX = "NTLM TlRMTVNTUAABAAAA";
+const NTLM_TYPE2_PREFIX = "NTLM TlRMTVNTUAACAAAA";
+const NTLM_TYPE3_PREFIX = "NTLM TlRMTVNTUAADAAAA";
+const NTLM_PREFIX_LEN = 21;
+
+const NTLM_CHALLENGE =
+ NTLM_TYPE2_PREFIX +
+ "DAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAAR" +
+ "ABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUg" +
+ "BWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwB" +
+ "lAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
+
+const PROXY_CHALLENGE =
+ NTLM_TYPE2_PREFIX +
+ "DgAOADgAAAAFgooCqLNOPe2aZOAAAAAAAAAAAFAAUABGAAAA" +
+ "BgEAAAAAAA9HAFcATAAtAE0ATwBaAAIADgBHAFcATAAtAE0A" +
+ "TwBaAAEADgBHAFcATAAtAE0ATwBaAAQAAgAAAAMAEgBsAG8A" +
+ "YwBhAGwAaABvAHMAdAAHAAgAOKEwGEZL0gEAAAAA";
+
+// Proxy and Web server responses for the happy path scenario.
+// i.e. successful proxy auth and successful web server auth
+//
+function authHandler(metadata, response) {
+ let authorization;
+ let authPrefix;
+ switch (requestsMade) {
+ case 0:
+ // Proxy - First request to the Proxy resppond with a 407 to start auth
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", "NTLM", false);
+ break;
+ case 1:
+ // Proxy - Expecting a type 1 negotiate message from the client
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false);
+ break;
+ case 2:
+ // Proxy - Expecting a type 3 Authenticate message from the client
+ // Will respond with a 401 to start web server auth sequence
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
+ response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+ response.setHeader("WWW-Authenticate", "NTLM", false);
+ break;
+ case 3:
+ // Web Server - Expecting a type 1 negotiate message from the client
+ authorization = metadata.getHeader("Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+ response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false);
+ break;
+ case 4:
+ // Web Server - Expecting a type 3 Authenticate message from the client
+ authorization = metadata.getHeader("Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
+ response.setStatusLine(metadata.httpVersion, 200, "Successful");
+ break;
+ default:
+ // We should be authenticated and further requests are permitted
+ authorization = metadata.getHeader("Authorization");
+ authorization = metadata.getHeader("Proxy-Authorization");
+ Assert.isnull(authorization);
+ response.setStatusLine(metadata.httpVersion, 200, "Successful");
+ }
+ requestsMade++;
+}
+
+// Proxy responses simulating an invalid proxy password
+// Note: that the connection should not be reused after the
+// proxy auth fails.
+//
+function authHandlerInvalidProxyPassword(metadata, response) {
+ let authorization;
+ let authPrefix;
+ switch (requestsMade) {
+ case 0:
+ // Proxy - First request respond with a 407 to initiate auth sequence
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", "NTLM", false);
+ break;
+ case 1:
+ // Proxy - Expecting a type 1 negotiate message from the client
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", PROXY_CHALLENGE, false);
+ break;
+ case 2:
+ // Proxy - Expecting a type 3 Authenticate message from the client
+ // Respond with a 407 to indicate invalid credentials
+ //
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", "NTLM", false);
+ break;
+ default:
+ // Strictly speaking the connection should not be reused at this point
+ // and reaching here should be an error, but have commented out for now
+ //dump( "ERROR: NTLM Proxy Authentication, connection should not be reused");
+ //Assert.fail();
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ }
+ requestsMade++;
+}
+
+// Proxy and web server responses simulating a successful Proxy auth
+// and a failed web server auth
+// Note: the connection should not be reused once the password failure is
+// detected
+function authHandlerInvalidWebPassword(metadata, response) {
+ let authorization;
+ let authPrefix;
+ switch (requestsMade) {
+ case 0:
+ // Proxy - First request return a 407 to start Proxy auth
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", "NTLM", false);
+ break;
+ case 1:
+ // Proxy - Expecting a type 1 negotiate message from the client
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 407, "Unauthorized");
+ response.setHeader("Proxy-Authenticate", NTLM_CHALLENGE, false);
+ break;
+ case 2:
+ // Proxy - Expecting a type 3 Authenticate message from the client
+ // Responds with a 401 to start web server auth
+ authorization = metadata.getHeader("Proxy-Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 3 message");
+ response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+ response.setHeader("WWW-Authenticate", "NTLM", false);
+ break;
+ case 3:
+ // Web Server - Expecting a type 1 negotiate message from the client
+ authorization = metadata.getHeader("Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE1_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+ response.setHeader("WWW-Authenticate", NTLM_CHALLENGE, false);
+ break;
+ case 4:
+ // Web Server - Expecting a type 3 Authenticate message from the client
+ // Respond with a 401 to restart the auth sequence.
+ authorization = metadata.getHeader("Authorization");
+ authPrefix = authorization.substring(0, NTLM_PREFIX_LEN);
+ Assert.equal(NTLM_TYPE3_PREFIX, authPrefix, "Expecting a Type 1 message");
+ response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
+ break;
+ default:
+ // We should not get called past step 4
+ Assert.ok(
+ false,
+ "ERROR: NTLM Auth failed connection should not be reused"
+ );
+ }
+ requestsMade++;
+}
+
+// Tests to run test_bad_proxy_pass and test_bad_web_pass are split into two stages
+// so that once we determine how detect password dialog displays we can check
+// that the are displayed correctly, i.e. proxy password should not be prompted
+// for when retrying the web server password
+
+var httpserver = null;
+function setup() {
+ httpserver = new HttpServer();
+ httpserver.start(-1);
+
+ 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.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();
+ });
+}
+setup();
+
+var expectedRequests = 0; // Number of HTTP requests that are expected
+var requestsMade = 0; // The number of requests that were made
+var expectedResponse = 0; // The response code
+// Note that any test failures in the HTTP handler
+// will manifest as a 500 response code
+
+// Common test setup
+// Parameters:
+// path - path component of the URL
+// handler - http handler function for the httpserver
+// requests - expected number oh http requests
+// response - expected http response code
+// clearCache - clear the authentication cache before running the test
+function setupTest(path, handler, requests, response, clearCache) {
+ requestsMade = 0;
+ expectedRequests = requests;
+ expectedResponse = response;
+
+ // clear the auth cache if requested
+ if (clearCache) {
+ dump("Clearing auth cache");
+ Cc["@mozilla.org/network/http-auth-manager;1"]
+ .getService(Ci.nsIHttpAuthManager)
+ .clearAll();
+ }
+
+ return new Promise(resolve => {
+ var chan = makeChan(URL + path, URL);
+ httpserver.registerPathHandler(path, handler);
+ chan.notificationCallbacks = new Requestor();
+ chan.asyncOpen(new TestListener(resolve));
+ });
+}
+
+// Happy code path
+// Succesful proxy and web server auth.
+add_task(async function test_happy_path() {
+ dump("RUNNING TEST: test_happy_path");
+ await setupTest("/auth", authHandler, 5, 200, 1);
+});
+
+// Failed proxy authentication
+add_task(async function test_bad_proxy_pass_stage01() {
+ dump("RUNNING TEST: test_bad_proxy_pass_stage01");
+ await setupTest("/auth", authHandlerInvalidProxyPassword, 4, 407, 1);
+});
+// Successful logon after failed proxy auth
+add_task(async function test_bad_proxy_pass_stage02() {
+ dump("RUNNING TEST: test_bad_proxy_pass_stage02");
+ await setupTest("/auth", authHandler, 5, 200, 0);
+});
+
+// successful proxy logon, unsuccessful web server sign on
+add_task(async function test_bad_web_pass_stage01() {
+ dump("RUNNING TEST: test_bad_web_pass_stage01");
+ await setupTest("/auth", authHandlerInvalidWebPassword, 5, 401, 1);
+});
+// successful logon after failed web server auth.
+add_task(async function test_bad_web_pass_stage02() {
+ dump("RUNNING TEST: test_bad_web_pass_stage02");
+ await setupTest("/auth", authHandler, 5, 200, 0);
+});