/* 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 DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js"); loader.lazyRequireGetter( this, "AuthenticationResult", "resource://devtools/shared/security/auth.js", true ); const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); 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 devtools server. * @param port number * The port number of the devtools server. * @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); } const title = L10N.getStr("clientSendOOBTitle"); const header = L10N.getStr("clientSendOOBHeader"); const hashMsg = L10N.getFormatStr("clientSendOOBHash", oob.sha256); const token = oob.sha256.replace(/:/g, "").toLowerCase() + oob.k; const tokenMsg = L10N.getFormatStr("clientSendOOBToken", token); const msg = `${header}\n\n${hashMsg}\n${tokenMsg}`; const prompt = Services.prompt; const flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_CANCEL; // Listen for the window our prompt opens, so we can close it programatically let promptWindow; const windowListener = { onOpenWindow(xulWindow) { const win = xulWindow.docShell.domWindow; win.addEventListener( "load", function () { if ( win.document.documentElement.getAttribute("id") != "commonDialog" ) { return; } // Found the window promptWindow = win; Services.wm.removeListener(windowListener); }, { once: true } ); }, onCloseWindow() {}, }; 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 devtools 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 }) => { const title = L10N.getStr("remoteIncomingPromptTitle"); const header = L10N.getStr("remoteIncomingPromptHeader"); const clientEndpoint = `${client.host}:${client.port}`; const clientMsg = L10N.getFormatStr( "remoteIncomingPromptClientEndpoint", clientEndpoint ); const serverEndpoint = `${server.host}:${server.port}`; const serverMsg = L10N.getFormatStr( "remoteIncomingPromptServerEndpoint", serverEndpoint ); const footer = L10N.getStr("remoteIncomingPromptFooter"); const msg = `${header}\n\n${clientMsg}\n${serverMsg}\n\n${footer}`; const disableButton = L10N.getStr("remoteIncomingPromptDisable"); const prompt = Services.prompt; const 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; const 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 = () => { const title = L10N.getStr("serverReceiveOOBTitle"); const msg = L10N.getStr("serverReceiveOOBBody"); let input = { value: null }; const prompt = Services.prompt; const 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(); const k = input.substring(64); return { sha256, k }; };