Bug 1555963 - Add WindowGlobalParent.getSecurityInfo(). r=nika,mconley

This adds an API for fetching security info per frame, no matter if we have
a certificate error or a valid certificate.

I tried to make this work in a Fission-compatible way, let me know if this
is the right approach.

Differential Revision: https://phabricator.services.mozilla.com/D34354

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Johann Hofmann 2019-06-21 05:58:40 +00:00
parent a494b08766
commit 90ecafd3e1
9 changed files with 166 additions and 1 deletions

View File

@ -42,6 +42,10 @@ tags = mcb
support-files =
file_csp_block_all_mixedcontent.html
file_csp_block_all_mixedcontent.js
[browser_deprecatedTLSVersions.js]
[browser_getSecurityInfo.js]
support-files =
dummy_iframe_page.html
[browser_identity_UI.js]
[browser_identityBlock_focus.js]
support-files = ../permissions/permissions.html
@ -115,4 +119,3 @@ support-files =
support-files =
file_mixedPassiveContent.html
file_bug1045809_1.html
[browser_deprecatedTLSVersions.js]

View File

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = MOZILLA_PKIX_ERROR_BASE + 14;
const IFRAME_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com") + "dummy_iframe_page.html";
// Tests the getSecurityInfo() function exposed on WindowGlobalParent.
add_task(async function test() {
await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
let loaded = BrowserTestUtils.waitForErrorPage(browser);
await BrowserTestUtils.loadURI(browser, "https://self-signed.example.com");
await loaded;
let securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
ok(securityInfo, "Found some security info");
ok(securityInfo.failedCertChain, "Has a failed cert chain");
is(securityInfo.errorCode, MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT, "Has the correct error code");
is(securityInfo.serverCert.commonName, "self-signed.example.com", "Has the correct certificate");
loaded = BrowserTestUtils.browserLoaded(browser);
await BrowserTestUtils.loadURI(browser, "http://example.com");
await loaded;
securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
is(securityInfo, null, "Found no security info");
loaded = BrowserTestUtils.browserLoaded(browser);
await BrowserTestUtils.loadURI(browser, "https://example.com");
await loaded;
securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
ok(securityInfo, "Found some security info");
ok(securityInfo.succeededCertChain, "Has a succeeded cert chain");
is(securityInfo.errorCode, 0, "Has no error code");
is(securityInfo.serverCert.commonName, "example.com", "Has the correct certificate");
loaded = BrowserTestUtils.browserLoaded(browser);
await BrowserTestUtils.loadURI(browser, IFRAME_PAGE);
await loaded;
// Get the info of the parent, which is HTTP.
securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo();
is(securityInfo, null, "Found no security info");
// Get the info of the frame, which is HTTPS.
securityInfo = await browser.browsingContext.getChildren()[0].currentWindowGlobal.getSecurityInfo();
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
ok(securityInfo, "Found some security info");
ok(securityInfo.succeededCertChain, "Has a succeeded cert chain");
is(securityInfo.errorCode, 0, "Has no error code");
is(securityInfo.serverCert.commonName, "example.com", "Has the correct certificate");
});
});

View File

@ -0,0 +1,10 @@
<html>
<head>
<title>Dummy iframe test page</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
</head>
<body>
<iframe src="https://example.org"></iframe>
<p>Dummy test page</p>
</body>
</html>

View File

@ -7,6 +7,7 @@ interface Principal;
interface URI;
interface nsIDocShell;
interface RemoteTab;
interface nsITransportSecurityInfo;
[Exposed=Window, ChromeOnly]
interface WindowGlobalParent {
@ -54,6 +55,18 @@ interface WindowGlobalParent {
Promise<unsigned long long> changeFrameRemoteness(
BrowsingContext? bc, DOMString remoteType,
unsigned long long pendingSwitchId);
/**
* Fetches the securityInfo object for this window. This function will
* look for failed and successful channels to find the security info,
* thus it will work on regular HTTPS pages as well as certificate
* error pages.
*
* This returns a Promise which resolves to an nsITransportSecurity
* object with certificate data or undefined if no security info is available.
*/
[Throws]
Promise<nsITransportSecurityInfo> getSecurityInfo();
};
[Exposed=Window, ChromeOnly]

View File

@ -40,6 +40,11 @@ child:
uint64_t aSwitchId)
returns (nsresult rv, nullable PBrowserBridge bridge);
/**
* Returns the serialized security info associated with this window.
*/
async GetSecurityInfo() returns(nsCString? serializedSecInfo);
both:
async RawMessage(JSWindowActorMessageMeta aMetadata, ClonedMessageData aData);

