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