Bug 1153134 - Part 1, establish TLS socket to encrypted control server. r=junior.
authorShih-Chiang Chien <schien@mozilla.com>
Mon, 18 Jul 2016 18:45:07 +0800
changeset 398690 10159ae9f4999a30952f17035c914bc0399e9231
parent 398689 6af03871248e7c9841d02d8af38ad9a1b2b7f5a2
child 398691 99869310b0ecaaa03693ab0438c0ca01c248003a
push id25600
push userbmo:tchiovoloni@mozilla.com
push dateTue, 09 Aug 2016 16:33:05 +0000
reviewersjunior
bugs1153134
milestone51.0a1
Bug 1153134 - Part 1, establish TLS socket to encrypted control server. r=junior. MozReview-Commit-ID: BamVPUoQP1r
dom/presentation/interfaces/nsIPresentationControlService.idl
dom/presentation/provider/DisplayDeviceProvider.cpp
dom/presentation/provider/LegacyMDNSDeviceProvider.cpp
dom/presentation/provider/MulticastDNSDeviceProvider.cpp
dom/presentation/provider/MulticastDNSDeviceProvider.h
dom/presentation/provider/PresentationControlService.js
dom/presentation/provider/nsTCPDeviceInfo.h
--- a/dom/presentation/interfaces/nsIPresentationControlService.idl
+++ b/dom/presentation/interfaces/nsIPresentationControlService.idl
@@ -15,16 +15,19 @@ interface nsIPresentationControlChannel;
  * The device information required for establishing control channel.
  */
 [scriptable, uuid(296fd171-e4d0-4de0-99ff-ad8ed52ddef3)]
 interface nsITCPDeviceInfo: nsISupports
 {
   readonly attribute AUTF8String id;
   readonly attribute AUTF8String address;
   readonly attribute uint16_t port;
+  // SHA-256 fingerprint of server certificate. Empty string represents
+  // server doesn't support TLS or not available.
+  readonly attribute AUTF8String certFingerprint;
 };
 
 [scriptable, uuid(09bddfaf-fcc2-4dc9-b33e-a509a1c2fb6d)]
 interface nsIPresentationControlServerListener: nsISupports
 {
   /**
    * Callback while the server socket changes port.
    * This event won't be cached so you should get current port after setting
--- a/dom/presentation/provider/DisplayDeviceProvider.cpp
+++ b/dom/presentation/provider/DisplayDeviceProvider.cpp
@@ -501,16 +501,17 @@ DisplayDeviceProvider::Connect(HDMIDispl
 {
   MOZ_ASSERT(aDevice);
   MOZ_ASSERT(mPresentationService);
   NS_ENSURE_ARG_POINTER(aControlChannel);
   *aControlChannel = nullptr;
 
   nsCOMPtr<nsITCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
                                                             aDevice->Address(),
-                                                            mPort);
+                                                            mPort,
+                                                            EmptyCString());
 
   return mPresentationService->Connect(deviceInfo, aControlChannel);
 }
 
 } // namespace presentation
 } // namespace dom
 } // namespace mozilla
--- a/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/LegacyMDNSDeviceProvider.cpp
@@ -200,17 +200,18 @@ nsresult
 LegacyMDNSDeviceProvider::Connect(Device* aDevice,
                                   nsIPresentationControlChannel** aRetVal)
 {
   MOZ_ASSERT(aDevice);
   MOZ_ASSERT(mPresentationService);
 
   RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
                                                        aDevice->Address(),
-                                                       aDevice->Port());
+                                                       aDevice->Port(),
+                                                       EmptyCString());
 
   return mPresentationService->Connect(deviceInfo, aRetVal);
 }
 
 nsresult
 LegacyMDNSDeviceProvider::AddDevice(const nsACString& aId,
                                     const nsACString& aServiceName,
                                     const nsACString& aServiceType,
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
@@ -22,16 +22,17 @@
 
 #define PREF_PRESENTATION_DISCOVERY "dom.presentation.discovery.enabled"
 #define PREF_PRESENTATION_DISCOVERY_TIMEOUT_MS "dom.presentation.discovery.timeout_ms"
 #define PREF_PRESENTATION_DISCOVERABLE "dom.presentation.discoverable"
 #define PREF_PRESENTATION_DEVICE_NAME "dom.presentation.device.name"
 
 #define SERVICE_TYPE "_presentation-ctrl._tcp"
 #define PROTOCOL_VERSION_TAG "version"
+#define CERT_FINGERPRINT_TAG "certFingerprint"
 
 static mozilla::LazyLogModule sMulticastDNSProviderLogModule("MulticastDNSDeviceProvider");
 
 #undef LOG_I
 #define LOG_I(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Debug, (__VA_ARGS__))
 #undef LOG_E
 #define LOG_E(...) MOZ_LOG(sMulticastDNSProviderLogModule, mozilla::LogLevel::Error, (__VA_ARGS__))
 
@@ -317,17 +318,18 @@ nsresult
 MulticastDNSDeviceProvider::Connect(Device* aDevice,
                                     nsIPresentationControlChannel** aRetVal)
 {
   MOZ_ASSERT(aDevice);
   MOZ_ASSERT(mPresentationService);
 
   RefPtr<TCPDeviceInfo> deviceInfo = new TCPDeviceInfo(aDevice->Id(),
                                                        aDevice->Address(),
-                                                       aDevice->Port());
+                                                       aDevice->Port(),
+                                                       aDevice->CertFingerprint());
 
   return mPresentationService->Connect(deviceInfo, aRetVal);
 }
 
 bool
 MulticastDNSDeviceProvider::IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo)
 {
   MOZ_ASSERT(aServiceInfo);
@@ -353,26 +355,28 @@ MulticastDNSDeviceProvider::IsCompatible
   return isCompatible;
 }
 
 nsresult
 MulticastDNSDeviceProvider::AddDevice(const nsACString& aId,
                                       const nsACString& aServiceName,
                                       const nsACString& aServiceType,
                                       const nsACString& aAddress,
-                                      const uint16_t aPort)
+                                      const uint16_t aPort,
+                                      const nsACString& aCertFingerprint)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mPresentationService);
 
   RefPtr<Device> device = new Device(aId, /* ID */
                                      aServiceName,
                                      aServiceType,
                                      aAddress,
                                      aPort,
+                                     aCertFingerprint,
                                      DeviceState::eActive,
                                      this);
 
   nsCOMPtr<nsIPresentationDeviceListener> listener;
   if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
     Unused << listener->AddDevice(device);
   }
 
