Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | */ |
michael@0 | 5 | "use strict"; |
michael@0 | 6 | |
michael@0 | 7 | const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components; |
michael@0 | 8 | |
michael@0 | 9 | let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); |
michael@0 | 10 | let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); |
michael@0 | 11 | let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); |
michael@0 | 12 | let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {}); |
michael@0 | 13 | let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {}); |
michael@0 | 14 | let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm"); |
michael@0 | 15 | |
michael@0 | 16 | let gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc); |
michael@0 | 17 | |
michael@0 | 18 | const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"] |
michael@0 | 19 | .getService(Ci.nsIDebug2).isDebugBuild; |
michael@0 | 20 | |
michael@0 | 21 | const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE; |
michael@0 | 22 | const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; |
michael@0 | 23 | |
michael@0 | 24 | // Sort in numerical order |
michael@0 | 25 | const SEC_ERROR_INVALID_ARGS = SEC_ERROR_BASE + 5; // -8187 |
michael@0 | 26 | const SEC_ERROR_BAD_DER = SEC_ERROR_BASE + 9; |
michael@0 | 27 | const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11; |
michael@0 | 28 | const SEC_ERROR_REVOKED_CERTIFICATE = SEC_ERROR_BASE + 12; // -8180 |
michael@0 | 29 | const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13; |
michael@0 | 30 | const SEC_ERROR_BAD_DATABASE = SEC_ERROR_BASE + 18; |
michael@0 | 31 | const SEC_ERROR_UNTRUSTED_ISSUER = SEC_ERROR_BASE + 20; // -8172 |
michael@0 | 32 | const SEC_ERROR_UNTRUSTED_CERT = SEC_ERROR_BASE + 21; // -8171 |
michael@0 | 33 | const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30; // -8162 |
michael@0 | 34 | const SEC_ERROR_EXTENSION_VALUE_INVALID = SEC_ERROR_BASE + 34; // -8158 |
michael@0 | 35 | const SEC_ERROR_EXTENSION_NOT_FOUND = SEC_ERROR_BASE + 35; // -8157 |
michael@0 | 36 | const SEC_ERROR_CA_CERT_INVALID = SEC_ERROR_BASE + 36; |
michael@0 | 37 | const SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION = SEC_ERROR_BASE + 41; |
michael@0 | 38 | const SEC_ERROR_INADEQUATE_KEY_USAGE = SEC_ERROR_BASE + 90; // -8102 |
michael@0 | 39 | const SEC_ERROR_INADEQUATE_CERT_TYPE = SEC_ERROR_BASE + 91; // -8101 |
michael@0 | 40 | const SEC_ERROR_CERT_NOT_IN_NAME_SPACE = SEC_ERROR_BASE + 112; // -8080 |
michael@0 | 41 | const SEC_ERROR_CERT_BAD_ACCESS_LOCATION = SEC_ERROR_BASE + 117; // -8075 |
michael@0 | 42 | const SEC_ERROR_OCSP_MALFORMED_REQUEST = SEC_ERROR_BASE + 120; |
michael@0 | 43 | const SEC_ERROR_OCSP_SERVER_ERROR = SEC_ERROR_BASE + 121; // -8071 |
michael@0 | 44 | const SEC_ERROR_OCSP_TRY_SERVER_LATER = SEC_ERROR_BASE + 122; |
michael@0 | 45 | const SEC_ERROR_OCSP_REQUEST_NEEDS_SIG = SEC_ERROR_BASE + 123; |
michael@0 | 46 | const SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST = SEC_ERROR_BASE + 124; |
michael@0 | 47 | const SEC_ERROR_OCSP_UNKNOWN_CERT = SEC_ERROR_BASE + 126; // -8066 |
michael@0 | 48 | const SEC_ERROR_OCSP_MALFORMED_RESPONSE = SEC_ERROR_BASE + 129; |
michael@0 | 49 | const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = SEC_ERROR_BASE + 130; |
michael@0 | 50 | const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132; |
michael@0 | 51 | const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144; |
michael@0 | 52 | const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032 |
michael@0 | 53 | const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157; |
michael@0 | 54 | const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176; |
michael@0 | 55 | const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178; |
michael@0 | 56 | |
michael@0 | 57 | const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12; |
michael@0 | 58 | |
michael@0 | 59 | // Supported Certificate Usages |
michael@0 | 60 | const certificateUsageSSLClient = 0x0001; |
michael@0 | 61 | const certificateUsageSSLServer = 0x0002; |
michael@0 | 62 | const certificateUsageSSLCA = 0x0008; |
michael@0 | 63 | const certificateUsageEmailSigner = 0x0010; |
michael@0 | 64 | const certificateUsageEmailRecipient = 0x0020; |
michael@0 | 65 | const certificateUsageObjectSigner = 0x0040; |
michael@0 | 66 | const certificateUsageVerifyCA = 0x0100; |
michael@0 | 67 | const certificateUsageStatusResponder = 0x0400; |
michael@0 | 68 | |
michael@0 | 69 | const NO_FLAGS = 0; |
michael@0 | 70 | |
michael@0 | 71 | function readFile(file) { |
michael@0 | 72 | let fstream = Cc["@mozilla.org/network/file-input-stream;1"] |
michael@0 | 73 | .createInstance(Ci.nsIFileInputStream); |
michael@0 | 74 | fstream.init(file, -1, 0, 0); |
michael@0 | 75 | let data = NetUtil.readInputStreamToString(fstream, fstream.available()); |
michael@0 | 76 | fstream.close(); |
michael@0 | 77 | return data; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | function addCertFromFile(certdb, filename, trustString) { |
michael@0 | 81 | let certFile = do_get_file(filename, false); |
michael@0 | 82 | let der = readFile(certFile); |
michael@0 | 83 | certdb.addCert(der, trustString, null); |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | function constructCertFromFile(filename) { |
michael@0 | 87 | let certFile = do_get_file(filename, false); |
michael@0 | 88 | let certDER = readFile(certFile); |
michael@0 | 89 | let certdb = Cc["@mozilla.org/security/x509certdb;1"] |
michael@0 | 90 | .getService(Ci.nsIX509CertDB); |
michael@0 | 91 | return certdb.constructX509(certDER, certDER.length); |
michael@0 | 92 | } |
michael@0 | 93 | |
michael@0 | 94 | function setCertTrust(cert, trustString) { |
michael@0 | 95 | let certdb = Cc["@mozilla.org/security/x509certdb;1"] |
michael@0 | 96 | .getService(Ci.nsIX509CertDB); |
michael@0 | 97 | certdb.setCertTrustFromString(cert, trustString); |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | function getXPCOMStatusFromNSS(statusNSS) { |
michael@0 | 101 | let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"] |
michael@0 | 102 | .getService(Ci.nsINSSErrorsService); |
michael@0 | 103 | return nssErrorsService.getXPCOMFromNSSError(statusNSS); |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | function checkCertErrorGeneric(certdb, cert, expectedError, usage) { |
michael@0 | 107 | let hasEVPolicy = {}; |
michael@0 | 108 | let verifiedChain = {}; |
michael@0 | 109 | let error = certdb.verifyCertNow(cert, usage, NO_FLAGS, verifiedChain, |
michael@0 | 110 | hasEVPolicy); |
michael@0 | 111 | // expected error == -1 is a special marker for any error is OK |
michael@0 | 112 | if (expectedError != -1 ) { |
michael@0 | 113 | do_check_eq(error, expectedError); |
michael@0 | 114 | } else { |
michael@0 | 115 | do_check_neq (error, 0); |
michael@0 | 116 | } |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | function _getLibraryFunctionWithNoArguments(functionName, libraryName) { |
michael@0 | 120 | // Open the NSS library. copied from services/crypto/modules/WeaveCrypto.js |
michael@0 | 121 | let path = ctypes.libraryName(libraryName); |
michael@0 | 122 | |
michael@0 | 123 | // XXX really want to be able to pass specific dlopen flags here. |
michael@0 | 124 | let nsslib; |
michael@0 | 125 | try { |
michael@0 | 126 | nsslib = ctypes.open(path); |
michael@0 | 127 | } catch(e) { |
michael@0 | 128 | // In case opening the library without a full path fails, |
michael@0 | 129 | // try again with a full path. |
michael@0 | 130 | let file = Services.dirsvc.get("GreD", Ci.nsILocalFile); |
michael@0 | 131 | file.append(path); |
michael@0 | 132 | nsslib = ctypes.open(file.path); |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | let SECStatus = ctypes.int; |
michael@0 | 136 | let func = nsslib.declare(functionName, ctypes.default_abi, SECStatus); |
michael@0 | 137 | return func; |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | function clearOCSPCache() { |
michael@0 | 141 | let certdb = Cc["@mozilla.org/security/x509certdb;1"] |
michael@0 | 142 | .getService(Ci.nsIX509CertDB); |
michael@0 | 143 | certdb.clearOCSPCache(); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | function clearSessionCache() { |
michael@0 | 147 | let SSL_ClearSessionCache = null; |
michael@0 | 148 | try { |
michael@0 | 149 | SSL_ClearSessionCache = |
michael@0 | 150 | _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "ssl3"); |
michael@0 | 151 | } catch (e) { |
michael@0 | 152 | // On Windows, this is actually in the nss3 library. |
michael@0 | 153 | SSL_ClearSessionCache = |
michael@0 | 154 | _getLibraryFunctionWithNoArguments("SSL_ClearSessionCache", "nss3"); |
michael@0 | 155 | } |
michael@0 | 156 | if (!SSL_ClearSessionCache || SSL_ClearSessionCache() != 0) { |
michael@0 | 157 | throw "Failed to clear SSL session cache"; |
michael@0 | 158 | } |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | // Set up a TLS testing environment that has a TLS server running and |
michael@0 | 162 | // ready to accept connections. This async function starts the server and |
michael@0 | 163 | // waits for the server to indicate that it is ready. |
michael@0 | 164 | // |
michael@0 | 165 | // Each test should have its own subdomain of example.com, for example |
michael@0 | 166 | // my-first-connection-test.example.com. The server can use the server |
michael@0 | 167 | // name (passed through the SNI TLS extension) to determine what behavior |
michael@0 | 168 | // the server side of the text should exhibit. See TLSServer.h for more |
michael@0 | 169 | // information on how to write the server side of tests. |
michael@0 | 170 | // |
michael@0 | 171 | // Create a new source file for your new server executable in |
michael@0 | 172 | // security/manager/ssl/tests/unit/tlsserver/cmd similar to the other ones in |
michael@0 | 173 | // that directory, and add a reference to it to the sources variable in that |
michael@0 | 174 | // directory's moz.build. |
michael@0 | 175 | // |
michael@0 | 176 | // Modify TEST_HARNESS_BINS in |
michael@0 | 177 | // testing/mochitest/Makefile.in and NO_PKG_FILES in |
michael@0 | 178 | // toolkit/mozapps/installer/packager.mk to make sure the new executable |
michael@0 | 179 | // gets included in the packages used for shipping the tests to the test |
michael@0 | 180 | // runners in our build/test farm. (Things will work fine locally without |
michael@0 | 181 | // these changes but will break on TBPL.) |
michael@0 | 182 | // |
michael@0 | 183 | // Your test script should look something like this: |
michael@0 | 184 | /* |
michael@0 | 185 | |
michael@0 | 186 | // -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 187 | // This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 188 | // License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 189 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 190 | "use strict"; |
michael@0 | 191 | |
michael@0 | 192 | // <documentation on your test> |
michael@0 | 193 | |
michael@0 | 194 | function run_test() { |
michael@0 | 195 | do_get_profile(); |
michael@0 | 196 | add_tls_server_setup("<test-server-name>"); |
michael@0 | 197 | |
michael@0 | 198 | add_connection_test("<test-name-1>.example.com", |
michael@0 | 199 | getXPCOMStatusFromNSS(SEC_ERROR_xxx), |
michael@0 | 200 | function() { ... }, |
michael@0 | 201 | function(aTransportSecurityInfo) { ... }); |
michael@0 | 202 | [...] |
michael@0 | 203 | add_connection_test("<test-name-n>.example.com", Cr.NS_OK); |
michael@0 | 204 | |
michael@0 | 205 | run_next_test(); |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | */ |
michael@0 | 209 | function add_tls_server_setup(serverBinName) { |
michael@0 | 210 | add_test(function() { |
michael@0 | 211 | _setupTLSServerTest(serverBinName); |
michael@0 | 212 | }); |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | // Add a TLS connection test case. aHost is the hostname to pass in the SNI TLS |
michael@0 | 216 | // extension; this should unambiguously identifiy which test is being run. |
michael@0 | 217 | // aExpectedResult is the expected nsresult of the connection. |
michael@0 | 218 | // aBeforeConnect is a callback function that takes no arguments that will be |
michael@0 | 219 | // called before the connection is attempted. |
michael@0 | 220 | // aWithSecurityInfo is a callback function that takes an |
michael@0 | 221 | // nsITransportSecurityInfo, which is called after the TLS handshake succeeds. |
michael@0 | 222 | function add_connection_test(aHost, aExpectedResult, |
michael@0 | 223 | aBeforeConnect, aWithSecurityInfo) { |
michael@0 | 224 | const REMOTE_PORT = 8443; |
michael@0 | 225 | |
michael@0 | 226 | function Connection(aHost) { |
michael@0 | 227 | this.host = aHost; |
michael@0 | 228 | let threadManager = Cc["@mozilla.org/thread-manager;1"] |
michael@0 | 229 | .getService(Ci.nsIThreadManager); |
michael@0 | 230 | this.thread = threadManager.currentThread; |
michael@0 | 231 | this.defer = Promise.defer(); |
michael@0 | 232 | let sts = Cc["@mozilla.org/network/socket-transport-service;1"] |
michael@0 | 233 | .getService(Ci.nsISocketTransportService); |
michael@0 | 234 | this.transport = sts.createTransport(["ssl"], 1, aHost, REMOTE_PORT, null); |
michael@0 | 235 | this.transport.setEventSink(this, this.thread); |
michael@0 | 236 | this.inputStream = null; |
michael@0 | 237 | this.outputStream = null; |
michael@0 | 238 | this.connected = false; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | Connection.prototype = { |
michael@0 | 242 | // nsITransportEventSink |
michael@0 | 243 | onTransportStatus: function(aTransport, aStatus, aProgress, aProgressMax) { |
michael@0 | 244 | if (!this.connected && aStatus == Ci.nsISocketTransport.STATUS_CONNECTED_TO) { |
michael@0 | 245 | this.connected = true; |
michael@0 | 246 | this.outputStream.asyncWait(this, 0, 0, this.thread); |
michael@0 | 247 | } |
michael@0 | 248 | }, |
michael@0 | 249 | |
michael@0 | 250 | // nsIInputStreamCallback |
michael@0 | 251 | onInputStreamReady: function(aStream) { |
michael@0 | 252 | try { |
michael@0 | 253 | // this will throw if the stream has been closed by an error |
michael@0 | 254 | let str = NetUtil.readInputStreamToString(aStream, aStream.available()); |
michael@0 | 255 | do_check_eq(str, "0"); |
michael@0 | 256 | this.inputStream.close(); |
michael@0 | 257 | this.outputStream.close(); |
michael@0 | 258 | this.result = Cr.NS_OK; |
michael@0 | 259 | } catch (e) { |
michael@0 | 260 | this.result = e.result; |
michael@0 | 261 | } |
michael@0 | 262 | this.defer.resolve(this); |
michael@0 | 263 | }, |
michael@0 | 264 | |
michael@0 | 265 | // nsIOutputStreamCallback |
michael@0 | 266 | onOutputStreamReady: function(aStream) { |
michael@0 | 267 | let sslSocketControl = this.transport.securityInfo |
michael@0 | 268 | .QueryInterface(Ci.nsISSLSocketControl); |
michael@0 | 269 | sslSocketControl.proxyStartSSL(); |
michael@0 | 270 | this.outputStream.write("0", 1); |
michael@0 | 271 | let inStream = this.transport.openInputStream(0, 0, 0) |
michael@0 | 272 | .QueryInterface(Ci.nsIAsyncInputStream); |
michael@0 | 273 | this.inputStream = inStream; |
michael@0 | 274 | this.inputStream.asyncWait(this, 0, 0, this.thread); |
michael@0 | 275 | }, |
michael@0 | 276 | |
michael@0 | 277 | go: function() { |
michael@0 | 278 | this.outputStream = this.transport.openOutputStream(0, 0, 0) |
michael@0 | 279 | .QueryInterface(Ci.nsIAsyncOutputStream); |
michael@0 | 280 | return this.defer.promise; |
michael@0 | 281 | } |
michael@0 | 282 | }; |
michael@0 | 283 | |
michael@0 | 284 | /* Returns a promise to connect to aHost that resolves to the result of that |
michael@0 | 285 | * connection */ |
michael@0 | 286 | function connectTo(aHost) { |
michael@0 | 287 | Services.prefs.setCharPref("network.dns.localDomains", aHost); |
michael@0 | 288 | let connection = new Connection(aHost); |
michael@0 | 289 | return connection.go(); |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | add_test(function() { |
michael@0 | 293 | if (aBeforeConnect) { |
michael@0 | 294 | aBeforeConnect(); |
michael@0 | 295 | } |
michael@0 | 296 | connectTo(aHost).then(function(conn) { |
michael@0 | 297 | do_check_eq(conn.result, aExpectedResult); |
michael@0 | 298 | if (aWithSecurityInfo) { |
michael@0 | 299 | aWithSecurityInfo(conn.transport.securityInfo |
michael@0 | 300 | .QueryInterface(Ci.nsITransportSecurityInfo)); |
michael@0 | 301 | } |
michael@0 | 302 | run_next_test(); |
michael@0 | 303 | }); |
michael@0 | 304 | }); |
michael@0 | 305 | } |
michael@0 | 306 | |
michael@0 | 307 | function _getBinaryUtil(binaryUtilName) { |
michael@0 | 308 | let directoryService = Cc["@mozilla.org/file/directory_service;1"] |
michael@0 | 309 | .getService(Ci.nsIProperties); |
michael@0 | 310 | |
michael@0 | 311 | let utilBin = directoryService.get("CurProcD", Ci.nsILocalFile); |
michael@0 | 312 | utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : "")); |
michael@0 | 313 | // If we're testing locally, the above works. If not, the server executable |
michael@0 | 314 | // is in another location. |
michael@0 | 315 | if (!utilBin.exists()) { |
michael@0 | 316 | utilBin = directoryService.get("CurWorkD", Ci.nsILocalFile); |
michael@0 | 317 | while (utilBin.path.indexOf("xpcshell") != -1) { |
michael@0 | 318 | utilBin = utilBin.parent; |
michael@0 | 319 | } |
michael@0 | 320 | utilBin.append("bin"); |
michael@0 | 321 | utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : "")); |
michael@0 | 322 | } |
michael@0 | 323 | do_check_true(utilBin.exists()); |
michael@0 | 324 | return utilBin; |
michael@0 | 325 | } |
michael@0 | 326 | |
michael@0 | 327 | // Do not call this directly; use add_tls_server_setup |
michael@0 | 328 | function _setupTLSServerTest(serverBinName) |
michael@0 | 329 | { |
michael@0 | 330 | let certdb = Cc["@mozilla.org/security/x509certdb;1"] |
michael@0 | 331 | .getService(Ci.nsIX509CertDB); |
michael@0 | 332 | // The trusted CA that is typically used for "good" certificates. |
michael@0 | 333 | addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u"); |
michael@0 | 334 | |
michael@0 | 335 | const CALLBACK_PORT = 8444; |
michael@0 | 336 | |
michael@0 | 337 | let directoryService = Cc["@mozilla.org/file/directory_service;1"] |
michael@0 | 338 | .getService(Ci.nsIProperties); |
michael@0 | 339 | let envSvc = Cc["@mozilla.org/process/environment;1"] |
michael@0 | 340 | .getService(Ci.nsIEnvironment); |
michael@0 | 341 | let greDir = directoryService.get("GreD", Ci.nsIFile); |
michael@0 | 342 | envSvc.set("DYLD_LIBRARY_PATH", greDir.path); |
michael@0 | 343 | envSvc.set("LD_LIBRARY_PATH", greDir.path); |
michael@0 | 344 | envSvc.set("MOZ_TLS_SERVER_DEBUG_LEVEL", "3"); |
michael@0 | 345 | envSvc.set("MOZ_TLS_SERVER_CALLBACK_PORT", CALLBACK_PORT); |
michael@0 | 346 | |
michael@0 | 347 | let httpServer = new HttpServer(); |
michael@0 | 348 | httpServer.registerPathHandler("/", |
michael@0 | 349 | function handleServerCallback(aRequest, aResponse) { |
michael@0 | 350 | aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); |
michael@0 | 351 | aResponse.setHeader("Content-Type", "text/plain"); |
michael@0 | 352 | let responseBody = "OK!"; |
michael@0 | 353 | aResponse.bodyOutputStream.write(responseBody, responseBody.length); |
michael@0 | 354 | do_execute_soon(function() { |
michael@0 | 355 | httpServer.stop(run_next_test); |
michael@0 | 356 | }); |
michael@0 | 357 | }); |
michael@0 | 358 | httpServer.start(CALLBACK_PORT); |
michael@0 | 359 | |
michael@0 | 360 | let serverBin = _getBinaryUtil(serverBinName); |
michael@0 | 361 | let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); |
michael@0 | 362 | process.init(serverBin); |
michael@0 | 363 | let certDir = directoryService.get("CurWorkD", Ci.nsILocalFile); |
michael@0 | 364 | certDir.append("tlsserver"); |
michael@0 | 365 | do_check_true(certDir.exists()); |
michael@0 | 366 | process.run(false, [certDir.path], 1); |
michael@0 | 367 | |
michael@0 | 368 | do_register_cleanup(function() { |
michael@0 | 369 | process.kill(); |
michael@0 | 370 | }); |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | // Returns an Array of OCSP responses for a given ocspRespArray and a location |
michael@0 | 374 | // for a nssDB where the certs and public keys are prepopulated. |
michael@0 | 375 | // ocspRespArray is an array of arrays like: |
michael@0 | 376 | // [ [typeOfResponse, certnick, extracertnick]...] |
michael@0 | 377 | function generateOCSPResponses(ocspRespArray, nssDBlocation) |
michael@0 | 378 | { |
michael@0 | 379 | let utilBinName = "GenerateOCSPResponse"; |
michael@0 | 380 | let ocspGenBin = _getBinaryUtil(utilBinName); |
michael@0 | 381 | let retArray = new Array(); |
michael@0 | 382 | |
michael@0 | 383 | for (let i = 0; i < ocspRespArray.length; i++) { |
michael@0 | 384 | let argArray = new Array(); |
michael@0 | 385 | let ocspFilepre = do_get_file(i.toString() + ".ocsp", true); |
michael@0 | 386 | let filename = ocspFilepre.path; |
michael@0 | 387 | argArray.push(nssDBlocation); |
michael@0 | 388 | argArray.push(ocspRespArray[i][0]); // ocsRespType; |
michael@0 | 389 | argArray.push(ocspRespArray[i][1]); // nick; |
michael@0 | 390 | argArray.push(ocspRespArray[i][2]); // extranickname |
michael@0 | 391 | argArray.push(filename); |
michael@0 | 392 | do_print("arg_array ="+argArray); |
michael@0 | 393 | |
michael@0 | 394 | let process = Cc["@mozilla.org/process/util;1"] |
michael@0 | 395 | .createInstance(Ci.nsIProcess); |
michael@0 | 396 | process.init(ocspGenBin); |
michael@0 | 397 | process.run(true, argArray, 5); |
michael@0 | 398 | do_check_eq(0, process.exitValue); |
michael@0 | 399 | let ocspFile = do_get_file(i.toString() + ".ocsp", false); |
michael@0 | 400 | retArray.push(readFile(ocspFile)); |
michael@0 | 401 | ocspFile.remove(false); |
michael@0 | 402 | } |
michael@0 | 403 | return retArray; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | // Starts and returns an http responder that will cause a test failure if it is |
michael@0 | 407 | // queried. The server identities are given by a non-empty array |
michael@0 | 408 | // serverIdentities. |
michael@0 | 409 | function getFailingHttpServer(serverPort, serverIdentities) { |
michael@0 | 410 | let httpServer = new HttpServer(); |
michael@0 | 411 | httpServer.registerPrefixHandler("/", function(request, response) { |
michael@0 | 412 | do_check_true(false); |
michael@0 | 413 | }); |
michael@0 | 414 | httpServer.identity.setPrimary("http", serverIdentities.shift(), serverPort); |
michael@0 | 415 | serverIdentities.forEach(function(identity) { |
michael@0 | 416 | httpServer.identity.add("http", identity, serverPort); |
michael@0 | 417 | }); |
michael@0 | 418 | httpServer.start(serverPort); |
michael@0 | 419 | return httpServer; |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | // Starts an http OCSP responder that serves good OCSP responses and |
michael@0 | 423 | // returns an object with a method stop that should be called to stop |
michael@0 | 424 | // the http server. |
michael@0 | 425 | // NB: Because generating OCSP responses inside the HTTP request |
michael@0 | 426 | // handler can cause timeouts, the expected responses are pre-generated |
michael@0 | 427 | // all at once before starting the server. This means that their producedAt |
michael@0 | 428 | // times will all be the same. If a test depends on this not being the case, |
michael@0 | 429 | // perhaps calling startOCSPResponder twice (at different times) will be |
michael@0 | 430 | // necessary. |
michael@0 | 431 | // |
michael@0 | 432 | // serverPort is the port of the http OCSP responder |
michael@0 | 433 | // identity is the http hostname that will answer the OCSP requests |
michael@0 | 434 | // invalidIdentities is an array of identities that if used an |
michael@0 | 435 | // will cause a test failure |
michael@0 | 436 | // nssDBlocaion is the location of the NSS database from where the OCSP |
michael@0 | 437 | // responses will be generated (assumes appropiate keys are present) |
michael@0 | 438 | // expectedCertNames is an array of nicks of the certs to be responsed |
michael@0 | 439 | // expectedBasePaths is an optional array that is used to indicate |
michael@0 | 440 | // what is the expected base path of the OCSP request. |
michael@0 | 441 | function startOCSPResponder(serverPort, identity, invalidIdentities, |
michael@0 | 442 | nssDBLocation, expectedCertNames, |
michael@0 | 443 | expectedBasePaths, expectedMethods, |
michael@0 | 444 | expectedResponseTypes) { |
michael@0 | 445 | let ocspResponseGenerationArgs = expectedCertNames.map( |
michael@0 | 446 | function(expectedNick) { |
michael@0 | 447 | let responseType = "good"; |
michael@0 | 448 | if (expectedResponseTypes && expectedResponseTypes.length >= 1) { |
michael@0 | 449 | responseType = expectedResponseTypes.shift(); |
michael@0 | 450 | } |
michael@0 | 451 | return [responseType, expectedNick, "unused"]; |
michael@0 | 452 | } |
michael@0 | 453 | ); |
michael@0 | 454 | let ocspResponses = generateOCSPResponses(ocspResponseGenerationArgs, |
michael@0 | 455 | nssDBLocation); |
michael@0 | 456 | let httpServer = new HttpServer(); |
michael@0 | 457 | httpServer.registerPrefixHandler("/", |
michael@0 | 458 | function handleServerCallback(aRequest, aResponse) { |
michael@0 | 459 | invalidIdentities.forEach(function(identity) { |
michael@0 | 460 | do_check_neq(aRequest.host, identity) |
michael@0 | 461 | }); |
michael@0 | 462 | do_print("got request for: " + aRequest.path); |
michael@0 | 463 | let basePath = aRequest.path.slice(1).split("/")[0]; |
michael@0 | 464 | if (expectedBasePaths.length >= 1) { |
michael@0 | 465 | do_check_eq(basePath, expectedBasePaths.shift()); |
michael@0 | 466 | } |
michael@0 | 467 | do_check_true(expectedCertNames.length >= 1); |
michael@0 | 468 | if (expectedMethods && expectedMethods.length >= 1) { |
michael@0 | 469 | do_check_eq(aRequest.method, expectedMethods.shift()); |
michael@0 | 470 | } |
michael@0 | 471 | aResponse.setStatusLine(aRequest.httpVersion, 200, "OK"); |
michael@0 | 472 | aResponse.setHeader("Content-Type", "application/ocsp-response"); |
michael@0 | 473 | aResponse.write(ocspResponses.shift()); |
michael@0 | 474 | }); |
michael@0 | 475 | httpServer.identity.setPrimary("http", identity, serverPort); |
michael@0 | 476 | invalidIdentities.forEach(function(identity) { |
michael@0 | 477 | httpServer.identity.add("http", identity, serverPort); |
michael@0 | 478 | }); |
michael@0 | 479 | httpServer.start(serverPort); |
michael@0 | 480 | return { |
michael@0 | 481 | stop: function(callback) { |
michael@0 | 482 | // make sure we consumed each expected response |
michael@0 | 483 | do_check_eq(ocspResponses.length, 0); |
michael@0 | 484 | if (expectedBasePaths) { |
michael@0 | 485 | do_check_eq(expectedBasePaths.length, 0); |
michael@0 | 486 | } |
michael@0 | 487 | if (expectedResponseTypes) { |
michael@0 | 488 | do_check_eq(expectedResponseTypes.length, 0); |
michael@0 | 489 | } |
michael@0 | 490 | httpServer.stop(callback); |
michael@0 | 491 | } |
michael@0 | 492 | }; |
michael@0 | 493 | } |