1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/WebAuthnUtil.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsICryptoHash.h"
10 #include "nsIEffectiveTLDService.h"
11 #include "nsNetUtil.h"
12 #include "mozpkix/pkixutil.h"
13 #include "nsHTMLDocument.h"
16 namespace mozilla::dom
{
18 // Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
19 constexpr auto kGoogleAccountsAppId1
=
20 u
"https://www.gstatic.com/securitykey/origins.json"_ns
;
21 constexpr auto kGoogleAccountsAppId2
=
22 u
"https://www.gstatic.com/securitykey/a/google.com/origins.json"_ns
;
24 bool EvaluateAppID(nsPIDOMWindowInner
* aParent
, const nsString
& aOrigin
,
25 /* in/out */ nsString
& aAppId
) {
26 // Facet is the specification's way of referring to the web origin.
27 nsAutoCString facetString
= NS_ConvertUTF16toUTF8(aOrigin
);
28 nsCOMPtr
<nsIURI
> facetUri
;
29 if (NS_FAILED(NS_NewURI(getter_AddRefs(facetUri
), facetString
))) {
33 // If the facetId (origin) is not HTTPS, reject
34 if (!facetUri
->SchemeIs("https")) {
38 // If the appId is empty or null, overwrite it with the facetId and accept
39 if (aAppId
.IsEmpty() || aAppId
.EqualsLiteral("null")) {
40 aAppId
.Assign(aOrigin
);
44 // AppID is user-supplied. It's quite possible for this parse to fail.
45 nsAutoCString appIdString
= NS_ConvertUTF16toUTF8(aAppId
);
46 nsCOMPtr
<nsIURI
> appIdUri
;
47 if (NS_FAILED(NS_NewURI(getter_AddRefs(appIdUri
), appIdString
))) {
51 // if the appId URL is not HTTPS, reject.
52 if (!appIdUri
->SchemeIs("https")) {
56 nsAutoCString appIdHost
;
57 if (NS_FAILED(appIdUri
->GetAsciiHost(appIdHost
))) {
62 if (appIdHost
.EqualsLiteral("localhost")) {
63 nsAutoCString facetHost
;
64 if (NS_FAILED(facetUri
->GetAsciiHost(facetHost
))) {
68 if (facetHost
.EqualsLiteral("localhost")) {
73 // Run the HTML5 algorithm to relax the same-origin policy, copied from W3C
74 // Web Authentication. See Bug 1244959 comment #8 for context on why we are
75 // doing this instead of implementing the external-fetch FacetID logic.
76 nsCOMPtr
<Document
> document
= aParent
->GetDoc();
77 if (!document
|| !document
->IsHTMLOrXHTML()) {
81 nsHTMLDocument
* html
= document
->AsHTMLDocument();
82 // Use the base domain as the facet for evaluation. This lets this algorithm
83 // relax the whole eTLD+1.
84 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
85 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
90 nsAutoCString lowestFacetHost
;
91 if (NS_FAILED(tldService
->GetBaseDomain(facetUri
, 0, lowestFacetHost
))) {
95 if (html
->IsRegistrableDomainSuffixOfOrEqualTo(
96 NS_ConvertUTF8toUTF16(lowestFacetHost
), appIdHost
)) {
100 // Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
101 if (lowestFacetHost
.EqualsLiteral("google.com") &&
102 (aAppId
.Equals(kGoogleAccountsAppId1
) ||
103 aAppId
.Equals(kGoogleAccountsAppId2
))) {
110 static nsresult
HashCString(nsICryptoHash
* aHashService
, const nsACString
& aIn
,
111 /* out */ nsTArray
<uint8_t>& aOut
) {
112 MOZ_ASSERT(aHashService
);
114 nsresult rv
= aHashService
->Init(nsICryptoHash::SHA256
);
115 if (NS_WARN_IF(NS_FAILED(rv
))) {
119 rv
= aHashService
->Update(
120 reinterpret_cast<const uint8_t*>(aIn
.BeginReading()), aIn
.Length());
121 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 nsAutoCString fullHash
;
126 // Passing false below means we will get a binary result rather than a
127 // base64-encoded string.
128 rv
= aHashService
->Finish(false, fullHash
);
129 if (NS_WARN_IF(NS_FAILED(rv
))) {
134 aOut
.AppendElements(reinterpret_cast<uint8_t const*>(fullHash
.BeginReading()),
140 nsresult
HashCString(const nsACString
& aIn
, /* out */ nsTArray
<uint8_t>& aOut
) {
142 nsCOMPtr
<nsICryptoHash
> hashService
=
143 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID
, &srv
);
144 if (NS_FAILED(srv
)) {
148 srv
= HashCString(hashService
, aIn
, aOut
);
149 if (NS_WARN_IF(NS_FAILED(srv
))) {
150 return NS_ERROR_FAILURE
;
156 } // namespace mozilla::dom