@@ -381,27 +385,28 @@ MulticastDNSDeviceProvider::AddDevice(co
   return NS_OK;
 }
 
 nsresult
 MulticastDNSDeviceProvider::UpdateDevice(const uint32_t aIndex,
                                          const nsACString& aServiceName,
                                          const nsACString& aServiceType,
                                          const nsACString& aAddress,
-                                         const uint16_t aPort)
+                                         const uint16_t aPort,
+                                         const nsACString& aCertFingerprint)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mPresentationService);
 
   if (NS_WARN_IF(aIndex >= mDevices.Length())) {
     return NS_ERROR_INVALID_ARG;
   }
 
   RefPtr<Device> device = mDevices[aIndex];
-  device->Update(aServiceName, aServiceType, aAddress, aPort);
+  device->Update(aServiceName, aServiceType, aAddress, aPort, aCertFingerprint);
   device->ChangeState(DeviceState::eActive);
 
   nsCOMPtr<nsIPresentationDeviceListener> listener;
   if (NS_SUCCEEDED(GetListener(getter_AddRefs(listener))) && listener) {
     Unused << listener->UpdateDevice(device);
   }
 
   return NS_OK;
@@ -436,16 +441,17 @@ MulticastDNSDeviceProvider::FindDeviceBy
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<Device> device = new Device(aId,
                                      /* aName = */ EmptyCString(),
                                      /* aType = */ EmptyCString(),
                                      /* aHost = */ EmptyCString(),
                                      /* aPort = */ 0,
+                                     /* aCertFingerprint */ EmptyCString(),
                                      /* aState = */ DeviceState::eUnknown,
                                      /* aProvider = */ nullptr);
   size_t index = mDevices.IndexOf(device, 0, DeviceIdComparator());
 
   if (index == mDevices.NoIndex) {
     return false;
   }
 
@@ -459,16 +465,17 @@ MulticastDNSDeviceProvider::FindDeviceBy
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<Device> device = new Device(/* aId = */ EmptyCString(),
                                      /* aName = */ EmptyCString(),
                                      /* aType = */ EmptyCString(),
                                      aAddress,
                                      /* aPort = */ 0,
+                                     /* aCertFingerprint */ EmptyCString(),
                                      /* aState = */ DeviceState::eUnknown,
                                      /* aProvider = */ nullptr);
   size_t index = mDevices.IndexOf(device, 0, DeviceAddressComparator());
 
   if (index == mDevices.NoIndex) {
     return false;
   }
 
@@ -826,29 +833,41 @@ MulticastDNSDeviceProvider::OnServiceRes
     return rv;
   }
 
   nsAutoCString serviceType;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceType(serviceType)))) {
     return rv;
   }
 
