diff options
Diffstat (limited to 'devtools/shared/security/tests/xpcshell')
6 files changed, 501 insertions, 0 deletions
diff --git a/devtools/shared/security/tests/xpcshell/.eslintrc.js b/devtools/shared/security/tests/xpcshell/.eslintrc.js new file mode 100644 index 0000000000..8611c174f5 --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the common devtools xpcshell eslintrc config. + extends: "../../../../.eslintrc.xpcshell.js", +}; diff --git a/devtools/shared/security/tests/xpcshell/head_dbg.js b/devtools/shared/security/tests/xpcshell/head_dbg.js new file mode 100644 index 0000000000..6adf9bdd1b --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/head_dbg.js @@ -0,0 +1,95 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* exported defer, DevToolsClient, initTestDevToolsServer */ + +const { loader, require } = ChromeUtils.import( + "resource://devtools/shared/Loader.jsm" +); +const defer = require("devtools/shared/defer"); +const Services = require("Services"); +const xpcInspector = require("xpcInspector"); +const { DevToolsServer } = require("devtools/server/devtools-server"); +const { DevToolsClient } = require("devtools/client/devtools-client"); +// We need to require lazily since will be crashed if we load SocketListener too early +// in xpc shell test due to SocketListener loads PSM module. +loader.lazyRequireGetter( + this, + "SocketListener", + "devtools/shared/security/socket", + true +); + +// We do not want to log packets by default, because in some tests, +// we can be sending large amounts of data. The test harness has +// trouble dealing with logging all the data, and we end up with +// intermittent time outs (e.g. bug 775924). +// Services.prefs.setBoolPref("devtools.debugger.log", true); +// Services.prefs.setBoolPref("devtools.debugger.log.verbose", true); +// Enable remote debugging for the relevant tests. +Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true); +// Fast timeout for TLS tests +Services.prefs.setIntPref("devtools.remote.tls-handshake-timeout", 1000); + +// Convert an nsIScriptError 'logLevel' value into an appropriate string. +function scriptErrorLogLevel(message) { + switch (message.logLevel) { + case Ci.nsIConsoleMessage.info: + return "info"; + case Ci.nsIConsoleMessage.warn: + return "warning"; + default: + Assert.equal(message.logLevel, Ci.nsIConsoleMessage.error); + return "error"; + } +} + +// Register a console listener, so console messages don't just disappear +// into the ether. +var listener = { + observe: function(message) { + let string; + try { + message.QueryInterface(Ci.nsIScriptError); + dump( + message.sourceName + + ":" + + message.lineNumber + + ": " + + scriptErrorLogLevel(message) + + ": " + + message.errorMessage + + "\n" + ); + string = message.errorMessage; + } catch (ex) { + // Be a little paranoid with message, as the whole goal here is to lose + // no information. + try { + string = "" + message.message; + } catch (e) { + string = "<error converting error message to string>"; + } + } + + // Make sure we exit all nested event loops so that the test can finish. + while (xpcInspector.eventLoopNestLevel > 0) { + xpcInspector.exitNestedEventLoop(); + } + + info("head_dbg.js got console message: " + string + "\n"); + }, +}; + +Services.console.registerListener(listener); + +/** + * Initialize the testing devtools server. + */ +function initTestDevToolsServer() { + const { createRootActor } = require("xpcshell-test/testactors"); + DevToolsServer.setRootActor(createRootActor); + DevToolsServer.init(); +} diff --git a/devtools/shared/security/tests/xpcshell/test_encryption.js b/devtools/shared/security/tests/xpcshell/test_encryption.js new file mode 100644 index 0000000000..1721418a5b --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/test_encryption.js @@ -0,0 +1,106 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test basic functionality of DevTools client and server TLS encryption mode +function run_test() { + // Need profile dir to store the key / cert + do_get_profile(); + // Ensure PSM is initialized + Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); + run_next_test(); +} + +function connectClient(client) { + return client.connect(); +} + +add_task(async function() { + initTestDevToolsServer(); +}); + +// Client w/ encryption connects successfully to server w/ encryption +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + const AuthenticatorType = DevToolsServer.Authenticators.get("PROMPT"); + const authenticator = new AuthenticatorType.Server(); + authenticator.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + + const socketOptions = { + authenticator, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + const transport = await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + encryption: true, + }); + ok(transport, "Client transport created"); + + const client = new DevToolsClient(transport); + const onUnexpectedClose = () => { + do_throw("Closed unexpectedly"); + }; + client.on("closed", onUnexpectedClose); + await connectClient(client); + + // Send a message the server will echo back + const message = "secrets"; + const reply = await client.mainRoot.echo({ message }); + equal(reply.message, message, "Encrypted echo matches"); + + client.off("closed", onUnexpectedClose); + transport.close(); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); +}); + +// Client w/o encryption fails to connect to server w/ encryption +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + const AuthenticatorType = DevToolsServer.Authenticators.get("PROMPT"); + const authenticator = new AuthenticatorType.Server(); + authenticator.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + + const socketOptions = { + authenticator, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + try { + await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + // encryption: false is the default + }); + } catch (e) { + ok(true, "Client failed to connect as expected"); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + return; + } + + do_throw("Connection unexpectedly succeeded"); +}); + +add_task(async function() { + DevToolsServer.destroy(); +}); diff --git a/devtools/shared/security/tests/xpcshell/test_oob_cert_auth.js b/devtools/shared/security/tests/xpcshell/test_oob_cert_auth.js new file mode 100644 index 0000000000..229db1e62f --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/test_oob_cert_auth.js @@ -0,0 +1,267 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const cert = require("devtools/shared/security/cert"); + +// Test basic functionality of DevTools client and server OOB_CERT auth (used +// with WiFi debugging) +function run_test() { + // Need profile dir to store the key / cert + do_get_profile(); + // Ensure PSM is initialized + Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports); + run_next_test(); +} + +function connectClient(client) { + return client.connect(); +} + +add_task(async function() { + initTestDevToolsServer(); +}); + +// Client w/ OOB_CERT auth connects successfully to server w/ OOB_CERT auth +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + // Grab our cert, instead of relying on a discovery advertisement + const serverCert = await cert.local.getOrCreate(); + + const oobData = defer(); + const AuthenticatorType = DevToolsServer.Authenticators.get("OOB_CERT"); + const serverAuth = new AuthenticatorType.Server(); + serverAuth.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + // Skip prompt for tests + serverAuth.receiveOOB = () => oobData.promise; + + const socketOptions = { + authenticator: serverAuth, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + const clientAuth = new AuthenticatorType.Client(); + clientAuth.sendOOB = ({ oob }) => { + info(oob); + // Pass to server, skipping prompt for tests + oobData.resolve(oob); + }; + + const transport = await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + encryption: true, + authenticator: clientAuth, + cert: { + sha256: serverCert.sha256Fingerprint, + }, + }); + ok(transport, "Client transport created"); + + const client = new DevToolsClient(transport); + const onUnexpectedClose = () => { + do_throw("Closed unexpectedly"); + }; + client.on("closed", onUnexpectedClose); + await connectClient(client); + + // Send a message the server will echo back + const message = "secrets"; + const reply = await client.mainRoot.echo({ message }); + equal(reply.message, message, "Encrypted echo matches"); + + client.off("closed", onUnexpectedClose); + transport.close(); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); +}); + +// Client w/o OOB_CERT auth fails to connect to server w/ OOB_CERT auth +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + const oobData = defer(); + const AuthenticatorType = DevToolsServer.Authenticators.get("OOB_CERT"); + const serverAuth = new AuthenticatorType.Server(); + serverAuth.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + // Skip prompt for tests + serverAuth.receiveOOB = () => oobData.promise; + + const socketOptions = { + authenticator: serverAuth, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + // This will succeed, but leaves the client in confused state, and no data is + // actually accessible + const transport = await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + encryption: true, + // authenticator: PROMPT is the default + }); + + // Attempt to use the transport + const deferred = defer(); + const client = new DevToolsClient(transport); + client.onPacket = packet => { + // Client did not authenticate, so it ends up seeing the server's auth data + // which is effectively malformed data from the client's perspective + ok(!packet.from && packet.authResult, "Got auth packet instead of data"); + deferred.resolve(); + }; + client.connect(); + await deferred.promise; + + // Try to send a message the server will echo back + const message = "secrets"; + try { + await client.request({ + to: "root", + type: "echo", + message, + }); + } catch (e) { + ok(true, "Sending a message failed"); + transport.close(); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + return; + } + + do_throw("Connection unexpectedly succeeded"); +}); + +// Client w/ invalid K value fails to connect +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + // Grab our cert, instead of relying on a discovery advertisement + const serverCert = await cert.local.getOrCreate(); + + const oobData = defer(); + const AuthenticatorType = DevToolsServer.Authenticators.get("OOB_CERT"); + const serverAuth = new AuthenticatorType.Server(); + serverAuth.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + // Skip prompt for tests + serverAuth.receiveOOB = () => oobData.promise; + + const clientAuth = new AuthenticatorType.Client(); + clientAuth.sendOOB = ({ oob }) => { + info(oob); + info("Modifying K value, should fail"); + // Pass to server, skipping prompt for tests + oobData.resolve({ + k: oob.k + 1, + sha256: oob.sha256, + }); + }; + + const socketOptions = { + authenticator: serverAuth, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + try { + await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + encryption: true, + authenticator: clientAuth, + cert: { + sha256: serverCert.sha256Fingerprint, + }, + }); + } catch (e) { + ok(true, "Client failed to connect as expected"); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + return; + } + + do_throw("Connection unexpectedly succeeded"); +}); + +// Client w/ invalid cert hash fails to connect +add_task(async function() { + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + + // Grab our cert, instead of relying on a discovery advertisement + const serverCert = await cert.local.getOrCreate(); + + const oobData = defer(); + const AuthenticatorType = DevToolsServer.Authenticators.get("OOB_CERT"); + const serverAuth = new AuthenticatorType.Server(); + serverAuth.allowConnection = () => { + return DevToolsServer.AuthenticationResult.ALLOW; + }; + // Skip prompt for tests + serverAuth.receiveOOB = () => oobData.promise; + + const clientAuth = new AuthenticatorType.Client(); + clientAuth.sendOOB = ({ oob }) => { + info(oob); + info("Modifying cert hash, should fail"); + // Pass to server, skipping prompt for tests + oobData.resolve({ + k: oob.k, + sha256: oob.sha256 + 1, + }); + }; + + const socketOptions = { + authenticator: serverAuth, + encryption: true, + portOrPath: -1, + }; + const listener = new SocketListener(DevToolsServer, socketOptions); + ok(listener, "Socket listener created"); + await listener.open(); + equal(DevToolsServer.listeningSockets, 1, "1 listening socket"); + + try { + await DevToolsClient.socketConnect({ + host: "127.0.0.1", + port: listener.port, + encryption: true, + authenticator: clientAuth, + cert: { + sha256: serverCert.sha256Fingerprint, + }, + }); + } catch (e) { + ok(true, "Client failed to connect as expected"); + listener.close(); + equal(DevToolsServer.listeningSockets, 0, "0 listening sockets"); + return; + } + + do_throw("Connection unexpectedly succeeded"); +}); + +add_task(async function() { + DevToolsServer.destroy(); +}); diff --git a/devtools/shared/security/tests/xpcshell/testactors.js b/devtools/shared/security/tests/xpcshell/testactors.js new file mode 100644 index 0000000000..e5f849a431 --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/testactors.js @@ -0,0 +1,16 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const { RootActor } = require("devtools/server/actors/root"); +const { + ActorRegistry, +} = require("devtools/server/actors/utils/actor-registry"); + +exports.createRootActor = function createRootActor(connection) { + const root = new RootActor(connection, { + globalActorFactories: ActorRegistry.globalActorFactories, + }); + root.applicationType = "xpcshell-tests"; + return root; +}; diff --git a/devtools/shared/security/tests/xpcshell/xpcshell.ini b/devtools/shared/security/tests/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..c1a669c228 --- /dev/null +++ b/devtools/shared/security/tests/xpcshell/xpcshell.ini @@ -0,0 +1,11 @@ +[DEFAULT] +tags = devtools +head = head_dbg.js +skip-if = toolkit == 'android' || socketprocess_networking +firefox-appdir = browser + +support-files= + testactors.js + +[test_encryption.js] +[test_oob_cert_auth.js] |