Source code

Revision control

Other Tools

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 file,
3
* You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
"use strict";
6
7
const { XPCOMUtils } = ChromeUtils.import(
9
);
10
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
11
const { AppConstants } = ChromeUtils.import(
13
);
14
15
XPCOMUtils.defineLazyServiceGetters(this, {
16
gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
17
gXulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
18
});
19
20
XPCOMUtils.defineLazyModuleGetters(this, {
27
});
28
29
XPCOMUtils.defineLazyGlobalGetters(this, ["File", "FileReader"]);
30
31
const PREF_LOGLEVEL = "browser.policies.loglevel";
32
const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
33
34
XPCOMUtils.defineLazyGetter(this, "log", () => {
35
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
36
return new ConsoleAPI({
37
prefix: "Policies.jsm",
38
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
39
// messages during development. See LOG_LEVELS in Console.jsm for details.
40
maxLogLevel: "error",
41
maxLogLevelPref: PREF_LOGLEVEL,
42
});
43
});
44
45
var EXPORTED_SYMBOLS = ["Policies"];
46
47
/*
48
* ============================
49
* = POLICIES IMPLEMENTATIONS =
50
* ============================
51
*
52
* The Policies object below is where the implementation for each policy
53
* happens. An object for each policy should be defined, containing
54
* callback functions that will be called by the engine.
55
*
56
* See the _callbacks object in EnterprisePolicies.js for the list of
57
* possible callbacks and an explanation of each.
58
*
59
* Each callback will be called with two parameters:
60
* - manager
61
* This is the EnterprisePoliciesManager singleton object from
62
* EnterprisePolicies.js
63
*
64
* - param
65
* The parameter defined for this policy in policies-schema.json.
66
* It will be different for each policy. It could be a boolean,
67
* a string, an array or a complex object. All parameters have
68
* been validated according to the schema, and no unknown
69
* properties will be present on them.
70
*
71
* The callbacks will be bound to their parent policy object.
72
*/
73
var Policies = {
74
"3rdparty": {
75
onBeforeAddons(manager, param) {
76
manager.setExtensionPolicies(param.Extensions);
77
},
78
},
79
80
AppUpdateURL: {
81
onBeforeAddons(manager, param) {
82
setDefaultPref("app.update.url", param.href);
83
},
84
},
85
86
Authentication: {
87
onBeforeAddons(manager, param) {
88
let locked = true;
89
if ("Locked" in param) {
90
locked = param.Locked;
91
}
92
93
if ("SPNEGO" in param) {
94
setDefaultPref(
95
"network.negotiate-auth.trusted-uris",
96
param.SPNEGO.join(", "),
97
locked
98
);
99
}
100
if ("Delegated" in param) {
101
setDefaultPref(
102
"network.negotiate-auth.delegation-uris",
103
param.Delegated.join(", "),
104
locked
105
);
106
}
107
if ("NTLM" in param) {
108
setDefaultPref(
109
"network.automatic-ntlm-auth.trusted-uris",
110
param.NTLM.join(", "),
111
locked
112
);
113
}
114
if ("AllowNonFQDN" in param) {
115
if ("NTLM" in param.AllowNonFQDN) {
116
setDefaultPref(
117
"network.automatic-ntlm-auth.allow-non-fqdn",
118
param.AllowNonFQDN.NTLM,
119
locked
120
);
121
}
122
if ("SPNEGO" in param.AllowNonFQDN) {
123
setDefaultPref(
124
"network.negotiate-auth.allow-non-fqdn",
125
param.AllowNonFQDN.SPNEGO,
126
locked
127
);
128
}
129
}
130
if ("AllowProxies" in param) {
131
if ("NTLM" in param.AllowProxies) {
132
setDefaultPref(
133
"network.automatic-ntlm-auth.allow-proxies",
134
param.AllowProxies.NTLM,
135
locked
136
);
137
}
138
if ("SPNEGO" in param.AllowProxies) {
139
setDefaultPref(
140
"network.negotiate-auth.allow-proxies",
141
param.AllowProxies.SPNEGO,
142
locked
143
);
144
}
145
}
146
},
147
},
148
149
BlockAboutAddons: {
150
onBeforeUIStartup(manager, param) {
151
if (param) {
152
blockAboutPage(manager, "about:addons", true);
153
}
154
},
155
},
156
157
BlockAboutConfig: {
158
onBeforeUIStartup(manager, param) {
159
if (param) {
160
blockAboutPage(manager, "about:config");
161
setAndLockPref("devtools.chrome.enabled", false);
162
}
163
},
164
},
165
166
BlockAboutProfiles: {
167
onBeforeUIStartup(manager, param) {
168
if (param) {
169
blockAboutPage(manager, "about:profiles");
170
}
171
},
172
},
173
174
BlockAboutSupport: {
175
onBeforeUIStartup(manager, param) {
176
if (param) {
177
blockAboutPage(manager, "about:support");
178
}
179
},
180
},
181
182
Bookmarks: {
183
onAllWindowsRestored(manager, param) {
184
BookmarksPolicies.processBookmarks(param);
185
},
186
},
187
188
CaptivePortal: {
189
onBeforeAddons(manager, param) {
190
setAndLockPref("network.captive-portal-service.enabled", param);
191
},
192
},
193
194
Certificates: {
195
onBeforeAddons(manager, param) {
196
if ("ImportEnterpriseRoots" in param) {
197
setAndLockPref(
198
"security.enterprise_roots.enabled",
199
param.ImportEnterpriseRoots
200
);
201
}
202
if ("Install" in param) {
203
(async () => {
204
let dirs = [];
205
let platform = AppConstants.platform;
206
if (platform == "win") {
207
dirs = [
208
// Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
209
Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
210
// Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
211
Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
212
];
213
} else if (platform == "macosx" || platform == "linux") {
214
dirs = [
215
// These two keys are named wrong. They return the Mozilla directory.
216
Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile),
217
Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile),
218
];
219
}
220
dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
221
for (let certfilename of param.Install) {
222
let certfile;
223
try {
224
certfile = Cc["@mozilla.org/file/local;1"].createInstance(
225
Ci.nsIFile
226
);
227
certfile.initWithPath(certfilename);
228
} catch (e) {
229
for (let dir of dirs) {
230
certfile = dir.clone();
231
certfile.append(
232
platform == "linux" ? "certificates" : "Certificates"
233
);
234
certfile.append(certfilename);
235
if (certfile.exists()) {
236
break;
237
}
238
}
239
}
240
let file;
241
try {
242
file = await File.createFromNsIFile(certfile);
243
} catch (e) {
244
log.error(`Unable to find certificate - ${certfilename}`);
245
continue;
246
}
247
let reader = new FileReader();
248
reader.onloadend = function() {
249
if (reader.readyState != reader.DONE) {
250
log.error(`Unable to read certificate - ${certfile.path}`);
251
return;
252
}
253
let certFile = reader.result;
254
let cert;
255
try {
256
cert = gCertDB.constructX509(certFile);
257
} catch (e) {
258
try {
259
// It might be PEM instead of DER.
260
cert = gCertDB.constructX509FromBase64(pemToBase64(certFile));
261
} catch (ex) {
262
log.error(`Unable to add certificate - ${certfile.path}`);
263
}
264
}
265
let now = Date.now() / 1000;
266
if (cert) {
267
gCertDB.asyncVerifyCertAtTime(
268
cert,
269
0x0008 /* certificateUsageSSLCA */,
270
0,
271
null,
272
now,
273
(aPRErrorCode, aVerifiedChain, aHasEVPolicy) => {
274
if (aPRErrorCode == Cr.NS_OK) {
275
// Certificate is already installed.
276
return;
277
}
278
try {
279
gCertDB.addCert(certFile, "CT,CT,");
280
} catch (e) {
281
// It might be PEM instead of DER.
282
gCertDB.addCertFromBase64(
283
pemToBase64(certFile),
284
"CT,CT,"
285
);
286
}
287
}
288
);
289
}
290
};
291
reader.readAsBinaryString(file);
292
}
293
})();
294
}
295
},
296
},
297
298
Cookies: {
299
onBeforeUIStartup(manager, param) {
300
addAllowDenyPermissions("cookie", param.Allow, param.Block);
301
302
if (param.Block) {
303
const hosts = param.Block.map(url => url.hostname)
304
.sort()
305
.join("\n");
306
runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
307
for (let blocked of param.Block) {
308
Services.cookies.removeCookiesWithOriginAttributes(
309
"{}",
310
blocked.hostname
311
);
312
}
313
});
314
}
315
316
if (
317
param.Default !== undefined ||
318
param.AcceptThirdParty !== undefined ||
319
param.RejectTracker !== undefined ||
320
param.Locked
321
) {
322
const ACCEPT_COOKIES = 0;
323
const REJECT_THIRD_PARTY_COOKIES = 1;
324
const REJECT_ALL_COOKIES = 2;
325
const REJECT_UNVISITED_THIRD_PARTY = 3;
326
const REJECT_TRACKER = 4;
327
328
let newCookieBehavior = ACCEPT_COOKIES;
329
if (param.Default !== undefined && !param.Default) {
330
newCookieBehavior = REJECT_ALL_COOKIES;
331
} else if (param.AcceptThirdParty) {
332
if (param.AcceptThirdParty == "never") {
333
newCookieBehavior = REJECT_THIRD_PARTY_COOKIES;
334
} else if (param.AcceptThirdParty == "from-visited") {
335
newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY;
336
}
337
} else if (param.RejectTracker !== undefined && param.RejectTracker) {
338
newCookieBehavior = REJECT_TRACKER;
339
}
340
341
setDefaultPref(
342
"network.cookie.cookieBehavior",
343
newCookieBehavior,
344
param.Locked
345
);
346
}
347
348
const KEEP_COOKIES_UNTIL_EXPIRATION = 0;
349
const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2;
350
351
if (param.ExpireAtSessionEnd !== undefined || param.Locked) {
352
let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION;
353
if (param.ExpireAtSessionEnd) {
354
newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION;
355
}
356
357
setDefaultPref(
358
"network.cookie.lifetimePolicy",
359
newLifetimePolicy,
360
param.Locked
361
);
362
}
363
},
364
},
365
366
DefaultDownloadDirectory: {
367
onBeforeAddons(manager, param) {
368
setDefaultPref("browser.download.dir", replacePathVariables(param));
369
// If a custom download directory is being used, just lock folder list to 2.
370
setAndLockPref("browser.download.folderList", 2);
371
},
372
},
373
374
DisableAppUpdate: {
375
onBeforeAddons(manager, param) {
376
if (param) {
377
manager.disallowFeature("appUpdate");
378
}
379
},
380
},
381
382
DisableBuiltinPDFViewer: {
383
onBeforeAddons(manager, param) {
384
if (param) {
385
setAndLockPref("pdfjs.disabled", true);
386
}
387
},
388
},
389
390
DisableDeveloperTools: {
391
onBeforeAddons(manager, param) {
392
if (param) {
393
setAndLockPref("devtools.policy.disabled", true);
394
setAndLockPref("devtools.chrome.enabled", false);
395
396
manager.disallowFeature("devtools");
397
blockAboutPage(manager, "about:devtools");
398
blockAboutPage(manager, "about:debugging");
399
blockAboutPage(manager, "about:devtools-toolbox");
400
}
401
},
402
},
403
404
DisableFeedbackCommands: {
405
onBeforeUIStartup(manager, param) {
406
if (param) {
407
manager.disallowFeature("feedbackCommands");
408
}
409
},
410
},
411
412
DisableFirefoxAccounts: {
413
onBeforeAddons(manager, param) {
414
if (param) {
415
setAndLockPref("identity.fxaccounts.enabled", false);
416
setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
417
}
418
},
419
},
420
421
DisableFirefoxScreenshots: {
422
onBeforeAddons(manager, param) {
423
if (param) {
424
setAndLockPref("extensions.screenshots.disabled", true);
425
}
426
},
427
},
428
429
DisableFirefoxStudies: {
430
onBeforeAddons(manager, param) {
431
if (param) {
432
manager.disallowFeature("Shield");
433
setAndLockPref(
434
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
435
false
436
);
437
setAndLockPref(
438
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
439
false
440
);
441
}
442
},
443
},
444
445
DisableForgetButton: {
446
onProfileAfterChange(manager, param) {
447
if (param) {
448
setAndLockPref("privacy.panicButton.enabled", false);
449
}
450
},
451
},
452
453
DisableFormHistory: {
454
onBeforeUIStartup(manager, param) {
455
if (param) {
456
setAndLockPref("browser.formfill.enable", false);
457
}
458
},
459
},
460
461
DisableMasterPasswordCreation: {
462
onBeforeUIStartup(manager, param) {
463
if (param) {
464
manager.disallowFeature("createMasterPassword");
465
}
466
},
467
},
468
469
DisablePasswordReveal: {
470
onBeforeUIStartup(manager, param) {
471
if (param) {
472
manager.disallowFeature("passwordReveal");
473
}
474
},
475
},
476
477
DisablePocket: {
478
onBeforeAddons(manager, param) {
479
if (param) {
480
setAndLockPref("extensions.pocket.enabled", false);
481
}
482
},
483
},
484
485
DisablePrivateBrowsing: {
486
onBeforeAddons(manager, param) {
487
if (param) {
488
manager.disallowFeature("privatebrowsing");
489
blockAboutPage(manager, "about:privatebrowsing", true);
490
setAndLockPref("browser.privatebrowsing.autostart", false);
491
}
492
},
493
},
494
495
DisableProfileImport: {
496
onBeforeUIStartup(manager, param) {
497
if (param) {
498
manager.disallowFeature("profileImport");
499
setAndLockPref(
500
"browser.newtabpage.activity-stream.migrationExpired",
501
true
502
);
503
}
504
},
505
},
506
507
DisableProfileRefresh: {
508
onBeforeUIStartup(manager, param) {
509
if (param) {
510
manager.disallowFeature("profileRefresh");
511
setAndLockPref("browser.disableResetPrompt", true);
512
}
513
},
514
},
515
516
DisableSafeMode: {
517
onBeforeUIStartup(manager, param) {
518
if (param) {
519
manager.disallowFeature("safeMode");
520
}
521
},
522
},
523
524
DisableSecurityBypass: {
525
onBeforeUIStartup(manager, param) {
526
if ("InvalidCertificate" in param) {
527
setAndLockPref(
528
"security.certerror.hideAddException",
529
param.InvalidCertificate
530
);
531
}
532
533
if ("SafeBrowsing" in param) {
534
setAndLockPref(
535
"browser.safebrowsing.allowOverride",
536
!param.SafeBrowsing
537
);
538
}
539
},
540
},
541
542
DisableSetDesktopBackground: {
543
onBeforeUIStartup(manager, param) {
544
if (param) {
545
manager.disallowFeature("setDesktopBackground");
546
}
547
},
548
},
549
550
DisableSystemAddonUpdate: {
551
onBeforeAddons(manager, param) {
552
if (param) {
553
manager.disallowFeature("SysAddonUpdate");
554
}
555
},
556
},
557
558
DisableTelemetry: {
559
onBeforeAddons(manager, param) {
560
if (param) {
561
setAndLockPref("datareporting.healthreport.uploadEnabled", false);
562
setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
563
blockAboutPage(manager, "about:telemetry");
564
}
565
},
566
},
567
568
DisplayBookmarksToolbar: {
569
onBeforeUIStartup(manager, param) {
570
let value = (!param).toString();
571
// This policy is meant to change the default behavior, not to force it.
572
// If this policy was alreay applied and the user chose to re-hide the
573
// bookmarks toolbar, do not show it again.
574
runOncePerModification("displayBookmarksToolbar", value, () => {
575
gXulStore.setValue(
576
BROWSER_DOCUMENT_URL,
577
"PersonalToolbar",
578
"collapsed",
579
value
580
);
581
});
582
},
583
},
584
585
DisplayMenuBar: {
586
onBeforeUIStartup(manager, param) {
587
let value = (!param).toString();
588
// This policy is meant to change the default behavior, not to force it.
589
// If this policy was alreay applied and the user chose to re-hide the
590
// menu bar, do not show it again.
591
runOncePerModification("displayMenuBar", value, () => {
592
gXulStore.setValue(
593
BROWSER_DOCUMENT_URL,
594
"toolbar-menubar",
595
"autohide",
596
value
597
);
598
});
599
},
600
},
601
602
DNSOverHTTPS: {
603
onBeforeAddons(manager, param) {
604
if ("Enabled" in param) {
605
let mode = param.Enabled ? 2 : 5;
606
setDefaultPref("network.trr.mode", mode, param.Locked);
607
}
608
if (param.ProviderURL) {
609
setDefaultPref("network.trr.uri", param.ProviderURL.href, param.Locked);
610
}
611
},
612
},
613
614
DontCheckDefaultBrowser: {
615
onBeforeUIStartup(manager, param) {
616
setAndLockPref("browser.shell.checkDefaultBrowser", !param);
617
},
618
},
619
620
DownloadDirectory: {
621
onBeforeAddons(manager, param) {
622
setAndLockPref("browser.download.dir", replacePathVariables(param));
623
// If a custom download directory is being used, just lock folder list to 2.
624
setAndLockPref("browser.download.folderList", 2);
625
// Per Chrome spec, user can't choose to download every time
626
// if this is set.
627
setAndLockPref("browser.download.useDownloadDir", true);
628
},
629
},
630
631
EnableTrackingProtection: {
632
onBeforeUIStartup(manager, param) {
633
if (param.Value) {
634
setDefaultPref(
635
"privacy.trackingprotection.enabled",
636
true,
637
param.Locked
638
);
639
setDefaultPref(
640
"privacy.trackingprotection.pbmode.enabled",
641
true,
642
param.Locked
643
);
644
} else {
645
setAndLockPref("privacy.trackingprotection.enabled", false);
646
setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
647
}
648
if ("Cryptomining" in param) {
649
setDefaultPref(
650
"privacy.trackingprotection.cryptomining.enabled",
651
param.Cryptomining,
652
param.Locked
653
);
654
}
655
if ("Fingerprinting" in param) {
656
setDefaultPref(
657
"privacy.trackingprotection.fingerprinting.enabled",
658
param.Fingerprinting,
659
param.Locked
660
);
661
}
662
},
663
},
664
665
Extensions: {
666
onBeforeUIStartup(manager, param) {
667
let uninstallingPromise = Promise.resolve();
668
if ("Uninstall" in param) {
669
uninstallingPromise = runOncePerModification(
670
"extensionsUninstall",
671
JSON.stringify(param.Uninstall),
672
async () => {
673
// If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
674
// changed, which will allow add-ons to be updated.
675
Services.prefs.clearUserPref(
676
"browser.policies.runOncePerModification.extensionsInstall"
677
);
678
let addons = await AddonManager.getAddonsByIDs(param.Uninstall);
679
for (let addon of addons) {
680
if (addon) {
681
try {
682
await addon.uninstall();
683
} catch (e) {
684
// This can fail for add-ons that can't be uninstalled.
685
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
686
}
687
}
688
}
689
}
690
);
691
}
692
if ("Install" in param) {
693
runOncePerModification(
694
"extensionsInstall",
695
JSON.stringify(param.Install),
696
async () => {
697
await uninstallingPromise;
698
for (let location of param.Install) {
699
let uri;
700
try {
701
// We need to try as a file first because
702
// Windows paths are valid URIs.
703
// This is done for legacy support (old API)
704
let xpiFile = new FileUtils.File(location);
705
uri = Services.io.newFileURI(xpiFile);
706
} catch (e) {
707
uri = Services.io.newURI(location);
708
}
709
installAddonFromURL(uri.spec);
710
}
711
}
712
);
713
}
714
if ("Locked" in param) {
715
for (let ID of param.Locked) {
716
manager.disallowFeature(`uninstall-extension:${ID}`);
717
manager.disallowFeature(`disable-extension:${ID}`);
718
}
719
}
720
},
721
},
722
723
ExtensionSettings: {
724
onBeforeAddons(manager, param) {
725
try {
726
manager.setExtensionSettings(param);
727
} catch (e) {
728
log.error("Invalid ExtensionSettings");
729
}
730
},
731
async onBeforeUIStartup(manager, param) {
732
let extensionSettings = param;
733
let blockAllExtensions = false;
734
if ("*" in extensionSettings) {
735
if (
736
"installation_mode" in extensionSettings["*"] &&
737
extensionSettings["*"].installation_mode == "blocked"
738
) {
739
blockAllExtensions = true;
740
// Turn off discovery pane in about:addons
741
setAndLockPref("extensions.getAddons.showPane", false);
742
// Block about:debugging
743
blockAboutPage(manager, "about:debugging");
744
}
745
}
746
let { addons } = await AddonManager.getActiveAddons();
747
let allowedExtensions = [];
748
for (let extensionID in extensionSettings) {
749
if (extensionID == "*") {
750
// Ignore global settings
751
continue;
752
}
753
if ("installation_mode" in extensionSettings[extensionID]) {
754
if (
755
extensionSettings[extensionID].installation_mode ==
756
"force_installed" ||
757
extensionSettings[extensionID].installation_mode ==
758
"normal_installed"
759
) {
760
if (!extensionSettings[extensionID].install_url) {
761
throw new Error(`Missing install_url for ${extensionID}`);
762
}
763
if (!addons.find(addon => addon.id == extensionID)) {
764
installAddonFromURL(
765
extensionSettings[extensionID].install_url,
766
extensionID
767
);
768
}
769
manager.disallowFeature(`uninstall-extension:${extensionID}`);
770
if (
771
extensionSettings[extensionID].installation_mode ==
772
"force_installed"
773
) {
774
manager.disallowFeature(`disable-extension:${extensionID}`);
775
}
776
allowedExtensions.push(extensionID);
777
} else if (
778
extensionSettings[extensionID].installation_mode == "allowed"
779
) {
780
allowedExtensions.push(extensionID);
781
} else if (
782
extensionSettings[extensionID].installation_mode == "blocked"
783
) {
784
if (addons.find(addon => addon.id == extensionID)) {
785
// Can't use the addon from getActiveAddons since it doesn't have uninstall.
786
let addon = await AddonManager.getAddonByID(extensionID);
787
try {
788
await addon.uninstall();
789
} catch (e) {
790
// This can fail for add-ons that can't be uninstalled.
791
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
792
}
793
}
794
}
795
}
796
}
797
if (blockAllExtensions) {
798
for (let addon of addons) {
799
if (
800
addon.isSystem ||
801
addon.isBuiltin ||
802
!(addon.scope & AddonManager.SCOPE_PROFILE)
803
) {
804
continue;
805
}
806
if (!allowedExtensions.includes(addon.id)) {
807
try {
808
// Can't use the addon from getActiveAddons since it doesn't have uninstall.
809
let addonToUninstall = await AddonManager.getAddonByID(addon.id);
810
await addonToUninstall.uninstall();
811
} catch (e) {
812
// This can fail for add-ons that can't be uninstalled.
813
log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`);
814
}
815
}
816
}
817
}
818
},
819
},
820
821
ExtensionUpdate: {
822
onBeforeAddons(manager, param) {
823
if (!param) {
824
setAndLockPref("extensions.update.enabled", param);
825
}
826
},
827
},
828
829
FirefoxHome: {
830
onBeforeAddons(manager, param) {
831
let locked = param.Locked || false;
832
if ("Search" in param) {
833
setDefaultPref(
834
"browser.newtabpage.activity-stream.showSearch",
835
param.Search,
836
locked
837
);
838
}
839
if ("TopSites" in param) {
840
setDefaultPref(
841
"browser.newtabpage.activity-stream.feeds.topsites",
842
param.TopSites,
843
locked
844
);
845
}
846
if ("Highlights" in param) {
847
setDefaultPref(
848
"browser.newtabpage.activity-stream.feeds.section.highlights",
849
param.Highlights,
850
locked
851
);
852
}
853
if ("Pocket" in param) {
854
setDefaultPref(
855
"browser.newtabpage.activity-stream.feeds.section.topstories",
856
param.Pocket,
857
locked
858
);
859
}
860
if ("Snippets" in param) {
861
setDefaultPref(
862
"browser.newtabpage.activity-stream.feeds.snippets",
863
param.Snippets,
864
locked
865
);
866
}
867
},
868
},
869
870
FlashPlugin: {
871
onBeforeUIStartup(manager, param) {
872
addAllowDenyPermissions("plugin:flash", param.Allow, param.Block);
873
874
const FLASH_NEVER_ACTIVATE = 0;
875
const FLASH_ASK_TO_ACTIVATE = 1;
876
877
let flashPrefVal;
878
if (param.Default === undefined || param.Default) {
879
flashPrefVal = FLASH_ASK_TO_ACTIVATE;
880
} else {
881
flashPrefVal = FLASH_NEVER_ACTIVATE;
882
}
883
if (param.Locked) {
884
setAndLockPref("plugin.state.flash", flashPrefVal);
885
} else if (param.Default !== undefined) {
886
setDefaultPref("plugin.state.flash", flashPrefVal);
887
}
888
},
889
},
890
891
HardwareAcceleration: {
892
onBeforeAddons(manager, param) {
893
if (!param) {
894
setAndLockPref("layers.acceleration.disabled", true);
895
}
896
},
897
},
898
899
Homepage: {
900
onBeforeUIStartup(manager, param) {
901
// |homepages| will be a string containing a pipe-separated ('|') list of
902
// URLs because that is what the "Home page" section of about:preferences
903
// (and therefore what the pref |browser.startup.homepage|) accepts.
904
if (param.URL) {
905
let homepages = param.URL.href;
906
if (param.Additional && param.Additional.length) {
907
homepages += "|" + param.Additional.map(url => url.href).join("|");
908
}
909
setDefaultPref("browser.startup.homepage", homepages, param.Locked);
910
if (param.Locked) {
911
setAndLockPref(
912
"pref.browser.homepage.disable_button.current_page",
913
true
914
);
915
setAndLockPref(
916
"pref.browser.homepage.disable_button.bookmark_page",
917
true
918
);
919
setAndLockPref(
920
"pref.browser.homepage.disable_button.restore_default",
921
true
922
);
923
} else {
924
// Clear out old run once modification that is no longer used.
925
clearRunOnceModification("setHomepage");
926
}
927
}
928
if (param.StartPage) {
929
let prefValue;
930
switch (param.StartPage) {
931
case "none":
932
prefValue = 0;
933
break;
934
case "homepage":
935
prefValue = 1;
936
break;
937
case "previous-session":
938
prefValue = 3;
939
break;
940
}
941
setDefaultPref("browser.startup.page", prefValue, param.Locked);
942
}
943
},
944
},
945
946
InstallAddonsPermission: {
947
onBeforeUIStartup(manager, param) {
948
if ("Allow" in param) {
949
addAllowDenyPermissions("install", param.Allow, null);
950
}
951
if ("Default" in param) {
952
setAndLockPref("xpinstall.enabled", param.Default);
953
if (!param.Default) {
954
blockAboutPage(manager, "about:debugging");
955
setAndLockPref(
956
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
957
false
958
);
959
setAndLockPref(
960
"browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
961
false
962
);
963
manager.disallowFeature("xpinstall");
964
}
965
}
966
},
967
},
968
969
LegacyProfiles: {
970
// Handled in nsToolkitProfileService.cpp (Windows only)
971
},
972
973
LocalFileLinks: {
974
onBeforeAddons(manager, param) {
975
// If there are existing capabilities, lock them with the policy pref.
976
let policyNames = Services.prefs
977
.getCharPref("capability.policy.policynames", "")
978
.split(" ");
979
policyNames.push("localfilelinks_policy");
980
setAndLockPref("capability.policy.policynames", policyNames.join(" "));
981
setAndLockPref(
982
"capability.policy.localfilelinks_policy.checkloaduri.enabled",
983
"allAccess"
984
);
985
setAndLockPref(
986
"capability.policy.localfilelinks_policy.sites",
987
param.join(" ")
988
);
989
},
990
},
991
992
NetworkPrediction: {
993
onBeforeAddons(manager, param) {
994
setAndLockPref("network.dns.disablePrefetch", !param);
995
setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
996
},
997
},
998
999
NewTabPage: {
1000
onBeforeAddons(manager, param) {
1001
setAndLockPref("browser.newtabpage.enabled", param);
1002
},
1003
},
1004
1005
NoDefaultBookmarks: {
1006
onProfileAfterChange(manager, param) {
1007
if (param) {
1008
manager.disallowFeature("defaultBookmarks");
1009
}
1010
},
1011
},
1012
1013
OfferToSaveLogins: {
1014
onBeforeUIStartup(manager, param) {
1015
setAndLockPref("signon.rememberSignons", param);
1016
},
1017
},
1018
1019
OfferToSaveLoginsDefault: {
1020
onBeforeUIStartup(manager, param) {
1021
setDefaultPref("signon.rememberSignons", param);
1022
},
1023
},
1024
1025
OverrideFirstRunPage: {
1026
onProfileAfterChange(manager, param) {
1027
let url = param ? param.href : "";
1028
setAndLockPref("startup.homepage_welcome_url", url);
1029
setAndLockPref("trailhead.firstrun.branches", "nofirstrun");
1030
},
1031
},
1032
1033
OverridePostUpdatePage: {
1034
onProfileAfterChange(manager, param) {
1035
let url = param ? param.href : "";
1036
setAndLockPref("startup.homepage_override_url", url);
1037
// The pref startup.homepage_override_url is only used
1038
// as a fallback when the update.xml file hasn't provided
1039
// a specific post-update URL.
1040
manager.disallowFeature("postUpdateCustomPage");
1041
},
1042
},
1043
1044
PasswordManagerEnabled: {
1045
onBeforeUIStartup(manager, param) {
1046
if (!param) {
1047
blockAboutPage(manager, "about:logins", true);
1048
gBlockedChromePages.push("passwordManager.xul");
1049
setAndLockPref("pref.privacy.disable_button.view_passwords", true);
1050
}
1051
setAndLockPref("signon.rememberSignons", param);
1052
},
1053
},
1054
1055
Permissions: {
1056
onBeforeUIStartup(manager, param) {
1057
if (param.Camera) {
1058
addAllowDenyPermissions(
1059
"camera",
1060
param.Camera.Allow,
1061
param.Camera.Block
1062
);
1063
setDefaultPermission("camera", param.Camera);
1064
}
1065
1066
if (param.Microphone) {
1067
addAllowDenyPermissions(
1068
"microphone",
1069
param.Microphone.Allow,
1070
param.Microphone.Block
1071
);
1072
setDefaultPermission("microphone", param.Microphone);
1073
}
1074
1075
if (param.Location) {
1076
addAllowDenyPermissions(
1077
"geo",
1078
param.Location.Allow,
1079
param.Location.Block
1080
);
1081
setDefaultPermission("geo", param.Location);
1082
}
1083
1084
if (param.Notifications) {
1085
addAllowDenyPermissions(
1086
"desktop-notification",
1087
param.Notifications.Allow,
1088
param.Notifications.Block
1089
);
1090
setDefaultPermission("desktop-notification", param.Notifications);
1091
}
1092
},
1093
},
1094
1095
PopupBlocking: {
1096
onBeforeUIStartup(manager, param) {
1097
addAllowDenyPermissions("popup", param.Allow, null);
1098
1099
if (param.Locked) {
1100
let blockValue = true;
1101
if (param.Default !== undefined && !param.Default) {
1102
blockValue = false;
1103
}
1104
setAndLockPref("dom.disable_open_during_load", blockValue);
1105
} else if (param.Default !== undefined) {
1106
setDefaultPref("dom.disable_open_during_load", !!param.Default);
1107
}
1108
},
1109
},
1110
1111
Preferences: {
1112
onBeforeAddons(manager, param) {
1113
for (let preference in param) {
1114
setAndLockPref(preference, param[preference]);
1115
}
1116
},
1117
},
1118
1119
PromptForDownloadLocation: {
1120
onBeforeAddons(manager, param) {
1121
setAndLockPref("browser.download.useDownloadDir", !param);
1122
},
1123
},
1124
1125
Proxy: {
1126
onBeforeAddons(manager, param) {
1127
if (param.Locked) {
1128
manager.disallowFeature("changeProxySettings");
1129
ProxyPolicies.configureProxySettings(param, setAndLockPref);
1130
} else {
1131
ProxyPolicies.configureProxySettings(param, setDefaultPref);
1132
}
1133
},
1134
},
1135
1136
RequestedLocales: {
1137
onBeforeAddons(manager, param) {
1138
if (Array.isArray(param)) {
1139
Services.locale.requestedLocales = param;
1140
} else {
1141
Services.locale.requestedLocales = param.split(",");
1142
}
1143
},
1144
},
1145
1146
SanitizeOnShutdown: {
1147
onBeforeUIStartup(manager, param) {
1148
if (typeof param === "boolean") {
1149
setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param);
1150
if (param) {
1151
setAndLockPref("privacy.clearOnShutdown.cache", true);
1152
setAndLockPref("privacy.clearOnShutdown.cookies", true);
1153
setAndLockPref("privacy.clearOnShutdown.downloads", true);
1154
setAndLockPref("privacy.clearOnShutdown.formdata", true);
1155
setAndLockPref("privacy.clearOnShutdown.history", true);
1156
setAndLockPref("privacy.clearOnShutdown.sessions", true);
1157
setAndLockPref("privacy.clearOnShutdown.siteSettings", true);
1158
setAndLockPref("privacy.clearOnShutdown.offlineApps", true);
1159
}
1160
} else {
1161
setAndLockPref("privacy.sanitize.sanitizeOnShutdown", true);
1162
if ("Cache" in param) {
1163
setAndLockPref("privacy.clearOnShutdown.cache", param.Cache);
1164
} else {
1165
setAndLockPref("privacy.clearOnShutdown.cache", false);
1166
}
1167
if ("Cookies" in param) {
1168
setAndLockPref("privacy.clearOnShutdown.cookies", param.Cookies);
1169
} else {
1170
setAndLockPref("privacy.clearOnShutdown.cookies", false);
1171
}
1172
if ("Downloads" in param) {
1173
setAndLockPref("privacy.clearOnShutdown.downloads", param.Downloads);
1174
} else {
1175
setAndLockPref("privacy.clearOnShutdown.downloads", false);
1176
}
1177
if ("FormData" in param) {
1178
setAndLockPref("privacy.clearOnShutdown.formdata", param.FormData);
1179
} else {
1180
setAndLockPref("privacy.clearOnShutdown.formdata", false);
1181
}
1182
if ("History" in param) {
1183
setAndLockPref("privacy.clearOnShutdown.history", param.History);
1184
} else {
1185
setAndLockPref("privacy.clearOnShutdown.history", false);
1186
}
1187
if ("Sessions" in param) {
1188
setAndLockPref("privacy.clearOnShutdown.sessions", param.Sessions);
1189
} else {
1190
setAndLockPref("privacy.clearOnShutdown.sessions", false);
1191
}
1192
if ("SiteSettings" in param) {
1193
setAndLockPref(
1194
"privacy.clearOnShutdown.siteSettings",
1195
param.SiteSettings
1196
);
1197
}
1198
if ("OfflineApps" in param) {
1199
setAndLockPref(
1200
"privacy.clearOnShutdown.offlineApps",
1201
param.OfflineApps
1202
);
1203
}
1204
}
1205
},
1206
},
1207
1208
SearchBar: {
1209
onAllWindowsRestored(manager, param) {
1210
// This policy is meant to change the default behavior, not to force it.
1211
// If this policy was already applied and the user chose move the search
1212
// bar, don't move it again.
1213
runOncePerModification("searchInNavBar", param, () => {
1214
if (param == "separate") {
1215
CustomizableUI.addWidgetToArea(
1216
"search-container",
1217
CustomizableUI.AREA_NAVBAR,
1218
CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1
1219
);
1220
} else if (param == "unified") {
1221
CustomizableUI.removeWidgetFromArea("search-container");
1222
}
1223
});
1224
},
1225
},
1226
1227
SearchEngines: {
1228
onBeforeUIStartup(manager, param) {
1229
if (param.PreventInstalls) {
1230
manager.disallowFeature("installSearchEngine", true);
1231
}
1232
},
1233
onAllWindowsRestored(manager, param) {
1234
Services.search.init().then(async () => {
1235
if (param.Remove) {
1236
// Only rerun if the list of engine names has changed.
1237
await runOncePerModification(
1238
"removeSearchEngines",
1239
JSON.stringify(param.Remove),
1240
async function() {
1241
for (let engineName of param.Remove) {
1242
let engine = Services.search.getEngineByName(engineName);
1243
if (engine) {
1244
try {
1245
await Services.search.removeEngine(engine);
1246
} catch (ex) {
1247
log.error("Unable to remove the search engine", ex);
1248
}
1249
}
1250
}
1251
}
1252
);
1253
}
1254
if (param.Add) {
1255
// Only rerun if the list of engine names has changed.
1256
let engineNameList = param.Add.map(engine => engine.Name);
1257
await runOncePerModification(
1258
"addSearchEngines",
1259
JSON.stringify(engineNameList),
1260
async function() {
1261
for (let newEngine of param.Add) {
1262
let newEngineParameters = {
1263
template: newEngine.URLTemplate,
1264
iconURL: newEngine.IconURL ? newEngine.IconURL.href : null,
1265
alias: newEngine.Alias,
1266
description: newEngine.Description,
1267
method: newEngine.Method,
1268
postData: newEngine.PostData,
1269
suggestURL: newEngine.SuggestURLTemplate,
1270
extensionID: "set-via-policy",
1271
queryCharset: "UTF-8",
1272
};
1273
try {
1274
await Services.search.addEngineWithDetails(
1275
newEngine.Name,
1276
newEngineParameters
1277
);
1278
} catch (ex) {
1279
log.error("Unable to add search engine", ex);
1280
}
1281
}
1282
}
1283
);
1284
}
1285
if (param.Default) {
1286
await runOncePerModification(
1287
"setDefaultSearchEngine",
1288
param.Default,
1289
async () => {
1290
let defaultEngine;
1291
try {
1292
defaultEngine = Services.search.getEngineByName(param.Default);
1293
if (!defaultEngine) {
1294
throw new Error("No engine by that name could be found");
1295
}
1296
} catch (ex) {
1297
log.error(
1298
`Search engine lookup failed when attempting to set ` +
1299
`the default engine. Requested engine was ` +
1300
`"${param.Default}".`,
1301
ex
1302
);
1303
}
1304
if (defaultEngine) {
1305
try {
1306
await Services.search.setDefault(defaultEngine);
1307
} catch (ex) {
1308
log.error("Unable to set the default search engine", ex);
1309
}
1310
}
1311
}
1312
);
1313
}
1314
if (param.DefaultPrivate) {
1315
await runOncePerModification(
1316
"setDefaultPrivateSearchEngine",
1317
param.DefaultPrivate,
1318
async () => {
1319
let defaultPrivateEngine;
1320
try {
1321
defaultPrivateEngine = Services.search.getEngineByName(
1322
param.DefaultPrivate
1323
);
1324
if (!defaultPrivateEngine) {
1325
throw new Error("No engine by that name could be found");
1326
}
1327
} catch (ex) {
1328
log.error(
1329
`Search engine lookup failed when attempting to set ` +
1330
`the default private engine. Requested engine was ` +
1331
`"${param.DefaultPrivate}".`,
1332
ex
1333
);
1334
}
1335
if (defaultPrivateEngine) {
1336
try {
1337
await Services.search.setDefaultPrivate(defaultPrivateEngine);
1338
} catch (ex) {
1339
log.error(
1340
"Unable to set the default private search engine",
1341
ex
1342
);
1343
}
1344
}
1345
}
1346
);
1347
}
1348
});
1349
},
1350
},
1351
1352
SearchSuggestEnabled: {
1353
onBeforeAddons(manager, param) {
1354
setAndLockPref("browser.urlbar.suggest.searches", param);
1355
setAndLockPref("browser.search.suggest.enabled", param);
1356
},
1357
},
1358
1359
SecurityDevices: {
1360
onProfileAfterChange(manager, param) {
1361
let securityDevices = param;
1362
let pkcs11db = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
1363
Ci.nsIPKCS11ModuleDB
1364
);
1365
let moduleList = pkcs11db.listModules();
1366
for (let deviceName in securityDevices) {
1367
let foundModule = false;
1368
for (let module of moduleList) {
1369
if (module && module.libName === securityDevices[deviceName]) {
1370
foundModule = true;
1371
break;
1372
}
1373
}
1374
if (foundModule) {
1375
continue;
1376
}
1377
try {
1378
pkcs11db.addModule(deviceName, securityDevices[deviceName], 0, 0);
1379
} catch (ex) {
1380
log.error(`Unable to add security device ${deviceName}`);
1381
log.debug(ex);
1382
}
1383
}
1384
},
1385
},
1386
1387
SSLVersionMax: {
1388
onBeforeAddons(manager, param) {
1389
let tlsVersion;
1390
switch (param) {
1391
case "tls1":
1392
tlsVersion = 1;
1393
break;
1394
case "tls1.1":
1395
tlsVersion = 2;
1396
break;
1397
case "tls1.2":
1398
tlsVersion = 3;
1399
break;
1400
case "tls1.3":
1401
tlsVersion = 4;
1402
break;
1403
}
1404
setAndLockPref("security.tls.version.max", tlsVersion);
1405
},
1406
},
1407
1408
SSLVersionMin: {
1409
onBeforeAddons(manager, param) {
1410
let tlsVersion;
1411
switch (param) {
1412
case "tls1":
1413
tlsVersion = 1;
1414
break;
1415
case "tls1.1":
1416
tlsVersion = 2;
1417
break;
1418
case "tls1.2":
1419
tlsVersion = 3;
1420
break;
1421
case "tls1.3":
1422
tlsVersion = 4;
1423
break;
1424
}
1425
setAndLockPref("security.tls.version.min", tlsVersion);
1426
},
1427
},
1428
1429
SupportMenu: {
1430
onProfileAfterChange(manager, param) {
1431
manager.setSupportMenu(param);
1432
},
1433
},
1434
1435
WebsiteFilter: {
1436
onBeforeUIStartup(manager, param) {
1437
this.filter = new WebsiteFilter(
1438
param.Block || [],
1439
param.Exceptions || []
1440
);
1441
},
1442
},
1443
};
1444
1445
/*
1446
* ====================
1447
* = HELPER FUNCTIONS =
1448
* ====================
1449
*
1450
* The functions below are helpers to be used by several policies.
1451
*/
1452
1453
/**
1454
* setAndLockPref
1455
*
1456
* Sets the _default_ value of a pref, and locks it (meaning that
1457
* the default value will always be returned, independent from what
1458
* is stored as the user value).
1459
* The value is only changed in memory, and not stored to disk.
1460
*
1461
* @param {string} prefName
1462
* The pref to be changed
1463
* @param {boolean,number,string} prefValue
1464
* The value to set and lock
1465
*/
1466
function setAndLockPref(prefName, prefValue) {
1467
setDefaultPref(prefName, prefValue, true);
1468
}
1469
1470
/**
1471
* setDefaultPref
1472
*
1473
* Sets the _default_ value of a pref and optionally locks it.
1474
* The value is only changed in memory, and not stored to disk.
1475
*
1476
* @param {string} prefName
1477
* The pref to be changed
1478
* @param {boolean,number,string} prefValue
1479
* The value to set
1480
* @param {boolean} locked
1481
* Optionally lock the pref
1482
*/
1483
function setDefaultPref(prefName, prefValue, locked = false) {
1484
if (Services.prefs.prefIsLocked(prefName)) {
1485
Services.prefs.unlockPref(prefName);
1486
}
1487
1488
let defaults = Services.prefs.getDefaultBranch("");
1489
1490
switch (typeof prefValue) {
1491
case "boolean":
1492
defaults.setBoolPref(prefName, prefValue);
1493
break;
1494
1495
case "number":
1496
if (!Number.isInteger(prefValue)) {
1497
throw new Error(`Non-integer value for ${prefName}`);
1498
}
1499
1500
defaults.setIntPref(prefName, prefValue);
1501
break;
1502
1503
case "string":
1504
defaults.setStringPref(prefName, prefValue);
1505
break;
1506
}
1507
1508
if (locked) {
1509
Services.prefs.lockPref(prefName);
1510
}
1511
}
1512
1513
/**
1514
* setDefaultPermission
1515
*
1516
* Helper function to set preferences appropriately for the policy
1517
*
1518
* @param {string} policyName
1519
* The name of the policy to set
1520
* @param {object} policyParam
1521
* The object containing param for the policy
1522
*/
1523
function setDefaultPermission(policyName, policyParam) {
1524
if ("BlockNewRequests" in policyParam) {
1525
let prefName = "permissions.default." + policyName;
1526
1527
if (policyParam.BlockNewRequests) {
1528
setDefaultPref(prefName, 2, policyParam.Locked);
1529
} else {
1530
setDefaultPref(prefName, 0, policyParam.Locked);
1531
}
1532
}
1533
}
1534
1535
/**
1536
* addAllowDenyPermissions
1537
*
1538
* Helper function to call the permissions manager (Services.perms.addFromPrincipal)
1539
* for two arrays of URLs.
1540
*
1541
* @param {string} permissionName
1542
* The name of the permission to change
1543
* @param {array} allowList
1544
* The list of URLs to be set as ALLOW_ACTION for the chosen permission.
1545
* @param {array} blockList
1546
* The list of URLs to be set as DENY_ACTION for the chosen permission.
1547
*/
1548
function addAllowDenyPermissions(permissionName, allowList, blockList) {
1549
allowList = allowList || [];
1550
blockList = blockList || [];
1551
1552
for (let origin of allowList) {
1553
try {
1554
Services.perms.addFromPrincipal(
1555
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
1556
permissionName,
1557
Ci.nsIPermissionManager.ALLOW_ACTION,
1558
Ci.nsIPermissionManager.EXPIRE_POLICY
1559
);
1560
} catch (ex) {
1561
log.error(`Added by default for ${permissionName} permission in the permission
1562
manager - ${origin.href}`);
1563
}
1564
}
1565
1566
for (let origin of blockList) {
1567
Services.perms.addFromPrincipal(
1568
Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
1569
permissionName,
1570
Ci.nsIPermissionManager.DENY_ACTION,
1571
Ci.nsIPermissionManager.EXPIRE_POLICY
1572
);
1573
}
1574
}
1575
1576
/**
1577
* runOnce
1578
*
1579
* Helper function to run a callback only once per policy.
1580
*
1581
* @param {string} actionName
1582
* A given name which will be used to track if this callback has run.
1583
* @param {Functon} callback
1584
* The callback to run only once.
1585
*/
1586
// eslint-disable-next-line no-unused-vars
1587
function runOnce(actionName, callback) {
1588
let prefName = `browser.policies.runonce.${actionName}`;
1589
if (Services.prefs.getBoolPref(prefName, false)) {
1590
log.debug(
1591
`Not running action ${actionName} again because it has already run.`
1592
);
1593
return;
1594
}
1595
Services.prefs.setBoolPref(prefName, true);
1596
callback();
1597
}
1598
1599
/**
1600
* runOncePerModification
1601
*
1602
* Helper function similar to runOnce. The difference is that runOnce runs the
1603
* callback once when the policy is set, then never again.
1604
* runOncePerModification runs the callback once each time the policy value
1605
* changes from its previous value.
1606
* If the callback that was passed is an async function, you can await on this
1607
* function to await for the callback.
1608
*
1609
* @param {string} actionName
1610
* A given name which will be used to track if this callback has run.
1611
* This string will be part of a pref name.
1612
* @param {string} policyValue
1613
* The current value of the policy. This will be compared to previous
1614
* values given to this function to determine if the policy value has
1615
* changed. Regardless of the data type of the policy, this must be a
1616
* string.
1617
* @param {Function} callback
1618
* The callback to be run when the pref value changes
1619
* @returns Promise
1620
* A promise that will resolve once the callback finishes running.
1621
*
1622
*/
1623
async function runOncePerModification(actionName, policyValue, callback) {
1624
let prefName = `browser.policies.runOncePerModification.${actionName}`;
1625
let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
1626
if (policyValue === oldPolicyValue) {
1627
log.debug(
1628
`Not running action ${actionName} again because the policy's value is unchanged`
1629
);
1630
return Promise.resolve();
1631
}
1632
Services.prefs.setStringPref(prefName, policyValue);
1633
return callback();
1634
}
1635
1636
/**
1637
* clearRunOnceModification
1638
*
1639
* Helper function that clears a runOnce policy.
1640
*/
1641
function clearRunOnceModification(actionName) {
1642
let prefName = `browser.policies.runOncePerModification.${actionName}`;
1643
Services.prefs.clearUserPref(prefName);
1644
}
1645
1646
function replacePathVariables(path) {
1647
if (path.includes("${home}")) {
1648
return path.replace("${home}", FileUtils.getFile("Home", []).path);
1649
}
1650
return path;
1651
}
1652
1653
/**
1654
* installAddonFromURL
1655
*
1656
* Helper function that installs an addon from a URL
1657
* and verifies that the addon ID matches.
1658
*/
1659
function installAddonFromURL(url, extensionID) {
1660
AddonManager.getInstallForURL(url, {
1661
telemetryInfo: { source: "enterprise-policy" },
1662
}).then(install => {
1663
if (install.addon && install.addon.appDisabled) {
1664
log.error(`Incompatible add-on - ${location}`);
1665
install.cancel();
1666
return;
1667
}
1668
let listener = {
1669
/* eslint-disable-next-line no-shadow */
1670
onDownloadEnded: install => {
1671
if (extensionID && install.addon.id != extensionID) {
1672
log.error(
1673
`Add-on downloaded from ${url} had unexpected id (got ${
1674
install.addon.id
1675
} expected ${extensionID})`
1676
);
1677
install.removeListener(listener);
1678
install.cancel();
1679
}
1680
if (install.addon && install.addon.appDisabled) {
1681
log.error(`Incompatible add-on - ${url}`);
1682
install.removeListener(listener);
1683
install.cancel();
1684
}
1685
},
1686
onDownloadFailed: () => {
1687
install.removeListener(listener);
1688
log.error(
1689
`Download failed - ${AddonManager.errorToString(
1690
install.error
1691
)} - ${url}`
1692
);
1693
clearRunOnceModification("extensionsInstall");
1694
},
1695
onInstallFailed: () => {
1696
install.removeListener(listener);
1697
log.error(
1698
`Installation failed - ${AddonManager.errorToString(
1699
install.error
1700
)} - {url}`
1701
);
1702
},
1703
onInstallEnded: () => {
1704
install.removeListener(listener);
1705
log.debug(`Installation succeeded - ${url}`);
1706
},
1707
};
1708
install.addListener(listener);
1709
install.install();
1710
});
1711
}
1712
1713
let gBlockedChromePages = [];
1714
1715
function blockAboutPage(manager, feature, neededOnContentProcess = false) {
1716
if (!gBlockedChromePages.length) {
1717
addChromeURLBlocker();
1718
}
1719
manager.disallowFeature(feature, neededOnContentProcess);
1720
let splitURL = Services.io
1721
.newChannelFromURI(
1722
Services.io.newURI(feature),
1723
null,
1724
Services.scriptSecurityManager.getSystemPrincipal(),
1725
null,
1726
0,
1727
Ci.nsIContentPolicy.TYPE_OTHER
1728
)
1729
.URI.spec.split("/");
1730
// about:debugging uses index.html for a filename, so we need to rely
1731
// on more than just the filename.
1732
let fileName =
1733
splitURL[splitURL.length - 2] + "/" + splitURL[splitURL.length - 1];
1734
gBlockedChromePages.push(fileName);
1735
if (feature == "about:config") {
1736
// Hide old page until it is removed
1737
gBlockedChromePages.push("config.xul");
1738
}
1739
}
1740
1741
let ChromeURLBlockPolicy = {
1742
shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
1743
let contentType = loadInfo.externalContentPolicyType;
1744
if (
1745
contentLocation.scheme == "chrome" &&
1746
contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT &&
1747
loadInfo.loadingContext &&
1748
loadInfo.loadingContext.baseURI == AppConstants.BROWSER_CHROME_URL &&
1749
gBlockedChromePages.some(function(fileName) {
1750
return contentLocation.filePath.endsWith(fileName);
1751
})
1752
) {
1753
return Ci.nsIContentPolicy.REJECT_REQUEST;
1754
}
1755
return Ci.nsIContentPolicy.ACCEPT;
1756
},
1757
shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
1758
return Ci.nsIContentPolicy.ACCEPT;
1759
},
1760
classDescription: "Policy Engine Content Policy",
1761
contractID: "@mozilla-org/policy-engine-content-policy-service;1",
1762
classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"),
1763
QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPolicy]),
1764
createInstance(outer, iid) {
1765
return this.QueryInterface(iid);
1766
},
1767
};
1768
1769
function addChromeURLBlocker() {
1770
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
1771
registrar.registerFactory(
1772
ChromeURLBlockPolicy.classID,
1773
ChromeURLBlockPolicy.classDescription,
1774
ChromeURLBlockPolicy.contractID,
1775
ChromeURLBlockPolicy
1776
);
1777
1778
Services.catMan.addCategoryEntry(
1779
"content-policy",
1780
ChromeURLBlockPolicy.contractID,
1781
ChromeURLBlockPolicy.contractID,
1782
false,
1783
true
1784
);
1785
}
1786
1787
function pemToBase64(pem) {
1788
return pem
1789
.replace(/-----BEGIN CERTIFICATE-----/, "")
1790
.replace(/-----END CERTIFICATE-----/, "")
1791
.replace(/[\r\n]/g, "");
1792
}