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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "AddonManagerWebAPI.h"
8
9 #include "mozilla/BasePrincipal.h"
10 #include "mozilla/dom/Navigator.h"
11 #include "mozilla/dom/NavigatorBinding.h"
12
13 #include "mozilla/Preferences.h"
14 #include "nsGlobalWindow.h"
15 #include "xpcpublic.h"
16
17 #include "nsIDocShell.h"
18 #include "nsIScriptObjectPrincipal.h"
19
20 namespace mozilla {
21 using namespace mozilla::dom;
22
IsValidHost(const nsACString & host)23 static bool IsValidHost(const nsACString& host) {
24 // This hidden pref allows users to disable mozAddonManager entirely if they
25 // want for fingerprinting resistance. Someone like Tor browser will use this
26 // pref.
27 if (Preferences::GetBool(
28 "privacy.resistFingerprinting.block_mozAddonManager")) {
29 return false;
30 }
31
32 if (host.EqualsLiteral("addons.mozilla.org") ||
33 host.EqualsLiteral("discovery.addons.mozilla.org")) {
34 return true;
35 }
36
37 // When testing allow access to the developer sites.
38 if (Preferences::GetBool("extensions.webapi.testing", false)) {
39 if (host.LowerCaseEqualsLiteral("addons.allizom.org") ||
40 host.LowerCaseEqualsLiteral("discovery.addons.allizom.org") ||
41 host.LowerCaseEqualsLiteral("addons-dev.allizom.org") ||
42 host.LowerCaseEqualsLiteral("discovery.addons-dev.allizom.org") ||
43 host.LowerCaseEqualsLiteral("example.com")) {
44 return true;
45 }
46 }
47
48 return false;
49 }
50
51 // Checks if the given uri is secure and matches one of the hosts allowed to
52 // access the API.
IsValidSite(nsIURI * uri)53 bool AddonManagerWebAPI::IsValidSite(nsIURI* uri) {
54 if (!uri) {
55 return false;
56 }
57
58 if (!uri->SchemeIs("https")) {
59 if (!(xpc::IsInAutomation() &&
60 Preferences::GetBool("extensions.webapi.testing.http", false))) {
61 return false;
62 }
63 }
64
65 nsAutoCString host;
66 nsresult rv = uri->GetHost(host);
67 if (NS_FAILED(rv)) {
68 return false;
69 }
70
71 return IsValidHost(host);
72 }
73
74 #ifndef ANDROID
IsAPIEnabled(JSContext * aCx,JSObject * aGlobal)75 bool AddonManagerWebAPI::IsAPIEnabled(JSContext* aCx, JSObject* aGlobal) {
76 MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
77 nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(aGlobal);
78 if (!win) {
79 return false;
80 }
81
82 // Check that the current window and all parent frames are allowed access to
83 // the API.
84 while (win) {
85 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(win);
86 if (!sop) {
87 return false;
88 }
89
90 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
91 if (!principal) {
92 return false;
93 }
94
95 // Reaching a window with a system principal means we have reached
96 // privileged UI of some kind so stop at this point and allow access.
97 if (principal->IsSystemPrincipal()) {
98 return true;
99 }
100
101 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
102 if (!docShell) {
103 // This window has been torn down so don't allow access to the API.
104 return false;
105 }
106
107 if (!IsValidSite(win->GetDocumentURI())) {
108 return false;
109 }
110
111 // Checks whether there is a parent frame of the same type. This won't cross
112 // mozbrowser or chrome or fission/process boundaries.
113 nsCOMPtr<nsIDocShellTreeItem> parent;
114 nsresult rv = docShell->GetInProcessSameTypeParent(getter_AddRefs(parent));
115 if (NS_FAILED(rv)) {
116 return false;
117 }
118
119 // No parent means we've hit a mozbrowser or chrome or process boundary.
120 if (!parent) {
121 // With Fission, a cross-origin iframe has an out-of-process parent, but
122 // DocShell knows nothing about it. We need to ask BrowsingContext here,
123 // and only allow API access if AMO is actually at the top, not framed
124 // by evilleagueofevil.com.
125 return docShell->GetBrowsingContext()->IsTopContent();
126 }
127
128 Document* doc = win->GetDoc();
129 if (!doc) {
130 return false;
131 }
132
133 doc = doc->GetInProcessParentDocument();
134 if (!doc) {
135 // Getting here means something has been torn down so fail safe.
136 return false;
137 }
138
139 win = doc->GetInnerWindow();
140 }
141
142 // Found a document with no inner window, don't grant access to the API.
143 return false;
144 }
145 #else // We don't support mozAddonManager on Android
IsAPIEnabled(JSContext * aCx,JSObject * aGlobal)146 bool AddonManagerWebAPI::IsAPIEnabled(JSContext* aCx, JSObject* aGlobal) {
147 return false;
148 }
149 #endif // ifndef ANDROID
150
151 namespace dom {
152
IsHostPermitted(const GlobalObject &,const nsAString & host)153 bool AddonManagerPermissions::IsHostPermitted(const GlobalObject& /*unused*/,
154 const nsAString& host) {
155 return IsValidHost(NS_ConvertUTF16toUTF8(host));
156 }
157
158 } // namespace dom
159
160 } // namespace mozilla
161