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 });