1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045 |
- From 7d313f93cebc14b7eb6fc1654b60a143b5cd7f52 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:20:39 +0200
- Subject: [PATCH 01/13] Point to local omni.ja files, not remote server
- This patch series tries to remove any network communication with Remote
- Settings [1], which can be used by Mozilla to silently push data to client
- browsers. This data can include references to nonfree software, for example,
- to search engines or other websites that contain nonfree JavaScript code.
- Without this patching, it would be hard to make sure the browser does not
- violate paragraph 4 of [2]: "Programs in the system should not suggest
- installing nonfree plugins, documentation, and so on."
- Changes in the current patch:
- First of all, replace every occurrence of Remote Settings server domain name
- with URIs that point to built-in local files within omni.ja.
- Some links to json files may point to non-existing files, but that's OK
- because it's better than leave them point to Remote Settings server.
- If necessary, missing files can be added later.
- [1] https://remote-settings.readthedocs.io/en/latest/introduction.html
- [2] https://www.gnu.org/distros/free-system-distribution-guidelines.en.html#license-rules
- ---
- .../components/ASRouterAdmin/ASRouterAdmin.jsx | 2 +-
- .../newtab/data/content/activity-stream.bundle.js | 2 +-
- services/settings/Utils.jsm | 2 +-
- .../periodic-updates/scripts/periodic_file_updates.sh | 2 +-
- .../backgroundtasks/BackgroundTask_message.sys.mjs | 4 ++--
- toolkit/components/search/SearchUtils.sys.mjs | 8 ++++----
- toolkit/components/search/docs/DefaultSearchEngines.rst | 2 +-
- .../components/search/docs/SearchEngineConfiguration.rst | 2 +-
- toolkit/modules/AppConstants.sys.mjs | 4 ++--
- toolkit/mozapps/defaultagent/RemoteSettings.cpp | 2 +-
- 10 files changed, 15 insertions(+), 15 deletions(-)
- diff --git a/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx b/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
- index 17155bb4ba..859560e25b 100644
- --- a/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
- +++ b/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.jsx
- @@ -1237,7 +1237,7 @@ export class ASRouterAdminInner extends React.PureComponent {
- <a
- className="providerUrl"
- target="_blank"
- - href="https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/nimbus-desktop-experiments/records"
- + href="resource://app/defaults/settings/main/nimbus-desktop-experiments.json"
- rel="noopener noreferrer"
- >
- nimbus-desktop-experiments
- diff --git a/browser/components/newtab/data/content/activity-stream.bundle.js b/browser/components/newtab/data/content/activity-stream.bundle.js
- index 159e948352..5cbe5ac2c2 100644
- --- a/browser/components/newtab/data/content/activity-stream.bundle.js
- +++ b/browser/components/newtab/data/content/activity-stream.bundle.js
- @@ -1593,7 +1593,7 @@ class ASRouterAdminInner extends (external_React_default()).PureComponent {
- label = /*#__PURE__*/external_React_default().createElement("span", null, "remote settings (", /*#__PURE__*/external_React_default().createElement("a", {
- className: "providerUrl",
- target: "_blank",
- - href: "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/nimbus-desktop-experiments/records",
- + href: "resource://app/defaults/settings/main/nimbus-desktop-experiments.json",
- rel: "noopener noreferrer"
- }, "nimbus-desktop-experiments"), ")");
- }
- diff --git a/services/settings/Utils.jsm b/services/settings/Utils.jsm
- index 50114dfbbc..9d957283da 100644
- --- a/services/settings/Utils.jsm
- +++ b/services/settings/Utils.jsm
- @@ -101,7 +101,7 @@ var Utils = {
- : AppConstants.REMOTE_SETTINGS_SERVER_URL;
- },
-
- - CHANGES_PATH: "/buckets/monitor/collections/changes/changeset",
- + CHANGES_PATH: "/monitor/changes",
-
- /**
- * Logger instance.
- diff --git a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh b/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
- index b88ee476da..440615e4e1 100755
- --- a/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
- +++ b/taskcluster/docker/periodic-updates/scripts/periodic_file_updates.sh
- @@ -286,7 +286,7 @@ function compare_suffix_lists {
- }
-
- function compare_remote_settings_files {
- - REMOTE_SETTINGS_SERVER="https://firefox.settings.services.mozilla.com/v1"
- + REMOTE_SETTINGS_SERVER="resource://app/defaults/settings"
-
- # 1. List remote settings collections from server.
- echo "INFO: fetch remote settings list from server"
- diff --git a/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
- index dbbfa53d39..11b93b07f2 100644
- --- a/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
- +++ b/toolkit/components/backgroundtasks/BackgroundTask_message.sys.mjs
- @@ -19,8 +19,8 @@
- // environment variables still apply.
- //
- // --stage: use stage Remote Settings
- -// (`https://settings-cdn.stage.mozaws.net/v1`) rather than production
- -// (`https://firefox.settings.services.mozilla.com/v1`)
- +// (`resource://app/defaults/settings/`) rather than production
- +// (`resource://app/defaults/settings/`)
- //
- // --preview: enable Remote Settings and Experiment previews.
- //
- diff --git a/toolkit/components/search/SearchUtils.sys.mjs b/toolkit/components/search/SearchUtils.sys.mjs
- index 65581754e8..4e7ccf48e4 100644
- --- a/toolkit/components/search/SearchUtils.sys.mjs
- +++ b/toolkit/components/search/SearchUtils.sys.mjs
- @@ -150,13 +150,13 @@ export var SearchUtils = {
-
- ENGINES_URLS: {
- "prod-main":
- - "https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config/records",
- + "resource://app/defaults/settings/main/search-config.json",
- "prod-preview":
- - "https://firefox.settings.services.mozilla.com/v1/buckets/main-preview/collections/search-config/records",
- + "resource://app/defaults/settings/main/search-config.json",
- "stage-main":
- - "https://settings.stage.mozaws.net/v1/buckets/main/collections/search-config/records",
- + "resource://app/defaults/settings/main/search-config.json",
- "stage-preview":
- - "https://settings.stage.mozaws.net/v1/buckets/main-preview/collections/search-config/records",
- + "resource://app/defaults/settings/main/search-config.json",
- },
-
- // The following constants are left undocumented in nsISearchService.idl
- diff --git a/toolkit/components/search/docs/DefaultSearchEngines.rst b/toolkit/components/search/docs/DefaultSearchEngines.rst
- index 3dfe68abb1..26d5f18a7b 100644
- --- a/toolkit/components/search/docs/DefaultSearchEngines.rst
- +++ b/toolkit/components/search/docs/DefaultSearchEngines.rst
- @@ -99,4 +99,4 @@ is updated.
-
- .. _configuration schema: SearchConfigurationSchema.html
- .. _remote settings: /services/settings/index.html
- -.. _search-default-override-allowlist bucket: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-default-override-allowlist/records
- +.. _search-default-override-allowlist bucket: resource://app/defaults/settings/main/search-default-override-allowlist.json
- diff --git a/toolkit/components/search/docs/SearchEngineConfiguration.rst b/toolkit/components/search/docs/SearchEngineConfiguration.rst
- index c782f9f7c3..4d773d27c5 100644
- --- a/toolkit/components/search/docs/SearchEngineConfiguration.rst
- +++ b/toolkit/components/search/docs/SearchEngineConfiguration.rst
- @@ -68,5 +68,5 @@ related. As a result several situations may occur:
- .. _JSON schema: https://json-schema.org/
- .. _stored in mozilla-central: https://searchfox.org/mozilla-central/source/toolkit/components/search/schema/
- .. _Search Configuration Schema: SearchConfigurationSchema.html
- -.. _viewed live: https://firefox.settings.services.mozilla.com/v1/buckets/main/collections/search-config/records
- +.. _viewed live: resource://app/defaults/settings/main/search-config.json
- .. _Normandy: /toolkit/components/normandy/normandy/services.html
- diff --git a/toolkit/modules/AppConstants.sys.mjs b/toolkit/modules/AppConstants.sys.mjs
- index 59ba540c6d..2b9ff5796b 100644
- --- a/toolkit/modules/AppConstants.sys.mjs
- +++ b/toolkit/modules/AppConstants.sys.mjs
- @@ -416,9 +416,9 @@ export var AppConstants = Object.freeze({
-
- REMOTE_SETTINGS_SERVER_URL:
- #ifdef MOZ_THUNDERBIRD
- - "https://thunderbird-settings.thunderbird.net/v1",
- + "resource://app/defaults/settings",
- #else
- - "https://firefox.settings.services.mozilla.com/v1",
- + "resource://app/defaults/settings",
- #endif
-
- REMOTE_SETTINGS_VERIFY_SIGNATURE:
- diff --git a/toolkit/mozapps/defaultagent/RemoteSettings.cpp b/toolkit/mozapps/defaultagent/RemoteSettings.cpp
- index 667d9fc628..b2bf628f29 100644
- --- a/toolkit/mozapps/defaultagent/RemoteSettings.cpp
- +++ b/toolkit/mozapps/defaultagent/RemoteSettings.cpp
- @@ -23,7 +23,7 @@ extern "C" {
- HRESULT IsAgentRemoteDisabledRust(const char* szUrl, DWORD* lpdwDisabled);
- }
-
- -#define PROD_ENDPOINT "https://firefox.settings.services.mozilla.com/v1"
- +#define PROD_ENDPOINT "resource://app/defaults/settings"
- #define PROD_BID "main"
- #define PROD_CID "windows-default-browser-agent"
- #define PROD_ID "state"
- --
- 2.39.0
- From 233227d0f2da317fe71a643b1c54d623f56f5d84 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:34:08 +0200
- Subject: [PATCH 02/13] Remove polling triggered by push broadcasts
- When initialized, remote-settings.js adds a listener to push broadcasts,
- that let Remote Settings server send push messages to trigger polling
- for changes from the client side. This is not needed for local-only
- setup. Remove the record from broadcast-listeners.json file stored in
- the user profile, so that it doesn't get picked up by push broadcast
- service.
- ---
- dom/push/PushBroadcastService.sys.mjs | 13 +++++++++++++
- services/settings/remote-settings.js | 11 ++---------
- 2 files changed, 15 insertions(+), 9 deletions(-)
- diff --git a/dom/push/PushBroadcastService.sys.mjs b/dom/push/PushBroadcastService.sys.mjs
- index cca80fee6c..c83b0f77d7 100644
- --- a/dom/push/PushBroadcastService.sys.mjs
- +++ b/dom/push/PushBroadcastService.sys.mjs
- @@ -170,6 +170,19 @@ export var BroadcastService = class {
- }
- }
-
- + async deleteListener(broadcastId) {
- + await this.initializePromise;
- +
- + if (this.jsonFile.data.listeners.hasOwnProperty(broadcastId)) {
- + console.info(
- + "deleteListener: deleting listener",
- + broadcastId
- + );
- + delete this.jsonFile.data.listeners[broadcastId];
- + this.jsonFile.saveSoon();
- + }
- + }
- +
- /**
- * Call the listeners of the specified broadcasts.
- *
- diff --git a/services/settings/remote-settings.js b/services/settings/remote-settings.js
- index c479de2a17..f797df533a 100644
- --- a/services/settings/remote-settings.js
- +++ b/services/settings/remote-settings.js
- @@ -582,11 +582,7 @@ function remoteSettingsFunction() {
- moduleURI: __URI__,
- symbolName: "remoteSettingsBroadcastHandler",
- };
- - lazy.pushBroadcastService.addListener(
- - BROADCAST_ID,
- - currentVersion,
- - moduleInfo
- - );
- + lazy.pushBroadcastService.deleteListener(BROADCAST_ID);
- };
-
- return remoteSettings;
- @@ -606,9 +602,6 @@ var remoteSettingsBroadcastHandler = {
- `Push notification received (version=${version} phase=${phase})`
- );
-
- - return RemoteSettings.pollChanges({
- - expectedTimestamp: version,
- - trigger: isStartup ? "startup" : "broadcast",
- - });
- + return;
- },
- };
- --
- 2.39.0
- From c4bd08fa8614bcf212975f4c0b3555437e8a8d59 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:41:54 +0200
- Subject: [PATCH 03/13] Remove timer that triggers polling for changes
- That is not needed for local-only setup.
- ---
- services/settings/components.conf | 9 +--------
- services/settings/servicesSettings.manifest | 4 ----
- 2 files changed, 1 insertion(+), 12 deletions(-)
- diff --git a/services/settings/components.conf b/services/settings/components.conf
- index 9a737802ee..25109415a7 100644
- --- a/services/settings/components.conf
- +++ b/services/settings/components.conf
- @@ -4,11 +4,4 @@
- # License, v. 2.0. If a copy of the MPL was not distributed with this
- # file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
- -Classes = [
- - {
- - 'cid': '{5e756573-234a-49ea-bbe4-59ec7a70657d}',
- - 'contract_ids': ['@mozilla.org/services/settings;1'],
- - 'jsm': 'resource://services-settings/RemoteSettingsComponents.jsm',
- - 'constructor': 'RemoteSettingsTimer',
- - },
- -]
- +Classes = []
- diff --git a/services/settings/servicesSettings.manifest b/services/settings/servicesSettings.manifest
- index 3bfed26ea4..807eb220ec 100644
- --- a/services/settings/servicesSettings.manifest
- +++ b/services/settings/servicesSettings.manifest
- @@ -1,7 +1,3 @@
- # Register resource aliases
- resource services-settings resource://gre/modules/services-settings/
-
- -# Schedule polling of remote settings changes
- -# (default 24H, max 72H)
- -# see syntax https://searchfox.org/mozilla-central/rev/cc280c4be94ff8cf64a27cc9b3d6831ffa49fa45/toolkit/components/timermanager/UpdateTimerManager.jsm#155
- -category update-timer RemoteSettingsComponents @mozilla.org/services/settings;1,getService,services-settings-poll-changes,services.settings.poll_interval,86400,259200
- --
- 2.39.0
- From b106c8b4e919e1d6c42f66bb07461c63848ebb2b Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:47:41 +0200
- Subject: [PATCH 04/13] Utils: fetch timestamps of each collection locally
- Utils.CHANGES_PATH points to
- services/settings/dumps/monitor/changes
- which will be generated later by JSON processing script. Fetch the
- timestamps from that file and mock response headers to not confuse any
- code that expects them.
- ---
- browser/installer/package-manifest.in | 1 +
- services/settings/Utils.jsm | 28 ++++++++++++++++++++---
- services/settings/dumps/monitor/moz.build | 8 +++++++
- services/settings/dumps/moz.build | 1 +
- 4 files changed, 35 insertions(+), 3 deletions(-)
- create mode 100644 services/settings/dumps/monitor/moz.build
- diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
- index 9229a0bd0e..565a7b7508 100644
- --- a/browser/installer/package-manifest.in
- +++ b/browser/installer/package-manifest.in
- @@ -263,6 +263,7 @@
- @RESPATH@/browser/defaults/settings/last_modified.json
- @RESPATH@/browser/defaults/settings/blocklists
- @RESPATH@/browser/defaults/settings/main
- +@RESPATH@/browser/defaults/settings/monitor
- @RESPATH@/browser/defaults/settings/security-state
-
- ; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325)
- diff --git a/services/settings/Utils.jsm b/services/settings/Utils.jsm
- index 9d957283da..abdd0a53fa 100644
- --- a/services/settings/Utils.jsm
- +++ b/services/settings/Utils.jsm
- @@ -370,7 +370,7 @@ var Utils = {
- async fetchLatestChanges(serverUrl, options = {}) {
- const { expectedTimestamp, lastEtag = "", filters = {} } = options;
-
- - let url = serverUrl + Utils.CHANGES_PATH;
- + let url = Utils.SERVER_URL + Utils.CHANGES_PATH;
- const params = {
- ...filters,
- _expected: expectedTimestamp ?? 0,
- @@ -385,7 +385,21 @@ var Utils = {
- .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
- .join("&");
- }
- - const response = await Utils.fetch(url);
- + const rawResponse = await fetch(url);
- + const responseDate = new Date().toUTCString();
- +
- + const headers = new Headers();
- + headers.set("Date", responseDate);
- + headers.set("Last-Modified", responseDate);
- + headers.set("Content-Type", "application/json");
- +
- + const responseAttributes = {
- + status: rawResponse.status,
- + statusText: rawResponse.statusText,
- + headers,
- + };
- +
- + const response = new Response(rawResponse.body, responseAttributes);
-
- if (response.status >= 500) {
- throw new Error(`Server error ${response.status} ${response.statusText}`);
- @@ -420,7 +434,15 @@ var Utils = {
- }
- }
-
- - const { changes = [], timestamp } = payload;
- + const { timestamp } = payload;
- + const { bucket, collection } = filters;
- + if (!bucket || !collection) {
- + throw new Error('Unable to fetch latest change without bucket or collection');
- + }
- + const change = payload.changes.find(
- + change => change.bucket === bucket && change.collection === collection
- + ) ?? { last_modified: 0, bucket, collection };
- + const changes = [change];
-
- let serverTimeMillis = Date.parse(response.headers.get("Date"));
- // Since the response is served via a CDN, the Date header value could have been cached.
- diff --git a/services/settings/dumps/monitor/moz.build b/services/settings/dumps/monitor/moz.build
- new file mode 100644
- index 0000000000..d3d017fda5
- --- /dev/null
- +++ b/services/settings/dumps/monitor/moz.build
- @@ -0,0 +1,8 @@
- +# This Source Code Form is subject to the terms of the Mozilla Public
- +# License, v. 2.0. If a copy of the MPL was not distributed with this
- +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- +
- +FINAL_TARGET_FILES.defaults.settings.monitor += ["changes"]
- +
- +if CONFIG["MOZ_BUILD_APP"] == "browser":
- + DIST_SUBDIR = "browser"
- diff --git a/services/settings/dumps/moz.build b/services/settings/dumps/moz.build
- index f407580bfa..53e9d8b45e 100644
- --- a/services/settings/dumps/moz.build
- +++ b/services/settings/dumps/moz.build
- @@ -5,6 +5,7 @@
- DIRS += [
- "blocklists",
- "main",
- + "monitor",
- "security-state",
- ]
-
- --
- 2.39.0
- From bd6216d755e4d1c1c8ebba4123732b0b2358101c Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:52:10 +0200
- Subject: [PATCH 05/13] Utils: disable offline checking
- Since only local data is read now, it should always return false for the
- current and any future code that relies on it.
- ---
- services/settings/Utils.jsm | 10 ----------
- 1 file changed, 10 deletions(-)
- diff --git a/services/settings/Utils.jsm b/services/settings/Utils.jsm
- index abdd0a53fa..2cc3ba917b 100644
- --- a/services/settings/Utils.jsm
- +++ b/services/settings/Utils.jsm
- @@ -184,16 +184,6 @@ var Utils = {
- * @return {bool} Whether network is down or not.
- */
- get isOffline() {
- - try {
- - return (
- - Services.io.offline ||
- - lazy.CaptivePortalService.state ==
- - lazy.CaptivePortalService.LOCKED_PORTAL ||
- - !lazy.gNetworkLinkService.isLinkUp
- - );
- - } catch (ex) {
- - log.warn("Could not determine network status.", ex);
- - }
- return false;
- },
-
- --
- 2.39.0
- From 237ecd7acdd299b2f89787e4f5f50bb5cc4d970c Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 17:56:02 +0200
- Subject: [PATCH 06/13] Refactor hashing logic to a separate function
- It is used instead of internal signature validation mechanism, for
- integrity checking of the locally cached data.
- ---
- services/settings/RemoteSettingsWorker.jsm | 4 ++++
- services/settings/SharedUtils.jsm | 9 +++++++--
- 2 files changed, 11 insertions(+), 2 deletions(-)
- diff --git a/services/settings/RemoteSettingsWorker.jsm b/services/settings/RemoteSettingsWorker.jsm
- index 57754f0a2b..0ae0c8762a 100644
- --- a/services/settings/RemoteSettingsWorker.jsm
- +++ b/services/settings/RemoteSettingsWorker.jsm
- @@ -191,6 +191,10 @@ class Worker {
- // task on the current thread instead of the worker thread.
- return lazy.SharedUtils.checkContentHash(buffer, size, hash);
- }
- +
- + async getContentHash(bytes) {
- + return lazy.SharedUtils.getContentHash(bytes);
- + }
- }
-
- // Now, first add a shutdown blocker. If that fails, we must have
- diff --git a/services/settings/SharedUtils.jsm b/services/settings/SharedUtils.jsm
- index 9804e80c91..de67b0fb6f 100644
- --- a/services/settings/SharedUtils.jsm
- +++ b/services/settings/SharedUtils.jsm
- @@ -23,11 +23,16 @@ var SharedUtils = {
- return false;
- }
- // Has expected content?
- + const hashStr = await this.getContentHash(bytes);
- + return hashStr == hash;
- + },
- +
- + async getContentHash(bytes) {
- const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
- const hashBytes = new Uint8Array(hashBuffer);
- const toHex = b => b.toString(16).padStart(2, "0");
- - const hashStr = Array.from(hashBytes, toHex).join("");
- - return hashStr == hash;
- +
- + return Array.from(hashBytes, toHex).join("");
- },
-
- /**
- --
- 2.39.0
- From afc7c3a747931dbd1ed02b5bc2af2fd29b607875 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 18:05:02 +0200
- Subject: [PATCH 07/13] Client: Fetch and hash records from local dump
- Read the records from local dumps. See [1] for details on how to prepare
- custom dumps). Records are cached in the local IndexedDB, and the client
- updates cached records each time there's a change. Also it verifies
- integrity of the data. Then the list of current / created / updated /
- deleted records is generated and emitted to every registered listener.
- Change upstream signature validation mechanism to a simpler one.
- Otherwise, it'd be necessary to sign local records, which is redundant,
- because the application package should be signed already by the distro.
- Instead of signature property from metadata records, json_dump_metadata
- has been introduced. It contains the checksum of the records and size in
- bytes. Also added app_build_id property for version checking and updates
- of cached data.
- Although it's possible to disable integrity checking via preference, it
- seems to be not a good idea, because the logic that detects invalid
- local data relies on it. In the context of local-only setup, data that
- has been received from real Remote Settings server will not contain the
- custom metadata, and thus will be considered invalid and then discarded,
- while the client gets a chance to gracefully inform registered listeners
- about these changes so that they can discard the data received before
- the upgrade to local-only setup.
- [1] https://firefox-source-docs.mozilla.org/services/common/services/RemoteSettings.html#initial-data
- ---
- services/settings/RemoteSettingsClient.jsm | 66 +++++++++-------------
- 1 file changed, 28 insertions(+), 38 deletions(-)
- diff --git a/services/settings/RemoteSettingsClient.jsm b/services/settings/RemoteSettingsClient.jsm
- index 1d42fc98ff..c8a0898367 100644
- --- a/services/settings/RemoteSettingsClient.jsm
- +++ b/services/settings/RemoteSettingsClient.jsm
- @@ -692,11 +692,9 @@ class RemoteSettingsClient extends EventEmitter {
-
- // If the data is up-to-date but don't have metadata (records loaded from dump),
- // we fetch them and validate the signature immediately.
- - if (this.verifySignature && lazy.ObjectUtils.isEmpty(localMetadata)) {
- + if (this.verifySignature && lazy.ObjectUtils.isEmpty(localMetadata?.json_dump_metadata)) {
- lazy.console.debug(`${this.identifier} pull collection metadata`);
- - const metadata = await this.httpClient().getData({
- - query: { _expected: expectedTimestamp },
- - });
- + const { metadata } = await this._fetchChangeset(expectedTimestamp);
- await this.db.importChanges(metadata);
- // We don't bother validating the signature if the dump was just loaded. We do
- // if the dump was loaded at some other point (eg. from .get()).
- @@ -959,38 +957,28 @@ class RemoteSettingsClient extends EventEmitter {
- * @returns {Promise}
- */
- async _validateCollectionSignature(records, timestamp, metadata) {
- - if (!metadata?.signature) {
- + if (!metadata?.json_dump_metadata) {
- throw new MissingSignatureError(this.identifier);
- }
-
- - if (!this._verifier) {
- - this._verifier = Cc[
- - "@mozilla.org/security/contentsignatureverifier;1"
- - ].createInstance(Ci.nsIContentSignatureVerifier);
- - }
- -
- - // This is a content-signature field from an autograph response.
- const {
- - signature: { x5u, signature },
- + json_dump_metadata: { hash, size },
- } = metadata;
- - const certChain = await (await lazy.Utils.fetch(x5u)).text();
- // Merge remote records with local ones and serialize as canonical JSON.
- const serialized = await lazy.RemoteSettingsWorker.canonicalStringify(
- records,
- timestamp
- );
-
- - lazy.console.debug(`${this.identifier} verify signature using ${x5u}`);
- + lazy.console.debug(`${this.identifier} verify signature using size ${size} and hash ${hash}`);
- if (
- - !(await this._verifier.asyncVerifyContentSignature(
- - serialized,
- - "p384ecdsa=" + signature,
- - certChain,
- - this.signerName,
- - lazy.Utils.CERT_CHAIN_ROOT_IDENTIFIER
- + !(await lazy.RemoteSettingsWorker.checkContentHash(
- + new TextEncoder().encode(serialized),
- + size,
- + hash
- ))
- ) {
- - throw new InvalidSignatureError(this.identifier, x5u);
- + throw new InvalidSignatureError(this.identifier);
- }
- }
-
- @@ -1160,24 +1148,26 @@ class RemoteSettingsClient extends EventEmitter {
- * @param since timestamp of last sync (optional)
- */
- async _fetchChangeset(expectedTimestamp, since) {
- - const client = this.httpClient();
- - const {
- - metadata,
- - timestamp: remoteTimestamp,
- - changes: remoteRecords,
- - } = await client.execute(
- - {
- - path: `/buckets/${this.bucketName}/collections/${this.collectionName}/changeset`,
- - },
- - {
- - query: {
- - _expected: expectedTimestamp,
- - _since: since,
- - },
- - }
- + const { data } = await lazy.SharedUtils.loadJSONDump(
- + this.bucketName,
- + this.collectionName
- );
- + const remoteRecords = data ?? [];
- +
- + const serialized = await lazy.RemoteSettingsWorker.canonicalStringify(
- + remoteRecords,
- + expectedTimestamp
- + );
- + const bytes = new TextEncoder().encode(serialized);
- + const metadata = {
- + app_build_id: Services.appinfo.appBuildID,
- + json_dump_metadata: {
- + hash: await lazy.RemoteSettingsWorker.getContentHash(bytes),
- + size: bytes.length,
- + },
- + }
- return {
- - remoteTimestamp,
- + remoteTimestamp: expectedTimestamp,
- metadata,
- remoteRecords,
- };
- --
- 2.39.0
- From 3f0d975225fb9d786eb8b4ae303f49f96f8d91e1 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 18:42:56 +0200
- Subject: [PATCH 08/13] Client: start deferred sync on get() or on()
- The users of the RemoteSettingsClient.jsm can receive records from it in
- two ways: by calling get(), and by subscribing to events by calling
- on().
- So hook a deferred sync whenever something calls these methods. Because
- multiple of those calls can be made quite early and in very short time,
- set up a deferred task that will be armed only when needed and only once
- in a second. When the task is running it first checks if the local data
- came from the dump of the current app build, and no-ops if true. If
- false, it triggers a sync. Then adds a flag if the client has been
- correctly synchronized with the dump, so that no metadata checking
- occurs during the session.
- ---
- services/settings/RemoteSettingsClient.jsm | 27 +++++++++++++++++++++-
- 1 file changed, 26 insertions(+), 1 deletion(-)
- diff --git a/services/settings/RemoteSettingsClient.jsm b/services/settings/RemoteSettingsClient.jsm
- index c8a0898367..4b35ad0eef 100644
- --- a/services/settings/RemoteSettingsClient.jsm
- +++ b/services/settings/RemoteSettingsClient.jsm
- @@ -22,6 +22,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
- ClientEnvironmentBase:
- "resource://gre/modules/components-utils/ClientEnvironment.jsm",
- Database: "resource://services-settings/Database.jsm",
- + DeferredTask: "resource://gre/modules/DeferredTask.jsm",
- IDBHelpers: "resource://services-settings/IDBHelpers.jsm",
- KintoHttpClient: "resource://services-common/kinto-http-client.js",
- ObjectUtils: "resource://gre/modules/ObjectUtils.jsm",
- @@ -32,6 +33,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
- });
-
- const TELEMETRY_COMPONENT = "remotesettings";
- +const DEFERRED_SYNC_DELAY_MILLISECONDS = 1000;
-
- XPCOMUtils.defineLazyGetter(lazy, "console", () => lazy.Utils.log);
-
- @@ -324,6 +326,11 @@ class RemoteSettingsClient extends EventEmitter {
- this._lastCheckTimePref = lastCheckTimePref;
- this._verifier = null;
- this._syncRunning = false;
- + this._deferredSync = new lazy.DeferredTask(async () => {
- + if (!this._syncRunning && !(await this._isSynced())) {
- + await this.sync();
- + }
- + }, DEFERRED_SYNC_DELAY_MILLISECONDS);
-
- // This attribute allows signature verification to be disabled, when running tests
- // or when pulling data from a dev server.
- @@ -353,6 +360,11 @@ class RemoteSettingsClient extends EventEmitter {
- this.db.identifier = this.identifier;
- }
-
- + on(event, callback) {
- + super.on(event, callback);
- + this._deferredSync.arm();
- + }
- +
- get identifier() {
- return `${this.bucketName}/${this.collectionName}`;
- }
- @@ -426,6 +438,10 @@ class RemoteSettingsClient extends EventEmitter {
- let lastModified = forceSync ? null : await this.db.getLastModified();
- let hasLocalData = lastModified !== null;
-
- + if (!(await this._isSynced())) {
- + throw new MissingSignatureError(this.identifier);
- + }
- +
- if (forceSync) {
- if (!this._importingPromise) {
- this._importingPromise = (async () => {
- @@ -541,7 +557,10 @@ class RemoteSettingsClient extends EventEmitter {
- // No need to verify signature on JSON dumps.
- // If local DB cannot be read, then we don't even try to do anything,
- // we return results early.
- - return this._filterEntries(data);
- + const filtered = this._filterEntries(data);
- + this._deferredSync.arm();
- +
- + return filtered;
- }
-
- lazy.console.debug(
- @@ -581,6 +600,12 @@ class RemoteSettingsClient extends EventEmitter {
- return final;
- }
-
- + async _isSynced() {
- + this._synced ||=
- + Services.appinfo.appBuildID === (await this.db?.getMetadata())?.app_build_id;
- + return this._synced;
- + }
- +
- /**
- * Synchronize the local database with the remote server.
- *
- --
- 2.39.0
- From 137f0c81f37356fb97e7037ee64c2fa65ed242ea Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 18:53:51 +0200
- Subject: [PATCH 09/13] Client: deep compare records if timestamps match
- When the list of current / updated / deleted records is generated, their
- modification timestamps are compared to detect the updates.
- Although in practice this is unlikely to happen, in theory the
- timestamp of some older record received from Remote Settings can match
- with the modified record in the dump. Although JSON processing script
- makes sure to add unique timestamps to each of the modified records,
- it's still possible to update dumps manually and simply forget to update
- timestamps. So serialize the records and compare them as strings to be
- on the safe side. This should happen only once after upgrading to each
- new version of the application, so is not likely to introduce any
- noticeable performance issues.
- ---
- services/settings/RemoteSettingsClient.jsm | 6 +++++-
- 1 file changed, 5 insertions(+), 1 deletion(-)
- diff --git a/services/settings/RemoteSettingsClient.jsm b/services/settings/RemoteSettingsClient.jsm
- index 4b35ad0eef..a8ce913d8e 100644
- --- a/services/settings/RemoteSettingsClient.jsm
- +++ b/services/settings/RemoteSettingsClient.jsm
- @@ -19,6 +19,7 @@ const { Downloader } = ChromeUtils.import(
- const lazy = {};
-
- XPCOMUtils.defineLazyModuleGetters(lazy, {
- + CanonicalJSON: "resource://gre/modules/CanonicalJSON.jsm",
- ClientEnvironmentBase:
- "resource://gre/modules/components-utils/ClientEnvironment.jsm",
- Database: "resource://services-settings/Database.jsm",
- @@ -1148,7 +1149,10 @@ class RemoteSettingsClient extends EventEmitter {
- const old = oldById.get(r.id);
- if (old) {
- oldById.delete(r.id);
- - if (r.last_modified != old.last_modified) {
- + if (
- + r.last_modified != old.last_modified ||
- + lazy.CanonicalJSON.stringify(r) != lazy.CanonicalJSON.stringify(old)
- + ) {
- syncResult.updated.push({ old, new: r });
- }
- } else {
- --
- 2.39.0
- From 3e1e4c19780fd84fd0fdeaaa222239b4572fb251 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 19:01:39 +0200
- Subject: [PATCH 10/13] Client: delete more data on cleanup
- When the client detects the local data is invalid (i.e. it came from
- real Remote Settings and can have unwanted records), delete not only
- the records, but also the attachments that came with them, because they
- too can be problematic. And last check time preference, because it's not
- useful anyway when remote-settings.js doesn't do any polling for changes.
- Note that attachments should be deleted before the records, because the
- logic gets the data about the attachments from those records.
- ---
- services/settings/RemoteSettingsClient.jsm | 12 +++++++++---
- 1 file changed, 9 insertions(+), 3 deletions(-)
- diff --git a/services/settings/RemoteSettingsClient.jsm b/services/settings/RemoteSettingsClient.jsm
- index a8ce913d8e..19883607f2 100644
- --- a/services/settings/RemoteSettingsClient.jsm
- +++ b/services/settings/RemoteSettingsClient.jsm
- @@ -261,7 +261,7 @@ class AttachmentDownloader extends Downloader {
- allRecords
- .filter(r => !!r.attachment)
- .map(r =>
- - Promise.all([this.deleteDownloaded(r), this.deleteFromDisk(r)])
- + Promise.all([this.deleteDownloaded(r), this.deleteFromDisk(r), this.deleteCached(r.id)])
- )
- );
- }
- @@ -1108,7 +1108,7 @@ class RemoteSettingsClient extends EventEmitter {
- // Signature failed, clear local DB because it contains
- // bad data (local + remote changes).
- lazy.console.debug(`${this.identifier} clear local data`);
- - await this.db.clear();
- + await this._clearAll();
- // Local data was tampered, throw and it will retry from empty DB.
- lazy.console.error(`${this.identifier} local data was corrupted`);
- throw new CorruptedDataError(this.identifier);
- @@ -1130,7 +1130,7 @@ class RemoteSettingsClient extends EventEmitter {
- // _importJSONDump() only clears DB if dump is available,
- // therefore do it here!
- if (imported < 0) {
- - await this.db.clear();
- + await this._clearAll();
- }
- }
- }
- @@ -1170,6 +1170,12 @@ class RemoteSettingsClient extends EventEmitter {
- return syncResult;
- }
-
- + async _clearAll() {
- + await this.attachments.deleteAll();
- + await this.db.clear();
- + Services.prefs.clearUserPref(this.lastCheckTimePref);
- + }
- +
- /**
- * Fetch information from changeset endpoint.
- *
- --
- 2.39.0
- From 8da7335ac9baa4f524293ec810af7b360009ec19 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 19:07:56 +0200
- Subject: [PATCH 11/13] Client: remove comparison of collection timestamps
- In case if the cached data that came from real Remote Settings server
- (before the upgrade to local-only setup) has collection timestamp, that
- is newer than the packaged local dump, then this comparison logic can
- lead to early return of old data, skipping the integrity checking and
- necessary cleanup. So remove the checks.
- ---
- services/settings/RemoteSettingsClient.jsm | 5 -----
- 1 file changed, 5 deletions(-)
- diff --git a/services/settings/RemoteSettingsClient.jsm b/services/settings/RemoteSettingsClient.jsm
- index 19883607f2..8b4420943f 100644
- --- a/services/settings/RemoteSettingsClient.jsm
- +++ b/services/settings/RemoteSettingsClient.jsm
- @@ -1050,14 +1050,9 @@ class RemoteSettingsClient extends EventEmitter {
- updated: [],
- deleted: [],
- };
- - // If data wasn't changed, return empty sync result.
- - // This can happen when we update the signature but not the data.
- lazy.console.debug(
- `${this.identifier} local timestamp: ${localTimestamp}, remote: ${remoteTimestamp}`
- );
- - if (localTimestamp && remoteTimestamp < localTimestamp) {
- - return syncResult;
- - }
-
- await this.db.importChanges(metadata, remoteTimestamp, remoteRecords, {
- clear: retry,
- --
- 2.39.0
- From 5b40750eb05b0eb230ec44f0f5640ecc8d5e3b47 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 19:15:44 +0200
- Subject: [PATCH 12/13] Attachments: load only from dump and drop cached
- ---
- services/settings/Attachments.jsm | 37 +++++++------------------------
- 1 file changed, 8 insertions(+), 29 deletions(-)
- diff --git a/services/settings/Attachments.jsm b/services/settings/Attachments.jsm
- index 67a1f32261..6ff0a3b7c6 100644
- --- a/services/settings/Attachments.jsm
- +++ b/services/settings/Attachments.jsm
- @@ -151,9 +151,10 @@ class Downloader {
- retries,
- checkHash,
- attachmentId = record?.id,
- - fallbackToCache = false,
- fallbackToDump = false,
- } = options || {};
- + const fallbackToCache = false;
- +
- if (!attachmentId) {
- // Check for pre-condition. This should not happen, but it is explicitly
- // checked to avoid mixing up attachments, which could be dangerous.
- @@ -204,6 +205,7 @@ class Downloader {
- const newBuffer = await this.downloadAsBytes(record, {
- retries,
- checkHash,
- + dumpInfo,
- });
- const blob = new Blob([newBuffer]);
- // Store in cache but don't wait for it before returning.
- @@ -237,7 +239,7 @@ class Downloader {
- }
-
- try {
- - return { ...(await cacheInfo.getResult()), _source: "cache_fallback" };
- + await this.cacheImpl.delete(attachmentId);
- } catch (e) {
- // Failed to read from cache, e.g. IndexedDB unusable.
- console.error(e);
- @@ -300,7 +302,7 @@ class Downloader {
- * @returns {String} the absolute file path to the downloaded attachment.
- */
- async downloadToDisk(record, options = {}) {
- - const { retries = 3 } = options;
- + const retries = 0;
- const {
- attachment: { filename, size, hash },
- } = record;
- @@ -359,33 +361,10 @@ class Downloader {
- */
- async downloadAsBytes(record, options = {}) {
- const {
- - attachment: { location, hash, size },
- - } = record;
- -
- - const remoteFileUrl = (await this._baseAttachmentsURL()) + location;
- + dumpInfo = new LazyRecordAndBuffer(() => this._readAttachmentDump(attachmentId))
- + } = options;
-
- - const { retries = 3, checkHash = true } = options;
- - let retried = 0;
- - while (true) {
- - try {
- - const buffer = await this._fetchAttachment(remoteFileUrl);
- - if (!checkHash) {
- - return buffer;
- - }
- - if (
- - await lazy.RemoteSettingsWorker.checkContentHash(buffer, size, hash)
- - ) {
- - return buffer;
- - }
- - // Content is corrupted.
- - throw new Downloader.BadContentError(location);
- - } catch (e) {
- - if (retried >= retries) {
- - throw e;
- - }
- - }
- - retried++;
- - }
- + return (await dumpInfo.getResult()).buffer;
- }
-
- /**
- --
- 2.39.0
- From cac0e35f9b818570f37681ee7cd12071e8e5d747 Mon Sep 17 00:00:00 2001
- From: grizzlyuser <grizzlyuser@protonmail.com>
- Date: Wed, 30 Dec 2020 19:22:20 +0200
- Subject: [PATCH 13/13] Disable CRLite entirely for now
- It's designed to fetch the data from Remote Settings. One of the main
- selling points is that new revocations can be pushed to the clients
- within minutes. That won't work with local-only setup. Although (some?)
- of the JSON dumps for it are in place, obviously the updates won't
- happen that fast.
- Disable the preference right in the source code, so that the patch fails
- to apply when the upstream decides to set it to fully enforcing mode
- by default.
- The solution with CRLite is up for discussion. If necessary, it's
- possible to make clients for blessed collections to communicate to real
- Remote Settings server. For example, for collections related to
- certificate revocations.
- ---
- modules/libpref/init/StaticPrefList.yaml | 2 +-
- security/manager/ssl/nsNSSComponent.cpp | 1 +
- 2 files changed, 2 insertions(+), 1 deletion(-)
- diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
- index fca74af174..d05d118e58 100644
- --- a/modules/libpref/init/StaticPrefList.yaml
- +++ b/modules/libpref/init/StaticPrefList.yaml
- @@ -13250,7 +13250,7 @@
- # 3: Consult CRLite and enforce "Not Revoked" results, but defer to OCSP for "Revoked".
- - name: security.pki.crlite_mode
- type: RelaxedAtomicUint32
- - value: 3
- + value: 0
- mirror: always
-
- - name: security.tls.version.min
- diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp
- index 5844ffecfd..6084ffc93c 100644
- --- a/security/manager/ssl/nsNSSComponent.cpp
- +++ b/security/manager/ssl/nsNSSComponent.cpp
- @@ -1430,6 +1430,7 @@ void nsNSSComponent::setValidationOptions(
- CRLiteMode defaultCRLiteMode = CRLiteMode::Disabled;
- CRLiteMode crliteMode =
- static_cast<CRLiteMode>(StaticPrefs::security_pki_crlite_mode());
- + // Adding a comment just in case so that the patch breaks as soon as any surrounding lines get changed
- switch (crliteMode) {
- case CRLiteMode::Disabled:
- case CRLiteMode::TelemetryOnly:
- --
- 2.39.0
|