+  nsCOMPtr<nsIPropertyBag2> propBag;
+  if (NS_WARN_IF(NS_FAILED(
+          aServiceInfo->GetAttributes(getter_AddRefs(propBag)))) || !propBag) {
+    return rv;
+  }
+
+  nsAutoCString certFingerprint;
+  Unused << propBag->GetPropertyAsACString(NS_LITERAL_STRING(CERT_FINGERPRINT_TAG),
+                                           certFingerprint);
+
   uint32_t index;
   if (FindDeviceById(host, index)) {
     return UpdateDevice(index,
                         serviceName,
                         serviceType,
                         address,
-                        port);
+                        port,
+                        certFingerprint);
   } else {
     return AddDevice(host,
                      serviceName,
                      serviceType,
                      address,
-                     port);
+                     port,
+                     certFingerprint);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MulticastDNSDeviceProvider::OnResolveFailed(nsIDNSServiceInfo* aServiceInfo,
                                             int32_t aErrorCode)
@@ -893,16 +912,17 @@ MulticastDNSDeviceProvider::GetOrCreateD
     uint16_t port;
     Unused << aDeviceInfo->GetPort(&port);
 
     device = new Device(id,
                         /* aName = */ id,
                         /* aType = */ EmptyCString(),
                         address,
                         port,
+                        /* aCertFingerprint */ EmptyCString(),
                         DeviceState::eActive,
                         /* aProvider = */ nullptr);
   }
 
   return device.forget();
 }
 
 NS_IMETHODIMP
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.h
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.h
@@ -61,23 +61,25 @@ private:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIPRESENTATIONDEVICE
 
     explicit Device(const nsACString& aId,
                     const nsACString& aName,
                     const nsACString& aType,
                     const nsACString& aAddress,
                     const uint16_t aPort,
+                    const nsACString& aCertFingerprint,
                     DeviceState aState,
                     MulticastDNSDeviceProvider* aProvider)
       : mId(aId)
       , mName(aName)
       , mType(aType)
       , mAddress(aAddress)
       , mPort(aPort)
+      , mCertFingerprint(aCertFingerprint)
       , mState(aState)
       , mProvider(aProvider)
     {
     }
 
     const nsCString& Id() const
     {
       return mId;
@@ -88,45 +90,53 @@ private:
       return mAddress;
     }
 
     uint16_t Port() const
     {
       return mPort;
     }
 
+    const nsCString& CertFingerprint() const
+    {
+      return mCertFingerprint;
+    }
+
     DeviceState State() const
     {
       return mState;
     }
 
     void ChangeState(DeviceState aState)
     {
       mState = aState;
     }
 
     void Update(const nsACString& aName,
                 const nsACString& aType,
                 const nsACString& aAddress,
-                const uint16_t aPort)
+                const uint16_t aPort,
+                const nsACString& aCertFingerprint)
     {
       mName = aName;
       mType = aType;
       mAddress = aAddress;
       mPort = aPort;
+      mCertFingerprint = aCertFingerprint;
     }
 
   private:
     virtual ~Device() = default;
 
     nsCString mId;
     nsCString mName;
     nsCString mType;
     nsCString mAddress;
     uint16_t mPort;
+    nsCString mCertFingerprint;
     DeviceState mState;
     MulticastDNSDeviceProvider* mProvider;
   };
 
   struct DeviceIdComparator {
     bool Equals(const RefPtr<Device>& aA, const RefPtr<Device>& aB) const {
       return aA->Id() == aB->Id();
     }
@@ -146,22 +156,24 @@ private:
                    nsIPresentationControlChannel** aRetVal);
   bool IsCompatibleServer(nsIDNSServiceInfo* aServiceInfo);
 
   // device manipulation
   nsresult AddDevice(const nsACString& aId,
                      const nsACString& aServiceName,
                      const nsACString& aServiceType,
                      const nsACString& aAddress,
-                     const uint16_t aPort);
+                     const uint16_t aPort,
+                     const nsACString& aCertFingerprint);
   nsresult UpdateDevice(const uint32_t aIndex,
                         const nsACString& aServiceName,
                         const nsACString& aServiceType,
                         const nsACString& aAddress,
-                        const uint16_t aPort);
+                        const uint16_t aPort,
+                        const nsACString& aCertFingerprint);
   nsresult RemoveDevice(const uint32_t aIndex);
   bool FindDeviceById(const nsACString& aId,
                       uint32_t& aIndex);
 
   bool FindDeviceByAddress(const nsACString& aAddress,
                            uint32_t& aIndex);
 
   already_AddRefed<Device>
--- a/dom/presentation/provider/PresentationControlService.js
+++ b/dom/presentation/provider/PresentationControlService.js
@@ -23,20 +23,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 const kProtocolVersion = 1; // need to review isCompatibleServer while fiddling the version number.
 
 const DEBUG = Services.prefs.getBoolPref("dom.presentation.tcp_server.debug");
 function log(aMsg) {
   dump("-*- PresentationControlService.js: " + aMsg + "\n");
 }
 
