1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5"use strict";
6
7var EXPORTED_SYMBOLS = ["RemotePageAccessManager"];
8
9/*
10 * Used for all kinds of permissions checks which requires explicit
11 * whitelisting of specific permissions granted through Remote Pages.
12 * An RPM function will be exported into the page only if it appears
13 * in the access managers's accessMap for that page's uri.
14 *
15 * This module may be used from both the child and parent process.
16 *
17 * Please note that prefs that one wants to update need to be
18 * whitelisted within AsyncPrefs.jsm.
19 */
20let RemotePageAccessManager = {
21  /* The accessMap lists the permissions that are allowed per page.
22   * The structure should be of the following form:
23   *   <URL> : {
24   *     <function name>: [<keys>],
25   *     ...
26   *   }
27   * For the page with given URL, permission is allowed for each
28   * listed function with a matching key. The first argument to the
29   * function must match one of the keys. If keys is an array with a
30   * single asterisk element ["*"], then all values are permitted.
31   */
32  accessMap: {
33    "about:certerror": {
34      RPMSendAsyncMessage: [
35        "Browser:EnableOnlineMode",
36        "Browser:ResetSSLPreferences",
37        "GetChangedCertPrefs",
38        "Browser:OpenCaptivePortalPage",
39        "Browser:SSLErrorGoBack",
40        "Browser:PrimeMitm",
41        "Browser:ResetEnterpriseRootsPref",
42        "DisplayOfflineSupportPage",
43      ],
44      RPMRecordTelemetryEvent: ["*"],
45      RPMAddMessageListener: ["*"],
46      RPMRemoveMessageListener: ["*"],
47      RPMGetFormatURLPref: ["app.support.baseURL"],
48      RPMGetBoolPref: [
49        "security.certerrors.mitm.priming.enabled",
50        "security.certerrors.permanentOverride",
51        "security.enterprise_roots.auto-enabled",
52        "security.certerror.hideAddException",
53      ],
54      RPMSetBoolPref: ["security.tls.version.enable-deprecated"],
55      RPMGetIntPref: [
56        "services.settings.clock_skew_seconds",
57        "services.settings.last_update_seconds",
58      ],
59      RPMGetAppBuildID: ["*"],
60      RPMGetInnerMostURI: ["*"],
61      RPMIsWindowPrivate: ["*"],
62      RPMAddToHistogram: ["*"],
63    },
64    "about:httpsonlyerror": {
65      RPMGetFormatURLPref: ["app.support.baseURL"],
66      RPMSendAsyncMessage: ["goBack", "openInsecure"],
67      RPMAddMessageListener: ["WWWReachable"],
68      RPMTryPingSecureWWWLink: ["*"],
69      RPMOpenSecureWWWLink: ["*"],
70    },
71    "about:certificate": {
72      RPMSendQuery: ["getCertificates"],
73    },
74    "about:neterror": {
75      RPMSendAsyncMessage: [
76        "Browser:EnableOnlineMode",
77        "Browser:ResetSSLPreferences",
78        "GetChangedCertPrefs",
79        "Browser:OpenCaptivePortalPage",
80        "Browser:SSLErrorGoBack",
81        "Browser:PrimeMitm",
82        "Browser:ResetEnterpriseRootsPref",
83        "ReportBlockingError",
84        "DisplayOfflineSupportPage",
85      ],
86      RPMAddMessageListener: ["*"],
87      RPMRemoveMessageListener: ["*"],
88      RPMGetFormatURLPref: ["app.support.baseURL"],
89      RPMGetBoolPref: [
90        "security.certerror.hideAddException",
91        "security.tls.version.enable-deprecated",
92        "security.certerrors.tls.version.show-override",
93        "security.xfocsp.errorReporting.automatic",
94        "security.xfocsp.errorReporting.enabled",
95        "browser.proton.enabled",
96      ],
97      RPMSetBoolPref: [
98        "security.tls.version.enable-deprecated",
99        "security.xfocsp.errorReporting.automatic",
100      ],
101      RPMPrefIsLocked: ["security.tls.version.min"],
102      RPMAddToHistogram: ["*"],
103      RPMGetInnerMostURI: ["*"],
104      RPMGetHttpResponseHeader: ["*"],
105    },
106    "about:plugins": {
107      RPMSendQuery: ["RequestPlugins"],
108    },
109    "about:pocket-saved": {
110      RPMSendAsyncMessage: ["*"],
111      RPMAddMessageListener: ["*"],
112      RPMRemoveMessageListener: ["*"],
113    },
114    "about:pocket-signup": {
115      RPMSendAsyncMessage: ["*"],
116      RPMAddMessageListener: ["*"],
117      RPMRemoveMessageListener: ["*"],
118    },
119    "about:pocket-home": {
120      RPMSendAsyncMessage: ["*"],
121      RPMAddMessageListener: ["*"],
122      RPMRemoveMessageListener: ["*"],
123    },
124    "about:privatebrowsing": {
125      RPMSendAsyncMessage: [
126        "OpenPrivateWindow",
127        "SearchBannerDismissed",
128        "OpenSearchPreferences",
129        "SearchHandoff",
130      ],
131      RPMSendQuery: [
132        "ShouldShowSearch",
133        "ShouldShowSearchBanner",
134        "ShouldShowVPNPromo",
135      ],
136      RPMAddMessageListener: ["*"],
137      RPMRemoveMessageListener: ["*"],
138      RPMGetFormatURLPref: [
139        "app.support.baseURL",
140        "browser.privatebrowsing.vpnpromourl",
141      ],
142      RPMIsWindowPrivate: ["*"],
143    },
144    "about:protections": {
145      RPMSendAsyncMessage: [
146        "OpenContentBlockingPreferences",
147        "OpenAboutLogins",
148        "OpenSyncPreferences",
149        "ClearMonitorCache",
150        "RecordEntryPoint",
151      ],
152      RPMSendQuery: [
153        "FetchUserLoginsData",
154        "FetchMonitorData",
155        "FetchContentBlockingEvents",
156        "FetchMobileDeviceConnected",
157        "GetShowProxyCard",
158        "FetchEntryPoint",
159        "FetchVPNSubStatus",
160        "FetchShowVPNCard",
161      ],
162      RPMAddMessageListener: ["*"],
163      RPMRemoveMessageListener: ["*"],
164      RPMSetBoolPref: [
165        "browser.contentblocking.report.show_mobile_app",
166        "browser.contentblocking.report.hide_vpn_banner",
167      ],
168      RPMGetBoolPref: [
169        "browser.contentblocking.report.lockwise.enabled",
170        "browser.contentblocking.report.monitor.enabled",
171        "privacy.socialtracking.block_cookies.enabled",
172        "browser.contentblocking.report.proxy.enabled",
173        "privacy.trackingprotection.cryptomining.enabled",
174        "privacy.trackingprotection.fingerprinting.enabled",
175        "privacy.trackingprotection.enabled",
176        "privacy.trackingprotection.socialtracking.enabled",
177        "browser.contentblocking.report.show_mobile_app",
178        "browser.contentblocking.report.hide_vpn_banner",
179        "browser.contentblocking.report.vpn.enabled",
180      ],
181      RPMGetStringPref: [
182        "browser.contentblocking.category",
183        "browser.contentblocking.report.monitor.url",
184        "browser.contentblocking.report.monitor.sign_in_url",
185        "browser.contentblocking.report.manage_devices.url",
186        "browser.contentblocking.report.proxy_extension.url",
187        "browser.contentblocking.report.lockwise.mobile-android.url",
188        "browser.contentblocking.report.lockwise.mobile-ios.url",
189        "browser.contentblocking.report.mobile-ios.url",
190        "browser.contentblocking.report.mobile-android.url",
191        "browser.contentblocking.report.vpn.url",
192        "browser.contentblocking.report.vpn-promo.url",
193        "browser.contentblocking.report.vpn-android.url",
194        "browser.contentblocking.report.vpn-ios.url",
195        "browser.contentblocking.report.vpn_platforms",
196      ],
197      RPMGetIntPref: ["network.cookie.cookieBehavior"],
198      RPMGetFormatURLPref: [
199        "browser.contentblocking.report.monitor.how_it_works.url",
200        "browser.contentblocking.report.lockwise.how_it_works.url",
201        "browser.contentblocking.report.monitor.preferences_url",
202        "browser.contentblocking.report.monitor.home_page_url",
203        "browser.contentblocking.report.social.url",
204        "browser.contentblocking.report.cookie.url",
205        "browser.contentblocking.report.tracker.url",
206        "browser.contentblocking.report.fingerprinter.url",
207        "browser.contentblocking.report.cryptominer.url",
208      ],
209      RPMRecordTelemetryEvent: ["*"],
210    },
211    "about:tabcrashed": {
212      RPMSendAsyncMessage: ["Load", "closeTab", "restoreTab", "restoreAll"],
213      RPMAddMessageListener: ["*"],
214      RPMRemoveMessageListener: ["*"],
215    },
216  },
217
218  /**
219   * Check if access is allowed to the given feature for a given document.
220   * This should be called from within the child process. A feature must
221   * contain the value in the whitelist array in the list within the
222   * accessMap for access to be granted.
223   *
224   * @param aDocument child process document to call from
225   * @param aFeature to feature to check access to
226   * @param aValue value that must be included with that feature's whitelist
227   * @returns true if access is allowed or false otherwise
228   */
229  checkAllowAccess(aDocument, aFeature, aValue) {
230    let principal = aDocument.nodePrincipal;
231    // if there is no content principal; deny access
232    if (!principal) {
233      return false;
234    }
235
236    return this.checkAllowAccessWithPrincipal(
237      principal,
238      aFeature,
239      aValue,
240      aDocument
241    );
242  },
243
244  /**
245   * Check if access is allowed to the given feature for a given principal.
246   * This may be called from within the child or parent process. A feature
247   * must contain the value in the whitelist array in the list within the
248   * accessMap for access to be granted.
249   *
250   * In the parent process, the passed-in document is expected to be null.
251   *
252   * @param aPrincipal principal being called from
253   * @param aFeature to feature to check access to
254   * @param aValue value that must be included with that feature's whitelist
255   * @param aDocument optional child process document to call from
256   * @returns true if access is allowed or false otherwise
257   */
258  checkAllowAccessWithPrincipal(aPrincipal, aFeature, aValue, aDocument) {
259    let accessMapForFeature = this.checkAllowAccessToFeature(
260      aPrincipal,
261      aFeature,
262      aDocument
263    );
264    if (!accessMapForFeature) {
265      Cu.reportError(
266        "RemotePageAccessManager does not allow access to Feature: " +
267          aFeature +
268          " for: " +
269          aDocument.location
270      );
271
272      return false;
273    }
274
275    // If the actual value is in the whitelist for that feature;
276    // allow access
277    if (accessMapForFeature.includes(aValue) || accessMapForFeature[0] == "*") {
278      return true;
279    }
280
281    return false;
282  },
283
284  /**
285   * Check if a particular feature can be accessed without checking for a
286   * value within the whitelist.
287   *
288   * @param aPrincipal principal being called from
289   * @param aFeature to feature to check access to
290   * @param aDocument optional child process document to call from
291   * @returns non-null whitelist if access is allowed or null otherwise
292   */
293  checkAllowAccessToFeature(aPrincipal, aFeature, aDocument) {
294    let spec;
295    if (!aPrincipal.isContentPrincipal) {
296      // For the sake of remote pages, when the principal has no uri,
297      // we want to access the "real" document URI directly, e.g. if the
298      // about: page is sandboxed.
299      if (!aDocument) {
300        return null;
301      }
302      if (!aDocument.documentURIObject.schemeIs("about")) {
303        return null;
304      }
305      spec =
306        aDocument.documentURIObject.prePath +
307        aDocument.documentURIObject.filePath;
308    } else {
309      if (!aPrincipal.schemeIs("about")) {
310        return null;
311      }
312      spec = aPrincipal.prePath + aPrincipal.filePath;
313    }
314
315    // Check if there is an entry for that requestying URI in the accessMap;
316    // if not, deny access.
317    let accessMapForURI = this.accessMap[spec];
318    if (!accessMapForURI) {
319      return null;
320    }
321
322    // Check if the feature is allowed to be accessed for that URI;
323    // if not, deny access.
324    return accessMapForURI[aFeature];
325  },
326};
327