Policies.sys.mjs (107759B)
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 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 6 7 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 8 9 const lazy = {}; 10 11 XPCOMUtils.defineLazyServiceGetters(lazy, { 12 gCertDB: ["@mozilla.org/security/x509certdb;1", Ci.nsIX509CertDB], 13 gExternalProtocolService: [ 14 "@mozilla.org/uriloader/external-protocol-service;1", 15 Ci.nsIExternalProtocolService, 16 ], 17 gHandlerService: [ 18 "@mozilla.org/uriloader/handler-service;1", 19 Ci.nsIHandlerService, 20 ], 21 gMIMEService: ["@mozilla.org/mime;1", Ci.nsIMIMEService], 22 }); 23 24 ChromeUtils.defineESModuleGetters(lazy, { 25 AddonManager: "resource://gre/modules/AddonManager.sys.mjs", 26 BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.sys.mjs", 27 CustomizableUI: 28 "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs", 29 FileUtils: "resource://gre/modules/FileUtils.sys.mjs", 30 ProxyPolicies: "resource:///modules/policies/ProxyPolicies.sys.mjs", 31 QuickSuggest: "moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs", 32 WebsiteFilter: "resource:///modules/policies/WebsiteFilter.sys.mjs", 33 }); 34 35 const PREF_LOGLEVEL = "browser.policies.loglevel"; 36 const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL; 37 const ABOUT_CONTRACT = "@mozilla.org/network/protocol/about;1?what="; 38 39 const isXpcshell = Services.env.exists("XPCSHELL_TEST_PROFILE_DIR"); 40 41 ChromeUtils.defineLazyGetter(lazy, "log", () => { 42 let { ConsoleAPI } = ChromeUtils.importESModule( 43 "resource://gre/modules/Console.sys.mjs" 44 ); 45 return new ConsoleAPI({ 46 prefix: "Policies", 47 // tip: set maxLogLevel to "debug" and use log.debug() to create detailed 48 // messages during development. See LOG_LEVELS in Console.sys.mjs for details. 49 maxLogLevel: "error", 50 maxLogLevelPref: PREF_LOGLEVEL, 51 }); 52 }); 53 54 /* 55 * ============================ 56 * = POLICIES IMPLEMENTATIONS = 57 * ============================ 58 * 59 * The Policies object below is where the implementation for each policy 60 * happens. An object for each policy should be defined, containing 61 * callback functions that will be called by the engine. 62 * 63 * See the _callbacks object in EnterprisePoliciesParent.sys.mjs for the list of 64 * possible callbacks and an explanation of each. 65 * 66 * Each callback will be called with two parameters: 67 * - manager 68 * This is the EnterprisePoliciesManager singleton object from 69 * EnterprisePoliciesParent.sys.mjs 70 * 71 * - param 72 * The parameter defined for this policy in policies-schema.json. 73 * It will be different for each policy. It could be a boolean, 74 * a string, an array or a complex object. All parameters have 75 * been validated according to the schema, and no unknown 76 * properties will be present on them. 77 * 78 * The callbacks will be bound to their parent policy object. 79 */ 80 export var Policies = { 81 // Used for cleaning up policies. 82 // Use the same timing that you used for setting up the policy. 83 _cleanup: { 84 onBeforeAddons() { 85 if (Cu.isInAutomation || isXpcshell) { 86 console.log("_cleanup from onBeforeAddons"); 87 clearBlockedAboutPages(); 88 } 89 }, 90 onProfileAfterChange() { 91 if (Cu.isInAutomation || isXpcshell) { 92 console.log("_cleanup from onProfileAfterChange"); 93 } 94 }, 95 onBeforeUIStartup() { 96 if (Cu.isInAutomation || isXpcshell) { 97 console.log("_cleanup from onBeforeUIStartup"); 98 } 99 }, 100 onAllWindowsRestored() { 101 if (Cu.isInAutomation || isXpcshell) { 102 console.log("_cleanup from onAllWindowsRestored"); 103 } 104 }, 105 }, 106 107 "3rdparty": { 108 onBeforeAddons(manager, param) { 109 manager.setExtensionPolicies(param.Extensions); 110 }, 111 }, 112 113 AllowedDomainsForApps: { 114 onBeforeAddons(manager, param) { 115 Services.obs.addObserver(function (subject) { 116 let channel = subject.QueryInterface(Ci.nsIHttpChannel); 117 if (channel.URI.host.endsWith(".google.com")) { 118 channel.setRequestHeader("X-GoogApps-Allowed-Domains", param, true); 119 } 120 }, "http-on-modify-request"); 121 }, 122 }, 123 124 AllowFileSelectionDialogs: { 125 onBeforeUIStartup(manager, param) { 126 if (!param) { 127 setAndLockPref("widget.disable_file_pickers", true); 128 setAndLockPref("browser.download.useDownloadDir", true); 129 manager.disallowFeature("filepickers"); 130 } 131 }, 132 }, 133 134 AppAutoUpdate: { 135 onBeforeUIStartup(manager, param) { 136 // Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is 137 // true, we disallow turning off auto updating, and visa versa. 138 if (param) { 139 manager.disallowFeature("app-auto-updates-off"); 140 } else { 141 manager.disallowFeature("app-auto-updates-on"); 142 } 143 }, 144 }, 145 146 AppUpdatePin: { 147 validate(param) { 148 // This is the version when pinning was introduced. Attempting to set a 149 // pin before this will not work, because Balrog's pinning table will 150 // never have the necessary entry. 151 const earliestPinMajorVersion = 102; 152 const earliestPinMinorVersion = 0; 153 154 let pinParts = param.split("."); 155 156 if (pinParts.length < 2) { 157 lazy.log.error("AppUpdatePin has too few dots."); 158 return false; 159 } 160 if (pinParts.length > 3) { 161 lazy.log.error("AppUpdatePin has too many dots."); 162 return false; 163 } 164 165 const trailingPinPart = pinParts.pop(); 166 if (trailingPinPart != "") { 167 lazy.log.error("AppUpdatePin does not end with a trailing dot."); 168 return false; 169 } 170 171 const pinMajorVersionStr = pinParts.shift(); 172 if (!pinMajorVersionStr.length) { 173 lazy.log.error("AppUpdatePin's major version is empty."); 174 return false; 175 } 176 if (!/^\d+$/.test(pinMajorVersionStr)) { 177 lazy.log.error( 178 "AppUpdatePin's major version contains a non-numeric character." 179 ); 180 return false; 181 } 182 if (/^0/.test(pinMajorVersionStr)) { 183 lazy.log.error("AppUpdatePin's major version contains a leading 0."); 184 return false; 185 } 186 const pinMajorVersionInt = parseInt(pinMajorVersionStr, 10); 187 if (isNaN(pinMajorVersionInt)) { 188 lazy.log.error( 189 "AppUpdatePin's major version could not be parsed to an integer." 190 ); 191 return false; 192 } 193 if (pinMajorVersionInt < earliestPinMajorVersion) { 194 lazy.log.error( 195 `AppUpdatePin must not be earlier than '${earliestPinMajorVersion}.${earliestPinMinorVersion}.'.` 196 ); 197 return false; 198 } 199 200 if (pinParts.length) { 201 const pinMinorVersionStr = pinParts.shift(); 202 if (!pinMinorVersionStr.length) { 203 lazy.log.error("AppUpdatePin's minor version is empty."); 204 return false; 205 } 206 if (!/^\d+$/.test(pinMinorVersionStr)) { 207 lazy.log.error( 208 "AppUpdatePin's minor version contains a non-numeric character." 209 ); 210 return false; 211 } 212 if (/^0\d/.test(pinMinorVersionStr)) { 213 lazy.log.error("AppUpdatePin's minor version contains a leading 0."); 214 return false; 215 } 216 const pinMinorVersionInt = parseInt(pinMinorVersionStr, 10); 217 if (isNaN(pinMinorVersionInt)) { 218 lazy.log.error( 219 "AppUpdatePin's minor version could not be parsed to an integer." 220 ); 221 return false; 222 } 223 if ( 224 pinMajorVersionInt == earliestPinMajorVersion && 225 pinMinorVersionInt < earliestPinMinorVersion 226 ) { 227 lazy.log.error( 228 `AppUpdatePin must not be earlier than '${earliestPinMajorVersion}.${earliestPinMinorVersion}.'.` 229 ); 230 return false; 231 } 232 } 233 234 return true; 235 }, 236 // No additional implementation needed here. UpdateService.sys.mjs will check 237 // for this policy directly when determining the update URL. 238 }, 239 240 AppUpdateURL: { 241 // No implementation needed here. UpdateService.sys.mjs will check for this 242 // policy directly when determining the update URL. 243 }, 244 245 Authentication: { 246 onBeforeAddons(manager, param) { 247 // When Authentication was originally implemented, it was always 248 // locked, so it defaults to locked. 249 let locked = true; 250 if ("Locked" in param) { 251 locked = param.Locked; 252 } 253 if ("SPNEGO" in param) { 254 PoliciesUtils.setDefaultPref( 255 "network.negotiate-auth.trusted-uris", 256 param.SPNEGO.join(", "), 257 locked 258 ); 259 } 260 if ("Delegated" in param) { 261 PoliciesUtils.setDefaultPref( 262 "network.negotiate-auth.delegation-uris", 263 param.Delegated.join(", "), 264 locked 265 ); 266 } 267 if ("NTLM" in param) { 268 PoliciesUtils.setDefaultPref( 269 "network.automatic-ntlm-auth.trusted-uris", 270 param.NTLM.join(", "), 271 locked 272 ); 273 } 274 if ("AllowNonFQDN" in param) { 275 if ("NTLM" in param.AllowNonFQDN) { 276 PoliciesUtils.setDefaultPref( 277 "network.automatic-ntlm-auth.allow-non-fqdn", 278 param.AllowNonFQDN.NTLM, 279 locked 280 ); 281 } 282 if ("SPNEGO" in param.AllowNonFQDN) { 283 PoliciesUtils.setDefaultPref( 284 "network.negotiate-auth.allow-non-fqdn", 285 param.AllowNonFQDN.SPNEGO, 286 locked 287 ); 288 } 289 } 290 if ("AllowProxies" in param) { 291 if ("NTLM" in param.AllowProxies) { 292 PoliciesUtils.setDefaultPref( 293 "network.automatic-ntlm-auth.allow-proxies", 294 param.AllowProxies.NTLM, 295 locked 296 ); 297 } 298 if ("SPNEGO" in param.AllowProxies) { 299 PoliciesUtils.setDefaultPref( 300 "network.negotiate-auth.allow-proxies", 301 param.AllowProxies.SPNEGO, 302 locked 303 ); 304 } 305 } 306 if ("PrivateBrowsing" in param) { 307 PoliciesUtils.setDefaultPref( 308 "network.auth.private-browsing-sso", 309 param.PrivateBrowsing, 310 locked 311 ); 312 } 313 }, 314 }, 315 316 AutofillAddressEnabled: { 317 onBeforeAddons(manager, param) { 318 setAndLockPref("extensions.formautofill.addresses.enabled", param); 319 }, 320 }, 321 322 AutofillCreditCardEnabled: { 323 onBeforeAddons(manager, param) { 324 setAndLockPref("extensions.formautofill.creditCards.enabled", param); 325 }, 326 }, 327 328 AutoLaunchProtocolsFromOrigins: { 329 onBeforeAddons(manager, param) { 330 for (let info of param) { 331 addAllowDenyPermissions( 332 `open-protocol-handler^${info.protocol}`, 333 info.allowed_origins 334 ); 335 } 336 }, 337 }, 338 339 BackgroundAppUpdate: { 340 onBeforeAddons(manager, param) { 341 if (param) { 342 manager.disallowFeature("app-background-update-off"); 343 } else { 344 manager.disallowFeature("app-background-update-on"); 345 } 346 }, 347 }, 348 349 BlockAboutAddons: { 350 onBeforeUIStartup(manager, param) { 351 if (param) { 352 blockAboutPage(manager, "about:addons", true); 353 } 354 }, 355 }, 356 357 BlockAboutConfig: { 358 onBeforeUIStartup(manager, param) { 359 if (param) { 360 blockAboutPage(manager, "about:config"); 361 setAndLockPref("devtools.chrome.enabled", false); 362 } 363 }, 364 }, 365 366 BlockAboutProfiles: { 367 onBeforeAddons(manager, param) { 368 if (param) { 369 manager.disallowFeature("profileManagement"); 370 } 371 }, 372 onBeforeUIStartup(manager, param) { 373 if (param) { 374 blockAboutPage(manager, "about:profiles"); 375 blockAboutPage(manager, "about:profilemanager"); 376 blockAboutPage(manager, "about:editprofile"); 377 blockAboutPage(manager, "about:deleteprofile"); 378 blockAboutPage(manager, "about:newprofile"); 379 } 380 }, 381 }, 382 383 BlockAboutSupport: { 384 onBeforeUIStartup(manager, param) { 385 if (param) { 386 blockAboutPage(manager, "about:support"); 387 manager.disallowFeature("aboutSupport"); 388 } 389 }, 390 }, 391 392 Bookmarks: { 393 onAllWindowsRestored(manager, param) { 394 lazy.BookmarksPolicies.processBookmarks(param); 395 }, 396 }, 397 398 BrowserDataBackup: { 399 onBeforeUIStartup(manager, param) { 400 if (typeof param === "boolean") { 401 setAndLockPref("browser.backup.enabled", param); 402 setAndLockPref("browser.backup.archive.enabled", param); 403 setAndLockPref("browser.backup.restore.enabled", param); 404 } else { 405 const hasBackup = "AllowBackup" in param; 406 const hasRestore = "AllowRestore" in param; 407 let serviceValue; 408 409 if (hasBackup && hasRestore) { 410 // both present but could be set to false 411 serviceValue = param.AllowBackup || param.AllowRestore; 412 } else if (hasBackup && param.AllowBackup) { 413 // only AllowBackup is true 414 serviceValue = true; 415 } else if (hasRestore && param.AllowRestore) { 416 // only AllowRestore is true 417 serviceValue = true; 418 } 419 420 if (serviceValue !== undefined) { 421 PoliciesUtils.setDefaultPref( 422 "browser.backup.enabled", 423 serviceValue, 424 true 425 ); 426 } 427 428 if (hasBackup) { 429 PoliciesUtils.setDefaultPref( 430 "browser.backup.archive.enabled", 431 param.AllowBackup, 432 true 433 ); 434 } 435 436 if (hasRestore) { 437 PoliciesUtils.setDefaultPref( 438 "browser.backup.restore.enabled", 439 param.AllowRestore, 440 true 441 ); 442 } 443 } 444 }, 445 }, 446 447 CaptivePortal: { 448 onBeforeAddons(manager, param) { 449 setAndLockPref("network.captive-portal-service.enabled", param); 450 }, 451 }, 452 453 Certificates: { 454 onBeforeAddons(manager, param) { 455 if ("ImportEnterpriseRoots" in param) { 456 setAndLockPref( 457 "security.enterprise_roots.enabled", 458 param.ImportEnterpriseRoots 459 ); 460 } 461 if ("Install" in param) { 462 (async () => { 463 let dirs = []; 464 let platform = AppConstants.platform; 465 if (platform == "win") { 466 dirs = [ 467 // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla. 468 Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent, 469 // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla. 470 Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent, 471 ]; 472 } else if (platform == "macosx" || platform == "linux") { 473 dirs = [ 474 // These two keys are named wrong. They return the Mozilla directory. 475 Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile), 476 Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile), 477 ]; 478 } 479 dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile)); 480 for (let certfilename of param.Install) { 481 let certfile; 482 try { 483 certfile = Cc["@mozilla.org/file/local;1"].createInstance( 484 Ci.nsIFile 485 ); 486 certfile.initWithPath(certfilename); 487 } catch (e) { 488 for (let dir of dirs) { 489 certfile = dir.clone(); 490 certfile.append( 491 platform == "linux" ? "certificates" : "Certificates" 492 ); 493 certfile.append(certfilename); 494 if (certfile.exists()) { 495 break; 496 } 497 } 498 } 499 let file; 500 try { 501 file = await File.createFromNsIFile(certfile); 502 } catch (e) { 503 lazy.log.error(`Unable to find certificate - ${certfilename}`); 504 continue; 505 } 506 let reader = new FileReader(); 507 reader.onloadend = function () { 508 if (reader.readyState != reader.DONE) { 509 lazy.log.error(`Unable to read certificate - ${certfile.path}`); 510 return; 511 } 512 let certFile = reader.result; 513 let certFileArray = []; 514 for (let i = 0; i < certFile.length; i++) { 515 certFileArray.push(certFile.charCodeAt(i)); 516 } 517 let cert; 518 try { 519 cert = lazy.gCertDB.constructX509(certFileArray); 520 } catch (e) { 521 lazy.log.debug( 522 `constructX509 failed with error '${e}' - trying constructX509FromBase64.` 523 ); 524 try { 525 // It might be PEM instead of DER. 526 cert = lazy.gCertDB.constructX509FromBase64( 527 pemToBase64(certFile) 528 ); 529 } catch (ex) { 530 lazy.log.error( 531 `Unable to add certificate - ${certfile.path}`, 532 ex 533 ); 534 } 535 } 536 if (cert) { 537 if ( 538 lazy.gCertDB.isCertTrusted( 539 cert, 540 Ci.nsIX509Cert.CA_CERT, 541 Ci.nsIX509CertDB.TRUSTED_SSL 542 ) 543 ) { 544 // Certificate is already installed. 545 return; 546 } 547 try { 548 lazy.gCertDB.addCert(certFile, "CT,CT,"); 549 } catch (e) { 550 // It might be PEM instead of DER. 551 lazy.gCertDB.addCertFromBase64( 552 pemToBase64(certFile), 553 "CT,CT," 554 ); 555 } 556 } 557 }; 558 reader.readAsBinaryString(file); 559 } 560 })(); 561 } 562 }, 563 }, 564 565 Containers: { 566 // Queried directly by ContextualIdentityService.sys.mjs 567 }, 568 569 ContentAnalysis: { 570 onBeforeAddons(manager, param) { 571 // For security reasons, all of the Content Analysis related prefs should be locked in 572 // this method, even if the values aren't specified in Enterprise Policies. 573 setPrefIfPresentAndLock( 574 param, 575 "PipePathName", 576 "browser.contentanalysis.pipe_path_name" 577 ); 578 if ("AgentTimeout" in param) { 579 if (!Number.isInteger(param.AgentTimeout)) { 580 lazy.log.error( 581 `Non-integer value for AgentTimeout: ${param.AgentTimeout}` 582 ); 583 } else { 584 setAndLockPref( 585 "browser.contentanalysis.agent_timeout", 586 param.AgentTimeout 587 ); 588 } 589 } else { 590 Services.prefs.lockPref("browser.contentanalysis.agent_timeout"); 591 } 592 setPrefIfPresentAndLock( 593 param, 594 "AllowUrlRegexList", 595 "browser.contentanalysis.allow_url_regex_list" 596 ); 597 setPrefIfPresentAndLock( 598 param, 599 "DenyUrlRegexList", 600 "browser.contentanalysis.deny_url_regex_list" 601 ); 602 setPrefIfPresentAndLock( 603 param, 604 "AgentName", 605 "browser.contentanalysis.agent_name" 606 ); 607 setPrefIfPresentAndLock( 608 param, 609 "ClientSignature", 610 "browser.contentanalysis.client_signature" 611 ); 612 setPrefIfPresentAndLock( 613 param, 614 "MaxConnectionsCount", 615 "browser.contentanalysis.max_connections" 616 ); 617 let resultPrefs = [ 618 ["DefaultResult", "default_result"], 619 ["TimeoutResult", "timeout_result"], 620 ]; 621 for (let pref of resultPrefs) { 622 if (pref[0] in param) { 623 if ( 624 !Number.isInteger(param[pref[0]]) || 625 param[pref[0]] < 0 || 626 param[pref[0]] > 2 627 ) { 628 lazy.log.error( 629 `Non-integer or out of range value for ${pref[0]}: ${param[pref[0]]}` 630 ); 631 Services.prefs.lockPref(`browser.contentanalysis.${pref[1]}`); 632 } else { 633 setAndLockPref( 634 `browser.contentanalysis.${pref[1]}`, 635 param[pref[0]] 636 ); 637 } 638 } else { 639 Services.prefs.lockPref(`browser.contentanalysis.${pref[1]}`); 640 } 641 } 642 let boolPrefs = [ 643 ["IsPerUser", "is_per_user"], 644 ["ShowBlockedResult", "show_blocked_result"], 645 ["BypassForSameTabOperations", "bypass_for_same_tab_operations"], 646 ]; 647 for (let pref of boolPrefs) { 648 if (pref[0] in param) { 649 setAndLockPref( 650 `browser.contentanalysis.${pref[1]}`, 651 !!param[pref[0]] 652 ); 653 } else { 654 Services.prefs.lockPref(`browser.contentanalysis.${pref[1]}`); 655 } 656 } 657 let interceptionPointPrefs = [ 658 ["Clipboard", "clipboard"], 659 ["Download", "download"], 660 ["DragAndDrop", "drag_and_drop"], 661 ["FileUpload", "file_upload"], 662 ["Print", "print"], 663 ]; 664 if ("InterceptionPoints" in param) { 665 for (let pref of interceptionPointPrefs) { 666 let value = true; 667 if (pref[0] in param.InterceptionPoints) { 668 if ("Enabled" in param.InterceptionPoints[pref[0]]) { 669 value = !!param.InterceptionPoints[pref[0]].Enabled; 670 } 671 } 672 setAndLockPref( 673 `browser.contentanalysis.interception_point.${pref[1]}.enabled`, 674 value 675 ); 676 } 677 } else { 678 for (let pref of interceptionPointPrefs) { 679 Services.prefs.lockPref( 680 `browser.contentanalysis.interception_point.${pref[1]}.enabled` 681 ); 682 } 683 } 684 let plainTextOnlyPrefs = [ 685 ["Clipboard", "clipboard"], 686 ["DragAndDrop", "drag_and_drop"], 687 ]; 688 if ("InterceptionPoints" in param) { 689 for (let pref of plainTextOnlyPrefs) { 690 // Need to set and lock this value even if the enterprise 691 // policy isn't set so users can't change it 692 let value = true; 693 if ("InterceptionPoints" in param) { 694 if (pref[0] in param.InterceptionPoints) { 695 if ("PlainTextOnly" in param.InterceptionPoints[pref[0]]) { 696 value = !!param.InterceptionPoints[pref[0]].PlainTextOnly; 697 } 698 } 699 } 700 setAndLockPref( 701 `browser.contentanalysis.interception_point.${pref[1]}.plain_text_only`, 702 value 703 ); 704 } 705 } else { 706 for (let pref of plainTextOnlyPrefs) { 707 Services.prefs.lockPref( 708 `browser.contentanalysis.interception_point.${pref[1]}.plain_text_only` 709 ); 710 } 711 } 712 if ("Enabled" in param) { 713 let enabled = !!param.Enabled; 714 setAndLockPref("browser.contentanalysis.enabled", enabled); 715 let ca = Cc["@mozilla.org/contentanalysis;1"].getService( 716 Ci.nsIContentAnalysis 717 ); 718 ca.isSetByEnterprisePolicy = true; 719 } else { 720 // Probably not strictly necessary, but let's lock everything 721 // to be consistent. 722 Services.prefs.lockPref("browser.contentanalysis.enabled"); 723 } 724 }, 725 }, 726 727 Cookies: { 728 onBeforeUIStartup(manager, param) { 729 addAllowDenyPermissions("cookie", param.Allow, param.Block); 730 731 if (param.AllowSession) { 732 for (let origin of param.AllowSession) { 733 try { 734 Services.perms.addFromPrincipal( 735 Services.scriptSecurityManager.createContentPrincipalFromOrigin( 736 origin 737 ), 738 "cookie", 739 Ci.nsICookiePermission.ACCESS_SESSION, 740 Ci.nsIPermissionManager.EXPIRE_POLICY 741 ); 742 } catch (ex) { 743 lazy.log.error( 744 `Unable to add cookie session permission - ${origin.href}` 745 ); 746 } 747 } 748 } 749 750 if (param.Block) { 751 const hosts = param.Block.map(url => url.hostname) 752 .sort() 753 .join("\n"); 754 runOncePerModification("clearCookiesForBlockedHosts", hosts, () => { 755 for (let blocked of param.Block) { 756 Services.cookies.removeCookiesWithOriginAttributes( 757 "{}", 758 blocked.hostname 759 ); 760 } 761 }); 762 } 763 764 if (param.ExpireAtSessionEnd != undefined) { 765 lazy.log.error( 766 "'ExpireAtSessionEnd' has been deprecated and it has no effect anymore." 767 ); 768 } 769 770 // New Cookie Behavior option takes precendence 771 let defaultPref = Services.prefs.getDefaultBranch(""); 772 let newCookieBehavior = defaultPref.getIntPref( 773 "network.cookie.cookieBehavior" 774 ); 775 let newCookieBehaviorPB = defaultPref.getIntPref( 776 "network.cookie.cookieBehavior.pbmode" 777 ); 778 if ("Behavior" in param || "BehaviorPrivateBrowsing" in param) { 779 let behaviors = { 780 accept: Ci.nsICookieService.BEHAVIOR_ACCEPT, 781 "reject-foreign": Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, 782 reject: Ci.nsICookieService.BEHAVIOR_REJECT, 783 "limit-foreign": Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN, 784 "reject-tracker": Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, 785 "reject-tracker-and-partition-foreign": 786 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, 787 }; 788 if ("Behavior" in param) { 789 newCookieBehavior = behaviors[param.Behavior]; 790 } 791 if ("BehaviorPrivateBrowsing" in param) { 792 newCookieBehaviorPB = behaviors[param.BehaviorPrivateBrowsing]; 793 } 794 } else { 795 // Default, AcceptThirdParty, and RejectTracker are being 796 // deprecated in favor of Behavior. They will continue 797 // to be supported, though. 798 if ( 799 param.Default !== undefined || 800 param.AcceptThirdParty !== undefined || 801 param.RejectTracker !== undefined || 802 param.Locked 803 ) { 804 newCookieBehavior = Ci.nsICookieService.BEHAVIOR_ACCEPT; 805 if (param.Default !== undefined && !param.Default) { 806 newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT; 807 } else if (param.AcceptThirdParty) { 808 if (param.AcceptThirdParty == "never") { 809 newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; 810 } else if (param.AcceptThirdParty == "from-visited") { 811 newCookieBehavior = Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; 812 } 813 } else if (param.RejectTracker) { 814 newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; 815 } 816 } 817 // With the old cookie policy, we made private browsing the same. 818 newCookieBehaviorPB = newCookieBehavior; 819 } 820 // We set the values no matter what just in case the policy was only used to lock. 821 PoliciesUtils.setDefaultPref( 822 "network.cookie.cookieBehavior", 823 newCookieBehavior, 824 param.Locked 825 ); 826 PoliciesUtils.setDefaultPref( 827 "network.cookie.cookieBehavior.pbmode", 828 newCookieBehaviorPB, 829 param.Locked 830 ); 831 }, 832 }, 833 834 DefaultDownloadDirectory: { 835 onBeforeAddons(manager, param) { 836 PoliciesUtils.setDefaultPref( 837 "browser.download.dir", 838 replacePathVariables(param) 839 ); 840 }, 841 }, 842 843 DisableAccounts: { 844 onBeforeAddons(manager, param) { 845 if (param) { 846 setAndLockPref("identity.fxaccounts.enabled", false); 847 setAndLockPref("browser.aboutwelcome.enabled", false); 848 } 849 }, 850 }, 851 852 DisableAppUpdate: { 853 onBeforeAddons(manager, param) { 854 if (param) { 855 manager.disallowFeature("appUpdate"); 856 } 857 }, 858 }, 859 860 DisableBuiltinPDFViewer: { 861 onBeforeAddons(manager, param) { 862 let policies = Services.policies.getActivePolicies(); 863 if ( 864 policies.Handlers?.mimeTypes?.["application/pdf"] || 865 policies.Handlers?.extensions?.pdf 866 ) { 867 // If there is an existing Handlers policy modifying PDF behavior, 868 // don't do anything. 869 return; 870 } 871 let pdfMIMEInfo = lazy.gMIMEService.getFromTypeAndExtension( 872 "application/pdf", 873 "pdf" 874 ); 875 let mimeInfo = { 876 action: param ? "useSystemDefault" : "handleInternally", 877 }; 878 processMIMEInfo(mimeInfo, pdfMIMEInfo); 879 }, 880 }, 881 882 DisabledCiphers: { 883 onBeforeAddons(manager, param) { 884 let cipherPrefs = { 885 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: 886 "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", 887 TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: 888 "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", 889 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: 890 "security.ssl3.ecdhe_ecdsa_chacha20_poly1305_sha256", 891 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: 892 "security.ssl3.ecdhe_rsa_chacha20_poly1305_sha256", 893 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: 894 "security.ssl3.ecdhe_ecdsa_aes_256_gcm_sha384", 895 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: 896 "security.ssl3.ecdhe_rsa_aes_256_gcm_sha384", 897 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: 898 "security.ssl3.ecdhe_rsa_aes_128_sha", 899 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: 900 "security.ssl3.ecdhe_ecdsa_aes_128_sha", 901 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: 902 "security.ssl3.ecdhe_rsa_aes_256_sha", 903 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: 904 "security.ssl3.ecdhe_ecdsa_aes_256_sha", 905 TLS_DHE_RSA_WITH_AES_128_CBC_SHA: "security.ssl3.dhe_rsa_aes_128_sha", 906 TLS_DHE_RSA_WITH_AES_256_CBC_SHA: "security.ssl3.dhe_rsa_aes_256_sha", 907 TLS_RSA_WITH_AES_128_GCM_SHA256: "security.ssl3.rsa_aes_128_gcm_sha256", 908 TLS_RSA_WITH_AES_256_GCM_SHA384: "security.ssl3.rsa_aes_256_gcm_sha384", 909 TLS_RSA_WITH_AES_128_CBC_SHA: "security.ssl3.rsa_aes_128_sha", 910 TLS_RSA_WITH_AES_256_CBC_SHA: "security.ssl3.rsa_aes_256_sha", 911 TLS_RSA_WITH_3DES_EDE_CBC_SHA: 912 "security.ssl3.deprecated.rsa_des_ede3_sha", 913 TLS_CHACHA20_POLY1305_SHA256: "security.tls13.chacha20_poly1305_sha256", 914 TLS_AES_128_GCM_SHA256: "security.tls13.aes_128_gcm_sha256", 915 TLS_AES_256_GCM_SHA384: "security.tls13.aes_256_gcm_sha384", 916 }; 917 918 for (let cipher in param) { 919 setAndLockPref(cipherPrefs[cipher], !param[cipher]); 920 } 921 }, 922 }, 923 924 DisableDefaultBrowserAgent: { 925 // The implementation of this policy is in the default browser agent itself 926 // (/toolkit/mozapps/defaultagent); we need an entry for it here so that it 927 // shows up in about:policies as a real policy and not as an error. 928 }, 929 930 DisableDeveloperTools: { 931 onBeforeAddons(manager, param) { 932 if (param) { 933 setAndLockPref("devtools.policy.disabled", true); 934 setAndLockPref("devtools.chrome.enabled", false); 935 936 manager.disallowFeature("devtools"); 937 blockAboutPage(manager, "about:debugging"); 938 blockAboutPage(manager, "about:devtools-toolbox"); 939 blockAboutPage(manager, "about:profiling"); 940 } 941 }, 942 }, 943 944 DisableEncryptedClientHello: { 945 onBeforeAddons(manager, param) { 946 if (param) { 947 setAndLockPref("network.dns.echconfig.enabled", false); 948 setAndLockPref("network.dns.http3_echconfig.enabled", false); 949 } 950 }, 951 }, 952 953 DisableFeedbackCommands: { 954 onBeforeUIStartup(manager, param) { 955 if (param) { 956 manager.disallowFeature("feedbackCommands"); 957 } 958 }, 959 }, 960 961 DisableFirefoxAccounts: { 962 onBeforeAddons(manager, param) { 963 // If DisableAccounts is set, let it take precedence. 964 if ("DisableAccounts" in manager.getActivePolicies()) { 965 return; 966 } 967 968 if (param) { 969 setAndLockPref("identity.fxaccounts.enabled", false); 970 setAndLockPref("browser.aboutwelcome.enabled", false); 971 } 972 }, 973 }, 974 975 DisableFirefoxScreenshots: { 976 onBeforeUIStartup(manager, param) { 977 if (param) { 978 setAndLockPref("screenshots.browser.component.enabled", false); 979 } 980 }, 981 }, 982 983 DisableFirefoxStudies: { 984 onBeforeAddons(manager, param) { 985 if (param) { 986 manager.disallowFeature("Shield"); 987 setAndLockPref( 988 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 989 false 990 ); 991 setAndLockPref( 992 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 993 false 994 ); 995 } 996 }, 997 }, 998 999 DisableForgetButton: { 1000 onProfileAfterChange(manager, param) { 1001 if (param) { 1002 setAndLockPref("privacy.panicButton.enabled", false); 1003 } 1004 }, 1005 }, 1006 1007 DisableFormHistory: { 1008 onBeforeUIStartup(manager, param) { 1009 if (param) { 1010 setAndLockPref("browser.formfill.enable", false); 1011 } 1012 }, 1013 }, 1014 1015 DisableMasterPasswordCreation: { 1016 onBeforeUIStartup(manager, param) { 1017 if (param) { 1018 manager.disallowFeature("createMasterPassword"); 1019 } 1020 }, 1021 }, 1022 1023 DisablePasswordReveal: { 1024 onBeforeUIStartup(manager, param) { 1025 if (param) { 1026 manager.disallowFeature("passwordReveal"); 1027 } 1028 }, 1029 }, 1030 1031 DisablePrivateBrowsing: { 1032 onBeforeAddons(manager, param) { 1033 if (param) { 1034 manager.disallowFeature("privatebrowsing"); 1035 blockAboutPage(manager, "about:privatebrowsing", true); 1036 setAndLockPref("browser.privatebrowsing.autostart", false); 1037 } 1038 }, 1039 }, 1040 1041 DisableProfileImport: { 1042 onBeforeUIStartup(manager, param) { 1043 if (param) { 1044 manager.disallowFeature("profileImport"); 1045 setAndLockPref( 1046 "browser.newtabpage.activity-stream.migrationExpired", 1047 true 1048 ); 1049 } 1050 }, 1051 }, 1052 1053 DisableProfileRefresh: { 1054 onBeforeUIStartup(manager, param) { 1055 if (param) { 1056 manager.disallowFeature("profileRefresh"); 1057 setAndLockPref("browser.disableResetPrompt", true); 1058 } 1059 }, 1060 }, 1061 1062 DisableSafeMode: { 1063 onBeforeUIStartup(manager, param) { 1064 if (param) { 1065 manager.disallowFeature("safeMode"); 1066 } 1067 }, 1068 }, 1069 1070 DisableSecurityBypass: { 1071 onBeforeUIStartup(manager, param) { 1072 if ("InvalidCertificate" in param) { 1073 setAndLockPref( 1074 "security.certerror.hideAddException", 1075 param.InvalidCertificate 1076 ); 1077 } 1078 1079 if ("SafeBrowsing" in param) { 1080 setAndLockPref( 1081 "browser.safebrowsing.allowOverride", 1082 !param.SafeBrowsing 1083 ); 1084 } 1085 }, 1086 }, 1087 1088 DisableSetDesktopBackground: { 1089 onBeforeUIStartup(manager, param) { 1090 if (param) { 1091 manager.disallowFeature("setDesktopBackground"); 1092 } 1093 }, 1094 }, 1095 1096 DisableSystemAddonUpdate: { 1097 onBeforeAddons(manager, param) { 1098 if (param) { 1099 manager.disallowFeature("SysAddonUpdate"); 1100 } 1101 }, 1102 }, 1103 1104 DisableTelemetry: { 1105 onBeforeAddons(manager, param) { 1106 if (param) { 1107 setAndLockPref("datareporting.healthreport.uploadEnabled", false); 1108 setAndLockPref("datareporting.policy.dataSubmissionEnabled", false); 1109 setAndLockPref("toolkit.telemetry.archive.enabled", false); 1110 setAndLockPref("datareporting.usage.uploadEnabled", false); 1111 blockAboutPage(manager, "about:telemetry"); 1112 } 1113 }, 1114 }, 1115 1116 DisableThirdPartyModuleBlocking: { 1117 onBeforeUIStartup(manager, param) { 1118 if (param) { 1119 manager.disallowFeature("thirdPartyModuleBlocking"); 1120 } 1121 }, 1122 }, 1123 1124 DisplayBookmarksToolbar: { 1125 onBeforeUIStartup(manager, param) { 1126 let visibility; 1127 if (typeof param === "boolean") { 1128 visibility = param ? "always" : "newtab"; 1129 } else { 1130 visibility = param; 1131 } 1132 // This policy is meant to change the default behavior, not to force it. 1133 // If this policy was already applied and the user chose to re-hide the 1134 // bookmarks toolbar, do not show it again. 1135 runOncePerModification("displayBookmarksToolbar", visibility, () => { 1136 let visibilityPref = "browser.toolbars.bookmarks.visibility"; 1137 Services.prefs.setCharPref(visibilityPref, visibility); 1138 }); 1139 }, 1140 }, 1141 1142 DisplayMenuBar: { 1143 onBeforeUIStartup(manager, param) { 1144 let value; 1145 if ( 1146 typeof param === "boolean" || 1147 param == "default-on" || 1148 param == "default-off" 1149 ) { 1150 switch (param) { 1151 case "default-on": 1152 value = false; 1153 break; 1154 case "default-off": 1155 value = true; 1156 break; 1157 default: 1158 value = !param; 1159 break; 1160 } 1161 // This policy is meant to change the default behavior, not to force it. 1162 // If this policy was already applied and the user chose to re-hide the 1163 // menu bar, do not show it again. 1164 runOncePerModification("displayMenuBar", value, () => { 1165 Services.xulStore.setValue( 1166 BROWSER_DOCUMENT_URL, 1167 "toolbar-menubar", 1168 "autohide", 1169 value ? "" : "-moz-missing\n" 1170 ); 1171 }); 1172 } else { 1173 switch (param) { 1174 case "always": 1175 value = false; 1176 break; 1177 case "never": 1178 // Make sure Alt key doesn't show the menubar 1179 setAndLockPref("ui.key.menuAccessKeyFocuses", false); 1180 value = true; 1181 break; 1182 } 1183 Services.xulStore.setValue( 1184 BROWSER_DOCUMENT_URL, 1185 "toolbar-menubar", 1186 "autohide", 1187 value ? "" : "-moz-missing\n" 1188 ); 1189 manager.disallowFeature("hideShowMenuBar"); 1190 } 1191 }, 1192 }, 1193 1194 DNSOverHTTPS: { 1195 onBeforeAddons(manager, param) { 1196 if ("Enabled" in param) { 1197 let mode = param.Enabled ? 2 : 5; 1198 // Fallback only matters if DOH is enabled. 1199 if (param.Fallback === false) { 1200 mode = 3; 1201 } 1202 PoliciesUtils.setDefaultPref("network.trr.mode", mode, param.Locked); 1203 } 1204 if ("ProviderURL" in param) { 1205 PoliciesUtils.setDefaultPref( 1206 "network.trr.uri", 1207 param.ProviderURL.href, 1208 param.Locked 1209 ); 1210 } 1211 if ("ExcludedDomains" in param) { 1212 PoliciesUtils.setDefaultPref( 1213 "network.trr.excluded-domains", 1214 param.ExcludedDomains.join(","), 1215 param.Locked 1216 ); 1217 } 1218 }, 1219 }, 1220 1221 DontCheckDefaultBrowser: { 1222 onBeforeUIStartup(manager, param) { 1223 setAndLockPref("browser.shell.checkDefaultBrowser", !param); 1224 }, 1225 }, 1226 1227 DownloadDirectory: { 1228 onBeforeAddons(manager, param) { 1229 setAndLockPref("browser.download.dir", replacePathVariables(param)); 1230 // If a custom download directory is being used, just lock folder list to 2. 1231 setAndLockPref("browser.download.folderList", 2); 1232 // Per Chrome spec, user can't choose to download every time 1233 // if this is set. 1234 setAndLockPref("browser.download.useDownloadDir", true); 1235 }, 1236 }, 1237 1238 EnableTrackingProtection: { 1239 onAllWindowsRestored(manager, param) { 1240 if (param.Category) { 1241 // browser.contentblocking.category only works as a default pref if 1242 // it is locked. 1243 PoliciesUtils.setDefaultPref( 1244 "browser.contentblocking.category", 1245 param.Category, 1246 true 1247 ); 1248 let { ContentBlockingPrefs } = ChromeUtils.importESModule( 1249 "moz-src:///browser/components/protections/ContentBlockingPrefs.sys.mjs" 1250 ); 1251 // These are always locked because they would reset at 1252 // startup anyway. 1253 ContentBlockingPrefs.setPrefsToCategory( 1254 param.Category, 1255 true, // locked 1256 false // preserveAllowListSettings 1257 ); 1258 ContentBlockingPrefs.matchCBCategory(); 1259 // We don't want to lock the new exceptions UI unless 1260 // that policy was explicitly set. 1261 if (param.Category == "strict" && !param.Locked) { 1262 Services.prefs.unlockPref( 1263 "privacy.trackingprotection.allow_list.baseline.enabled" 1264 ); 1265 Services.prefs.unlockPref( 1266 "privacy.trackingprotection.allow_list.convenience.enabled" 1267 ); 1268 } 1269 } 1270 }, 1271 onBeforeUIStartup(manager, param) { 1272 if ("Exceptions" in param) { 1273 addAllowDenyPermissions("trackingprotection", param.Exceptions); 1274 } 1275 if ("BaselineExceptions" in param) { 1276 PoliciesUtils.setDefaultPref( 1277 "privacy.trackingprotection.allow_list.baseline.enabled", 1278 param.BaselineExceptions, 1279 param.Locked 1280 ); 1281 } 1282 if ("ConvenienceExceptions" in param) { 1283 PoliciesUtils.setDefaultPref( 1284 "privacy.trackingprotection.allow_list.convenience.enabled", 1285 param.ConvenienceExceptions, 1286 param.Locked 1287 ); 1288 } 1289 if (param.Category) { 1290 // If a category is set, we ignore everything except exceptions 1291 // and the allow lists. 1292 return; 1293 } 1294 if (param.Value) { 1295 PoliciesUtils.setDefaultPref( 1296 "privacy.trackingprotection.enabled", 1297 true, 1298 param.Locked 1299 ); 1300 PoliciesUtils.setDefaultPref( 1301 "privacy.trackingprotection.pbmode.enabled", 1302 true, 1303 param.Locked 1304 ); 1305 } else { 1306 setAndLockPref("privacy.trackingprotection.enabled", false); 1307 setAndLockPref("privacy.trackingprotection.pbmode.enabled", false); 1308 } 1309 if ("Cryptomining" in param) { 1310 PoliciesUtils.setDefaultPref( 1311 "privacy.trackingprotection.cryptomining.enabled", 1312 param.Cryptomining, 1313 param.Locked 1314 ); 1315 } 1316 if ("HarmfulAddon" in param) { 1317 PoliciesUtils.setDefaultPref( 1318 "privacy.trackingprotection.harmfuladdon.enabled", 1319 param.HarmfulAddon, 1320 param.Locked 1321 ); 1322 } 1323 if ("Fingerprinting" in param) { 1324 PoliciesUtils.setDefaultPref( 1325 "privacy.trackingprotection.fingerprinting.enabled", 1326 param.Fingerprinting, 1327 param.Locked 1328 ); 1329 } 1330 if ("EmailTracking" in param) { 1331 PoliciesUtils.setDefaultPref( 1332 "privacy.trackingprotection.emailtracking.enabled", 1333 param.EmailTracking, 1334 param.Locked 1335 ); 1336 PoliciesUtils.setDefaultPref( 1337 "privacy.trackingprotection.emailtracking.pbmode.enabled", 1338 param.EmailTracking, 1339 param.Locked 1340 ); 1341 } 1342 if ("SuspectedFingerprinting" in param) { 1343 PoliciesUtils.setDefaultPref( 1344 "privacy.fingerprintingProtection", 1345 param.SuspectedFingerprinting, 1346 param.Locked 1347 ); 1348 PoliciesUtils.setDefaultPref( 1349 "privacy.fingerprintingProtection.pbmode", 1350 param.SuspectedFingerprinting, 1351 param.Locked 1352 ); 1353 } 1354 }, 1355 }, 1356 1357 EncryptedMediaExtensions: { 1358 onBeforeAddons(manager, param) { 1359 if ("Enabled" in param) { 1360 PoliciesUtils.setDefaultPref( 1361 "media.eme.enabled", 1362 param.Enabled, 1363 param.Locked 1364 ); 1365 } 1366 }, 1367 }, 1368 1369 ExemptDomainFileTypePairsFromFileTypeDownloadWarnings: { 1370 // This policy is handled directly in EnterprisePoliciesParent.sys.mjs 1371 // and requires no validation (It's done by the schema). 1372 }, 1373 1374 Extensions: { 1375 onBeforeUIStartup(manager, param) { 1376 let uninstallingPromise = Promise.resolve(); 1377 if ("Uninstall" in param) { 1378 uninstallingPromise = runOncePerModification( 1379 "extensionsUninstall", 1380 JSON.stringify(param.Uninstall), 1381 async () => { 1382 // If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't 1383 // changed, which will allow add-ons to be updated. 1384 Services.prefs.clearUserPref( 1385 "browser.policies.runOncePerModification.extensionsInstall" 1386 ); 1387 let addons = await lazy.AddonManager.getAddonsByIDs( 1388 param.Uninstall 1389 ); 1390 for (let addon of addons) { 1391 if (addon) { 1392 try { 1393 await addon.uninstall(); 1394 } catch (e) { 1395 // This can fail for add-ons that can't be uninstalled. 1396 lazy.log.debug( 1397 `Add-on ID (${addon.id}) couldn't be uninstalled.` 1398 ); 1399 } 1400 } 1401 } 1402 } 1403 ); 1404 } 1405 if ("Install" in param) { 1406 runOncePerModification( 1407 "extensionsInstall", 1408 JSON.stringify(param.Install), 1409 async () => { 1410 await uninstallingPromise; 1411 for (let location of param.Install) { 1412 let uri; 1413 try { 1414 // We need to try as a file first because 1415 // Windows paths are valid URIs. 1416 // This is done for legacy support (old API) 1417 let xpiFile = new lazy.FileUtils.File(location); 1418 uri = Services.io.newFileURI(xpiFile); 1419 } catch (e) { 1420 uri = Services.io.newURI(location); 1421 } 1422 installAddonFromURL(uri.spec); 1423 } 1424 } 1425 ); 1426 } 1427 if ("Locked" in param) { 1428 for (let ID of param.Locked) { 1429 manager.disallowFeature(`uninstall-extension:${ID}`); 1430 manager.disallowFeature(`disable-extension:${ID}`); 1431 } 1432 } 1433 }, 1434 }, 1435 1436 ExtensionSettings: { 1437 onBeforeAddons(manager, param) { 1438 try { 1439 manager.setExtensionSettings(param); 1440 } catch (e) { 1441 lazy.log.error("Invalid ExtensionSettings"); 1442 } 1443 }, 1444 async onBeforeUIStartup(manager, param) { 1445 let extensionSettings = param; 1446 let blockAllExtensions = false; 1447 if ("*" in extensionSettings) { 1448 if ( 1449 "installation_mode" in extensionSettings["*"] && 1450 extensionSettings["*"].installation_mode == "blocked" 1451 ) { 1452 blockAllExtensions = true; 1453 // Turn off discovery pane in about:addons 1454 setAndLockPref("extensions.getAddons.showPane", false); 1455 // Turn off recommendations 1456 setAndLockPref( 1457 "extensions.htmlaboutaddons.recommendations.enabled", 1458 false 1459 ); 1460 manager.disallowFeature("installTemporaryAddon"); 1461 } 1462 if ("restricted_domains" in extensionSettings["*"]) { 1463 let restrictedDomains = Services.prefs 1464 .getCharPref("extensions.webextensions.restrictedDomains") 1465 .split(","); 1466 setAndLockPref( 1467 "extensions.webextensions.restrictedDomains", 1468 restrictedDomains 1469 .concat(extensionSettings["*"].restricted_domains) 1470 .join(",") 1471 ); 1472 } 1473 } 1474 let addons = await lazy.AddonManager.getAllAddons(); 1475 let allowedExtensions = []; 1476 for (let extensionID in extensionSettings) { 1477 if (extensionID == "*") { 1478 // Ignore global settings 1479 continue; 1480 } 1481 if ("installation_mode" in extensionSettings[extensionID]) { 1482 if ( 1483 extensionSettings[extensionID].installation_mode == 1484 "force_installed" || 1485 extensionSettings[extensionID].installation_mode == 1486 "normal_installed" 1487 ) { 1488 if (!extensionSettings[extensionID].install_url) { 1489 throw new Error(`Missing install_url for ${extensionID}`); 1490 } 1491 installAddonFromURL( 1492 extensionSettings[extensionID].install_url, 1493 extensionID, 1494 addons.find(addon => addon.id == extensionID) 1495 ); 1496 manager.disallowFeature(`uninstall-extension:${extensionID}`); 1497 if ( 1498 extensionSettings[extensionID].installation_mode == 1499 "force_installed" 1500 ) { 1501 manager.disallowFeature(`disable-extension:${extensionID}`); 1502 } 1503 allowedExtensions.push(extensionID); 1504 } else if ( 1505 extensionSettings[extensionID].installation_mode == "allowed" 1506 ) { 1507 allowedExtensions.push(extensionID); 1508 } else if ( 1509 extensionSettings[extensionID].installation_mode == "blocked" 1510 ) { 1511 if (addons.find(addon => addon.id == extensionID)) { 1512 // Can't use the addon from getActiveAddons since it doesn't have uninstall. 1513 let addon = await lazy.AddonManager.getAddonByID(extensionID); 1514 try { 1515 await addon.uninstall(); 1516 } catch (e) { 1517 // This can fail for add-ons that can't be uninstalled. 1518 lazy.log.debug( 1519 `Add-on ID (${addon.id}) couldn't be uninstalled.` 1520 ); 1521 } 1522 } 1523 } 1524 } 1525 } 1526 if (blockAllExtensions) { 1527 for (let addon of addons) { 1528 if ( 1529 addon.isSystem || 1530 addon.isBuiltin || 1531 !(addon.scope & lazy.AddonManager.SCOPE_PROFILE) 1532 ) { 1533 continue; 1534 } 1535 if (!allowedExtensions.includes(addon.id)) { 1536 try { 1537 // Can't use the addon from getActiveAddons since it doesn't have uninstall. 1538 let addonToUninstall = await lazy.AddonManager.getAddonByID( 1539 addon.id 1540 ); 1541 await addonToUninstall.uninstall(); 1542 } catch (e) { 1543 // This can fail for add-ons that can't be uninstalled. 1544 lazy.log.debug( 1545 `Add-on ID (${addon.id}) couldn't be uninstalled.` 1546 ); 1547 } 1548 } 1549 } 1550 } 1551 }, 1552 }, 1553 1554 ExtensionUpdate: { 1555 onBeforeAddons(manager, param) { 1556 if (!param) { 1557 setAndLockPref("extensions.update.enabled", param); 1558 } 1559 }, 1560 }, 1561 1562 FirefoxHome: { 1563 onBeforeAddons(manager, param) { 1564 if ("Search" in param) { 1565 PoliciesUtils.setDefaultPref( 1566 "browser.newtabpage.activity-stream.showSearch", 1567 param.Search, 1568 param.Locked 1569 ); 1570 } 1571 if ("TopSites" in param) { 1572 PoliciesUtils.setDefaultPref( 1573 "browser.newtabpage.activity-stream.feeds.topsites", 1574 param.TopSites, 1575 param.Locked 1576 ); 1577 } 1578 if ("SponsoredTopSites" in param) { 1579 PoliciesUtils.setDefaultPref( 1580 "browser.newtabpage.activity-stream.showSponsoredTopSites", 1581 param.SponsoredTopSites, 1582 param.Locked 1583 ); 1584 } 1585 if ("Highlights" in param) { 1586 PoliciesUtils.setDefaultPref( 1587 "browser.newtabpage.activity-stream.feeds.section.highlights", 1588 param.Highlights, 1589 param.Locked 1590 ); 1591 } 1592 if ("Pocket" in param) { 1593 PoliciesUtils.setDefaultPref( 1594 "browser.newtabpage.activity-stream.feeds.system.topstories", 1595 param.Pocket, 1596 param.Locked 1597 ); 1598 PoliciesUtils.setDefaultPref( 1599 "browser.newtabpage.activity-stream.feeds.section.topstories", 1600 param.Pocket, 1601 param.Locked 1602 ); 1603 } 1604 if ("Stories" in param) { 1605 PoliciesUtils.setDefaultPref( 1606 "browser.newtabpage.activity-stream.feeds.system.topstories", 1607 param.Stories, 1608 param.Locked 1609 ); 1610 PoliciesUtils.setDefaultPref( 1611 "browser.newtabpage.activity-stream.feeds.section.topstories", 1612 param.Stories, 1613 param.Locked 1614 ); 1615 } 1616 if ("SponsoredPocket" in param) { 1617 PoliciesUtils.setDefaultPref( 1618 "browser.newtabpage.activity-stream.showSponsored", 1619 param.SponsoredPocket, 1620 param.Locked 1621 ); 1622 } 1623 if ("SponsoredStories" in param) { 1624 PoliciesUtils.setDefaultPref( 1625 "browser.newtabpage.activity-stream.showSponsored", 1626 param.SponsoredStories, 1627 param.Locked 1628 ); 1629 } 1630 }, 1631 }, 1632 1633 FirefoxSuggest: { 1634 onBeforeAddons(manager, param) { 1635 (async () => { 1636 await lazy.QuickSuggest.initPromise; 1637 if ("WebSuggestions" in param) { 1638 PoliciesUtils.setDefaultPref( 1639 "browser.urlbar.suggest.quicksuggest.all", 1640 param.WebSuggestions, 1641 param.Locked 1642 ); 1643 } 1644 if ("SponsoredSuggestions" in param) { 1645 PoliciesUtils.setDefaultPref( 1646 "browser.urlbar.suggest.quicksuggest.sponsored", 1647 param.SponsoredSuggestions, 1648 param.Locked 1649 ); 1650 } 1651 // `ImproveSuggest` is deprecated and replaced with `OnlineEnabled`. 1652 if ("OnlineEnabled" in param || "ImproveSuggest" in param) { 1653 PoliciesUtils.setDefaultPref( 1654 "browser.urlbar.quicksuggest.online.enabled", 1655 param.OnlineEnabled ?? param.ImproveSuggest, 1656 param.Locked 1657 ); 1658 } 1659 })(); 1660 }, 1661 }, 1662 1663 GenerativeAI: { 1664 onBeforeAddons(manager, param) { 1665 const defaultValue = "Enabled" in param ? param.Enabled : undefined; 1666 1667 const features = [ 1668 ["Chatbot", ["browser.ml.chat.enabled", "browser.ml.chat.page"]], 1669 ["LinkPreviews", ["browser.ml.linkPreview.optin"]], 1670 ["TabGroups", ["browser.tabs.groups.smart.userEnabled"]], 1671 ]; 1672 1673 for (const [key, prefs] of features) { 1674 const value = key in param ? param[key] : defaultValue; 1675 if (value !== undefined) { 1676 for (const pref of prefs) { 1677 PoliciesUtils.setDefaultPref(pref, value, param.Locked); 1678 } 1679 } 1680 } 1681 }, 1682 }, 1683 1684 GoToIntranetSiteForSingleWordEntryInAddressBar: { 1685 onBeforeAddons(manager, param) { 1686 setAndLockPref("browser.fixup.dns_first_for_single_words", param); 1687 }, 1688 }, 1689 1690 Handlers: { 1691 onBeforeAddons(manager, param) { 1692 if ("mimeTypes" in param) { 1693 for (let mimeType in param.mimeTypes) { 1694 let mimeInfo = param.mimeTypes[mimeType]; 1695 let realMIMEInfo = lazy.gMIMEService.getFromTypeAndExtension( 1696 mimeType, 1697 "" 1698 ); 1699 processMIMEInfo(mimeInfo, realMIMEInfo); 1700 } 1701 } 1702 if ("extensions" in param) { 1703 for (let extension in param.extensions) { 1704 let mimeInfo = param.extensions[extension]; 1705 try { 1706 let realMIMEInfo = lazy.gMIMEService.getFromTypeAndExtension( 1707 "", 1708 extension 1709 ); 1710 processMIMEInfo(mimeInfo, realMIMEInfo); 1711 } catch (e) { 1712 lazy.log.error(`Invalid file extension (${extension})`); 1713 } 1714 } 1715 } 1716 if ("schemes" in param) { 1717 for (let scheme in param.schemes) { 1718 let handlerInfo = param.schemes[scheme]; 1719 let realHandlerInfo = 1720 lazy.gExternalProtocolService.getProtocolHandlerInfo(scheme); 1721 processMIMEInfo(handlerInfo, realHandlerInfo); 1722 } 1723 } 1724 }, 1725 }, 1726 1727 HardwareAcceleration: { 1728 onBeforeAddons(manager, param) { 1729 if (!param) { 1730 setAndLockPref("layers.acceleration.disabled", true); 1731 } 1732 }, 1733 }, 1734 1735 Homepage: { 1736 onBeforeUIStartup(manager, param) { 1737 if ("StartPage" in param && param.StartPage == "none") { 1738 // For blank startpage, we use about:blank rather 1739 // than messing with browser.startup.page 1740 param.URL = new URL("about:blank"); 1741 } 1742 // |homepages| will be a string containing a pipe-separated ('|') list of 1743 // URLs because that is what the "Home page" section of about:preferences 1744 // (and therefore what the pref |browser.startup.homepage|) accepts. 1745 if ("URL" in param) { 1746 let homepages = param.URL.href; 1747 if (param.Additional && param.Additional.length) { 1748 homepages += "|" + param.Additional.map(url => url.href).join("|"); 1749 } 1750 PoliciesUtils.setDefaultPref( 1751 "browser.startup.homepage", 1752 homepages, 1753 param.Locked 1754 ); 1755 if (param.Locked) { 1756 setAndLockPref( 1757 "pref.browser.homepage.disable_button.current_page", 1758 true 1759 ); 1760 setAndLockPref( 1761 "pref.browser.homepage.disable_button.bookmark_page", 1762 true 1763 ); 1764 setAndLockPref( 1765 "pref.browser.homepage.disable_button.restore_default", 1766 true 1767 ); 1768 } else { 1769 // Clear out old run once modification that is no longer used. 1770 clearRunOnceModification("setHomepage"); 1771 } 1772 // If a homepage has been set via policy, show the home button 1773 if (param.URL != "about:blank") { 1774 manager.disallowFeature("removeHomeButtonByDefault"); 1775 } 1776 } 1777 if (param.StartPage) { 1778 let prefValue; 1779 switch (param.StartPage) { 1780 case "homepage": 1781 case "homepage-locked": 1782 case "none": 1783 prefValue = 1; 1784 break; 1785 case "previous-session": 1786 prefValue = 3; 1787 break; 1788 } 1789 PoliciesUtils.setDefaultPref( 1790 "browser.startup.page", 1791 prefValue, 1792 param.StartPage == "homepage-locked" 1793 ); 1794 } 1795 }, 1796 }, 1797 1798 HttpAllowlist: { 1799 onBeforeAddons(manager, param) { 1800 addAllowDenyPermissions("https-only-load-insecure", param); 1801 }, 1802 }, 1803 1804 HttpsOnlyMode: { 1805 onBeforeAddons(manager, param) { 1806 switch (param) { 1807 case "disallowed": 1808 setAndLockPref("dom.security.https_only_mode", false); 1809 break; 1810 case "enabled": 1811 PoliciesUtils.setDefaultPref("dom.security.https_only_mode", true); 1812 break; 1813 case "force_enabled": 1814 setAndLockPref("dom.security.https_only_mode", true); 1815 break; 1816 case "allowed": 1817 // The default case. 1818 break; 1819 } 1820 }, 1821 }, 1822 1823 InstallAddonsPermission: { 1824 onBeforeUIStartup(manager, param) { 1825 if ("Allow" in param) { 1826 addAllowDenyPermissions("install", param.Allow, null); 1827 } 1828 if ("Default" in param) { 1829 setAndLockPref("xpinstall.enabled", param.Default); 1830 if (!param.Default) { 1831 manager.disallowFeature("installTemporaryAddon"); 1832 setAndLockPref( 1833 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 1834 false 1835 ); 1836 setAndLockPref( 1837 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 1838 false 1839 ); 1840 manager.disallowFeature("xpinstall"); 1841 } 1842 } 1843 }, 1844 }, 1845 1846 LegacyProfiles: { 1847 // Handled in nsToolkitProfileService.cpp (Windows only) 1848 }, 1849 1850 LegacySameSiteCookieBehaviorEnabled: { 1851 onBeforeAddons(manager, param) { 1852 PoliciesUtils.setDefaultPref( 1853 "network.cookie.sameSite.laxByDefault", 1854 !param 1855 ); 1856 }, 1857 }, 1858 1859 LegacySameSiteCookieBehaviorEnabledForDomainList: { 1860 onBeforeAddons(manager, param) { 1861 PoliciesUtils.setDefaultPref( 1862 "network.cookie.sameSite.laxByDefault.disabledHosts", 1863 param.join(",") 1864 ); 1865 }, 1866 }, 1867 1868 LocalFileLinks: { 1869 onBeforeAddons(manager, param) { 1870 // If there are existing capabilities, lock them with the policy pref. 1871 let policyNames = Services.prefs 1872 .getCharPref("capability.policy.policynames", "") 1873 .split(" "); 1874 policyNames.push("localfilelinks_policy"); 1875 setAndLockPref("capability.policy.policynames", policyNames.join(" ")); 1876 setAndLockPref( 1877 "capability.policy.localfilelinks_policy.checkloaduri.enabled", 1878 "allAccess" 1879 ); 1880 setAndLockPref( 1881 "capability.policy.localfilelinks_policy.sites", 1882 param.join(" ") 1883 ); 1884 }, 1885 }, 1886 1887 LocalNetworkAccess: { 1888 onBeforeAddons(manager, param) { 1889 // Only process if "Enabled" is explicitly specified 1890 if ("Enabled" in param) { 1891 PoliciesUtils.setDefaultPref( 1892 "network.lna.enabled", 1893 param.Enabled, 1894 param.Locked 1895 ); 1896 1897 if (param.Enabled === false) { 1898 // If LNA is explicitly disabled, disable other features too 1899 PoliciesUtils.setDefaultPref( 1900 "network.lna.block_trackers", 1901 false, 1902 param.Locked 1903 ); 1904 PoliciesUtils.setDefaultPref( 1905 "network.lna.blocking", 1906 false, 1907 param.Locked 1908 ); 1909 } else { 1910 // LNA is enabled - handle fine-grained controls 1911 // For backward compatibility, default to true if not specified 1912 let blockTrackers = 1913 "BlockTrackers" in param ? param.BlockTrackers : true; 1914 let enablePrompting = 1915 "EnablePrompting" in param ? param.EnablePrompting : true; 1916 1917 PoliciesUtils.setDefaultPref( 1918 "network.lna.block_trackers", 1919 blockTrackers, 1920 param.Locked 1921 ); 1922 PoliciesUtils.setDefaultPref( 1923 "network.lna.blocking", 1924 enablePrompting, 1925 param.Locked 1926 ); 1927 } 1928 } 1929 1930 // Handle SkipDomains separately (can be set independently of Enabled) 1931 if ("SkipDomains" in param && Array.isArray(param.SkipDomains)) { 1932 let skipDomainsValue = param.SkipDomains.join(","); 1933 PoliciesUtils.setDefaultPref( 1934 "network.lna.skip-domains", 1935 skipDomainsValue, 1936 param.Locked 1937 ); 1938 } 1939 }, 1940 }, 1941 1942 ManagedBookmarks: {}, 1943 1944 ManualAppUpdateOnly: { 1945 onBeforeAddons(manager, param) { 1946 if (param) { 1947 manager.disallowFeature("autoAppUpdateChecking"); 1948 } 1949 }, 1950 }, 1951 1952 MicrosoftEntraSSO: { 1953 onBeforeAddons(manager, param) { 1954 setAndLockPref("network.http.microsoft-entra-sso.enabled", param); 1955 }, 1956 }, 1957 1958 NetworkPrediction: { 1959 onBeforeAddons(manager, param) { 1960 setAndLockPref("network.dns.disablePrefetch", !param); 1961 setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param); 1962 }, 1963 }, 1964 1965 NewTabPage: { 1966 onBeforeAddons(manager, param) { 1967 setAndLockPref("browser.newtabpage.enabled", param); 1968 }, 1969 }, 1970 1971 NoDefaultBookmarks: { 1972 onProfileAfterChange(manager, param) { 1973 if (param) { 1974 manager.disallowFeature("defaultBookmarks"); 1975 } 1976 }, 1977 }, 1978 1979 OfferToSaveLogins: { 1980 onBeforeUIStartup(manager, param) { 1981 setAndLockPref("signon.rememberSignons", param); 1982 setAndLockPref("services.passwordSavingEnabled", param); 1983 }, 1984 }, 1985 1986 OfferToSaveLoginsDefault: { 1987 onBeforeUIStartup(manager, param) { 1988 let policies = Services.policies.getActivePolicies(); 1989 if ("OfferToSaveLogins" in policies) { 1990 lazy.log.error( 1991 `OfferToSaveLoginsDefault ignored because OfferToSaveLogins is present.` 1992 ); 1993 } else { 1994 PoliciesUtils.setDefaultPref("signon.rememberSignons", param); 1995 } 1996 }, 1997 }, 1998 1999 OverrideFirstRunPage: { 2000 onProfileAfterChange(manager, param) { 2001 let url = param ? param : ""; 2002 setAndLockPref("startup.homepage_welcome_url", url); 2003 setAndLockPref("browser.aboutwelcome.enabled", false); 2004 }, 2005 }, 2006 2007 OverridePostUpdatePage: { 2008 onProfileAfterChange(manager, param) { 2009 let url = param ? param.href : ""; 2010 setAndLockPref("startup.homepage_override_url", url); 2011 // The pref startup.homepage_override_url is only used 2012 // as a fallback when the update.xml file hasn't provided 2013 // a specific post-update URL. 2014 manager.disallowFeature("postUpdateCustomPage"); 2015 }, 2016 }, 2017 2018 PasswordManagerEnabled: { 2019 onBeforeUIStartup(manager, param) { 2020 if (!param) { 2021 blockAboutPage(manager, "about:logins", true); 2022 setAndLockPref("pref.privacy.disable_button.view_passwords", true); 2023 } 2024 setAndLockPref("signon.rememberSignons", param); 2025 }, 2026 }, 2027 2028 PasswordManagerExceptions: { 2029 onBeforeUIStartup(manager, param) { 2030 addAllowDenyPermissions("login-saving", null, param); 2031 }, 2032 }, 2033 2034 PDFjs: { 2035 onBeforeAddons(manager, param) { 2036 if ("Enabled" in param) { 2037 setAndLockPref("pdfjs.disabled", !param.Enabled); 2038 } 2039 if ("EnablePermissions" in param) { 2040 setAndLockPref("pdfjs.enablePermissions", param.EnablePermissions); 2041 } 2042 }, 2043 }, 2044 2045 Permissions: { 2046 onBeforeUIStartup(manager, param) { 2047 if (param.Camera) { 2048 addAllowDenyPermissions( 2049 "camera", 2050 param.Camera.Allow, 2051 param.Camera.Block 2052 ); 2053 setDefaultPermission("camera", param.Camera); 2054 } 2055 2056 if (param.Microphone) { 2057 addAllowDenyPermissions( 2058 "microphone", 2059 param.Microphone.Allow, 2060 param.Microphone.Block 2061 ); 2062 setDefaultPermission("microphone", param.Microphone); 2063 } 2064 2065 if (param.Autoplay) { 2066 addAllowDenyPermissions( 2067 "autoplay-media", 2068 param.Autoplay.Allow, 2069 param.Autoplay.Block 2070 ); 2071 if ("Default" in param.Autoplay) { 2072 let prefValue; 2073 switch (param.Autoplay.Default) { 2074 case "allow-audio-video": 2075 prefValue = 0; 2076 break; 2077 case "block-audio": 2078 prefValue = 1; 2079 break; 2080 case "block-audio-video": 2081 prefValue = 5; 2082 break; 2083 } 2084 PoliciesUtils.setDefaultPref( 2085 "media.autoplay.default", 2086 prefValue, 2087 param.Autoplay.Locked 2088 ); 2089 } 2090 } 2091 2092 if (param.Location) { 2093 addAllowDenyPermissions( 2094 "geo", 2095 param.Location.Allow, 2096 param.Location.Block 2097 ); 2098 setDefaultPermission("geo", param.Location); 2099 } 2100 2101 if (param.Notifications) { 2102 addAllowDenyPermissions( 2103 "desktop-notification", 2104 param.Notifications.Allow, 2105 param.Notifications.Block 2106 ); 2107 setDefaultPermission("desktop-notification", param.Notifications); 2108 } 2109 2110 if ("VirtualReality" in param) { 2111 addAllowDenyPermissions( 2112 "xr", 2113 param.VirtualReality.Allow, 2114 param.VirtualReality.Block 2115 ); 2116 setDefaultPermission("xr", param.VirtualReality); 2117 } 2118 2119 if ("ScreenShare" in param) { 2120 addAllowDenyPermissions( 2121 "screen", 2122 param.ScreenShare.Allow, 2123 param.ScreenShare.Block 2124 ); 2125 setDefaultPermission("screen", param.ScreenShare); 2126 } 2127 }, 2128 }, 2129 2130 PictureInPicture: { 2131 onBeforeAddons(manager, param) { 2132 if ("Enabled" in param) { 2133 PoliciesUtils.setDefaultPref( 2134 "media.videocontrols.picture-in-picture.video-toggle.enabled", 2135 param.Enabled 2136 ); 2137 } 2138 if (param.Locked) { 2139 Services.prefs.lockPref( 2140 "media.videocontrols.picture-in-picture.video-toggle.enabled" 2141 ); 2142 } 2143 }, 2144 }, 2145 2146 PopupBlocking: { 2147 onBeforeUIStartup(manager, param) { 2148 addAllowDenyPermissions("popup", param.Allow, null); 2149 2150 if (param.Locked) { 2151 let blockValue = true; 2152 if (param.Default !== undefined && !param.Default) { 2153 blockValue = false; 2154 } 2155 setAndLockPref("dom.disable_open_during_load", blockValue); 2156 } else if (param.Default !== undefined) { 2157 PoliciesUtils.setDefaultPref( 2158 "dom.disable_open_during_load", 2159 !!param.Default 2160 ); 2161 } 2162 }, 2163 }, 2164 2165 PostQuantumKeyAgreementEnabled: { 2166 onBeforeAddons(manager, param) { 2167 setAndLockPref("network.http.http3.enable_kyber", param); 2168 setAndLockPref("security.tls.enable_kyber", param); 2169 setAndLockPref("media.webrtc.enable_pq_hybrid_kex", param); 2170 }, 2171 }, 2172 2173 Preferences: { 2174 onBeforeAddons(manager, param) { 2175 let allowedPrefixes = [ 2176 "accessibility.", 2177 "alerts.", 2178 "app.update.", 2179 "browser.", 2180 "datareporting.policy.", 2181 "dom.", 2182 "extensions.", 2183 "general.autoScroll", 2184 "general.smoothScroll", 2185 "geo.", 2186 "gfx.", 2187 "identity.fxaccounts.toolbar.", 2188 "intl.", 2189 "keyword.enabled", 2190 "layers.", 2191 "layout.", 2192 "mathml.disabled", 2193 "media.", 2194 "network.", 2195 "pdfjs.", 2196 "places.", 2197 "pref.", 2198 "print.", 2199 "privacy.baselineFingerprintingProtection", 2200 "privacy.fingerprintingProtection", 2201 "privacy.globalprivacycontrol.enabled", 2202 "privacy.userContext.enabled", 2203 "privacy.userContext.ui.enabled", 2204 "signon.", 2205 "spellchecker.", 2206 "svg.context-properties.content.enabled", 2207 "svg.disabled", 2208 "toolkit.legacyUserProfileCustomizations.stylesheets", 2209 "ui.", 2210 "webgl.disabled", 2211 "webgl.force-enabled", 2212 "widget.", 2213 "xpinstall.enabled", 2214 "xpinstall.whitelist.required", 2215 ]; 2216 if (!AppConstants.MOZ_REQUIRE_SIGNING) { 2217 allowedPrefixes.push("xpinstall.signatures.required"); 2218 } 2219 const allowedSecurityPrefs = [ 2220 "security.block_fileuri_script_with_wrong_mime", 2221 "security.csp.reporting.enabled", 2222 "security.default_personal_cert", 2223 "security.disable_button.openCertManager", 2224 "security.disable_button.openDeviceManager", 2225 "security.insecure_connection_text.enabled", 2226 "security.insecure_connection_text.pbmode.enabled", 2227 "security.mixed_content.block_active_content", 2228 "security.mixed_content.block_display_content", 2229 "security.mixed_content.upgrade_display_content", 2230 "security.osclientcerts.autoload", 2231 "security.OCSP.enabled", 2232 "security.OCSP.require", 2233 "security.pki.certificate_transparency.disable_for_hosts", 2234 "security.pki.certificate_transparency.disable_for_spki_hashes", 2235 "security.pki.certificate_transparency.mode", 2236 "security.ssl.enable_ocsp_stapling", 2237 "security.ssl.errorReporting.enabled", 2238 "security.ssl.require_safe_negotiation", 2239 "security.tls.enable_0rtt_data", 2240 "security.tls.hello_downgrade_check", 2241 "security.tls.version.enable-deprecated", 2242 "security.warn_submit_secure_to_insecure", 2243 "security.webauthn.always_allow_direct_attestation", 2244 ]; 2245 const blockedPrefs = [ 2246 "app.update.channel", 2247 "app.update.lastUpdateTime", 2248 "app.update.migrated", 2249 "browser.vpn_promo.disallowed_regions", 2250 ]; 2251 2252 for (let preference in param) { 2253 if (blockedPrefs.includes(preference)) { 2254 lazy.log.error( 2255 `Unable to set preference ${preference}. Preference not allowed for security reasons.` 2256 ); 2257 continue; 2258 } 2259 if (preference.startsWith("security.")) { 2260 if (!allowedSecurityPrefs.includes(preference)) { 2261 lazy.log.error( 2262 `Unable to set preference ${preference}. Preference not allowed for security reasons.` 2263 ); 2264 continue; 2265 } 2266 } else if ( 2267 !allowedPrefixes.some(prefix => preference.startsWith(prefix)) 2268 ) { 2269 lazy.log.error( 2270 `Unable to set preference ${preference}. Preference not allowed for stability reasons.` 2271 ); 2272 continue; 2273 } 2274 if (typeof param[preference] != "object") { 2275 // Legacy policy preferences 2276 setAndLockPref(preference, param[preference]); 2277 } else { 2278 if (param[preference].Status == "clear") { 2279 Services.prefs.clearUserPref(preference); 2280 continue; 2281 } 2282 2283 let prefBranch; 2284 if (param[preference].Status == "user") { 2285 prefBranch = Services.prefs; 2286 } else { 2287 prefBranch = Services.prefs.getDefaultBranch(""); 2288 } 2289 2290 // Prefs that were previously locked should stay locked, 2291 // but policy can update the value. 2292 let prefWasLocked = Services.prefs.prefIsLocked(preference); 2293 if (prefWasLocked) { 2294 Services.prefs.unlockPref(preference); 2295 } 2296 try { 2297 let prefType = 2298 param[preference].Type || typeof param[preference].Value; 2299 switch (prefType) { 2300 case "boolean": 2301 prefBranch.setBoolPref(preference, param[preference].Value); 2302 break; 2303 2304 case "number": 2305 if (!Number.isInteger(param[preference].Value)) { 2306 throw new Error(`Non-integer value for ${preference}`); 2307 } 2308 2309 // This is ugly, but necessary. On Windows GPO and macOS 2310 // configs, booleans are converted to 0/1. In the previous 2311 // Preferences implementation, the schema took care of 2312 // automatically converting these values to booleans. 2313 // Since we allow arbitrary prefs now, we have to do 2314 // something different. See bug 1666836, 1668374, and 1872267. 2315 2316 // We only set something as int if it was explicit in policy, 2317 // the same type as the default pref, or NOT 0/1. Otherwise 2318 // we set it as bool. 2319 if ( 2320 param[preference].Type == "number" || 2321 prefBranch.getPrefType(preference) == prefBranch.PREF_INT || 2322 ![0, 1].includes(param[preference].Value) 2323 ) { 2324 prefBranch.setIntPref(preference, param[preference].Value); 2325 } else { 2326 prefBranch.setBoolPref(preference, !!param[preference].Value); 2327 } 2328 break; 2329 2330 case "string": 2331 prefBranch.setStringPref(preference, param[preference].Value); 2332 break; 2333 } 2334 } catch (e) { 2335 lazy.log.error( 2336 `Unable to set preference ${preference}. Probable type mismatch.` 2337 ); 2338 } 2339 2340 if (param[preference].Status == "locked" || prefWasLocked) { 2341 Services.prefs.lockPref(preference); 2342 } 2343 } 2344 } 2345 }, 2346 }, 2347 2348 PrimaryPassword: { 2349 onAllWindowsRestored(manager, param) { 2350 if (param) { 2351 manager.disallowFeature("removeMasterPassword"); 2352 } else { 2353 manager.disallowFeature("createMasterPassword"); 2354 } 2355 }, 2356 }, 2357 2358 PrintingEnabled: { 2359 onBeforeUIStartup(manager, param) { 2360 setAndLockPref("print.enabled", param); 2361 }, 2362 }, 2363 2364 PrivateBrowsingModeAvailability: { 2365 onBeforeAddons(manager, param) { 2366 switch (param) { 2367 // Private Browsing mode disabled 2368 case 1: 2369 manager.disallowFeature("privatebrowsing"); 2370 blockAboutPage(manager, "about:privatebrowsing", true); 2371 setAndLockPref("browser.privatebrowsing.autostart", false); 2372 break; 2373 // Private Browsing mode forced 2374 case 2: 2375 setAndLockPref("browser.privatebrowsing.autostart", true); 2376 break; 2377 // Private Browsing mode available 2378 case 0: 2379 break; 2380 } 2381 }, 2382 }, 2383 2384 PromptForDownloadLocation: { 2385 onBeforeAddons(manager, param) { 2386 setAndLockPref("browser.download.useDownloadDir", !param); 2387 }, 2388 }, 2389 2390 Proxy: { 2391 onBeforeAddons(manager, param) { 2392 if (param.Locked) { 2393 manager.disallowFeature("changeProxySettings"); 2394 } 2395 lazy.ProxyPolicies.configureProxySettings( 2396 param, 2397 PoliciesUtils.setDefaultPref 2398 ); 2399 }, 2400 }, 2401 2402 RequestedLocales: { 2403 onBeforeAddons(manager, param) { 2404 let requestedLocales; 2405 if (Array.isArray(param)) { 2406 requestedLocales = param; 2407 } else if (param) { 2408 requestedLocales = param.split(","); 2409 } else { 2410 requestedLocales = []; 2411 } 2412 runOncePerModification( 2413 "requestedLocales", 2414 JSON.stringify(requestedLocales), 2415 () => { 2416 Services.locale.requestedLocales = requestedLocales; 2417 } 2418 ); 2419 }, 2420 }, 2421 2422 SanitizeOnShutdown: { 2423 onBeforeUIStartup(manager, param) { 2424 if (typeof param === "boolean") { 2425 setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param); 2426 setAndLockPref("privacy.clearOnShutdown.cache", param); 2427 setAndLockPref("privacy.clearOnShutdown.cookies", param); 2428 setAndLockPref("privacy.clearOnShutdown.downloads", param); 2429 setAndLockPref("privacy.clearOnShutdown.formdata", param); 2430 setAndLockPref("privacy.clearOnShutdown.history", param); 2431 setAndLockPref("privacy.clearOnShutdown.sessions", param); 2432 setAndLockPref("privacy.clearOnShutdown.siteSettings", param); 2433 setAndLockPref("privacy.clearOnShutdown.offlineApps", param); 2434 setAndLockPref( 2435 "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads", 2436 param 2437 ); 2438 setAndLockPref("privacy.clearOnShutdown_v2.cookiesAndStorage", param); 2439 setAndLockPref("privacy.clearOnShutdown_v2.cache", param); 2440 setAndLockPref("privacy.clearOnShutdown_v2.siteSettings", param); 2441 setAndLockPref("privacy.clearOnShutdown_v2.formdata", param); 2442 } else { 2443 let locked = true; 2444 // Needed to preserve original behavior in perpetuity. 2445 let lockDefaultPrefs = true; 2446 if ("Locked" in param) { 2447 locked = param.Locked; 2448 lockDefaultPrefs = false; 2449 } 2450 PoliciesUtils.setDefaultPref( 2451 "privacy.sanitize.sanitizeOnShutdown", 2452 true, 2453 locked 2454 ); 2455 if ("Cache" in param) { 2456 PoliciesUtils.setDefaultPref( 2457 "privacy.clearOnShutdown.cache", 2458 param.Cache, 2459 locked 2460 ); 2461 PoliciesUtils.setDefaultPref( 2462 "privacy.clearOnShutdown_v2.cache", 2463 param.Cache, 2464 locked 2465 ); 2466 } else { 2467 PoliciesUtils.setDefaultPref( 2468 "privacy.clearOnShutdown.cache", 2469 false, 2470 lockDefaultPrefs 2471 ); 2472 PoliciesUtils.setDefaultPref( 2473 "privacy.clearOnShutdown_v2.cache", 2474 false, 2475 lockDefaultPrefs 2476 ); 2477 } 2478 if ("Cookies" in param) { 2479 PoliciesUtils.setDefaultPref( 2480 "privacy.clearOnShutdown.cookies", 2481 param.Cookies, 2482 locked 2483 ); 2484 2485 // We set cookiesAndStorage to follow lock and pref 2486 // settings for cookies, and deprecate offlineApps 2487 // and sessions in the new clear on shutdown dialog - Bug 1853996 2488 PoliciesUtils.setDefaultPref( 2489 "privacy.clearOnShutdown_v2.cookiesAndStorage", 2490 param.Cookies, 2491 locked 2492 ); 2493 } else { 2494 PoliciesUtils.setDefaultPref( 2495 "privacy.clearOnShutdown.cookies", 2496 false, 2497 lockDefaultPrefs 2498 ); 2499 PoliciesUtils.setDefaultPref( 2500 "privacy.clearOnShutdown_v2.cookiesAndStorage", 2501 false, 2502 lockDefaultPrefs 2503 ); 2504 } 2505 if ("Downloads" in param) { 2506 PoliciesUtils.setDefaultPref( 2507 "privacy.clearOnShutdown.downloads", 2508 param.Downloads, 2509 locked 2510 ); 2511 } else { 2512 PoliciesUtils.setDefaultPref( 2513 "privacy.clearOnShutdown.downloads", 2514 false, 2515 lockDefaultPrefs 2516 ); 2517 } 2518 if ("FormData" in param) { 2519 PoliciesUtils.setDefaultPref( 2520 "privacy.clearOnShutdown.formdata", 2521 param.FormData, 2522 locked 2523 ); 2524 2525 PoliciesUtils.setDefaultPref( 2526 "privacy.clearOnShutdown_v2.formdata", 2527 param.FormData, 2528 locked 2529 ); 2530 } else { 2531 PoliciesUtils.setDefaultPref( 2532 "privacy.clearOnShutdown.formdata", 2533 false, 2534 lockDefaultPrefs 2535 ); 2536 2537 PoliciesUtils.setDefaultPref( 2538 "privacy.clearOnShutdown_v2.formdata", 2539 false, 2540 lockDefaultPrefs 2541 ); 2542 } 2543 if ("History" in param) { 2544 PoliciesUtils.setDefaultPref( 2545 "privacy.clearOnShutdown.history", 2546 param.History, 2547 locked 2548 ); 2549 2550 // We set browsingHistoryAndDownloads to follow lock and pref 2551 // settings for history, and deprecate downloads 2552 // in the new clear on shutdown dialog - Bug 1853996 2553 PoliciesUtils.setDefaultPref( 2554 "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads", 2555 param.History, 2556 locked 2557 ); 2558 } else { 2559 PoliciesUtils.setDefaultPref( 2560 "privacy.clearOnShutdown.history", 2561 false, 2562 lockDefaultPrefs 2563 ); 2564 PoliciesUtils.setDefaultPref( 2565 "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads", 2566 false, 2567 lockDefaultPrefs 2568 ); 2569 } 2570 if ("Sessions" in param) { 2571 PoliciesUtils.setDefaultPref( 2572 "privacy.clearOnShutdown.sessions", 2573 param.Sessions, 2574 locked 2575 ); 2576 } else { 2577 PoliciesUtils.setDefaultPref( 2578 "privacy.clearOnShutdown.sessions", 2579 false, 2580 lockDefaultPrefs 2581 ); 2582 } 2583 if ("SiteSettings" in param) { 2584 PoliciesUtils.setDefaultPref( 2585 "privacy.clearOnShutdown.siteSettings", 2586 param.SiteSettings, 2587 locked 2588 ); 2589 PoliciesUtils.setDefaultPref( 2590 "privacy.clearOnShutdown_v2.siteSettings", 2591 param.SiteSettings, 2592 locked 2593 ); 2594 } 2595 if ("OfflineApps" in param) { 2596 PoliciesUtils.setDefaultPref( 2597 "privacy.clearOnShutdown.offlineApps", 2598 param.OfflineApps, 2599 locked 2600 ); 2601 } 2602 } 2603 }, 2604 }, 2605 2606 SearchBar: { 2607 onAllWindowsRestored(manager, param) { 2608 // This policy is meant to change the default behavior, not to force it. 2609 // If this policy was already applied and the user chose move the search 2610 // bar, don't move it again. 2611 runOncePerModification("searchInNavBar", param, () => { 2612 if (param == "separate") { 2613 lazy.CustomizableUI.addWidgetToArea( 2614 "search-container", 2615 lazy.CustomizableUI.AREA_NAVBAR, 2616 lazy.CustomizableUI.getPlacementOfWidget("urlbar-container") 2617 .position + 1 2618 ); 2619 } else if (param == "unified") { 2620 lazy.CustomizableUI.removeWidgetFromArea("search-container"); 2621 } 2622 }); 2623 }, 2624 }, 2625 2626 SearchEngines: { 2627 onBeforeUIStartup(manager, param) { 2628 if (param.PreventInstalls) { 2629 manager.disallowFeature("installSearchEngine", true); 2630 } 2631 }, 2632 onAllWindowsRestored(manager, param) { 2633 Services.search.init().then(async () => { 2634 // Adding of engines is handled by the SearchService in the init(). 2635 // Remove can happen after those are added - no engines are allowed 2636 // to replace the application provided engines, even if they have been 2637 // removed. 2638 if (param.Remove) { 2639 // Only rerun if the list of engine names has changed. 2640 await runOncePerModification( 2641 "removeSearchEngines", 2642 JSON.stringify(param.Remove), 2643 async function () { 2644 for (let engineName of param.Remove) { 2645 let engine = Services.search.getEngineByName(engineName); 2646 if (engine) { 2647 try { 2648 await Services.search.removeEngine( 2649 engine, 2650 Ci.nsISearchService.CHANGE_REASON_ENTERPRISE 2651 ); 2652 } catch (ex) { 2653 lazy.log.error("Unable to remove the search engine", ex); 2654 } 2655 } 2656 } 2657 } 2658 ); 2659 } 2660 if (param.Default) { 2661 await runOncePerModification( 2662 "setDefaultSearchEngine", 2663 param.Default, 2664 async () => { 2665 let defaultEngine; 2666 try { 2667 defaultEngine = Services.search.getEngineByName(param.Default); 2668 if (!defaultEngine) { 2669 throw new Error("No engine by that name could be found"); 2670 } 2671 } catch (ex) { 2672 lazy.log.error( 2673 `Search engine lookup failed when attempting to set ` + 2674 `the default engine. Requested engine was ` + 2675 `"${param.Default}".`, 2676 ex 2677 ); 2678 } 2679 if (defaultEngine) { 2680 try { 2681 await Services.search.setDefault( 2682 defaultEngine, 2683 Ci.nsISearchService.CHANGE_REASON_ENTERPRISE 2684 ); 2685 } catch (ex) { 2686 lazy.log.error("Unable to set the default search engine", ex); 2687 } 2688 } 2689 } 2690 ); 2691 } 2692 if (param.DefaultPrivate) { 2693 await runOncePerModification( 2694 "setDefaultPrivateSearchEngine", 2695 param.DefaultPrivate, 2696 async () => { 2697 let defaultPrivateEngine; 2698 try { 2699 defaultPrivateEngine = Services.search.getEngineByName( 2700 param.DefaultPrivate 2701 ); 2702 if (!defaultPrivateEngine) { 2703 throw new Error("No engine by that name could be found"); 2704 } 2705 } catch (ex) { 2706 lazy.log.error( 2707 `Search engine lookup failed when attempting to set ` + 2708 `the default private engine. Requested engine was ` + 2709 `"${param.DefaultPrivate}".`, 2710 ex 2711 ); 2712 } 2713 if (defaultPrivateEngine) { 2714 try { 2715 await Services.search.setDefaultPrivate( 2716 defaultPrivateEngine, 2717 Ci.nsISearchService.CHANGE_REASON_ENTERPRISE 2718 ); 2719 } catch (ex) { 2720 lazy.log.error( 2721 "Unable to set the default private search engine", 2722 ex 2723 ); 2724 } 2725 } 2726 } 2727 ); 2728 } 2729 }); 2730 }, 2731 }, 2732 2733 SearchSuggestEnabled: { 2734 onBeforeAddons(manager, param) { 2735 setAndLockPref("browser.urlbar.suggest.searches", param); 2736 setAndLockPref("browser.search.suggest.enabled", param); 2737 }, 2738 }, 2739 2740 SecurityDevices: { 2741 onProfileAfterChange(manager, param) { 2742 let pkcs11db = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService( 2743 Ci.nsIPKCS11ModuleDB 2744 ); 2745 let securityDevices; 2746 if (param.Add || param.Delete) { 2747 // We're using the new syntax. 2748 securityDevices = param.Add; 2749 if (param.Delete) { 2750 for (let deviceName of param.Delete) { 2751 try { 2752 pkcs11db.deleteModule(deviceName); 2753 } catch (e) { 2754 // Ignoring errors here since it might stick around in policy 2755 // after removing. Alternative would be to listModules and 2756 // make sure it's there before removing, but that seems 2757 // like unnecessary work. 2758 } 2759 } 2760 } 2761 } else { 2762 securityDevices = param; 2763 } 2764 if (!securityDevices) { 2765 return; 2766 } 2767 for (let deviceName in securityDevices) { 2768 let foundModule = false; 2769 for (let module of pkcs11db.listModules()) { 2770 if (module && module.libName === securityDevices[deviceName]) { 2771 foundModule = true; 2772 break; 2773 } 2774 } 2775 if (foundModule) { 2776 continue; 2777 } 2778 try { 2779 pkcs11db.addModule(deviceName, securityDevices[deviceName], 0, 0); 2780 } catch (ex) { 2781 lazy.log.error(`Unable to add security device ${deviceName}`); 2782 lazy.log.debug(ex); 2783 } 2784 } 2785 }, 2786 }, 2787 2788 ShowHomeButton: { 2789 onBeforeAddons(manager, param) { 2790 if (param) { 2791 manager.disallowFeature("removeHomeButtonByDefault"); 2792 } 2793 }, 2794 onAllWindowsRestored(manager, param) { 2795 if (param) { 2796 let homeButtonPlacement = 2797 lazy.CustomizableUI.getPlacementOfWidget("home-button"); 2798 if (!homeButtonPlacement) { 2799 let placement = 2800 lazy.CustomizableUI.getPlacementOfWidget("forward-button"); 2801 lazy.CustomizableUI.addWidgetToArea( 2802 "home-button", 2803 lazy.CustomizableUI.AREA_NAVBAR, 2804 placement.position + 2 2805 ); 2806 } 2807 } else { 2808 lazy.CustomizableUI.removeWidgetFromArea("home-button"); 2809 } 2810 }, 2811 }, 2812 2813 SkipTermsOfUse: { 2814 onBeforeAddons(manager, param) { 2815 if (param) { 2816 setAndLockPref("termsofuse.acceptedVersion", 999); 2817 setAndLockPref("termsofuse.acceptedDate", Date.now().toString()); 2818 } 2819 }, 2820 }, 2821 2822 SSLVersionMax: { 2823 onBeforeAddons(manager, param) { 2824 let tlsVersion; 2825 switch (param) { 2826 case "tls1": 2827 tlsVersion = 1; 2828 break; 2829 case "tls1.1": 2830 tlsVersion = 2; 2831 break; 2832 case "tls1.2": 2833 tlsVersion = 3; 2834 break; 2835 case "tls1.3": 2836 tlsVersion = 4; 2837 break; 2838 } 2839 setAndLockPref("security.tls.version.max", tlsVersion); 2840 }, 2841 }, 2842 2843 SSLVersionMin: { 2844 onBeforeAddons(manager, param) { 2845 let tlsVersion; 2846 switch (param) { 2847 case "tls1": 2848 tlsVersion = 1; 2849 break; 2850 case "tls1.1": 2851 tlsVersion = 2; 2852 break; 2853 case "tls1.2": 2854 tlsVersion = 3; 2855 break; 2856 case "tls1.3": 2857 tlsVersion = 4; 2858 break; 2859 } 2860 setAndLockPref("security.tls.version.min", tlsVersion); 2861 }, 2862 }, 2863 2864 StartDownloadsInTempDirectory: { 2865 onBeforeAddons(manager, param) { 2866 setAndLockPref("browser.download.start_downloads_in_tmp_dir", param); 2867 }, 2868 }, 2869 2870 SupportMenu: { 2871 onProfileAfterChange(manager, param) { 2872 manager.setSupportMenu(param); 2873 }, 2874 }, 2875 2876 TranslateEnabled: { 2877 onBeforeAddons(manager, param) { 2878 setAndLockPref("browser.translations.enable", param); 2879 }, 2880 }, 2881 2882 UserMessaging: { 2883 onBeforeAddons(manager, param) { 2884 if ("ExtensionRecommendations" in param) { 2885 PoliciesUtils.setDefaultPref( 2886 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 2887 param.ExtensionRecommendations, 2888 param.Locked 2889 ); 2890 } 2891 if ("FeatureRecommendations" in param) { 2892 PoliciesUtils.setDefaultPref( 2893 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 2894 param.FeatureRecommendations, 2895 param.Locked 2896 ); 2897 2898 // We use the mostRecentTargetLanguages pref to control the 2899 // translations panel intro. Setting a language value simulates a 2900 // first translation, which skips the intro panel for users with 2901 // FeatureRecommendations disabled. 2902 const topWebPreferredLanguage = Services.locale.acceptLanguages 2903 .split(",")[0] 2904 .trim(); 2905 2906 const preferredLanguage = topWebPreferredLanguage.length 2907 ? topWebPreferredLanguage 2908 : Services.locale.appLocaleAsBCP47; 2909 2910 PoliciesUtils.setDefaultPref( 2911 "browser.translations.mostRecentTargetLanguages", 2912 param.FeatureRecommendations ? "" : preferredLanguage, 2913 param.Locked 2914 ); 2915 } 2916 if ("UrlbarInterventions" in param && !param.UrlbarInterventions) { 2917 manager.disallowFeature("urlbarinterventions"); 2918 } 2919 if ("SkipOnboarding" in param) { 2920 PoliciesUtils.setDefaultPref( 2921 "browser.aboutwelcome.enabled", 2922 !param.SkipOnboarding, 2923 param.Locked 2924 ); 2925 } 2926 if ("MoreFromMozilla" in param) { 2927 PoliciesUtils.setDefaultPref( 2928 "browser.preferences.moreFromMozilla", 2929 param.MoreFromMozilla, 2930 param.Locked 2931 ); 2932 } 2933 if ("FirefoxLabs" in param && !param.FirefoxLabs) { 2934 manager.disallowFeature("FirefoxLabs"); 2935 } 2936 }, 2937 }, 2938 2939 UseSystemPrintDialog: { 2940 onBeforeAddons(manager, param) { 2941 setAndLockPref("print.prefer_system_dialog", param); 2942 }, 2943 }, 2944 2945 VisualSearchEnabled: { 2946 onBeforeAddons(manager, param) { 2947 setAndLockPref("browser.search.visualSearch.featureGate", param); 2948 }, 2949 }, 2950 2951 WebsiteFilter: { 2952 onBeforeUIStartup(manager, param) { 2953 lazy.WebsiteFilter.init(param.Block || [], param.Exceptions || []); 2954 }, 2955 }, 2956 2957 WindowsSSO: { 2958 onBeforeAddons(manager, param) { 2959 setAndLockPref("network.http.windows-sso.enabled", param); 2960 }, 2961 }, 2962 }; 2963 2964 /* 2965 * ==================== 2966 * = HELPER FUNCTIONS = 2967 * ==================== 2968 * 2969 * The functions below are helpers to be used by several policies. 2970 */ 2971 2972 /** 2973 * setAndLockPref 2974 * 2975 * Sets the _default_ value of a pref, and locks it (meaning that 2976 * the default value will always be returned, independent from what 2977 * is stored as the user value). 2978 * The value is only changed in memory, and not stored to disk. 2979 * 2980 * @param {string} prefName 2981 * The pref to be changed 2982 * @param {boolean|number|string} prefValue 2983 * The value to set and lock 2984 */ 2985 export function setAndLockPref(prefName, prefValue) { 2986 PoliciesUtils.setDefaultPref(prefName, prefValue, true); 2987 } 2988 2989 /** 2990 * 2991 * setPrefIfPresentAndLock 2992 * 2993 * Sets the pref to the value param[paramKey] if that exists. Either 2994 * way, the pref is locked. 2995 * 2996 * @param {object} param 2997 * Object with pref values 2998 * @param {string} paramKey 2999 * The key to look up the value in param 3000 * @param {string} prefName 3001 * The pref to be changed 3002 */ 3003 function setPrefIfPresentAndLock(param, paramKey, prefName) { 3004 if (paramKey in param) { 3005 setAndLockPref(prefName, param[paramKey]); 3006 } else { 3007 Services.prefs.lockPref(prefName); 3008 } 3009 } 3010 3011 /** 3012 * setDefaultPref 3013 * 3014 * Sets the _default_ value of a pref and optionally locks it. 3015 * The value is only changed in memory, and not stored to disk. 3016 * 3017 * @param {string} prefName 3018 * The pref to be changed 3019 * @param {boolean|number|string} prefValue 3020 * The value to set 3021 * @param {boolean} locked 3022 * Optionally lock the pref 3023 */ 3024 3025 export var PoliciesUtils = { 3026 setDefaultPref(prefName, prefValue, locked) { 3027 let prefWasLocked = Services.prefs.prefIsLocked(prefName); 3028 if (prefWasLocked) { 3029 Services.prefs.unlockPref(prefName); 3030 } 3031 3032 let defaults = Services.prefs.getDefaultBranch(""); 3033 3034 switch (typeof prefValue) { 3035 case "boolean": 3036 defaults.setBoolPref(prefName, prefValue); 3037 break; 3038 3039 case "number": 3040 if (!Number.isInteger(prefValue)) { 3041 throw new Error(`Non-integer value for ${prefName}`); 3042 } 3043 3044 // This is ugly, but necessary. On Windows GPO and macOS 3045 // configs, booleans are converted to 0/1. In the previous 3046 // Preferences implementation, the schema took care of 3047 // automatically converting these values to booleans. 3048 // Since we allow arbitrary prefs now, we have to do 3049 // something different. See bug 1666836. 3050 if ( 3051 defaults.getPrefType(prefName) == defaults.PREF_INT || 3052 ![0, 1].includes(prefValue) 3053 ) { 3054 defaults.setIntPref(prefName, prefValue); 3055 } else { 3056 defaults.setBoolPref(prefName, !!prefValue); 3057 } 3058 break; 3059 3060 case "string": 3061 defaults.setStringPref(prefName, prefValue); 3062 break; 3063 } 3064 3065 // Prefs can only be unlocked explicitly. 3066 // If they were locked before, they stay locked. 3067 if (locked || (prefWasLocked && locked !== false)) { 3068 Services.prefs.lockPref(prefName); 3069 } 3070 }, 3071 }; 3072 3073 /** 3074 * setDefaultPermission 3075 * 3076 * Helper function to set preferences appropriately for the policy 3077 * 3078 * @param {string} policyName 3079 * The name of the policy to set 3080 * @param {object} policyParam 3081 * The object containing param for the policy 3082 */ 3083 function setDefaultPermission(policyName, policyParam) { 3084 if ("BlockNewRequests" in policyParam) { 3085 let prefName = "permissions.default." + policyName; 3086 3087 if (policyParam.BlockNewRequests) { 3088 PoliciesUtils.setDefaultPref(prefName, 2, policyParam.Locked); 3089 } else { 3090 PoliciesUtils.setDefaultPref(prefName, 0, policyParam.Locked); 3091 } 3092 } 3093 } 3094 3095 /** 3096 * addAllowDenyPermissions 3097 * 3098 * Helper function to call the permissions manager (Services.perms.addFromPrincipal) 3099 * for two arrays of URLs. 3100 * 3101 * @param {string} permissionName 3102 * The name of the permission to change 3103 * @param {Array} allowList 3104 * The list of URLs to be set as ALLOW_ACTION for the chosen permission. 3105 * @param {Array} blockList 3106 * The list of URLs to be set as DENY_ACTION for the chosen permission. 3107 */ 3108 function addAllowDenyPermissions(permissionName, allowList, blockList) { 3109 allowList = allowList || []; 3110 blockList = blockList || []; 3111 3112 for (let origin of allowList) { 3113 try { 3114 Services.perms.addFromPrincipal( 3115 Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin), 3116 permissionName, 3117 Ci.nsIPermissionManager.ALLOW_ACTION, 3118 Ci.nsIPermissionManager.EXPIRE_POLICY 3119 ); 3120 } catch (ex) { 3121 // It's possible if the origin was invalid, we'll have a string instead of an origin. 3122 lazy.log.error( 3123 `Unable to add ${permissionName} permission for ${ 3124 origin.href || origin 3125 }` 3126 ); 3127 } 3128 } 3129 3130 for (let origin of blockList) { 3131 Services.perms.addFromPrincipal( 3132 Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin), 3133 permissionName, 3134 Ci.nsIPermissionManager.DENY_ACTION, 3135 Ci.nsIPermissionManager.EXPIRE_POLICY 3136 ); 3137 } 3138 } 3139 3140 /** 3141 * runOnce 3142 * 3143 * Helper function to run a callback only once per policy. 3144 * 3145 * @param {string} actionName 3146 * A given name which will be used to track if this callback has run. 3147 * @param {Functon} callback 3148 * The callback to run only once. 3149 */ 3150 // eslint-disable-next-line no-unused-vars 3151 export function runOnce(actionName, callback) { 3152 let prefName = `browser.policies.runonce.${actionName}`; 3153 if (Services.prefs.getBoolPref(prefName, false)) { 3154 lazy.log.debug( 3155 `Not running action ${actionName} again because it has already run.` 3156 ); 3157 return; 3158 } 3159 Services.prefs.setBoolPref(prefName, true); 3160 callback(); 3161 } 3162 3163 /** 3164 * runOncePerModification 3165 * 3166 * Helper function similar to runOnce. The difference is that runOnce runs the 3167 * callback once when the policy is set, then never again. 3168 * runOncePerModification runs the callback once each time the policy value 3169 * changes from its previous value. 3170 * If the callback that was passed is an async function, you can await on this 3171 * function to await for the callback. 3172 * 3173 * @param {string} actionName 3174 * A given name which will be used to track if this callback has run. 3175 * This string will be part of a pref name. 3176 * @param {string} policyValue 3177 * The current value of the policy. This will be compared to previous 3178 * values given to this function to determine if the policy value has 3179 * changed. Regardless of the data type of the policy, this must be a 3180 * string. 3181 * @param {Function} callback 3182 * The callback to be run when the pref value changes 3183 * @returns {Promise} 3184 * A promise that will resolve once the callback finishes running. 3185 */ 3186 async function runOncePerModification(actionName, policyValue, callback) { 3187 // Stringify the value so that it matches what we'd get from getStringPref. 3188 policyValue = policyValue + ""; 3189 let prefName = `browser.policies.runOncePerModification.${actionName}`; 3190 let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined); 3191 if (policyValue === oldPolicyValue) { 3192 lazy.log.debug( 3193 `Not running action ${actionName} again because the policy's value is unchanged` 3194 ); 3195 return Promise.resolve(); 3196 } 3197 Services.prefs.setStringPref(prefName, policyValue); 3198 return callback(); 3199 } 3200 3201 /** 3202 * clearRunOnceModification 3203 * 3204 * Helper function that clears a runOnce policy. 3205 */ 3206 function clearRunOnceModification(actionName) { 3207 let prefName = `browser.policies.runOncePerModification.${actionName}`; 3208 Services.prefs.clearUserPref(prefName); 3209 } 3210 3211 function replacePathVariables(path) { 3212 if (path.includes("${home}")) { 3213 return path.replace( 3214 "${home}", 3215 Services.dirsvc.get("Home", Ci.nsIFile).path 3216 ); 3217 } 3218 return path; 3219 } 3220 3221 /** 3222 * installAddonFromURL 3223 * 3224 * Helper function that installs an addon from a URL 3225 * and verifies that the addon ID matches. 3226 */ 3227 function installAddonFromURL(url, extensionID, addon) { 3228 if ( 3229 addon && 3230 addon.sourceURI && 3231 addon.sourceURI.spec == url && 3232 !addon.sourceURI.schemeIs("file") 3233 ) { 3234 // It's the same addon, don't reinstall. 3235 return; 3236 } 3237 lazy.AddonManager.getInstallForURL(url, { 3238 telemetryInfo: { source: "enterprise-policy" }, 3239 }).then(install => { 3240 if (install.addon && install.addon.appDisabled) { 3241 lazy.log.error(`Incompatible add-on - ${install.addon.id}`); 3242 install.cancel(); 3243 return; 3244 } 3245 let listener = { 3246 /* eslint-disable-next-line no-shadow */ 3247 onDownloadEnded: install => { 3248 // Install failed, error will be reported elsewhere. 3249 if (!install.addon) { 3250 return; 3251 } 3252 if (extensionID && install.addon.id != extensionID) { 3253 lazy.log.error( 3254 `Add-on downloaded from ${url} had unexpected id (got ${install.addon.id} expected ${extensionID})` 3255 ); 3256 install.removeListener(listener); 3257 install.cancel(); 3258 } 3259 if (install.addon.appDisabled) { 3260 lazy.log.error(`Incompatible add-on - ${url}`); 3261 install.removeListener(listener); 3262 install.cancel(); 3263 } 3264 if ( 3265 addon && 3266 Services.vc.compare(addon.version, install.addon.version) == 0 3267 ) { 3268 lazy.log.debug( 3269 "Installation cancelled because versions are the same" 3270 ); 3271 install.removeListener(listener); 3272 install.cancel(); 3273 } 3274 }, 3275 onDownloadFailed: () => { 3276 install.removeListener(listener); 3277 lazy.log.error( 3278 `Download failed - ${lazy.AddonManager.errorToString( 3279 install.error 3280 )} - ${url}` 3281 ); 3282 clearRunOnceModification("extensionsInstall"); 3283 }, 3284 onInstallFailed: () => { 3285 install.removeListener(listener); 3286 lazy.log.error( 3287 `Installation failed - ${lazy.AddonManager.errorToString( 3288 install.error 3289 )} - ${url}` 3290 ); 3291 }, 3292 /* eslint-disable-next-line no-shadow */ 3293 onInstallEnded: (install, addon) => { 3294 if (addon.type == "theme") { 3295 addon.enable(); 3296 } 3297 install.removeListener(listener); 3298 lazy.log.debug(`Installation succeeded - ${url}`); 3299 }, 3300 }; 3301 // If it's a local file install, onDownloadEnded is never called. 3302 // So we call it manually, to handle some error cases. 3303 if (url.startsWith("file:")) { 3304 listener.onDownloadEnded(install); 3305 if (install.state == lazy.AddonManager.STATE_CANCELLED) { 3306 return; 3307 } 3308 } 3309 install.addListener(listener); 3310 install.install(); 3311 }); 3312 } 3313 3314 let gBlockedAboutPages = []; 3315 3316 function clearBlockedAboutPages() { 3317 gBlockedAboutPages = []; 3318 } 3319 3320 function blockAboutPage(manager, feature) { 3321 addChromeURLBlocker(); 3322 gBlockedAboutPages.push(feature); 3323 3324 try { 3325 let aboutModule = Cc[ABOUT_CONTRACT + feature.split(":")[1]].getService( 3326 Ci.nsIAboutModule 3327 ); 3328 let chromeURL = aboutModule.getChromeURI(Services.io.newURI(feature)).spec; 3329 gBlockedAboutPages.push(chromeURL); 3330 } catch (e) { 3331 // Some about pages don't have chrome URLS (compat) 3332 } 3333 } 3334 3335 let ChromeURLBlockPolicy = { 3336 shouldLoad(contentLocation, loadInfo) { 3337 let contentType = loadInfo.externalContentPolicyType; 3338 if ( 3339 (contentLocation.scheme != "chrome" && 3340 contentLocation.scheme != "about") || 3341 (contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT && 3342 contentType != Ci.nsIContentPolicy.TYPE_SUBDOCUMENT) 3343 ) { 3344 return Ci.nsIContentPolicy.ACCEPT; 3345 } 3346 let contentLocationSpec = contentLocation.spec.toLowerCase(); 3347 if ( 3348 gBlockedAboutPages.some(function (aboutPage) { 3349 return contentLocationSpec.startsWith(aboutPage.toLowerCase()); 3350 }) 3351 ) { 3352 return Ci.nsIContentPolicy.REJECT_POLICY; 3353 } 3354 return Ci.nsIContentPolicy.ACCEPT; 3355 }, 3356 shouldProcess() { 3357 return Ci.nsIContentPolicy.ACCEPT; 3358 }, 3359 classDescription: "Policy Engine Content Policy", 3360 contractID: "@mozilla-org/policy-engine-content-policy-service;1", 3361 classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"), 3362 QueryInterface: ChromeUtils.generateQI(["nsIContentPolicy"]), 3363 createInstance(iid) { 3364 return this.QueryInterface(iid); 3365 }, 3366 }; 3367 3368 function addChromeURLBlocker() { 3369 if (Cc[ChromeURLBlockPolicy.contractID]) { 3370 return; 3371 } 3372 3373 let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); 3374 registrar.registerFactory( 3375 ChromeURLBlockPolicy.classID, 3376 ChromeURLBlockPolicy.classDescription, 3377 ChromeURLBlockPolicy.contractID, 3378 ChromeURLBlockPolicy 3379 ); 3380 3381 Services.catMan.addCategoryEntry( 3382 "content-policy", 3383 ChromeURLBlockPolicy.contractID, 3384 ChromeURLBlockPolicy.contractID, 3385 false, 3386 true 3387 ); 3388 } 3389 3390 function pemToBase64(pem) { 3391 return pem 3392 .replace(/(.*)-----BEGIN CERTIFICATE-----/, "") 3393 .replace(/-----END CERTIFICATE-----(.*)/, "") 3394 .replace(/[\r\n]/g, ""); 3395 } 3396 3397 function processMIMEInfo(mimeInfo, realMIMEInfo) { 3398 if ("handlers" in mimeInfo) { 3399 let firstHandler = true; 3400 for (let handler of mimeInfo.handlers) { 3401 // handler can be null which means they don't 3402 // want a preferred handler. 3403 if (handler) { 3404 let handlerApp; 3405 if ("path" in handler) { 3406 try { 3407 let file = new lazy.FileUtils.File(handler.path); 3408 handlerApp = Cc[ 3409 "@mozilla.org/uriloader/local-handler-app;1" 3410 ].createInstance(Ci.nsILocalHandlerApp); 3411 handlerApp.executable = file; 3412 } catch (ex) { 3413 lazy.log.error( 3414 `Unable to create handler executable (${handler.path})` 3415 ); 3416 continue; 3417 } 3418 } else if ("uriTemplate" in handler) { 3419 let templateURL = new URL(handler.uriTemplate); 3420 if (templateURL.protocol != "https:") { 3421 lazy.log.error( 3422 `Web handler must be https (${handler.uriTemplate})` 3423 ); 3424 continue; 3425 } 3426 if ( 3427 !templateURL.pathname.includes("%s") && 3428 !templateURL.search.includes("%s") 3429 ) { 3430 lazy.log.error( 3431 `Web handler must contain %s (${handler.uriTemplate})` 3432 ); 3433 continue; 3434 } 3435 handlerApp = Cc[ 3436 "@mozilla.org/uriloader/web-handler-app;1" 3437 ].createInstance(Ci.nsIWebHandlerApp); 3438 handlerApp.uriTemplate = handler.uriTemplate; 3439 } else { 3440 lazy.log.error("Invalid handler"); 3441 continue; 3442 } 3443 if ("name" in handler) { 3444 handlerApp.name = handler.name; 3445 } 3446 realMIMEInfo.possibleApplicationHandlers.appendElement(handlerApp); 3447 if (firstHandler) { 3448 realMIMEInfo.preferredApplicationHandler = handlerApp; 3449 } 3450 } 3451 firstHandler = false; 3452 } 3453 } 3454 if ("action" in mimeInfo) { 3455 let action = realMIMEInfo[mimeInfo.action]; 3456 if ( 3457 action == realMIMEInfo.useHelperApp && 3458 !realMIMEInfo.possibleApplicationHandlers.length 3459 ) { 3460 lazy.log.error("useHelperApp requires a handler"); 3461 return; 3462 } 3463 realMIMEInfo.preferredAction = action; 3464 } 3465 if ("ask" in mimeInfo) { 3466 realMIMEInfo.alwaysAskBeforeHandling = mimeInfo.ask; 3467 } 3468 lazy.gHandlerService.store(realMIMEInfo); 3469 }