1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5"use strict"; 6 7const { XPCOMUtils } = ChromeUtils.import( 8 "resource://gre/modules/XPCOMUtils.jsm" 9); 10const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); 11const { AppConstants } = ChromeUtils.import( 12 "resource://gre/modules/AppConstants.jsm" 13); 14 15XPCOMUtils.defineLazyServiceGetters(this, { 16 gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"], 17 gExternalProtocolService: [ 18 "@mozilla.org/uriloader/external-protocol-service;1", 19 "nsIExternalProtocolService", 20 ], 21 gHandlerService: [ 22 "@mozilla.org/uriloader/handler-service;1", 23 "nsIHandlerService", 24 ], 25 gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"], 26 gXulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"], 27}); 28 29XPCOMUtils.defineLazyModuleGetters(this, { 30 AddonManager: "resource://gre/modules/AddonManager.jsm", 31 BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm", 32 CustomizableUI: "resource:///modules/CustomizableUI.jsm", 33 FileUtils: "resource://gre/modules/FileUtils.jsm", 34 ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm", 35 WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm", 36}); 37 38XPCOMUtils.defineLazyGlobalGetters(this, ["File", "FileReader"]); 39 40const PREF_LOGLEVEL = "browser.policies.loglevel"; 41const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL; 42 43XPCOMUtils.defineLazyGetter(this, "log", () => { 44 let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); 45 return new ConsoleAPI({ 46 prefix: "Policies.jsm", 47 // tip: set maxLogLevel to "debug" and use log.debug() to create detailed 48 // messages during development. See LOG_LEVELS in Console.jsm for details. 49 maxLogLevel: "error", 50 maxLogLevelPref: PREF_LOGLEVEL, 51 }); 52}); 53 54var EXPORTED_SYMBOLS = ["Policies"]; 55 56/* 57 * ============================ 58 * = POLICIES IMPLEMENTATIONS = 59 * ============================ 60 * 61 * The Policies object below is where the implementation for each policy 62 * happens. An object for each policy should be defined, containing 63 * callback functions that will be called by the engine. 64 * 65 * See the _callbacks object in EnterprisePolicies.js for the list of 66 * possible callbacks and an explanation of each. 67 * 68 * Each callback will be called with two parameters: 69 * - manager 70 * This is the EnterprisePoliciesManager singleton object from 71 * EnterprisePolicies.js 72 * 73 * - param 74 * The parameter defined for this policy in policies-schema.json. 75 * It will be different for each policy. It could be a boolean, 76 * a string, an array or a complex object. All parameters have 77 * been validated according to the schema, and no unknown 78 * properties will be present on them. 79 * 80 * The callbacks will be bound to their parent policy object. 81 */ 82var Policies = { 83 "3rdparty": { 84 onBeforeAddons(manager, param) { 85 manager.setExtensionPolicies(param.Extensions); 86 }, 87 }, 88 89 AppAutoUpdate: { 90 onBeforeUIStartup(manager, param) { 91 // Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is 92 // true, we disallow turning off auto updating, and visa versa. 93 if (param) { 94 manager.disallowFeature("app-auto-updates-off"); 95 } else { 96 manager.disallowFeature("app-auto-updates-on"); 97 } 98 }, 99 }, 100 101 AppUpdateURL: { 102 // No implementation needed here. UpdateService.jsm will check for this 103 // policy directly when determining the update URL. 104 }, 105 106 Authentication: { 107 onBeforeAddons(manager, param) { 108 let locked = true; 109 if ("Locked" in param) { 110 locked = param.Locked; 111 } 112 113 if ("SPNEGO" in param) { 114 setDefaultPref( 115 "network.negotiate-auth.trusted-uris", 116 param.SPNEGO.join(", "), 117 locked 118 ); 119 } 120 if ("Delegated" in param) { 121 setDefaultPref( 122 "network.negotiate-auth.delegation-uris", 123 param.Delegated.join(", "), 124 locked 125 ); 126 } 127 if ("NTLM" in param) { 128 setDefaultPref( 129 "network.automatic-ntlm-auth.trusted-uris", 130 param.NTLM.join(", "), 131 locked 132 ); 133 } 134 if ("AllowNonFQDN" in param) { 135 if ("NTLM" in param.AllowNonFQDN) { 136 setDefaultPref( 137 "network.automatic-ntlm-auth.allow-non-fqdn", 138 param.AllowNonFQDN.NTLM, 139 locked 140 ); 141 } 142 if ("SPNEGO" in param.AllowNonFQDN) { 143 setDefaultPref( 144 "network.negotiate-auth.allow-non-fqdn", 145 param.AllowNonFQDN.SPNEGO, 146 locked 147 ); 148 } 149 } 150 if ("AllowProxies" in param) { 151 if ("NTLM" in param.AllowProxies) { 152 setDefaultPref( 153 "network.automatic-ntlm-auth.allow-proxies", 154 param.AllowProxies.NTLM, 155 locked 156 ); 157 } 158 if ("SPNEGO" in param.AllowProxies) { 159 setDefaultPref( 160 "network.negotiate-auth.allow-proxies", 161 param.AllowProxies.SPNEGO, 162 locked 163 ); 164 } 165 } 166 if ("PrivateBrowsing" in param) { 167 setDefaultPref( 168 "network.auth.private-browsing-sso", 169 param.PrivateBrowsing, 170 locked 171 ); 172 } 173 }, 174 }, 175 176 BlockAboutAddons: { 177 onBeforeUIStartup(manager, param) { 178 if (param) { 179 blockAboutPage(manager, "about:addons", true); 180 } 181 }, 182 }, 183 184 BlockAboutConfig: { 185 onBeforeUIStartup(manager, param) { 186 if (param) { 187 blockAboutPage(manager, "about:config"); 188 setAndLockPref("devtools.chrome.enabled", false); 189 } 190 }, 191 }, 192 193 BlockAboutProfiles: { 194 onBeforeUIStartup(manager, param) { 195 if (param) { 196 blockAboutPage(manager, "about:profiles"); 197 } 198 }, 199 }, 200 201 BlockAboutSupport: { 202 onBeforeUIStartup(manager, param) { 203 if (param) { 204 blockAboutPage(manager, "about:support"); 205 } 206 }, 207 }, 208 209 Bookmarks: { 210 onAllWindowsRestored(manager, param) { 211 BookmarksPolicies.processBookmarks(param); 212 }, 213 }, 214 215 CaptivePortal: { 216 onBeforeAddons(manager, param) { 217 setAndLockPref("network.captive-portal-service.enabled", param); 218 }, 219 }, 220 221 Certificates: { 222 onBeforeAddons(manager, param) { 223 if ("ImportEnterpriseRoots" in param) { 224 setAndLockPref( 225 "security.enterprise_roots.enabled", 226 param.ImportEnterpriseRoots 227 ); 228 } 229 if ("Install" in param) { 230 (async () => { 231 let dirs = []; 232 let platform = AppConstants.platform; 233 if (platform == "win") { 234 dirs = [ 235 // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla. 236 Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent, 237 // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla. 238 Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent, 239 ]; 240 } else if (platform == "macosx" || platform == "linux") { 241 dirs = [ 242 // These two keys are named wrong. They return the Mozilla directory. 243 Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile), 244 Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile), 245 ]; 246 } 247 dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile)); 248 for (let certfilename of param.Install) { 249 let certfile; 250 try { 251 certfile = Cc["@mozilla.org/file/local;1"].createInstance( 252 Ci.nsIFile 253 ); 254 certfile.initWithPath(certfilename); 255 } catch (e) { 256 for (let dir of dirs) { 257 certfile = dir.clone(); 258 certfile.append( 259 platform == "linux" ? "certificates" : "Certificates" 260 ); 261 certfile.append(certfilename); 262 if (certfile.exists()) { 263 break; 264 } 265 } 266 } 267 let file; 268 try { 269 file = await File.createFromNsIFile(certfile); 270 } catch (e) { 271 log.error(`Unable to find certificate - ${certfilename}`); 272 continue; 273 } 274 let reader = new FileReader(); 275 reader.onloadend = function() { 276 if (reader.readyState != reader.DONE) { 277 log.error(`Unable to read certificate - ${certfile.path}`); 278 return; 279 } 280 let certFile = reader.result; 281 let certFileArray = []; 282 for (let i = 0; i < certFile.length; i++) { 283 certFileArray.push(certFile.charCodeAt(i)); 284 } 285 let cert; 286 try { 287 cert = gCertDB.constructX509(certFileArray); 288 } catch (e) { 289 log.debug( 290 `constructX509 failed with error '${e}' - trying constructX509FromBase64.` 291 ); 292 try { 293 // It might be PEM instead of DER. 294 cert = gCertDB.constructX509FromBase64(pemToBase64(certFile)); 295 } catch (ex) { 296 log.error(`Unable to add certificate - ${certfile.path}`, ex); 297 } 298 } 299 if (cert) { 300 if ( 301 gCertDB.isCertTrusted( 302 cert, 303 Ci.nsIX509Cert.CA_CERT, 304 Ci.nsIX509CertDB.TRUSTED_SSL 305 ) 306 ) { 307 // Certificate is already installed. 308 return; 309 } 310 try { 311 gCertDB.addCert(certFile, "CT,CT,"); 312 } catch (e) { 313 // It might be PEM instead of DER. 314 gCertDB.addCertFromBase64(pemToBase64(certFile), "CT,CT,"); 315 } 316 } 317 }; 318 reader.readAsBinaryString(file); 319 } 320 })(); 321 } 322 }, 323 }, 324 325 Cookies: { 326 onBeforeUIStartup(manager, param) { 327 addAllowDenyPermissions("cookie", param.Allow, param.Block); 328 329 if (param.AllowSession) { 330 for (let origin of param.AllowSession) { 331 try { 332 Services.perms.addFromPrincipal( 333 Services.scriptSecurityManager.createContentPrincipalFromOrigin( 334 origin 335 ), 336 "cookie", 337 Ci.nsICookiePermission.ACCESS_SESSION, 338 Ci.nsIPermissionManager.EXPIRE_POLICY 339 ); 340 } catch (ex) { 341 log.error( 342 `Unable to add cookie session permission - ${origin.href}` 343 ); 344 } 345 } 346 } 347 348 if (param.Block) { 349 const hosts = param.Block.map(url => url.hostname) 350 .sort() 351 .join("\n"); 352 runOncePerModification("clearCookiesForBlockedHosts", hosts, () => { 353 for (let blocked of param.Block) { 354 Services.cookies.removeCookiesWithOriginAttributes( 355 "{}", 356 blocked.hostname 357 ); 358 } 359 }); 360 } 361 362 if ( 363 param.Default !== undefined || 364 param.AcceptThirdParty !== undefined || 365 param.RejectTracker !== undefined || 366 param.Locked 367 ) { 368 const ACCEPT_COOKIES = 0; 369 const REJECT_THIRD_PARTY_COOKIES = 1; 370 const REJECT_ALL_COOKIES = 2; 371 const REJECT_UNVISITED_THIRD_PARTY = 3; 372 const REJECT_TRACKER = 4; 373 374 let newCookieBehavior = ACCEPT_COOKIES; 375 if (param.Default !== undefined && !param.Default) { 376 newCookieBehavior = REJECT_ALL_COOKIES; 377 } else if (param.AcceptThirdParty) { 378 if (param.AcceptThirdParty == "never") { 379 newCookieBehavior = REJECT_THIRD_PARTY_COOKIES; 380 } else if (param.AcceptThirdParty == "from-visited") { 381 newCookieBehavior = REJECT_UNVISITED_THIRD_PARTY; 382 } 383 } else if (param.RejectTracker !== undefined && param.RejectTracker) { 384 newCookieBehavior = REJECT_TRACKER; 385 } 386 387 setDefaultPref( 388 "network.cookie.cookieBehavior", 389 newCookieBehavior, 390 param.Locked 391 ); 392 } 393 394 const KEEP_COOKIES_UNTIL_EXPIRATION = 0; 395 const KEEP_COOKIES_UNTIL_END_OF_SESSION = 2; 396 397 if (param.ExpireAtSessionEnd !== undefined || param.Locked) { 398 let newLifetimePolicy = KEEP_COOKIES_UNTIL_EXPIRATION; 399 if (param.ExpireAtSessionEnd) { 400 newLifetimePolicy = KEEP_COOKIES_UNTIL_END_OF_SESSION; 401 } 402 403 setDefaultPref( 404 "network.cookie.lifetimePolicy", 405 newLifetimePolicy, 406 param.Locked 407 ); 408 } 409 }, 410 }, 411 412 DefaultDownloadDirectory: { 413 onBeforeAddons(manager, param) { 414 setDefaultPref("browser.download.dir", replacePathVariables(param)); 415 // If a custom download directory is being used, just lock folder list to 2. 416 setAndLockPref("browser.download.folderList", 2); 417 }, 418 }, 419 420 DisableAppUpdate: { 421 onBeforeAddons(manager, param) { 422 if (param) { 423 manager.disallowFeature("appUpdate"); 424 } 425 }, 426 }, 427 428 DisableBuiltinPDFViewer: { 429 onBeforeAddons(manager, param) { 430 if (param) { 431 setAndLockPref("pdfjs.disabled", true); 432 } 433 }, 434 }, 435 436 DisabledCiphers: { 437 onBeforeAddons(manager, param) { 438 if ("TLS_DHE_RSA_WITH_AES_128_CBC_SHA" in param) { 439 setAndLockPref( 440 "security.ssl3.dhe_rsa_aes_128_sha", 441 !param.TLS_DHE_RSA_WITH_AES_128_CBC_SHA 442 ); 443 } 444 if ("TLS_DHE_RSA_WITH_AES_256_CBC_SHA" in param) { 445 setAndLockPref( 446 "security.ssl3.dhe_rsa_aes_256_sha", 447 !param.TLS_DHE_RSA_WITH_AES_256_CBC_SHA 448 ); 449 } 450 if ("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" in param) { 451 setAndLockPref( 452 "security.ssl3.ecdhe_rsa_aes_128_sha", 453 !param.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 454 ); 455 } 456 if ("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" in param) { 457 setAndLockPref( 458 "security.ssl3.ecdhe_rsa_aes_256_sha", 459 !param.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 460 ); 461 } 462 if ("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" in param) { 463 setAndLockPref( 464 "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", 465 !param.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 466 ); 467 } 468 if ("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" in param) { 469 setAndLockPref( 470 "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", 471 !param.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 472 ); 473 } 474 if ("TLS_RSA_WITH_AES_128_CBC_SHA" in param) { 475 setAndLockPref( 476 "security.ssl3.rsa_aes_128_sha", 477 !param.TLS_RSA_WITH_AES_128_CBC_SHA 478 ); 479 } 480 if ("TLS_RSA_WITH_AES_256_CBC_SHA" in param) { 481 setAndLockPref( 482 "security.ssl3.rsa_aes_256_sha", 483 !param.TLS_RSA_WITH_AES_256_CBC_SHA 484 ); 485 } 486 if ("TLS_RSA_WITH_3DES_EDE_CBC_SHA" in param) { 487 setAndLockPref( 488 "security.ssl3.rsa_des_ede3_sha", 489 !param.TLS_RSA_WITH_3DES_EDE_CBC_SHA 490 ); 491 } 492 if ("TLS_RSA_WITH_AES_128_GCM_SHA256" in param) { 493 setAndLockPref( 494 "security.ssl3.rsa_aes_128_gcm_sha256", 495 !param.TLS_RSA_WITH_AES_128_GCM_SHA256 496 ); 497 } 498 if ("TLS_RSA_WITH_AES_256_GCM_SHA384" in param) { 499 setAndLockPref( 500 "security.ssl3.rsa_aes_256_gcm_sha384", 501 !param.TLS_RSA_WITH_AES_256_GCM_SHA384 502 ); 503 } 504 }, 505 }, 506 507 DisableDefaultBrowserAgent: { 508 // The implementation of this policy is in the default browser agent itself 509 // (/toolkit/mozapps/defaultagent); we need an entry for it here so that it 510 // shows up in about:policies as a real policy and not as an error. 511 }, 512 513 DisableDeveloperTools: { 514 onBeforeAddons(manager, param) { 515 if (param) { 516 setAndLockPref("devtools.policy.disabled", true); 517 setAndLockPref("devtools.chrome.enabled", false); 518 519 manager.disallowFeature("devtools"); 520 blockAboutPage(manager, "about:devtools"); 521 blockAboutPage(manager, "about:debugging"); 522 blockAboutPage(manager, "about:devtools-toolbox"); 523 blockAboutPage(manager, "about:profiling"); 524 } 525 }, 526 }, 527 528 DisableFeedbackCommands: { 529 onBeforeUIStartup(manager, param) { 530 if (param) { 531 manager.disallowFeature("feedbackCommands"); 532 } 533 }, 534 }, 535 536 DisableFirefoxAccounts: { 537 onBeforeAddons(manager, param) { 538 if (param) { 539 setAndLockPref("identity.fxaccounts.enabled", false); 540 setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty"); 541 setAndLockPref("browser.aboutwelcome.enabled", false); 542 } 543 }, 544 }, 545 546 DisableFirefoxScreenshots: { 547 onBeforeAddons(manager, param) { 548 if (param) { 549 setAndLockPref("extensions.screenshots.disabled", true); 550 } 551 }, 552 }, 553 554 DisableFirefoxStudies: { 555 onBeforeAddons(manager, param) { 556 if (param) { 557 manager.disallowFeature("Shield"); 558 setAndLockPref( 559 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 560 false 561 ); 562 setAndLockPref( 563 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 564 false 565 ); 566 } 567 }, 568 }, 569 570 DisableForgetButton: { 571 onProfileAfterChange(manager, param) { 572 if (param) { 573 setAndLockPref("privacy.panicButton.enabled", false); 574 } 575 }, 576 }, 577 578 DisableFormHistory: { 579 onBeforeUIStartup(manager, param) { 580 if (param) { 581 setAndLockPref("browser.formfill.enable", false); 582 } 583 }, 584 }, 585 586 DisableMasterPasswordCreation: { 587 onBeforeUIStartup(manager, param) { 588 if (param) { 589 manager.disallowFeature("createMasterPassword"); 590 } 591 }, 592 }, 593 594 DisablePasswordReveal: { 595 onBeforeUIStartup(manager, param) { 596 if (param) { 597 manager.disallowFeature("passwordReveal"); 598 } 599 }, 600 }, 601 602 DisablePocket: { 603 onBeforeAddons(manager, param) { 604 if (param) { 605 setAndLockPref("extensions.pocket.enabled", false); 606 } 607 }, 608 }, 609 610 DisablePrivateBrowsing: { 611 onBeforeAddons(manager, param) { 612 if (param) { 613 manager.disallowFeature("privatebrowsing"); 614 blockAboutPage(manager, "about:privatebrowsing", true); 615 setAndLockPref("browser.privatebrowsing.autostart", false); 616 } 617 }, 618 }, 619 620 DisableProfileImport: { 621 onBeforeUIStartup(manager, param) { 622 if (param) { 623 manager.disallowFeature("profileImport"); 624 setAndLockPref( 625 "browser.newtabpage.activity-stream.migrationExpired", 626 true 627 ); 628 } 629 }, 630 }, 631 632 DisableProfileRefresh: { 633 onBeforeUIStartup(manager, param) { 634 if (param) { 635 manager.disallowFeature("profileRefresh"); 636 setAndLockPref("browser.disableResetPrompt", true); 637 } 638 }, 639 }, 640 641 DisableSafeMode: { 642 onBeforeUIStartup(manager, param) { 643 if (param) { 644 manager.disallowFeature("safeMode"); 645 } 646 }, 647 }, 648 649 DisableSecurityBypass: { 650 onBeforeUIStartup(manager, param) { 651 if ("InvalidCertificate" in param) { 652 setAndLockPref( 653 "security.certerror.hideAddException", 654 param.InvalidCertificate 655 ); 656 } 657 658 if ("SafeBrowsing" in param) { 659 setAndLockPref( 660 "browser.safebrowsing.allowOverride", 661 !param.SafeBrowsing 662 ); 663 } 664 }, 665 }, 666 667 DisableSetDesktopBackground: { 668 onBeforeUIStartup(manager, param) { 669 if (param) { 670 manager.disallowFeature("setDesktopBackground"); 671 } 672 }, 673 }, 674 675 DisableSystemAddonUpdate: { 676 onBeforeAddons(manager, param) { 677 if (param) { 678 manager.disallowFeature("SysAddonUpdate"); 679 } 680 }, 681 }, 682 683 DisableTelemetry: { 684 onBeforeAddons(manager, param) { 685 if (param) { 686 setAndLockPref("datareporting.healthreport.uploadEnabled", false); 687 setAndLockPref("datareporting.policy.dataSubmissionEnabled", false); 688 setAndLockPref("toolkit.telemetry.archive.enabled", false); 689 blockAboutPage(manager, "about:telemetry"); 690 } 691 }, 692 }, 693 694 DisplayBookmarksToolbar: { 695 onBeforeUIStartup(manager, param) { 696 let value = (!param).toString(); 697 // This policy is meant to change the default behavior, not to force it. 698 // If this policy was alreay applied and the user chose to re-hide the 699 // bookmarks toolbar, do not show it again. 700 runOncePerModification("displayBookmarksToolbar", value, () => { 701 gXulStore.setValue( 702 BROWSER_DOCUMENT_URL, 703 "PersonalToolbar", 704 "collapsed", 705 value 706 ); 707 }); 708 }, 709 }, 710 711 DisplayMenuBar: { 712 onBeforeUIStartup(manager, param) { 713 let value; 714 if ( 715 typeof param === "boolean" || 716 param == "default-on" || 717 param == "default-off" 718 ) { 719 switch (param) { 720 case "default-on": 721 value = "false"; 722 break; 723 case "default-off": 724 value = "true"; 725 break; 726 default: 727 value = (!param).toString(); 728 break; 729 } 730 // This policy is meant to change the default behavior, not to force it. 731 // If this policy was already applied and the user chose to re-hide the 732 // menu bar, do not show it again. 733 runOncePerModification("displayMenuBar", value, () => { 734 gXulStore.setValue( 735 BROWSER_DOCUMENT_URL, 736 "toolbar-menubar", 737 "autohide", 738 value 739 ); 740 }); 741 } else { 742 switch (param) { 743 case "always": 744 value = "false"; 745 break; 746 case "never": 747 // Make sure Alt key doesn't show the menubar 748 setAndLockPref("ui.key.menuAccessKeyFocuses", false); 749 value = "true"; 750 break; 751 } 752 gXulStore.setValue( 753 BROWSER_DOCUMENT_URL, 754 "toolbar-menubar", 755 "autohide", 756 value 757 ); 758 manager.disallowFeature("hideShowMenuBar"); 759 } 760 }, 761 }, 762 763 DNSOverHTTPS: { 764 onBeforeAddons(manager, param) { 765 let locked = false; 766 if ("Locked" in param) { 767 locked = param.Locked; 768 } 769 if ("Enabled" in param) { 770 let mode = param.Enabled ? 2 : 5; 771 setDefaultPref("network.trr.mode", mode, locked); 772 } 773 if ("ProviderURL" in param) { 774 setDefaultPref("network.trr.uri", param.ProviderURL.href, locked); 775 } 776 if ("ExcludedDomains" in param) { 777 setDefaultPref( 778 "network.trr.excluded-domains", 779 param.ExcludedDomains.join(","), 780 locked 781 ); 782 } 783 }, 784 }, 785 786 DontCheckDefaultBrowser: { 787 onBeforeUIStartup(manager, param) { 788 setAndLockPref("browser.shell.checkDefaultBrowser", !param); 789 }, 790 }, 791 792 DownloadDirectory: { 793 onBeforeAddons(manager, param) { 794 setAndLockPref("browser.download.dir", replacePathVariables(param)); 795 // If a custom download directory is being used, just lock folder list to 2. 796 setAndLockPref("browser.download.folderList", 2); 797 // Per Chrome spec, user can't choose to download every time 798 // if this is set. 799 setAndLockPref("browser.download.useDownloadDir", true); 800 }, 801 }, 802 803 EnableTrackingProtection: { 804 onBeforeUIStartup(manager, param) { 805 if (param.Value) { 806 setDefaultPref( 807 "privacy.trackingprotection.enabled", 808 true, 809 param.Locked 810 ); 811 setDefaultPref( 812 "privacy.trackingprotection.pbmode.enabled", 813 true, 814 param.Locked 815 ); 816 } else { 817 setAndLockPref("privacy.trackingprotection.enabled", false); 818 setAndLockPref("privacy.trackingprotection.pbmode.enabled", false); 819 } 820 if ("Cryptomining" in param) { 821 setDefaultPref( 822 "privacy.trackingprotection.cryptomining.enabled", 823 param.Cryptomining, 824 param.Locked 825 ); 826 } 827 if ("Fingerprinting" in param) { 828 setDefaultPref( 829 "privacy.trackingprotection.fingerprinting.enabled", 830 param.Fingerprinting, 831 param.Locked 832 ); 833 } 834 if ("Exceptions" in param) { 835 addAllowDenyPermissions("trackingprotection", param.Exceptions); 836 } 837 }, 838 }, 839 840 EncryptedMediaExtensions: { 841 onBeforeAddons(manager, param) { 842 let locked = false; 843 if ("Locked" in param) { 844 locked = param.Locked; 845 } 846 if ("Enabled" in param) { 847 setDefaultPref("media.eme.enabled", param.Enabled, locked); 848 } 849 }, 850 }, 851 852 Extensions: { 853 onBeforeUIStartup(manager, param) { 854 let uninstallingPromise = Promise.resolve(); 855 if ("Uninstall" in param) { 856 uninstallingPromise = runOncePerModification( 857 "extensionsUninstall", 858 JSON.stringify(param.Uninstall), 859 async () => { 860 // If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't 861 // changed, which will allow add-ons to be updated. 862 Services.prefs.clearUserPref( 863 "browser.policies.runOncePerModification.extensionsInstall" 864 ); 865 let addons = await AddonManager.getAddonsByIDs(param.Uninstall); 866 for (let addon of addons) { 867 if (addon) { 868 try { 869 await addon.uninstall(); 870 } catch (e) { 871 // This can fail for add-ons that can't be uninstalled. 872 log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`); 873 } 874 } 875 } 876 } 877 ); 878 } 879 if ("Install" in param) { 880 runOncePerModification( 881 "extensionsInstall", 882 JSON.stringify(param.Install), 883 async () => { 884 await uninstallingPromise; 885 for (let location of param.Install) { 886 let uri; 887 try { 888 // We need to try as a file first because 889 // Windows paths are valid URIs. 890 // This is done for legacy support (old API) 891 let xpiFile = new FileUtils.File(location); 892 uri = Services.io.newFileURI(xpiFile); 893 } catch (e) { 894 uri = Services.io.newURI(location); 895 } 896 installAddonFromURL(uri.spec); 897 } 898 } 899 ); 900 } 901 if ("Locked" in param) { 902 for (let ID of param.Locked) { 903 manager.disallowFeature(`uninstall-extension:${ID}`); 904 manager.disallowFeature(`disable-extension:${ID}`); 905 } 906 } 907 }, 908 }, 909 910 ExtensionSettings: { 911 onBeforeAddons(manager, param) { 912 try { 913 manager.setExtensionSettings(param); 914 } catch (e) { 915 log.error("Invalid ExtensionSettings"); 916 } 917 }, 918 async onBeforeUIStartup(manager, param) { 919 let extensionSettings = param; 920 let blockAllExtensions = false; 921 if ("*" in extensionSettings) { 922 if ( 923 "installation_mode" in extensionSettings["*"] && 924 extensionSettings["*"].installation_mode == "blocked" 925 ) { 926 blockAllExtensions = true; 927 // Turn off discovery pane in about:addons 928 setAndLockPref("extensions.getAddons.showPane", false); 929 // Turn off recommendations 930 setAndLockPref( 931 "extensions.htmlaboutaddons.recommendations.enable", 932 false 933 ); 934 // Block about:debugging 935 blockAboutPage(manager, "about:debugging"); 936 } 937 if ("restricted_domains" in extensionSettings["*"]) { 938 let restrictedDomains = Services.prefs 939 .getCharPref("extensions.webextensions.restrictedDomains") 940 .split(","); 941 setAndLockPref( 942 "extensions.webextensions.restrictedDomains", 943 restrictedDomains 944 .concat(extensionSettings["*"].restricted_domains) 945 .join(",") 946 ); 947 } 948 } 949 let addons = await AddonManager.getAllAddons(); 950 let allowedExtensions = []; 951 for (let extensionID in extensionSettings) { 952 if (extensionID == "*") { 953 // Ignore global settings 954 continue; 955 } 956 if ("installation_mode" in extensionSettings[extensionID]) { 957 if ( 958 extensionSettings[extensionID].installation_mode == 959 "force_installed" || 960 extensionSettings[extensionID].installation_mode == 961 "normal_installed" 962 ) { 963 if (!extensionSettings[extensionID].install_url) { 964 throw new Error(`Missing install_url for ${extensionID}`); 965 } 966 installAddonFromURL( 967 extensionSettings[extensionID].install_url, 968 extensionID, 969 addons.find(addon => addon.id == extensionID) 970 ); 971 manager.disallowFeature(`uninstall-extension:${extensionID}`); 972 if ( 973 extensionSettings[extensionID].installation_mode == 974 "force_installed" 975 ) { 976 manager.disallowFeature(`disable-extension:${extensionID}`); 977 } 978 allowedExtensions.push(extensionID); 979 } else if ( 980 extensionSettings[extensionID].installation_mode == "allowed" 981 ) { 982 allowedExtensions.push(extensionID); 983 } else if ( 984 extensionSettings[extensionID].installation_mode == "blocked" 985 ) { 986 if (addons.find(addon => addon.id == extensionID)) { 987 // Can't use the addon from getActiveAddons since it doesn't have uninstall. 988 let addon = await AddonManager.getAddonByID(extensionID); 989 try { 990 await addon.uninstall(); 991 } catch (e) { 992 // This can fail for add-ons that can't be uninstalled. 993 log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`); 994 } 995 } 996 } 997 } 998 } 999 if (blockAllExtensions) { 1000 for (let addon of addons) { 1001 if ( 1002 addon.isSystem || 1003 addon.isBuiltin || 1004 !(addon.scope & AddonManager.SCOPE_PROFILE) 1005 ) { 1006 continue; 1007 } 1008 if (!allowedExtensions.includes(addon.id)) { 1009 try { 1010 // Can't use the addon from getActiveAddons since it doesn't have uninstall. 1011 let addonToUninstall = await AddonManager.getAddonByID(addon.id); 1012 await addonToUninstall.uninstall(); 1013 } catch (e) { 1014 // This can fail for add-ons that can't be uninstalled. 1015 log.debug(`Add-on ID (${addon.id}) couldn't be uninstalled.`); 1016 } 1017 } 1018 } 1019 } 1020 }, 1021 }, 1022 1023 ExtensionUpdate: { 1024 onBeforeAddons(manager, param) { 1025 if (!param) { 1026 setAndLockPref("extensions.update.enabled", param); 1027 } 1028 }, 1029 }, 1030 1031 FirefoxHome: { 1032 onBeforeAddons(manager, param) { 1033 let locked = param.Locked || false; 1034 if ("Search" in param) { 1035 setDefaultPref( 1036 "browser.newtabpage.activity-stream.showSearch", 1037 param.Search, 1038 locked 1039 ); 1040 } 1041 if ("TopSites" in param) { 1042 setDefaultPref( 1043 "browser.newtabpage.activity-stream.feeds.topsites", 1044 param.TopSites, 1045 locked 1046 ); 1047 } 1048 if ("Highlights" in param) { 1049 setDefaultPref( 1050 "browser.newtabpage.activity-stream.feeds.section.highlights", 1051 param.Highlights, 1052 locked 1053 ); 1054 } 1055 if ("Pocket" in param) { 1056 setDefaultPref( 1057 "browser.newtabpage.activity-stream.feeds.section.topstories", 1058 param.Pocket, 1059 locked 1060 ); 1061 } 1062 if ("Snippets" in param) { 1063 setDefaultPref( 1064 "browser.newtabpage.activity-stream.feeds.snippets", 1065 param.Snippets, 1066 locked 1067 ); 1068 } 1069 }, 1070 }, 1071 1072 FlashPlugin: { 1073 onBeforeUIStartup(manager, param) { 1074 addAllowDenyPermissions("plugin:flash", param.Allow, param.Block); 1075 1076 const FLASH_NEVER_ACTIVATE = 0; 1077 const FLASH_ASK_TO_ACTIVATE = 1; 1078 1079 let flashPrefVal; 1080 if (param.Default === undefined || param.Default) { 1081 flashPrefVal = FLASH_ASK_TO_ACTIVATE; 1082 } else { 1083 flashPrefVal = FLASH_NEVER_ACTIVATE; 1084 } 1085 if (param.Locked) { 1086 setAndLockPref("plugin.state.flash", flashPrefVal); 1087 } else if (param.Default !== undefined) { 1088 setDefaultPref("plugin.state.flash", flashPrefVal); 1089 } 1090 }, 1091 }, 1092 1093 Handlers: { 1094 onBeforeAddons(manager, param) { 1095 if ("mimeTypes" in param) { 1096 for (let mimeType in param.mimeTypes) { 1097 let mimeInfo = param.mimeTypes[mimeType]; 1098 let realMIMEInfo = gMIMEService.getFromTypeAndExtension(mimeType, ""); 1099 processMIMEInfo(mimeInfo, realMIMEInfo); 1100 } 1101 } 1102 if ("extensions" in param) { 1103 for (let extension in param.extensions) { 1104 let mimeInfo = param.extensions[extension]; 1105 try { 1106 let realMIMEInfo = gMIMEService.getFromTypeAndExtension( 1107 "", 1108 extension 1109 ); 1110 processMIMEInfo(mimeInfo, realMIMEInfo); 1111 } catch (e) { 1112 log.error(`Invalid file extension (${extension})`); 1113 } 1114 } 1115 } 1116 if ("schemes" in param) { 1117 for (let scheme in param.schemes) { 1118 let handlerInfo = param.schemes[scheme]; 1119 let realHandlerInfo = gExternalProtocolService.getProtocolHandlerInfo( 1120 scheme 1121 ); 1122 processMIMEInfo(handlerInfo, realHandlerInfo); 1123 } 1124 } 1125 }, 1126 }, 1127 1128 HardwareAcceleration: { 1129 onBeforeAddons(manager, param) { 1130 if (!param) { 1131 setAndLockPref("layers.acceleration.disabled", true); 1132 } 1133 }, 1134 }, 1135 1136 Homepage: { 1137 onBeforeUIStartup(manager, param) { 1138 if ("StartPage" in param && param.StartPage == "none") { 1139 // For blank startpage, we use about:blank rather 1140 // than messing with browser.startup.page 1141 param.URL = new URL("about:blank"); 1142 } 1143 // |homepages| will be a string containing a pipe-separated ('|') list of 1144 // URLs because that is what the "Home page" section of about:preferences 1145 // (and therefore what the pref |browser.startup.homepage|) accepts. 1146 if ("URL" in param) { 1147 let homepages = param.URL.href; 1148 if (param.Additional && param.Additional.length) { 1149 homepages += "|" + param.Additional.map(url => url.href).join("|"); 1150 } 1151 setDefaultPref("browser.startup.homepage", homepages, param.Locked); 1152 if (param.Locked) { 1153 setAndLockPref( 1154 "pref.browser.homepage.disable_button.current_page", 1155 true 1156 ); 1157 setAndLockPref( 1158 "pref.browser.homepage.disable_button.bookmark_page", 1159 true 1160 ); 1161 setAndLockPref( 1162 "pref.browser.homepage.disable_button.restore_default", 1163 true 1164 ); 1165 } else { 1166 // Clear out old run once modification that is no longer used. 1167 clearRunOnceModification("setHomepage"); 1168 } 1169 } 1170 if (param.StartPage) { 1171 let prefValue; 1172 switch (param.StartPage) { 1173 case "homepage": 1174 case "homepage-locked": 1175 case "none": 1176 prefValue = 1; 1177 break; 1178 case "previous-session": 1179 prefValue = 3; 1180 break; 1181 } 1182 setDefaultPref( 1183 "browser.startup.page", 1184 prefValue, 1185 param.StartPage == "homepage-locked" 1186 ); 1187 } 1188 }, 1189 }, 1190 1191 InstallAddonsPermission: { 1192 onBeforeUIStartup(manager, param) { 1193 if ("Allow" in param) { 1194 addAllowDenyPermissions("install", param.Allow, null); 1195 } 1196 if ("Default" in param) { 1197 setAndLockPref("xpinstall.enabled", param.Default); 1198 if (!param.Default) { 1199 blockAboutPage(manager, "about:debugging"); 1200 setAndLockPref( 1201 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 1202 false 1203 ); 1204 setAndLockPref( 1205 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 1206 false 1207 ); 1208 manager.disallowFeature("xpinstall"); 1209 } 1210 } 1211 }, 1212 }, 1213 1214 LegacyProfiles: { 1215 // Handled in nsToolkitProfileService.cpp (Windows only) 1216 }, 1217 1218 LegacySameSiteCookieBehaviorEnabled: { 1219 onBeforeAddons(manager, param) { 1220 setDefaultPref("network.cookie.sameSite.laxByDefault", !param); 1221 }, 1222 }, 1223 1224 LegacySameSiteCookieBehaviorEnabledForDomainList: { 1225 onBeforeAddons(manager, param) { 1226 setDefaultPref( 1227 "network.cookie.sameSite.laxByDefault.disabledHosts", 1228 param.join(",") 1229 ); 1230 }, 1231 }, 1232 1233 LocalFileLinks: { 1234 onBeforeAddons(manager, param) { 1235 // If there are existing capabilities, lock them with the policy pref. 1236 let policyNames = Services.prefs 1237 .getCharPref("capability.policy.policynames", "") 1238 .split(" "); 1239 policyNames.push("localfilelinks_policy"); 1240 setAndLockPref("capability.policy.policynames", policyNames.join(" ")); 1241 setAndLockPref( 1242 "capability.policy.localfilelinks_policy.checkloaduri.enabled", 1243 "allAccess" 1244 ); 1245 setAndLockPref( 1246 "capability.policy.localfilelinks_policy.sites", 1247 param.join(" ") 1248 ); 1249 }, 1250 }, 1251 1252 ManagedBookmarks: {}, 1253 1254 NetworkPrediction: { 1255 onBeforeAddons(manager, param) { 1256 setAndLockPref("network.dns.disablePrefetch", !param); 1257 setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param); 1258 }, 1259 }, 1260 1261 NewTabPage: { 1262 onBeforeAddons(manager, param) { 1263 setAndLockPref("browser.newtabpage.enabled", param); 1264 }, 1265 }, 1266 1267 NoDefaultBookmarks: { 1268 onProfileAfterChange(manager, param) { 1269 if (param) { 1270 manager.disallowFeature("defaultBookmarks"); 1271 } 1272 }, 1273 }, 1274 1275 OfferToSaveLogins: { 1276 onBeforeUIStartup(manager, param) { 1277 setAndLockPref("signon.rememberSignons", param); 1278 }, 1279 }, 1280 1281 OfferToSaveLoginsDefault: { 1282 onBeforeUIStartup(manager, param) { 1283 let policies = Services.policies.getActivePolicies(); 1284 if ("OfferToSaveLogins" in policies) { 1285 log.error( 1286 `OfferToSaveLoginsDefault ignored because OfferToSaveLogins is present.` 1287 ); 1288 } else { 1289 setDefaultPref("signon.rememberSignons", param); 1290 } 1291 }, 1292 }, 1293 1294 OverrideFirstRunPage: { 1295 onProfileAfterChange(manager, param) { 1296 let url = param ? param : ""; 1297 setAndLockPref("startup.homepage_welcome_url", url); 1298 setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty"); 1299 setAndLockPref("browser.aboutwelcome.enabled", false); 1300 }, 1301 }, 1302 1303 OverridePostUpdatePage: { 1304 onProfileAfterChange(manager, param) { 1305 let url = param ? param.href : ""; 1306 setAndLockPref("startup.homepage_override_url", url); 1307 // The pref startup.homepage_override_url is only used 1308 // as a fallback when the update.xml file hasn't provided 1309 // a specific post-update URL. 1310 manager.disallowFeature("postUpdateCustomPage"); 1311 }, 1312 }, 1313 1314 PasswordManagerEnabled: { 1315 onBeforeUIStartup(manager, param) { 1316 if (!param) { 1317 blockAboutPage(manager, "about:logins", true); 1318 setAndLockPref("pref.privacy.disable_button.view_passwords", true); 1319 } 1320 setAndLockPref("signon.rememberSignons", param); 1321 }, 1322 }, 1323 1324 PDFjs: { 1325 onBeforeAddons(manager, param) { 1326 if ("Enabled" in param) { 1327 setAndLockPref("pdfjs.disabled", !param.Enabled); 1328 } 1329 if ("EnablePermissions" in param) { 1330 setAndLockPref("pdfjs.enablePermissions", !param.Enabled); 1331 } 1332 }, 1333 }, 1334 1335 Permissions: { 1336 onBeforeUIStartup(manager, param) { 1337 if (param.Camera) { 1338 addAllowDenyPermissions( 1339 "camera", 1340 param.Camera.Allow, 1341 param.Camera.Block 1342 ); 1343 setDefaultPermission("camera", param.Camera); 1344 } 1345 1346 if (param.Microphone) { 1347 addAllowDenyPermissions( 1348 "microphone", 1349 param.Microphone.Allow, 1350 param.Microphone.Block 1351 ); 1352 setDefaultPermission("microphone", param.Microphone); 1353 } 1354 1355 if (param.Autoplay) { 1356 addAllowDenyPermissions( 1357 "autoplay-media", 1358 param.Autoplay.Allow, 1359 param.Autoplay.Block 1360 ); 1361 if ("Default" in param.Autoplay) { 1362 let prefValue; 1363 switch (param.Autoplay.Default) { 1364 case "allow-audio-video": 1365 prefValue = 0; 1366 break; 1367 case "block-audio": 1368 prefValue = 1; 1369 break; 1370 case "block-audio-video": 1371 prefValue = 5; 1372 break; 1373 } 1374 setDefaultPref( 1375 "media.autoplay.default", 1376 prefValue, 1377 param.Autoplay.Locked 1378 ); 1379 } 1380 } 1381 1382 if (param.Location) { 1383 addAllowDenyPermissions( 1384 "geo", 1385 param.Location.Allow, 1386 param.Location.Block 1387 ); 1388 setDefaultPermission("geo", param.Location); 1389 } 1390 1391 if (param.Notifications) { 1392 addAllowDenyPermissions( 1393 "desktop-notification", 1394 param.Notifications.Allow, 1395 param.Notifications.Block 1396 ); 1397 setDefaultPermission("desktop-notification", param.Notifications); 1398 } 1399 1400 if ("VirtualReality" in param) { 1401 addAllowDenyPermissions( 1402 "xr", 1403 param.VirtualReality.Allow, 1404 param.VirtualReality.Block 1405 ); 1406 setDefaultPermission("xr", param.VirtualReality); 1407 } 1408 }, 1409 }, 1410 1411 PictureInPicture: { 1412 onBeforeAddons(manager, param) { 1413 if ("Enabled" in param) { 1414 setDefaultPref( 1415 "media.videocontrols.picture-in-picture.video-toggle.enabled", 1416 param.Enabled 1417 ); 1418 } 1419 if (param.Locked) { 1420 Services.prefs.lockPref( 1421 "media.videocontrols.picture-in-picture.video-toggle.enabled" 1422 ); 1423 } 1424 }, 1425 }, 1426 1427 PopupBlocking: { 1428 onBeforeUIStartup(manager, param) { 1429 addAllowDenyPermissions("popup", param.Allow, null); 1430 1431 if (param.Locked) { 1432 let blockValue = true; 1433 if (param.Default !== undefined && !param.Default) { 1434 blockValue = false; 1435 } 1436 setAndLockPref("dom.disable_open_during_load", blockValue); 1437 } else if (param.Default !== undefined) { 1438 setDefaultPref("dom.disable_open_during_load", !!param.Default); 1439 } 1440 }, 1441 }, 1442 1443 Preferences: { 1444 onBeforeAddons(manager, param) { 1445 const allowedPrefixes = [ 1446 "accessibility.", 1447 "app.update.", 1448 "browser.", 1449 "datareporting.policy.", 1450 "dom.", 1451 "extensions.", 1452 "general.autoScroll", 1453 "general.smoothScroll", 1454 "geo.", 1455 "intl.", 1456 "layout.", 1457 "media.", 1458 "network.", 1459 "pdfjs.", 1460 "places.", 1461 "print.", 1462 "signon.", 1463 "spellchecker.", 1464 "ui.", 1465 "widget.", 1466 ]; 1467 const allowedSecurityPrefs = [ 1468 "security.default_personal_cert", 1469 "security.insecure_connection_text.enabled", 1470 "security.insecure_connection_text.pbmode.enabled", 1471 "security.insecure_field_warning.contextual.enabled", 1472 "security.mixed_content.block_active_content", 1473 "security.osclientcerts.autoload", 1474 "security.ssl.errorReporting.enabled", 1475 "security.tls.hello_downgrade_check", 1476 "security.tls.version.enable-deprecated", 1477 "security.warn_submit_secure_to_insecure", 1478 ]; 1479 const blockedPrefs = [ 1480 "app.update.channel", 1481 "app.update.lastUpdateTime", 1482 "app.update.migrated", 1483 ]; 1484 1485 for (let preference in param) { 1486 if (blockedPrefs.includes(preference)) { 1487 log.error( 1488 `Unable to set preference ${preference}. Preference not allowed for security reasons.` 1489 ); 1490 continue; 1491 } 1492 if (preference.startsWith("security.")) { 1493 if (!allowedSecurityPrefs.includes(preference)) { 1494 log.error( 1495 `Unable to set preference ${preference}. Preference not allowed for security reasons.` 1496 ); 1497 continue; 1498 } 1499 } else if ( 1500 !allowedPrefixes.some(prefix => preference.startsWith(prefix)) 1501 ) { 1502 log.error( 1503 `Unable to set preference ${preference}. Preference not allowed for stability reasons.` 1504 ); 1505 continue; 1506 } 1507 if (typeof param[preference] != "object") { 1508 // Legacy policy preferences 1509 setAndLockPref(preference, param[preference]); 1510 } else { 1511 if (param[preference].Status == "clear") { 1512 Services.prefs.clearUserPref(preference); 1513 continue; 1514 } 1515 1516 if (param[preference].Status == "user") { 1517 var prefBranch = Services.prefs; 1518 } else { 1519 prefBranch = Services.prefs.getDefaultBranch(""); 1520 } 1521 1522 try { 1523 switch (typeof param[preference].Value) { 1524 case "boolean": 1525 prefBranch.setBoolPref(preference, param[preference].Value); 1526 break; 1527 1528 case "number": 1529 if (!Number.isInteger(param[preference].Value)) { 1530 throw new Error(`Non-integer value for ${preference}`); 1531 } 1532 1533 // This is ugly, but necessary. On Windows GPO and macOS 1534 // configs, booleans are converted to 0/1. In the previous 1535 // Preferences implementation, the schema took care of 1536 // automatically converting these values to booleans. 1537 // Since we allow arbitrary prefs now, we have to do 1538 // something different. See bug 1666836. 1539 if ( 1540 prefBranch.getPrefType(preference) == prefBranch.PREF_INT || 1541 ![0, 1].includes(param[preference].Value) 1542 ) { 1543 prefBranch.setIntPref(preference, param[preference].Value); 1544 } else { 1545 prefBranch.setBoolPref(preference, !!param[preference].Value); 1546 } 1547 break; 1548 1549 case "string": 1550 prefBranch.setStringPref(preference, param[preference].Value); 1551 break; 1552 } 1553 } catch (e) { 1554 log.error( 1555 `Unable to set preference ${preference}. Probable type mismatch.` 1556 ); 1557 } 1558 1559 if (param[preference].Status == "locked") { 1560 Services.prefs.lockPref(preference); 1561 } 1562 } 1563 } 1564 }, 1565 }, 1566 1567 PrimaryPassword: { 1568 onAllWindowsRestored(manager, param) { 1569 if (param) { 1570 manager.disallowFeature("removeMasterPassword"); 1571 } else { 1572 manager.disallowFeature("createMasterPassword"); 1573 } 1574 }, 1575 }, 1576 1577 PromptForDownloadLocation: { 1578 onBeforeAddons(manager, param) { 1579 setAndLockPref("browser.download.useDownloadDir", !param); 1580 }, 1581 }, 1582 1583 Proxy: { 1584 onBeforeAddons(manager, param) { 1585 if (param.Locked) { 1586 manager.disallowFeature("changeProxySettings"); 1587 ProxyPolicies.configureProxySettings(param, setAndLockPref); 1588 } else { 1589 ProxyPolicies.configureProxySettings(param, setDefaultPref); 1590 } 1591 }, 1592 }, 1593 1594 RequestedLocales: { 1595 onBeforeAddons(manager, param) { 1596 let requestedLocales; 1597 if (Array.isArray(param)) { 1598 requestedLocales = param; 1599 } else if (param) { 1600 requestedLocales = param.split(","); 1601 } else { 1602 requestedLocales = []; 1603 } 1604 runOncePerModification( 1605 "requestedLocales", 1606 JSON.stringify(requestedLocales), 1607 () => { 1608 Services.locale.requestedLocales = requestedLocales; 1609 } 1610 ); 1611 }, 1612 }, 1613 1614 SanitizeOnShutdown: { 1615 onBeforeUIStartup(manager, param) { 1616 if (typeof param === "boolean") { 1617 setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param); 1618 if (param) { 1619 setAndLockPref("privacy.clearOnShutdown.cache", true); 1620 setAndLockPref("privacy.clearOnShutdown.cookies", true); 1621 setAndLockPref("privacy.clearOnShutdown.downloads", true); 1622 setAndLockPref("privacy.clearOnShutdown.formdata", true); 1623 setAndLockPref("privacy.clearOnShutdown.history", true); 1624 setAndLockPref("privacy.clearOnShutdown.sessions", true); 1625 setAndLockPref("privacy.clearOnShutdown.siteSettings", true); 1626 setAndLockPref("privacy.clearOnShutdown.offlineApps", true); 1627 } 1628 } else { 1629 let locked = true; 1630 // Needed to preserve original behavior in perpetuity. 1631 let lockDefaultPrefs = true; 1632 if ("Locked" in param) { 1633 locked = param.Locked; 1634 lockDefaultPrefs = false; 1635 } 1636 setDefaultPref("privacy.sanitize.sanitizeOnShutdown", true, locked); 1637 if ("Cache" in param) { 1638 setDefaultPref("privacy.clearOnShutdown.cache", param.Cache, locked); 1639 } else { 1640 setDefaultPref( 1641 "privacy.clearOnShutdown.cache", 1642 false, 1643 lockDefaultPrefs 1644 ); 1645 } 1646 if ("Cookies" in param) { 1647 setDefaultPref( 1648 "privacy.clearOnShutdown.cookies", 1649 param.Cookies, 1650 locked 1651 ); 1652 } else { 1653 setDefaultPref( 1654 "privacy.clearOnShutdown.cookies", 1655 false, 1656 lockDefaultPrefs 1657 ); 1658 } 1659 if ("Downloads" in param) { 1660 setDefaultPref( 1661 "privacy.clearOnShutdown.downloads", 1662 param.Downloads, 1663 locked 1664 ); 1665 } else { 1666 setDefaultPref( 1667 "privacy.clearOnShutdown.downloads", 1668 false, 1669 lockDefaultPrefs 1670 ); 1671 } 1672 if ("FormData" in param) { 1673 setDefaultPref( 1674 "privacy.clearOnShutdown.formdata", 1675 param.FormData, 1676 locked 1677 ); 1678 } else { 1679 setDefaultPref( 1680 "privacy.clearOnShutdown.formdata", 1681 false, 1682 lockDefaultPrefs 1683 ); 1684 } 1685 if ("History" in param) { 1686 setDefaultPref( 1687 "privacy.clearOnShutdown.history", 1688 param.History, 1689 locked 1690 ); 1691 } else { 1692 setDefaultPref( 1693 "privacy.clearOnShutdown.history", 1694 false, 1695 lockDefaultPrefs 1696 ); 1697 } 1698 if ("Sessions" in param) { 1699 setDefaultPref( 1700 "privacy.clearOnShutdown.sessions", 1701 param.Sessions, 1702 locked 1703 ); 1704 } else { 1705 setDefaultPref( 1706 "privacy.clearOnShutdown.sessions", 1707 false, 1708 lockDefaultPrefs 1709 ); 1710 } 1711 if ("SiteSettings" in param) { 1712 setDefaultPref( 1713 "privacy.clearOnShutdown.siteSettings", 1714 param.SiteSettings, 1715 locked 1716 ); 1717 } 1718 if ("OfflineApps" in param) { 1719 setDefaultPref( 1720 "privacy.clearOnShutdown.offlineApps", 1721 param.OfflineApps, 1722 locked 1723 ); 1724 } 1725 } 1726 }, 1727 }, 1728 1729 SearchBar: { 1730 onAllWindowsRestored(manager, param) { 1731 // This policy is meant to change the default behavior, not to force it. 1732 // If this policy was already applied and the user chose move the search 1733 // bar, don't move it again. 1734 runOncePerModification("searchInNavBar", param, () => { 1735 if (param == "separate") { 1736 CustomizableUI.addWidgetToArea( 1737 "search-container", 1738 CustomizableUI.AREA_NAVBAR, 1739 CustomizableUI.getPlacementOfWidget("urlbar-container").position + 1 1740 ); 1741 } else if (param == "unified") { 1742 CustomizableUI.removeWidgetFromArea("search-container"); 1743 } 1744 }); 1745 }, 1746 }, 1747 1748 SearchEngines: { 1749 onBeforeUIStartup(manager, param) { 1750 if (param.PreventInstalls) { 1751 manager.disallowFeature("installSearchEngine", true); 1752 } 1753 }, 1754 onAllWindowsRestored(manager, param) { 1755 Services.search.init().then(async () => { 1756 if (param.Remove) { 1757 // Only rerun if the list of engine names has changed. 1758 await runOncePerModification( 1759 "removeSearchEngines", 1760 JSON.stringify(param.Remove), 1761 async function() { 1762 for (let engineName of param.Remove) { 1763 let engine = Services.search.getEngineByName(engineName); 1764 if (engine) { 1765 try { 1766 await Services.search.removeEngine(engine); 1767 } catch (ex) { 1768 log.error("Unable to remove the search engine", ex); 1769 } 1770 } 1771 } 1772 } 1773 ); 1774 } 1775 if (param.Add) { 1776 // Only rerun if the list of engine names has changed. 1777 let engineNameList = param.Add.map(engine => engine.Name); 1778 await runOncePerModification( 1779 "addSearchEngines", 1780 JSON.stringify(engineNameList), 1781 async function() { 1782 for (let newEngine of param.Add) { 1783 let newEngineParameters = { 1784 template: newEngine.URLTemplate, 1785 iconURL: newEngine.IconURL ? newEngine.IconURL.href : null, 1786 alias: newEngine.Alias, 1787 description: newEngine.Description, 1788 method: newEngine.Method, 1789 postData: newEngine.PostData, 1790 suggestURL: newEngine.SuggestURLTemplate, 1791 extensionID: "set-via-policy", 1792 queryCharset: "UTF-8", 1793 }; 1794 try { 1795 await Services.search.addEngineWithDetails( 1796 newEngine.Name, 1797 newEngineParameters 1798 ); 1799 } catch (ex) { 1800 log.error("Unable to add search engine", ex); 1801 } 1802 } 1803 } 1804 ); 1805 } 1806 if (param.Default) { 1807 await runOncePerModification( 1808 "setDefaultSearchEngine", 1809 param.Default, 1810 async () => { 1811 let defaultEngine; 1812 try { 1813 defaultEngine = Services.search.getEngineByName(param.Default); 1814 if (!defaultEngine) { 1815 throw new Error("No engine by that name could be found"); 1816 } 1817 } catch (ex) { 1818 log.error( 1819 `Search engine lookup failed when attempting to set ` + 1820 `the default engine. Requested engine was ` + 1821 `"${param.Default}".`, 1822 ex 1823 ); 1824 } 1825 if (defaultEngine) { 1826 try { 1827 await Services.search.setDefault(defaultEngine); 1828 } catch (ex) { 1829 log.error("Unable to set the default search engine", ex); 1830 } 1831 } 1832 } 1833 ); 1834 } 1835 if (param.DefaultPrivate) { 1836 await runOncePerModification( 1837 "setDefaultPrivateSearchEngine", 1838 param.DefaultPrivate, 1839 async () => { 1840 let defaultPrivateEngine; 1841 try { 1842 defaultPrivateEngine = Services.search.getEngineByName( 1843 param.DefaultPrivate 1844 ); 1845 if (!defaultPrivateEngine) { 1846 throw new Error("No engine by that name could be found"); 1847 } 1848 } catch (ex) { 1849 log.error( 1850 `Search engine lookup failed when attempting to set ` + 1851 `the default private engine. Requested engine was ` + 1852 `"${param.DefaultPrivate}".`, 1853 ex 1854 ); 1855 } 1856 if (defaultPrivateEngine) { 1857 try { 1858 await Services.search.setDefaultPrivate(defaultPrivateEngine); 1859 } catch (ex) { 1860 log.error( 1861 "Unable to set the default private search engine", 1862 ex 1863 ); 1864 } 1865 } 1866 } 1867 ); 1868 } 1869 }); 1870 }, 1871 }, 1872 1873 SearchSuggestEnabled: { 1874 onBeforeAddons(manager, param) { 1875 setAndLockPref("browser.urlbar.suggest.searches", param); 1876 setAndLockPref("browser.search.suggest.enabled", param); 1877 }, 1878 }, 1879 1880 SecurityDevices: { 1881 onProfileAfterChange(manager, param) { 1882 let securityDevices = param; 1883 let pkcs11db = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService( 1884 Ci.nsIPKCS11ModuleDB 1885 ); 1886 let moduleList = pkcs11db.listModules(); 1887 for (let deviceName in securityDevices) { 1888 let foundModule = false; 1889 for (let module of moduleList) { 1890 if (module && module.libName === securityDevices[deviceName]) { 1891 foundModule = true; 1892 break; 1893 } 1894 } 1895 if (foundModule) { 1896 continue; 1897 } 1898 try { 1899 pkcs11db.addModule(deviceName, securityDevices[deviceName], 0, 0); 1900 } catch (ex) { 1901 log.error(`Unable to add security device ${deviceName}`); 1902 log.debug(ex); 1903 } 1904 } 1905 }, 1906 }, 1907 1908 SSLVersionMax: { 1909 onBeforeAddons(manager, param) { 1910 let tlsVersion; 1911 switch (param) { 1912 case "tls1": 1913 tlsVersion = 1; 1914 break; 1915 case "tls1.1": 1916 tlsVersion = 2; 1917 break; 1918 case "tls1.2": 1919 tlsVersion = 3; 1920 break; 1921 case "tls1.3": 1922 tlsVersion = 4; 1923 break; 1924 } 1925 setAndLockPref("security.tls.version.max", tlsVersion); 1926 }, 1927 }, 1928 1929 SSLVersionMin: { 1930 onBeforeAddons(manager, param) { 1931 let tlsVersion; 1932 switch (param) { 1933 case "tls1": 1934 tlsVersion = 1; 1935 break; 1936 case "tls1.1": 1937 tlsVersion = 2; 1938 break; 1939 case "tls1.2": 1940 tlsVersion = 3; 1941 break; 1942 case "tls1.3": 1943 tlsVersion = 4; 1944 break; 1945 } 1946 setAndLockPref("security.tls.version.min", tlsVersion); 1947 }, 1948 }, 1949 1950 SupportMenu: { 1951 onProfileAfterChange(manager, param) { 1952 manager.setSupportMenu(param); 1953 }, 1954 }, 1955 1956 UserMessaging: { 1957 onBeforeAddons(manager, param) { 1958 let locked = false; 1959 if ("Locked" in param) { 1960 locked = param.Locked; 1961 } 1962 if ("WhatsNew" in param) { 1963 setDefaultPref( 1964 "browser.messaging-system.whatsNewPanel.enabled", 1965 param.WhatsNew, 1966 locked 1967 ); 1968 } 1969 if ("ExtensionRecommendations" in param) { 1970 setDefaultPref( 1971 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", 1972 param.ExtensionRecommendations, 1973 locked 1974 ); 1975 } 1976 if ("FeatureRecommendations" in param) { 1977 setDefaultPref( 1978 "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", 1979 param.FeatureRecommendations, 1980 locked 1981 ); 1982 } 1983 if ("UrlbarInterventions" in param && !param.UrlbarInterventions) { 1984 manager.disallowFeature("urlbarinterventions"); 1985 } 1986 if ("SkipOnboarding") { 1987 setAndLockPref("trailhead.firstrun.branches", "nofirstrun-empty"); 1988 setAndLockPref("browser.aboutwelcome.enabled", false); 1989 } 1990 }, 1991 }, 1992 1993 WebsiteFilter: { 1994 onBeforeUIStartup(manager, param) { 1995 WebsiteFilter.init(param.Block || [], param.Exceptions || []); 1996 }, 1997 }, 1998}; 1999 2000/* 2001 * ==================== 2002 * = HELPER FUNCTIONS = 2003 * ==================== 2004 * 2005 * The functions below are helpers to be used by several policies. 2006 */ 2007 2008/** 2009 * setAndLockPref 2010 * 2011 * Sets the _default_ value of a pref, and locks it (meaning that 2012 * the default value will always be returned, independent from what 2013 * is stored as the user value). 2014 * The value is only changed in memory, and not stored to disk. 2015 * 2016 * @param {string} prefName 2017 * The pref to be changed 2018 * @param {boolean,number,string} prefValue 2019 * The value to set and lock 2020 */ 2021function setAndLockPref(prefName, prefValue) { 2022 setDefaultPref(prefName, prefValue, true); 2023} 2024 2025/** 2026 * setDefaultPref 2027 * 2028 * Sets the _default_ value of a pref and optionally locks it. 2029 * The value is only changed in memory, and not stored to disk. 2030 * 2031 * @param {string} prefName 2032 * The pref to be changed 2033 * @param {boolean,number,string} prefValue 2034 * The value to set 2035 * @param {boolean} locked 2036 * Optionally lock the pref 2037 */ 2038function setDefaultPref(prefName, prefValue, locked = false) { 2039 if (Services.prefs.prefIsLocked(prefName)) { 2040 Services.prefs.unlockPref(prefName); 2041 } 2042 2043 let defaults = Services.prefs.getDefaultBranch(""); 2044 2045 switch (typeof prefValue) { 2046 case "boolean": 2047 defaults.setBoolPref(prefName, prefValue); 2048 break; 2049 2050 case "number": 2051 if (!Number.isInteger(prefValue)) { 2052 throw new Error(`Non-integer value for ${prefName}`); 2053 } 2054 2055 // This is ugly, but necessary. On Windows GPO and macOS 2056 // configs, booleans are converted to 0/1. In the previous 2057 // Preferences implementation, the schema took care of 2058 // automatically converting these values to booleans. 2059 // Since we allow arbitrary prefs now, we have to do 2060 // something different. See bug 1666836. 2061 if ( 2062 defaults.getPrefType(prefName) == defaults.PREF_INT || 2063 ![0, 1].includes(prefValue) 2064 ) { 2065 defaults.setIntPref(prefName, prefValue); 2066 } else { 2067 defaults.setBoolPref(prefName, !!prefValue); 2068 } 2069 break; 2070 2071 case "string": 2072 defaults.setStringPref(prefName, prefValue); 2073 break; 2074 } 2075 2076 if (locked) { 2077 Services.prefs.lockPref(prefName); 2078 } 2079} 2080 2081/** 2082 * setDefaultPermission 2083 * 2084 * Helper function to set preferences appropriately for the policy 2085 * 2086 * @param {string} policyName 2087 * The name of the policy to set 2088 * @param {object} policyParam 2089 * The object containing param for the policy 2090 */ 2091function setDefaultPermission(policyName, policyParam) { 2092 if ("BlockNewRequests" in policyParam) { 2093 let prefName = "permissions.default." + policyName; 2094 2095 if (policyParam.BlockNewRequests) { 2096 setDefaultPref(prefName, 2, policyParam.Locked); 2097 } else { 2098 setDefaultPref(prefName, 0, policyParam.Locked); 2099 } 2100 } 2101} 2102 2103/** 2104 * addAllowDenyPermissions 2105 * 2106 * Helper function to call the permissions manager (Services.perms.addFromPrincipal) 2107 * for two arrays of URLs. 2108 * 2109 * @param {string} permissionName 2110 * The name of the permission to change 2111 * @param {array} allowList 2112 * The list of URLs to be set as ALLOW_ACTION for the chosen permission. 2113 * @param {array} blockList 2114 * The list of URLs to be set as DENY_ACTION for the chosen permission. 2115 */ 2116function addAllowDenyPermissions(permissionName, allowList, blockList) { 2117 allowList = allowList || []; 2118 blockList = blockList || []; 2119 2120 for (let origin of allowList) { 2121 try { 2122 Services.perms.addFromPrincipal( 2123 Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin), 2124 permissionName, 2125 Ci.nsIPermissionManager.ALLOW_ACTION, 2126 Ci.nsIPermissionManager.EXPIRE_POLICY 2127 ); 2128 } catch (ex) { 2129 log.error(`Added by default for ${permissionName} permission in the permission 2130 manager - ${origin.href}`); 2131 } 2132 } 2133 2134 for (let origin of blockList) { 2135 Services.perms.addFromPrincipal( 2136 Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin), 2137 permissionName, 2138 Ci.nsIPermissionManager.DENY_ACTION, 2139 Ci.nsIPermissionManager.EXPIRE_POLICY 2140 ); 2141 } 2142} 2143 2144/** 2145 * runOnce 2146 * 2147 * Helper function to run a callback only once per policy. 2148 * 2149 * @param {string} actionName 2150 * A given name which will be used to track if this callback has run. 2151 * @param {Functon} callback 2152 * The callback to run only once. 2153 */ 2154// eslint-disable-next-line no-unused-vars 2155function runOnce(actionName, callback) { 2156 let prefName = `browser.policies.runonce.${actionName}`; 2157 if (Services.prefs.getBoolPref(prefName, false)) { 2158 log.debug( 2159 `Not running action ${actionName} again because it has already run.` 2160 ); 2161 return; 2162 } 2163 Services.prefs.setBoolPref(prefName, true); 2164 callback(); 2165} 2166 2167/** 2168 * runOncePerModification 2169 * 2170 * Helper function similar to runOnce. The difference is that runOnce runs the 2171 * callback once when the policy is set, then never again. 2172 * runOncePerModification runs the callback once each time the policy value 2173 * changes from its previous value. 2174 * If the callback that was passed is an async function, you can await on this 2175 * function to await for the callback. 2176 * 2177 * @param {string} actionName 2178 * A given name which will be used to track if this callback has run. 2179 * This string will be part of a pref name. 2180 * @param {string} policyValue 2181 * The current value of the policy. This will be compared to previous 2182 * values given to this function to determine if the policy value has 2183 * changed. Regardless of the data type of the policy, this must be a 2184 * string. 2185 * @param {Function} callback 2186 * The callback to be run when the pref value changes 2187 * @returns Promise 2188 * A promise that will resolve once the callback finishes running. 2189 * 2190 */ 2191async function runOncePerModification(actionName, policyValue, callback) { 2192 let prefName = `browser.policies.runOncePerModification.${actionName}`; 2193 let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined); 2194 if (policyValue === oldPolicyValue) { 2195 log.debug( 2196 `Not running action ${actionName} again because the policy's value is unchanged` 2197 ); 2198 return Promise.resolve(); 2199 } 2200 Services.prefs.setStringPref(prefName, policyValue); 2201 return callback(); 2202} 2203 2204/** 2205 * clearRunOnceModification 2206 * 2207 * Helper function that clears a runOnce policy. 2208 */ 2209function clearRunOnceModification(actionName) { 2210 let prefName = `browser.policies.runOncePerModification.${actionName}`; 2211 Services.prefs.clearUserPref(prefName); 2212} 2213 2214function replacePathVariables(path) { 2215 if (path.includes("${home}")) { 2216 return path.replace("${home}", FileUtils.getFile("Home", []).path); 2217 } 2218 return path; 2219} 2220 2221/** 2222 * installAddonFromURL 2223 * 2224 * Helper function that installs an addon from a URL 2225 * and verifies that the addon ID matches. 2226 */ 2227function installAddonFromURL(url, extensionID, addon) { 2228 if ( 2229 addon && 2230 addon.sourceURI && 2231 addon.sourceURI.spec == url && 2232 !addon.sourceURI.schemeIs("file") 2233 ) { 2234 // It's the same addon, don't reinstall. 2235 return; 2236 } 2237 AddonManager.getInstallForURL(url, { 2238 telemetryInfo: { source: "enterprise-policy" }, 2239 }).then(install => { 2240 if (install.addon && install.addon.appDisabled) { 2241 log.error(`Incompatible add-on - ${location}`); 2242 install.cancel(); 2243 return; 2244 } 2245 let listener = { 2246 /* eslint-disable-next-line no-shadow */ 2247 onDownloadEnded: install => { 2248 if (extensionID && install.addon.id != extensionID) { 2249 log.error( 2250 `Add-on downloaded from ${url} had unexpected id (got ${install.addon.id} expected ${extensionID})` 2251 ); 2252 install.removeListener(listener); 2253 install.cancel(); 2254 } 2255 if (install.addon && install.addon.appDisabled) { 2256 log.error(`Incompatible add-on - ${url}`); 2257 install.removeListener(listener); 2258 install.cancel(); 2259 } 2260 if ( 2261 addon && 2262 Services.vc.compare(addon.version, install.addon.version) == 0 2263 ) { 2264 log.debug("Installation cancelled because versions are the same"); 2265 install.removeListener(listener); 2266 install.cancel(); 2267 } 2268 }, 2269 onDownloadFailed: () => { 2270 install.removeListener(listener); 2271 log.error( 2272 `Download failed - ${AddonManager.errorToString( 2273 install.error 2274 )} - ${url}` 2275 ); 2276 clearRunOnceModification("extensionsInstall"); 2277 }, 2278 onInstallFailed: () => { 2279 install.removeListener(listener); 2280 log.error( 2281 `Installation failed - ${AddonManager.errorToString( 2282 install.error 2283 )} - {url}` 2284 ); 2285 }, 2286 /* eslint-disable-next-line no-shadow */ 2287 onInstallEnded: (install, addon) => { 2288 if (addon.type == "theme") { 2289 addon.enable(); 2290 } 2291 install.removeListener(listener); 2292 log.debug(`Installation succeeded - ${url}`); 2293 }, 2294 }; 2295 install.addListener(listener); 2296 install.install(); 2297 }); 2298} 2299 2300let gBlockedChromePages = []; 2301 2302function blockAboutPage(manager, feature, neededOnContentProcess = false) { 2303 if (!gBlockedChromePages.length) { 2304 addChromeURLBlocker(); 2305 } 2306 manager.disallowFeature(feature, neededOnContentProcess); 2307 let splitURL = Services.io 2308 .newChannelFromURI( 2309 Services.io.newURI(feature), 2310 null, 2311 Services.scriptSecurityManager.getSystemPrincipal(), 2312 null, 2313 0, 2314 Ci.nsIContentPolicy.TYPE_OTHER 2315 ) 2316 .URI.spec.split("/"); 2317 // about:debugging uses index.html for a filename, so we need to rely 2318 // on more than just the filename. 2319 let fileName = 2320 splitURL[splitURL.length - 2] + "/" + splitURL[splitURL.length - 1]; 2321 gBlockedChromePages.push(fileName); 2322 if (feature == "about:config") { 2323 // Hide old page until it is removed 2324 gBlockedChromePages.push("config.xhtml"); 2325 } 2326} 2327 2328let ChromeURLBlockPolicy = { 2329 shouldLoad(contentLocation, loadInfo, mimeTypeGuess) { 2330 let contentType = loadInfo.externalContentPolicyType; 2331 if ( 2332 contentLocation.scheme == "chrome" && 2333 contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT && 2334 loadInfo.loadingContext && 2335 loadInfo.loadingContext.baseURI == AppConstants.BROWSER_CHROME_URL && 2336 gBlockedChromePages.some(function(fileName) { 2337 return contentLocation.filePath.endsWith(fileName); 2338 }) 2339 ) { 2340 return Ci.nsIContentPolicy.REJECT_REQUEST; 2341 } 2342 return Ci.nsIContentPolicy.ACCEPT; 2343 }, 2344 shouldProcess(contentLocation, loadInfo, mimeTypeGuess) { 2345 return Ci.nsIContentPolicy.ACCEPT; 2346 }, 2347 classDescription: "Policy Engine Content Policy", 2348 contractID: "@mozilla-org/policy-engine-content-policy-service;1", 2349 classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"), 2350 QueryInterface: ChromeUtils.generateQI([Ci.nsIContentPolicy]), 2351 createInstance(outer, iid) { 2352 return this.QueryInterface(iid); 2353 }, 2354}; 2355 2356function addChromeURLBlocker() { 2357 let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); 2358 registrar.registerFactory( 2359 ChromeURLBlockPolicy.classID, 2360 ChromeURLBlockPolicy.classDescription, 2361 ChromeURLBlockPolicy.contractID, 2362 ChromeURLBlockPolicy 2363 ); 2364 2365 Services.catMan.addCategoryEntry( 2366 "content-policy", 2367 ChromeURLBlockPolicy.contractID, 2368 ChromeURLBlockPolicy.contractID, 2369 false, 2370 true 2371 ); 2372} 2373 2374function pemToBase64(pem) { 2375 return pem 2376 .replace(/(.*)-----BEGIN CERTIFICATE-----/, "") 2377 .replace(/-----END CERTIFICATE-----(.*)/, "") 2378 .replace(/[\r\n]/g, ""); 2379} 2380 2381function processMIMEInfo(mimeInfo, realMIMEInfo) { 2382 if ("handlers" in mimeInfo) { 2383 let firstHandler = true; 2384 for (let handler of mimeInfo.handlers) { 2385 // handler can be null which means they don't 2386 // want a preferred handler. 2387 if (handler) { 2388 let handlerApp; 2389 if ("path" in handler) { 2390 try { 2391 let file = new FileUtils.File(handler.path); 2392 handlerApp = Cc[ 2393 "@mozilla.org/uriloader/local-handler-app;1" 2394 ].createInstance(Ci.nsILocalHandlerApp); 2395 handlerApp.executable = file; 2396 } catch (ex) { 2397 log.error(`Unable to create handler executable (${handler.path})`); 2398 continue; 2399 } 2400 } else if ("uriTemplate" in handler) { 2401 let templateURL = new URL(handler.uriTemplate); 2402 if (templateURL.protocol != "https:") { 2403 log.error(`Web handler must be https (${handler.uriTemplate})`); 2404 continue; 2405 } 2406 if ( 2407 !templateURL.pathname.includes("%s") && 2408 !templateURL.search.includes("%s") 2409 ) { 2410 log.error(`Web handler must contain %s (${handler.uriTemplate})`); 2411 continue; 2412 } 2413 handlerApp = Cc[ 2414 "@mozilla.org/uriloader/web-handler-app;1" 2415 ].createInstance(Ci.nsIWebHandlerApp); 2416 handlerApp.uriTemplate = handler.uriTemplate; 2417 } else { 2418 log.error("Invalid handler"); 2419 continue; 2420 } 2421 if ("name" in handler) { 2422 handlerApp.name = handler.name; 2423 } 2424 realMIMEInfo.possibleApplicationHandlers.appendElement(handlerApp); 2425 if (firstHandler) { 2426 realMIMEInfo.preferredApplicationHandler = handlerApp; 2427 } 2428 } 2429 firstHandler = false; 2430 } 2431 } 2432 if ("action" in mimeInfo) { 2433 let action = realMIMEInfo[mimeInfo.action]; 2434 if ( 2435 action == realMIMEInfo.useHelperApp && 2436 !realMIMEInfo.possibleApplicationHandlers.length 2437 ) { 2438 log.error("useHelperApp requires a handler"); 2439 return; 2440 } 2441 realMIMEInfo.preferredAction = action; 2442 } 2443 if ("ask" in mimeInfo) { 2444 realMIMEInfo.alwaysAskBeforeHandling = mimeInfo.ask; 2445 } 2446 gHandlerService.store(realMIMEInfo); 2447} 2448