View File

@ -23,6 +23,7 @@
#include "nsGlobalWindowInner.h"
#include "nsFrameLoaderOwner.h"
#include "nsQueryObject.h"
#include "nsSerializationHelper.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorChild.h"
@ -253,6 +254,36 @@ IPCResult WindowGlobalChild::RecvChangeFrameRemoteness(
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalChild::RecvGetSecurityInfo(
GetSecurityInfoResolver&& aResolve) {
Maybe<nsCString> result;
if (nsCOMPtr<Document> doc = mWindowGlobal->GetDoc()) {
nsCOMPtr<nsISupports> secInfo;
nsresult rv = NS_OK;
// First check if there's a failed channel, in case of a certificate
// error.
if (nsIChannel* failedChannel = doc->GetFailedChannel()) {
rv = failedChannel->GetSecurityInfo(getter_AddRefs(secInfo));
} else {
// When there's no failed channel we should have a regular
// security info on the document. In some cases there's no
// security info at all, i.e. on HTTP sites.
secInfo = doc->GetSecurityInfo();
}
if (NS_SUCCEEDED(rv) && secInfo) {
nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfo);
result.emplace();
NS_SerializeToString(secInfoSer, result.ref());
}
}
aResolve(result);
return IPC_OK();
}
IPCResult WindowGlobalChild::RecvRawMessage(
const JSWindowActorMessageMeta& aMeta, const ClonedMessageData& aData) {
StructuredCloneData data;

View File

@ -103,6 +103,9 @@ class WindowGlobalChild final : public WindowGlobalActor,
dom::BrowsingContext* aBc, const nsString& aRemoteType,
uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
mozilla::ipc::IPCResult RecvGetSecurityInfo(
GetSecurityInfoResolver&& aResolve);
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:

View File

@ -27,6 +27,8 @@
#include "nsGlobalWindowInner.h"
#include "nsQueryObject.h"
#include "nsFrameLoaderOwner.h"
#include "nsSerializationHelper.h"
#include "nsITransportSecurityInfo.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorParent.h"
@ -319,6 +321,45 @@ already_AddRefed<Promise> WindowGlobalParent::ChangeFrameRemoteness(
return promise.forget();
}
already_AddRefed<Promise> WindowGlobalParent::GetSecurityInfo(
ErrorResult& aRv) {
RefPtr<BrowserParent> browserParent = GetBrowserParent();
if (NS_WARN_IF(!browserParent)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsIGlobalObject* global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
SendGetSecurityInfo(
[promise](Maybe<nsCString>&& aResult) {
if (aResult) {
nsCOMPtr<nsISupports> infoObj;
nsresult rv =
NS_DeserializeObject(aResult.value(), getter_AddRefs(infoObj));
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(NS_ERROR_FAILURE);
}
nsCOMPtr<nsITransportSecurityInfo> info = do_QueryInterface(infoObj);
if (!info) {
promise->MaybeReject(NS_ERROR_FAILURE);
}
promise->MaybeResolve(info);
} else {
promise->MaybeResolveWithUndefined();
}
},
[promise](ResponseRejectReason&& aReason) {
promise->MaybeReject(NS_ERROR_FAILURE);
});
return promise.forget();
}
void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
mIPCClosed = true;
gWindowGlobalParentsById->Remove(mInnerWindowId);

View File

@ -107,6 +107,8 @@ class WindowGlobalParent final : public WindowGlobalActor,
uint64_t aPendingSwitchId,
ErrorResult& aRv);
already_AddRefed<Promise> GetSecurityInfo(ErrorResult& aRv);
// Create a WindowGlobalParent from over IPC. This method should not be called
// from outside of the IPC constructors.
WindowGlobalParent(const WindowGlobalInit& aInit, bool aInProcess);