Bug 1103120 - Part 3: Server: Advertise cert for authentication. r=past
authorJ. Ryan Stinnett <jryans@gmail.com>
Mon, 26 Jan 2015 12:47:13 -0600
changeset 239165 b81f215db168e8dc19eb64e028d71d181c1fd388
parent 239164 c3b4fc516509a3b0cb1fca2e7b4a1673720b89c9
child 239166 8698f3b5a1b5ab233f2572d9fe6c59eccb06be51
push id487
push userbcampen@mozilla.com
push dateMon, 26 Jan 2015 23:32:56 +0000
reviewerspast
bugs1103120
milestone38.0a1
Bug 1103120 - Part 3: Server: Advertise cert for authentication. r=past
toolkit/devtools/security/auth.js
toolkit/devtools/security/socket.js
--- a/toolkit/devtools/security/auth.js
+++ b/toolkit/devtools/security/auth.js
@@ -36,16 +36,38 @@ Prompt.Client.prototype = {
 
 };
 
 Prompt.Server = function() {};
 Prompt.Server.prototype = {
 
   mode: Prompt.mode,
 
+  /**
+   * Verify that listener settings are appropriate for this authentication mode.
+   *
+   * @param listener SocketListener
+   *        The socket listener about to be opened.
+   * @throws if validation requirements are not met
+   */
+  validateOptions() {},
+
+  /**
+   * Augment the service discovery advertisement with any additional data needed
+   * to support this authentication mode.
+   *
+   * @param listener SocketListener
+   *        The socket listener that was just opened.
+   * @param advertisement object
+   *        The advertisement being built.
+   */
+  augmentAdvertisement(listener, advertisement) {
+    advertisement.authentication = Prompt.mode;
+  },
+
 };
 
 /**
  * The out-of-band (OOB) cert authenticator is based on self-signed X.509 certs
  * at both the client and server end.
  *
  * The user is first prompted to verify the connection, similar to the prompt
  * method above.  This prompt may display cert fingerprints if desired.
@@ -72,16 +94,48 @@ OOBCert.Client.prototype = {
 
 };
 
 OOBCert.Server = function() {};
 OOBCert.Server.prototype = {
 
   mode: OOBCert.mode,
 
+  /**
+   * Verify that listener settings are appropriate for this authentication mode.
+   *
+   * @param listener SocketListener
+   *        The socket listener about to be opened.
+   * @throws if validation requirements are not met
+   */
+  validateOptions(listener) {
+    if (!listener.encryption) {
+      throw new Error(OOBCert.mode + " authentication requires encryption.");
+    }
+  },
+
+  /**
+   * Augment the service discovery advertisement with any additional data needed
+   * to support this authentication mode.
+   *
+   * @param listener SocketListener
+   *        The socket listener that was just opened.
+   * @param advertisement object
+   *        The advertisement being built.
+   */
+  augmentAdvertisement(listener, advertisement) {
+    advertisement.authentication = OOBCert.mode;
+    // Step A.4
+    // Server announces itself via service discovery
+    // Announcement contains hash(ServerCert) as additional data
+    advertisement.cert = {
+      sha256: listener._socket.serverCert.sha256Fingerprint
+    };
+  },
+
 };
 
 exports.Authenticators = {
   get(mode) {
     if (!mode) {
       mode = Prompt.mode;
     }
     for (let key in Authenticators) {
--- a/toolkit/devtools/security/socket.js
+++ b/toolkit/devtools/security/socket.js
@@ -18,16 +18,18 @@ let { dumpn, dumpv } = DevToolsUtils;
 loader.lazyRequireGetter(this, "DebuggerTransport",
   "devtools/toolkit/transport/transport", true);
 loader.lazyRequireGetter(this, "DebuggerServer",
   "devtools/server/main", true);
 loader.lazyRequireGetter(this, "discovery",
   "devtools/toolkit/discovery/discovery");
 loader.lazyRequireGetter(this, "cert",
   "devtools/toolkit/security/cert");
+loader.lazyRequireGetter(this, "Authenticators",
+  "devtools/toolkit/security/auth", true);
 loader.lazyRequireGetter(this, "setTimeout", "Timer", true);
 loader.lazyRequireGetter(this, "clearTimeout", "Timer", true);
 
 DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
   return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
 });
 
 DevToolsUtils.defineLazyGetter(this, "socketTransportService", () => {
@@ -270,25 +272,35 @@ SocketListener.prototype = {
   discoverable: false,
 
   /**
    * Controls whether this listener's transport uses encryption.
    */
   encryption: false,
 
   /**
+   * Controls the |Authenticator| used, which hooks various socket steps to
+   * implement an authentication policy.  It is expected that different use
+   * cases may override pieces of the |Authenticator|.  See auth.js.
+   *
+   * Here we set the default |Authenticator|, which is |Prompt|.
+   */
+  authenticator: new (Authenticators.get().Server)(),
+
+  /**
    * Validate that all options have been set to a supported configuration.
    */
   _validateOptions: function() {
     if (this.portOrPath === null) {
       throw new Error("Must set a port / path to listen on.");
     }
     if (this.discoverable && !Number(this.portOrPath)) {
       throw new Error("Discovery only supported for TCP sockets.");
     }
+    this.authenticator.validateOptions(this);
   },
 
   /**
    * Listens on the given port or socket file for remote debugger connections.
    */
   open: function() {
     this._validateOptions();
     DebuggerServer._addListener(this);
@@ -312,29 +324,39 @@ SocketListener.prototype = {
           file.remove(false);
         }
         self._socket.initWithFilename(file, parseInt("666", 8), backlog);
       }
       yield self._setAdditionalSocketOptions();
       self._socket.asyncListen(self);
       dumpn("Socket listening on: " + (self.port || self.portOrPath));
     }).then(() => {
-      if (this.discoverable && this.port) {
-        discovery.addService("devtools", {
-          port: this.port,
-          encryption: this.encryption
-        });
-      }
+      this._advertise();
     }).catch(e => {
       dumpn("Could not start debugging listener on '" + this.portOrPath +
             "': " + e);
       this.close();
     });
   },
 
+  _advertise: function() {
+    if (!this.discoverable || !this.port) {
+      return;
+    }
+
+    let advertisement = {
+      port: this.port,
+      encryption: this.encryption,
+    };
+
+    this.authenticator.augmentAdvertisement(this, advertisement);
+
+    discovery.addService("devtools", advertisement);
+  },
+
   _createSocketInstance: function() {
     if (this.encryption) {
       return Cc["@mozilla.org/network/tls-server-socket;1"]
              .createInstance(Ci.nsITLSServerSocket);
     }
     return Cc["@mozilla.org/network/server-socket;1"]
            .createInstance(Ci.nsIServerSocket);
   },