Bug 1103120 - Part 15: Client: Compare server cert to advertisement. r=past
authorJ. Ryan Stinnett <jryans@gmail.com>
Mon, 26 Jan 2015 12:47:13 -0600
changeset 225857 77d19984fec7f0bce8d13dc07f65f6249e32e523
parent 225856 eba034ecc1287e8c1af9d46eb76088b2fdf08aec
child 225858 e815a6d601eb0f6ea2b2b2c76dfec44a39ac86f8
push id28176
push userryanvm@gmail.com
push dateMon, 26 Jan 2015 21:48:45 +0000
treeherdermozilla-central@38e4719e71af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs1103120
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1103120 - Part 15: Client: Compare server cert to advertisement. r=past
toolkit/devtools/security/auth.js
toolkit/devtools/security/socket.js
--- a/toolkit/devtools/security/auth.js
+++ b/toolkit/devtools/security/auth.js
@@ -4,16 +4,18 @@
  * 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";
 
 let { Ci, Cc } = require("chrome");
 let Services = require("Services");
 let promise = require("promise");
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
+let { dumpn, dumpv } = DevToolsUtils;
 loader.lazyRequireGetter(this, "prompt",
   "devtools/toolkit/security/prompt");
 loader.lazyRequireGetter(this, "cert",
   "devtools/toolkit/security/cert");
 
 /**
  * A simple enum-like object with keys mirrored to values.
  * This makes comparison to a specfic value simpler without having to repeat and
@@ -87,16 +89,37 @@ let Prompt = Authenticators.Prompt = {};
 Prompt.mode = "PROMPT";
 
 Prompt.Client = function() {};
 Prompt.Client.prototype = {
 
   mode: Prompt.mode,
 
   /**
+   * When client has just made a new socket connection, validate the connection
+   * to ensure it meets the authenticator's policies.
+   *
+   * @param host string
+   *        The host name or IP address of the debugger server.
+   * @param port number
+   *        The port number of the debugger server.
+   * @param encryption boolean (optional)
+   *        Whether the server requires encryption.  Defaults to false.
+   * @param cert object (optional)
+   *        The server's cert details.
+   * @param s nsISocketTransport
+   *        Underlying socket transport, in case more details are needed.
+   * @return boolean
+   *         Whether the connection is valid.
+   */
+  validateConnection() {
+    return true;
+  },
+
+  /**
    * Work with the server to complete any additional steps required by this
    * authenticator's policies.
    *
    * Debugging commences after this hook completes successfully.
    *
    * @param host string
    *        The host name or IP address of the debugger server.
    * @param port number
@@ -229,16 +252,48 @@ let OOBCert = Authenticators.OOBCert = {
 OOBCert.mode = "OOB_CERT";
 
 OOBCert.Client = function() {};
 OOBCert.Client.prototype = {
 
   mode: OOBCert.mode,
 
   /**
+   * When client has just made a new socket connection, validate the connection
+   * to ensure it meets the authenticator's policies.
+   *
+   * @param host string
+   *        The host name or IP address of the debugger server.
+   * @param port number
+   *        The port number of the debugger server.
+   * @param encryption boolean (optional)
+   *        Whether the server requires encryption.  Defaults to false.
+   * @param cert object (optional)
+   *        The server's cert details.
+   * @param socket nsISocketTransport
+   *        Underlying socket transport, in case more details are needed.
+   * @return boolean
+   *         Whether the connection is valid.
+   */
+  validateConnection({ cert, socket }) {
+    // Step B.7
+    // Client verifies that Server's cert matches hash(ServerCert) from the
+    // advertisement
+    dumpv("Validate server cert hash");
+    let serverCert = socket.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                           .SSLStatus.serverCert;
+    let advertisedCert = cert;
+    if (serverCert.sha256Fingerprint != advertisedCert.sha256) {
+      dumpn("Server cert hash doesn't match advertisement");
+      return false;
+    }
+    return true;
+  },
+
+  /**
    * Work with the server to complete any additional steps required by this
    * authenticator's policies.
    *
    * Debugging commences after this hook completes successfully.
    *
    * @param host string
    *        The host name or IP address of the debugger server.
    * @param port number
--- a/toolkit/devtools/security/socket.js
+++ b/toolkit/devtools/security/socket.js
@@ -67,22 +67,22 @@ let DebuggerSocket = {};
  *        |Authenticator| instance matching the mode in use by the server.
  *        Defaults to a PROMPT instance if not supplied.
  * @param cert object (optional)
  *        The server's cert details.  Used with OOB_CERT authentication.
  * @return promise
  *         Resolved to a DebuggerTransport instance.
  */
 DebuggerSocket.connect = Task.async(function*(settings) {
+  // Default to PROMPT |Authenticator| instance if not supplied
+  if (!settings.authenticator) {
+    settings.authenticator = new (Authenticators.get().Client)();
+  }
   let { host, port, encryption, authenticator, cert } = settings;
   let transport = yield _getTransport(settings);
-
-  // Default to PROMPT |Authenticator| instance if not supplied
-  authenticator = authenticator || new (Authenticators.get().Client)();
-
   yield authenticator.authenticate({
     host,
     port,
     encryption,
     cert,
     transport
   });
   return transport;
@@ -93,16 +93,21 @@ DebuggerSocket.connect = Task.async(func
  * connect attempts in the process.
  *
  * @param host string
  *        The host name or IP address of the debugger server.
  * @param port number
  *        The port number of the debugger server.
  * @param encryption boolean (optional)
  *        Whether the server requires encryption.  Defaults to false.
+ * @param authenticator Authenticator
+ *        |Authenticator| instance matching the mode in use by the server.
+ *        Defaults to a PROMPT instance if not supplied.
+ * @param cert object (optional)
+ *        The server's cert details.  Used with OOB_CERT authentication.
  * @return transport DebuggerTransport
  *         A possible DevTools transport (if connection succeeded and streams
  *         are actually alive and working)
  * @return certError boolean
  *         Flag noting if cert trouble caused the streams to fail
  * @return s nsISocketTransport
  *         Underlying socket transport, in case more details are needed.
  */
@@ -136,34 +141,50 @@ let _getTransport = Task.async(function*
  * after storing a cert override.
  *
  * @param host string
  *        The host name or IP address of the debugger server.
  * @param port number
  *        The port number of the debugger server.
  * @param encryption boolean (optional)
  *        Whether the server requires encryption.  Defaults to false.
+ * @param authenticator Authenticator
+ *        |Authenticator| instance matching the mode in use by the server.
+ *        Defaults to a PROMPT instance if not supplied.
+ * @param cert object (optional)
+ *        The server's cert details.  Used with OOB_CERT authentication.
  * @return transport DebuggerTransport
  *         A possible DevTools transport (if connection succeeded and streams
  *         are actually alive and working)
  * @return certError boolean
  *         Flag noting if cert trouble caused the streams to fail
  * @return s nsISocketTransport
  *         Underlying socket transport, in case more details are needed.
  */
-let _attemptTransport = Task.async(function*({ host, port, encryption }) {
+let _attemptTransport = Task.async(function*(settings) {
+  let { authenticator } = settings;
   // _attemptConnect only opens the streams.  Any failures at that stage
   // aborts the connection process immedidately.
-  let { s, input, output } = yield _attemptConnect({ host, port, encryption });
+  let { s, input, output } = yield _attemptConnect(settings);
 
   // Check if the input stream is alive.  If encryption is enabled, we need to
   // watch out for cert errors by testing the input stream.
   let { alive, certError } = yield _isInputAlive(input);
   dumpv("Server cert accepted? " + !certError);
 
+  // The |Authenticator| examines the connection as well and may determine it
+  // should be dropped.
+  alive = alive && authenticator.validateConnection({
+    host: settings.host,
+    port: settings.port,
+    encryption: settings.encryption,
+    cert: settings.cert,
+    socket: s
+  });
+
   let transport;
   if (alive) {
     transport = new DebuggerTransport(input, output);
   } else {
     // Something went wrong, close the streams.
     input.close();
     output.close();
   }