tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_client_auth_with_proxy.js (5300B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /* import-globals-from head_cache.js */
      8 /* import-globals-from head_cookies.js */
      9 /* import-globals-from head_channels.js */
     10 
     11 const { MockRegistrar } = ChromeUtils.importESModule(
     12  "resource://testing-common/MockRegistrar.sys.mjs"
     13 );
     14 
     15 const { NodeHTTPProxyServer, NodeHTTPSProxyServer, NodeHTTP2ProxyServer } =
     16  ChromeUtils.importESModule("resource://testing-common/NodeServer.sys.mjs");
     17 
     18 const certOverrideService = Cc[
     19  "@mozilla.org/security/certoverride;1"
     20 ].getService(Ci.nsICertOverrideService);
     21 
     22 // We don't normally allow localhost channels to be proxied, but this
     23 // is easier than updating all the certs and/or domains.
     24 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
     25 registerCleanupFunction(() => {
     26  Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
     27 });
     28 
     29 function makeChan(uri) {
     30  let chan = NetUtil.newChannel({
     31    uri,
     32    loadUsingSystemPrincipal: true,
     33  }).QueryInterface(Ci.nsIHttpChannel);
     34  chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
     35  return chan;
     36 }
     37 
     38 function channelOpenPromise(chan, flags) {
     39  return new Promise(resolve => {
     40    function finish(req, buffer) {
     41      resolve([req, buffer]);
     42    }
     43    chan.asyncOpen(new ChannelListener(finish, null, flags));
     44  });
     45 }
     46 
     47 class SecurityObserver {
     48  constructor(input, output) {
     49    this.input = input;
     50    this.output = output;
     51  }
     52 
     53  onHandshakeDone() {
     54    info("TLS handshake done");
     55 
     56    let output = this.output;
     57    this.input.asyncWait(
     58      {
     59        onInputStreamReady(readyInput) {
     60          let request = NetUtil.readInputStreamToString(
     61            readyInput,
     62            readyInput.available()
     63          );
     64          ok(
     65            request.startsWith("GET /") && request.includes("HTTP/1.1"),
     66            "expecting an HTTP/1.1 GET request"
     67          );
     68          let response =
     69            "HTTP/1.1 200 OK\r\nContent-Type:text/plain\r\n" +
     70            "Connection:Close\r\nContent-Length:2\r\n\r\nOK";
     71          output.write(response, response.length);
     72        },
     73      },
     74      0,
     75      0,
     76      Services.tm.currentThread
     77    );
     78  }
     79 }
     80 
     81 function startServer(cert) {
     82  let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
     83    Ci.nsITLSServerSocket
     84  );
     85  tlsServer.init(-1, true, -1);
     86  tlsServer.serverCert = cert;
     87 
     88  let securityObservers = [];
     89 
     90  let listener = {
     91    onSocketAccepted(socket, transport) {
     92      info("Accepted TLS client connection");
     93      let connectionInfo = transport.securityCallbacks.getInterface(
     94        Ci.nsITLSServerConnectionInfo
     95      );
     96      let input = transport.openInputStream(0, 0, 0);
     97      let output = transport.openOutputStream(0, 0, 0);
     98      connectionInfo.setSecurityObserver(new SecurityObserver(input, output));
     99    },
    100 
    101    onStopListening() {
    102      info("onStopListening");
    103      for (let securityObserver of securityObservers) {
    104        securityObserver.input.close();
    105        securityObserver.output.close();
    106      }
    107    },
    108  };
    109 
    110  tlsServer.setSessionTickets(false);
    111  tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUEST_ALWAYS);
    112 
    113  tlsServer.asyncListen(listener);
    114 
    115  return tlsServer;
    116 }
    117 
    118 // Replace the UI dialog that prompts the user to pick a client certificate.
    119 const clientAuthDialogService = {
    120  chooseCertificate(hostname, certArray, loadContext, caNames, callback) {
    121    callback.certificateChosen(certArray[0], false);
    122  },
    123  QueryInterface: ChromeUtils.generateQI(["nsIClientAuthDialogService"]),
    124 };
    125 
    126 let server;
    127 add_setup(async function setup() {
    128  do_get_profile();
    129 
    130  let clientAuthDialogServiceCID = MockRegistrar.register(
    131    "@mozilla.org/security/ClientAuthDialogService;1",
    132    clientAuthDialogService
    133  );
    134 
    135  let cert = getTestServerCertificate();
    136  ok(!!cert, "Got self-signed cert");
    137  server = startServer(cert);
    138 
    139  certOverrideService.rememberValidityOverride(
    140    "localhost",
    141    server.port,
    142    {},
    143    cert,
    144    true
    145  );
    146 
    147  registerCleanupFunction(async function () {
    148    MockRegistrar.unregister(clientAuthDialogServiceCID);
    149    certOverrideService.clearValidityOverride("localhost", server.port, {});
    150    server.close();
    151  });
    152 });
    153 
    154 add_task(async function test_client_auth_with_proxy() {
    155  let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
    156    Ci.nsIX509CertDB
    157  );
    158  addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
    159  addCertFromFile(certdb, "proxy-ca.pem", "CTu,u,u");
    160 
    161  let proxies = [
    162    NodeHTTPProxyServer,
    163    NodeHTTPSProxyServer,
    164    NodeHTTP2ProxyServer,
    165  ];
    166 
    167  for (let p of proxies) {
    168    info(`Test with proxy:${p.name}`);
    169    let proxy = new p();
    170    await proxy.start();
    171    registerCleanupFunction(async () => {
    172      await proxy.stop();
    173    });
    174 
    175    let chan = makeChan(`https://localhost:${server.port}`);
    176    let [req, buff] = await channelOpenPromise(chan, CL_ALLOW_UNKNOWN_CL);
    177    equal(req.status, Cr.NS_OK);
    178    equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200);
    179    equal(buff, "OK");
    180    req.QueryInterface(Ci.nsIProxiedChannel);
    181    ok(!!req.proxyInfo);
    182    notEqual(req.proxyInfo.type, "direct");
    183    await proxy.stop();
    184  }
    185 });