summaryrefslogtreecommitdiffstats
path: root/netwerk/test/unit/test_tls_flags.js
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/test/unit/test_tls_flags.js')
-rw-r--r--netwerk/test/unit/test_tls_flags.js274
1 files changed, 274 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_tls_flags.js b/netwerk/test/unit/test_tls_flags.js
new file mode 100644
index 0000000000..669465560a
--- /dev/null
+++ b/netwerk/test/unit/test_tls_flags.js
@@ -0,0 +1,274 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+"use strict";
+
+// a fork of test_be_conservative
+
+// Tests that nsIHttpChannelInternal.tlsFlags can be used to set the
+// client max version level. Flags can also be used to set the
+// level of intolerance rollback and to test out an experimental 1.3
+// hello, though they are not tested here.
+
+// Get a profile directory and ensure PSM initializes NSS.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+function getCert() {
+ return new Promise((resolve, reject) => {
+ let certService = Cc[
+ "@mozilla.org/security/local-cert-service;1"
+ ].getService(Ci.nsILocalCertService);
+ certService.getOrCreateCert("tlsflags-test", {
+ handleCert(c, rv) {
+ if (rv) {
+ reject(rv);
+ return;
+ }
+ resolve(c);
+ },
+ });
+ });
+}
+
+class InputStreamCallback {
+ constructor(output) {
+ this.output = output;
+ this.stopped = false;
+ }
+
+ onInputStreamReady(stream) {
+ info("input stream ready");
+ if (this.stopped) {
+ info("input stream callback stopped - bailing");
+ return;
+ }
+ let available = 0;
+ try {
+ available = stream.available();
+ } catch (e) {
+ // onInputStreamReady may fire when the stream has been closed.
+ equal(
+ e.result,
+ Cr.NS_BASE_STREAM_CLOSED,
+ "error should be NS_BASE_STREAM_CLOSED"
+ );
+ }
+ if (available > 0) {
+ let request = NetUtil.readInputStreamToString(stream, available, {
+ charset: "utf8",
+ });
+ ok(
+ request.startsWith("GET / HTTP/1.1\r\n"),
+ "Should get a simple GET / HTTP/1.1 request"
+ );
+ let response =
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Length: 2\r\n" +
+ "Content-Type: text/plain\r\n" +
+ "\r\nOK";
+ let written = this.output.write(response, response.length);
+ equal(
+ written,
+ response.length,
+ "should have been able to write entire response"
+ );
+ }
+ this.output.close();
+ info("done with input stream ready");
+ }
+
+ stop() {
+ this.stopped = true;
+ this.output.close();
+ }
+}
+
+class TLSServerSecurityObserver {
+ constructor(input, output, expectedVersion) {
+ this.input = input;
+ this.output = output;
+ this.expectedVersion = expectedVersion;
+ this.callbacks = [];
+ this.stopped = false;
+ }
+
+ onHandshakeDone(socket, status) {
+ info("TLS handshake done");
+ info(`TLS version used: ${status.tlsVersionUsed}`);
+ info(this.expectedVersion);
+ equal(
+ status.tlsVersionUsed,
+ this.expectedVersion,
+ "expected version check"
+ );
+ if (this.stopped) {
+ info("handshake done callback stopped - bailing");
+ return;
+ }
+
+ let callback = new InputStreamCallback(this.output);
+ this.callbacks.push(callback);
+ this.input.asyncWait(callback, 0, 0, Services.tm.currentThread);
+ }
+
+ stop() {
+ this.stopped = true;
+ this.input.close();
+ this.output.close();
+ this.callbacks.forEach(callback => {
+ callback.stop();
+ });
+ }
+}
+
+function startServer(
+ cert,
+ minServerVersion,
+ maxServerVersion,
+ expectedVersion
+) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+ tlsServer.setVersionRange(minServerVersion, maxServerVersion);
+ tlsServer.setSessionTickets(false);
+
+ let listener = {
+ securityObservers: [],
+
+ onSocketAccepted(socket, transport) {
+ info("accepted TLS client connection");
+ let connectionInfo = transport.securityInfo.QueryInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ let input = transport.openInputStream(0, 0, 0);
+ let output = transport.openOutputStream(0, 0, 0);
+ let securityObserver = new TLSServerSecurityObserver(
+ input,
+ output,
+ expectedVersion
+ );
+ this.securityObservers.push(securityObserver);
+ connectionInfo.setSecurityObserver(securityObserver);
+ },
+
+ // For some reason we get input stream callback events after we've stopped
+ // listening, so this ensures we just drop those events.
+ onStopListening() {
+ info("onStopListening");
+ this.securityObservers.forEach(observer => {
+ observer.stop();
+ });
+ },
+ };
+ tlsServer.asyncListen(listener);
+ return tlsServer;
+}
+
+const hostname = "example.com";
+
+function storeCertOverride(port, cert) {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+ let overrideBits =
+ Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+ Ci.nsICertOverrideService.ERROR_MISMATCH;
+ certOverrideService.rememberValidityOverride(
+ hostname,
+ port,
+ cert,
+ overrideBits,
+ true
+ );
+}
+
+function startClient(port, tlsFlags, expectSuccess) {
+ let req = new XMLHttpRequest();
+ req.open("GET", `https://${hostname}:${port}`);
+ let internalChannel = req.channel.QueryInterface(Ci.nsIHttpChannelInternal);
+ internalChannel.tlsFlags = tlsFlags;
+ return new Promise((resolve, reject) => {
+ req.onload = () => {
+ ok(
+ expectSuccess,
+ `should ${expectSuccess ? "" : "not "}have gotten load event`
+ );
+ equal(req.responseText, "OK", "response text should be 'OK'");
+ resolve();
+ };
+ req.onerror = () => {
+ ok(
+ !expectSuccess,
+ `should ${!expectSuccess ? "" : "not "}have gotten an error`
+ );
+ resolve();
+ };
+
+ req.send();
+ });
+}
+
+add_task(async function() {
+ Services.prefs.setIntPref("security.tls.version.max", 4);
+ Services.prefs.setCharPref("network.dns.localDomains", hostname);
+ let cert = await getCert();
+
+ // server that accepts 1.1->1.3 and a client max 1.3. expect 1.3
+ info("TEST 1");
+ let server = startServer(
+ cert,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_1,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_3,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_3
+ );
+ storeCertOverride(server.port, cert);
+ await startClient(server.port, 4, true /*should succeed*/);
+ server.close();
+
+ // server that accepts 1.1->1.3 and a client max 1.1. expect 1.1
+ info("TEST 2");
+ server = startServer(
+ cert,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_1,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_3,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_1
+ );
+ storeCertOverride(server.port, cert);
+ await startClient(server.port, 2, true);
+ server.close();
+
+ // server that accepts 1.2->1.2 and a client max 1.3. expect 1.2
+ info("TEST 3");
+ server = startServer(
+ cert,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_2
+ );
+ storeCertOverride(server.port, cert);
+ await startClient(server.port, 4, true);
+ server.close();
+
+ // server that accepts 1.2->1.2 and a client max 1.1. expect fail
+ info("TEST 4");
+ server = startServer(
+ cert,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ 0
+ );
+ storeCertOverride(server.port, cert);
+ await startClient(server.port, 2, false);
+
+ server.close();
+});
+
+registerCleanupFunction(function() {
+ Services.prefs.clearUserPref("security.tls.version.max");
+ Services.prefs.clearUserPref("network.dns.localDomains");
+});