Bug 1153134 - Part 1, establish TLS socket to encrypted control server. r=junior.
MozReview-Commit-ID: BamVPUoQP1r
--- 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__ */