diff options
Diffstat (limited to 'devtools/shared/security/prompt.js')
-rw-r--r-- | devtools/shared/security/prompt.js | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/devtools/shared/security/prompt.js b/devtools/shared/security/prompt.js new file mode 100644 index 000000000..aad9b7211 --- /dev/null +++ b/devtools/shared/security/prompt.js @@ -0,0 +1,179 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* 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/. */ + +"use strict"; + +var { Ci } = require("chrome"); +var Services = require("Services"); +var DevToolsUtils = require("devtools/shared/DevToolsUtils"); +loader.lazyRequireGetter(this, "DebuggerSocket", + "devtools/shared/security/socket", true); +loader.lazyRequireGetter(this, "AuthenticationResult", + "devtools/shared/security/auth", true); + +const {LocalizationHelper} = require("devtools/shared/l10n"); +const L10N = new LocalizationHelper("devtools/shared/locales/debugger.properties"); + +var Client = exports.Client = {}; +var Server = exports.Server = {}; + +/** + * During OOB_CERT authentication, a notification dialog like this is used to + * to display a token which the user must transfer through some mechanism to the + * server to authenticate the devices. + * + * This implementation presents the token as text for the user to transfer + * manually. For a mobile device, you should override this implementation with + * something more convenient, such as displaying a QR code. + * + * @param host string + * The host name or IP address of the debugger server. + * @param port number + * The port number of the debugger server. + * @param cert object (optional) + * The server's cert details. + * @param authResult AuthenticationResult + * Authentication result sent from the server. + * @param oob object (optional) + * The token data to be transferred during OOB_CERT step 8: + * * sha256: hash(ClientCert) + * * k : K(random 128-bit number) + * @return object containing: + * * close: Function to hide the notification + */ +Client.defaultSendOOB = ({ authResult, oob }) => { + // Only show in the PENDING state + if (authResult != AuthenticationResult.PENDING) { + throw new Error("Expected PENDING result, got " + authResult); + } + let title = L10N.getStr("clientSendOOBTitle"); + let header = L10N.getStr("clientSendOOBHeader"); + let hashMsg = L10N.getFormatStr("clientSendOOBHash", oob.sha256); + let token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k; + let tokenMsg = L10N.getFormatStr("clientSendOOBToken", token); + let msg = `${header}\n\n${hashMsg}\n${tokenMsg}`; + let prompt = Services.prompt; + let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_CANCEL; + + // Listen for the window our prompt opens, so we can close it programatically + let promptWindow; + let windowListener = { + onOpenWindow(xulWindow) { + let win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + win.addEventListener("load", function listener() { + win.removeEventListener("load", listener, false); + if (win.document.documentElement.getAttribute("id") != "commonDialog") { + return; + } + // Found the window + promptWindow = win; + Services.wm.removeListener(windowListener); + }, false); + }, + onCloseWindow() {}, + onWindowTitleChange() {} + }; + Services.wm.addListener(windowListener); + + // nsIPrompt is typically a blocking API, so |executeSoon| to get around this + DevToolsUtils.executeSoon(() => { + prompt.confirmEx(null, title, msg, flags, null, null, null, null, + { value: false }); + }); + + return { + close() { + if (!promptWindow) { + return; + } + promptWindow.document.documentElement.acceptDialog(); + promptWindow = null; + } + }; +}; + +/** + * Prompt the user to accept or decline the incoming connection. This is the + * default implementation that products embedding the debugger server may + * choose to override. This can be overridden via |allowConnection| on the + * socket's authenticator instance. + * + * @param session object + * The session object will contain at least the following fields: + * { + * authentication, + * client: { + * host, + * port + * }, + * server: { + * host, + * port + * } + * } + * Specific authentication modes may include additional fields. Check + * the different |allowConnection| methods in ./auth.js. + * @return An AuthenticationResult value. + * A promise that will be resolved to the above is also allowed. + */ +Server.defaultAllowConnection = ({ client, server }) => { + let title = L10N.getStr("remoteIncomingPromptTitle"); + let header = L10N.getStr("remoteIncomingPromptHeader"); + let clientEndpoint = `${client.host}:${client.port}`; + let clientMsg = L10N.getFormatStr("remoteIncomingPromptClientEndpoint", clientEndpoint); + let serverEndpoint = `${server.host}:${server.port}`; + let serverMsg = L10N.getFormatStr("remoteIncomingPromptServerEndpoint", serverEndpoint); + let footer = L10N.getStr("remoteIncomingPromptFooter"); + let msg = `${header}\n\n${clientMsg}\n${serverMsg}\n\n${footer}`; + let disableButton = L10N.getStr("remoteIncomingPromptDisable"); + let prompt = Services.prompt; + let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_OK + + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_CANCEL + + prompt.BUTTON_POS_2 * prompt.BUTTON_TITLE_IS_STRING + + prompt.BUTTON_POS_1_DEFAULT; + let result = prompt.confirmEx(null, title, msg, flags, null, null, + disableButton, null, { value: false }); + if (result === 0) { + return AuthenticationResult.ALLOW; + } + if (result === 2) { + return AuthenticationResult.DISABLE_ALL; + } + return AuthenticationResult.DENY; +}; + +/** + * During OOB_CERT authentication, the user must transfer some data through some + * out of band mechanism from the client to the server to authenticate the + * devices. + * + * This implementation prompts the user for a token as constructed by + * |Client.defaultSendOOB| that the user needs to transfer manually. For a + * mobile device, you should override this implementation with something more + * convenient, such as reading a QR code. + * + * @return An object containing: + * * sha256: hash(ClientCert) + * * k : K(random 128-bit number) + * A promise that will be resolved to the above is also allowed. + */ +Server.defaultReceiveOOB = () => { + let title = L10N.getStr("serverReceiveOOBTitle"); + let msg = L10N.getStr("serverReceiveOOBBody"); + let input = { value: null }; + let prompt = Services.prompt; + let result = prompt.prompt(null, title, msg, input, null, { value: false }); + if (!result) { + return null; + } + // Re-create original object from token + input = input.value.trim(); + let sha256 = input.substring(0, 64); + sha256 = sha256.replace(/\w{2}/g, "$&:").slice(0, -1).toUpperCase(); + let k = input.substring(64); + return { sha256, k }; +}; |