-function TCPDeviceInfo(aAddress, aPort, aId) {
+function TCPDeviceInfo(aAddress, aPort, aId, aCertFingerprint) {
   this.address = aAddress;
   this.port = aPort;
   this.id = aId;
+  this.certFingerprint = aCertFingerprint || "";
 }
 
 function PresentationControlService() {
   this._id = null;
   this._port = 0;
   this._serverSocket = null;
 }
 
@@ -122,35 +123,58 @@ PresentationControlService.prototype = {
 
   connect: function(aDeviceInfo) {
     if (!this.id) {
       DEBUG && log("PresentationControlService - Id has not initialized; connect fails"); // jshint ignore:line
       return null;
     }
     DEBUG && log("PresentationControlService - connect to " + aDeviceInfo.id); // jshint ignore:line
 
+    let socketTransport = this._attemptConnect(aDeviceInfo);
+    return new TCPControlChannel(this,
+                                 socketTransport,
+                                 aDeviceInfo,
+                                 "sender");
+  },
+
+  _attemptConnect: function(aDeviceInfo) {
     let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
                 .getService(Ci.nsISocketTransportService);
 
     let socketTransport;
     try {
-      socketTransport = sts.createTransport(null,
-                                            0,
-                                            aDeviceInfo.address,
-                                            aDeviceInfo.port,
-                                            null);
+      if (aDeviceInfo.certFingerprint) {
+        let overrideService = Cc["@mozilla.org/security/certoverride;1"]
+                                .getService(Ci.nsICertOverrideService);
+        overrideService.rememberTemporaryValidityOverrideUsingFingerprint(
+            aDeviceInfo.address,
+            aDeviceInfo.port,
+            aDeviceInfo.certFingerprint,
+            Ci.nsICertOverrideService.ERROR_UNTRUSTED | Ci.nsICertOverrideService.ERROR_MISMATCH);
+
+        socketTransport = sts.createTransport(["ssl"],
+                                              1,
+                                              aDeviceInfo.address,
+                                              aDeviceInfo.port,
+                                              null);
+      } else {
+        socketTransport = sts.createTransport(null,
+                                              0,
+                                              aDeviceInfo.address,
+                                              aDeviceInfo.port,
+                                              null);
+      }
+      // Shorten the connection failure procedure.
+      socketTransport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 2);
     } catch (e) {
       DEBUG && log("PresentationControlService - createTransport throws: " + e);  // jshint ignore:line
       // Pop the exception to |TCPDevice.establishControlChannel|
       throw Cr.NS_ERROR_FAILURE;
     }
-    return new TCPControlChannel(this,
-                                 socketTransport,
-                                 aDeviceInfo,
-                                 "sender");
+    return socketTransport;
   },
 
   responseSession: function(aDeviceInfo, aSocketTransport) {
     if (!this._isServiceInit()) {
       DEBUG && log("PresentationControlService - should never receive remote " +
                    "session request before server socket initialization"); // jshint ignore:line
       return null;
     }
--- a/dom/presentation/provider/nsTCPDeviceInfo.h
+++ b/dom/presentation/provider/nsTCPDeviceInfo.h
@@ -14,29 +14,32 @@ namespace presentation {
 class TCPDeviceInfo final : public nsITCPDeviceInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITCPDEVICEINFO
 
   explicit TCPDeviceInfo(const nsACString& aId,
                          const nsACString& aAddress,
-                         const uint16_t aPort)
+                         const uint16_t aPort,
+                         const nsACString& aCertFingerprint)
     : mId(aId)
     , mAddress(aAddress)
     , mPort(aPort)
+    , mCertFingerprint(aCertFingerprint)
   {
   }
 
 private:
   virtual ~TCPDeviceInfo() {}
 
   nsCString mId;
   nsCString mAddress;
   uint16_t mPort;
+  nsCString mCertFingerprint;
 };
 
 NS_IMPL_ISUPPORTS(TCPDeviceInfo,
                   nsITCPDeviceInfo)
 
 // nsITCPDeviceInfo
 NS_IMETHODIMP
 TCPDeviceInfo::GetId(nsACString& aId)
@@ -54,14 +57,21 @@ TCPDeviceInfo::GetAddress(nsACString& aA
 
 NS_IMETHODIMP
 TCPDeviceInfo::GetPort(uint16_t* aPort)
 {
   *aPort = mPort;
   return NS_OK;
 }
 
-}
+NS_IMETHODIMP
+TCPDeviceInfo::GetCertFingerprint(nsACString& aCertFingerprint)
+{
+  aCertFingerprint = mCertFingerprint;
+  return NS_OK;
 }
-}
+
+} // namespace presentation
+} // namespace dom
+} // namespace mozilla
 
 #endif /* !__TCPDeviceInfo_h__ */