/* -*- 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); DevToolsUtils.defineLazyGetter(this, "bundle", () => { const DBG_STRINGS_URI = "chrome://devtools-shared/locale/debugger.properties"; return Services.strings.createBundle(DBG_STRINGS_URI); }); 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 = bundle.GetStringFromName("clientSendOOBTitle"); let header = bundle.GetStringFromName("clientSendOOBHeader"); let hashMsg = bundle.formatStringFromName("clientSendOOBHash", [oob.sha256], 1); let token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k; let tokenMsg = bundle.formatStringFromName("clientSendOOBToken", [token], 1); 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 = bundle.GetStringFromName("remoteIncomingPromptTitle"); let header = bundle.GetStringFromName("remoteIncomingPromptHeader"); let clientEndpoint = `${client.host}:${client.port}`; let clientMsg = bundle.formatStringFromName("remoteIncomingPromptClientEndpoint", [clientEndpoint], 1); let serverEndpoint = `${server.host}:${server.port}`; let serverMsg = bundle.formatStringFromName("remoteIncomingPromptServerEndpoint", [serverEndpoint], 1); let footer = bundle.GetStringFromName("remoteIncomingPromptFooter"); let msg =`${header}\n\n${clientMsg}\n${serverMsg}\n\n${footer}`; let disableButton = bundle.GetStringFromName("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 = bundle.GetStringFromName("serverReceiveOOBTitle"); let msg = bundle.GetStringFromName("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 }; };