summaryrefslogtreecommitdiffstats
path: root/extensions/pref/autoconfig/src/nsReadConfig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/pref/autoconfig/src/nsReadConfig.cpp')
-rw-r--r--extensions/pref/autoconfig/src/nsReadConfig.cpp312
1 files changed, 312 insertions, 0 deletions
diff --git a/extensions/pref/autoconfig/src/nsReadConfig.cpp b/extensions/pref/autoconfig/src/nsReadConfig.cpp
new file mode 100644
index 0000000000..3da68faac6
--- /dev/null
+++ b/extensions/pref/autoconfig/src/nsReadConfig.cpp
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsReadConfig.h"
+#include "nsJSConfigTriggers.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/Components.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIAppStartup.h"
+#include "nsIChannel.h"
+#include "nsContentUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIInputStream.h"
+#include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIPromptService.h"
+#include "nsIStringBundle.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "nspr.h"
+#include "nsXULAppAPI.h"
+
+#if defined(MOZ_WIDGET_GTK)
+# include "mozilla/WidgetUtilsGtk.h"
+#endif // defined(MOZ_WIDGET_GTK)
+
+using namespace mozilla;
+
+extern bool sandboxEnabled;
+
+extern mozilla::LazyLogModule MCD;
+
+extern nsresult CentralizedAdminPrefManagerInit(bool aSandboxEnabled);
+extern nsresult CentralizedAdminPrefManagerFinish();
+
+static nsresult DisplayError(void) {
+ nsresult rv;
+
+ nsCOMPtr<nsIPromptService> promptService =
+ do_GetService("@mozilla.org/prompter;1");
+ if (!promptService) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+ if (!bundleService) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ bundleService->CreateBundle(
+ "chrome://autoconfig/locale/autoconfig.properties",
+ getter_AddRefs(bundle));
+ if (!bundle) return NS_ERROR_FAILURE;
+
+ nsAutoString title;
+ rv = bundle->GetStringFromName("readConfigTitle", title);
+ if (NS_FAILED(rv)) return rv;
+
+ nsAutoString err;
+ rv = bundle->GetStringFromName("readConfigMsg", err);
+ if (NS_FAILED(rv)) return rv;
+
+ return promptService->Alert(nullptr, title.get(), err.get());
+}
+
+// nsISupports Implementation
+
+NS_IMPL_ISUPPORTS(nsReadConfig, nsIObserver)
+
+nsReadConfig::nsReadConfig() : mRead(false) {}
+
+nsresult nsReadConfig::Init() {
+ nsresult rv;
+
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+
+ if (observerService) {
+ rv =
+ observerService->AddObserver(this, NS_PREFSERVICE_READ_TOPIC_ID, false);
+ }
+ return (rv);
+}
+
+nsReadConfig::~nsReadConfig() { CentralizedAdminPrefManagerFinish(); }
+
+NS_IMETHODIMP nsReadConfig::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* someData) {
+ nsresult rv = NS_OK;
+
+ if (!nsCRT::strcmp(aTopic, NS_PREFSERVICE_READ_TOPIC_ID)) {
+ rv = readConfigFile();
+ // Don't show error alerts if the sandbox is enabled, just show
+ // sandbox warning.
+ if (NS_FAILED(rv)) {
+ if (sandboxEnabled) {
+ nsContentUtils::ReportToConsoleNonLocalized(
+ u"Autoconfig is sandboxed by default. See "
+ "https://support.mozilla.org/products/"
+ "firefox-enterprise for more information."_ns,
+ nsIScriptError::warningFlag, "autoconfig"_ns, nullptr);
+ } else {
+ rv = DisplayError();
+ if (NS_FAILED(rv)) {
+ nsCOMPtr<nsIAppStartup> appStartup =
+ components::AppStartup::Service();
+ if (appStartup) {
+ bool userAllowedQuit = true;
+ appStartup->Quit(nsIAppStartup::eAttemptQuit, 0, &userAllowedQuit);
+ }
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+/**
+ * This is the blocklist for known bad autoconfig files.
+ */
+static const char* gBlockedConfigs[] = {"dsengine.cfg"};
+
+nsresult nsReadConfig::readConfigFile() {
+ nsresult rv = NS_OK;
+ nsAutoCString lockFileName;
+ nsAutoCString lockVendor;
+ uint32_t fileNameLen = 0;
+
+ nsCOMPtr<nsIPrefBranch> defaultPrefBranch;
+ nsCOMPtr<nsIPrefService> prefService =
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv =
+ prefService->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch));
+ if (NS_FAILED(rv)) return rv;
+
+ constexpr auto channel = nsLiteralCString{MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL)};
+
+ bool sandboxEnabled =
+ channel.EqualsLiteral("beta") || channel.EqualsLiteral("release");
+
+ mozilla::Unused << defaultPrefBranch->GetBoolPref(
+ "general.config.sandbox_enabled", &sandboxEnabled);
+
+ rv = defaultPrefBranch->GetCharPref("general.config.filename", lockFileName);
+
+ if (NS_FAILED(rv)) return rv;
+
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("general.config.filename = %s\n", lockFileName.get()));
+
+ for (size_t index = 0, len = mozilla::ArrayLength(gBlockedConfigs);
+ index < len; ++index) {
+ if (lockFileName == gBlockedConfigs[index]) {
+ // This is NS_OK because we don't want to show an error to the user
+ return rv;
+ }
+ }
+
+ // This needs to be read only once.
+ //
+ if (!mRead) {
+ // Initiate the new JS Context for Preference management
+
+ rv = CentralizedAdminPrefManagerInit(sandboxEnabled);
+ if (NS_FAILED(rv)) return rv;
+
+ // Open and evaluate function calls to set/lock/unlock prefs
+ rv = openAndEvaluateJSFile("prefcalls.js", 0, false, false);
+ if (NS_FAILED(rv)) return rv;
+
+ mRead = true;
+ }
+ // If the lockFileName is nullptr return ok, because no lockFile will be used
+
+ // Once the config file is read, we should check that the vendor name
+ // is consistent By checking for the vendor name after reading the config
+ // file we allow for the preference to be set (and locked) by the creator
+ // of the cfg file meaning the file can not be renamed (successfully).
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefService->GetBranch(nullptr, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t obscureValue = 0;
+ (void)defaultPrefBranch->GetIntPref("general.config.obscure_value",
+ &obscureValue);
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("evaluating .cfg file %s with obscureValue %d\n", lockFileName.get(),
+ obscureValue));
+ rv = openAndEvaluateJSFile(lockFileName.get(), obscureValue, true, true);
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(MCD, LogLevel::Debug,
+ ("error evaluating .cfg file %s %" PRIx32 "\n", lockFileName.get(),
+ static_cast<uint32_t>(rv)));
+ return rv;
+ }
+
+ rv = prefBranch->GetCharPref("general.config.filename", lockFileName);
+ if (NS_FAILED(rv))
+ // There is NO REASON we should ever get here. This is POST reading
+ // of the config file.
+ return NS_ERROR_FAILURE;
+
+ rv = prefBranch->GetCharPref("general.config.vendor", lockVendor);
+ // If vendor is not nullptr, do this check
+ if (NS_SUCCEEDED(rv)) {
+ fileNameLen = strlen(lockFileName.get());
+
+ // lockVendor and lockFileName should be the same with the addtion of
+ // .cfg to the filename by checking this post reading of the cfg file
+ // this value can be set within the cfg file adding a level of security.
+
+ if (strncmp(lockFileName.get(), lockVendor.get(), fileNameLen - 4) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // get the value of the autoconfig url
+ nsAutoCString urlName;
+ rv = prefBranch->GetCharPref("autoadmin.global_config_url", urlName);
+ if (NS_SUCCEEDED(rv) && !urlName.IsEmpty()) {
+ // Instantiating nsAutoConfig object if the pref is present
+ mAutoConfig = new nsAutoConfig();
+
+ rv = mAutoConfig->Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mAutoConfig->SetConfigURL(urlName.get());
+ }
+
+ return NS_OK;
+} // ReadConfigFile
+
+nsresult nsReadConfig::openAndEvaluateJSFile(const char* aFileName,
+ int32_t obscureValue,
+ bool isEncoded, bool isBinDir) {
+ nsresult rv;
+
+ nsCOMPtr<nsIInputStream> inStr;
+ if (isBinDir) {
+ nsCOMPtr<nsIFile> jsFile;
+#if defined(MOZ_WIDGET_GTK)
+ if (!mozilla::widget::IsRunningUnderFlatpakOrSnap()) {
+#endif // defined(MOZ_WIDGET_GTK)
+ rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(jsFile));
+#if defined(MOZ_WIDGET_GTK)
+ } else {
+ rv = NS_GetSpecialDirectory(NS_OS_SYSTEM_CONFIG_DIR,
+ getter_AddRefs(jsFile));
+ }
+#endif // defined(MOZ_WIDGET_GTK)
+ if (NS_FAILED(rv)) return rv;
+
+ rv = jsFile->AppendNative(nsDependentCString(aFileName));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), jsFile);
+ if (NS_FAILED(rv)) return rv;
+
+ } else {
+ nsAutoCString location("resource://gre/defaults/autoconfig/");
+ location += aFileName;
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), location);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel), uri,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = channel->Open(getter_AddRefs(inStr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint64_t fs64;
+ uint32_t amt = 0;
+ rv = inStr->Available(&fs64);
+ if (NS_FAILED(rv)) return rv;
+ // This used to use PR_Malloc(), which doesn't support over 4GB.
+ if (fs64 > UINT32_MAX) return NS_ERROR_FILE_TOO_BIG;
+ uint32_t fs = (uint32_t)fs64;
+
+ char* buf = (char*)malloc(fs * sizeof(char));
+ if (!buf) return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = inStr->Read(buf, (uint32_t)fs, &amt);
+ NS_ASSERTION((amt == fs), "failed to read the entire configuration file!!");
+ if (NS_SUCCEEDED(rv)) {
+ if (obscureValue > 0) {
+ // Unobscure file by subtracting some value from every char.
+ for (uint32_t i = 0; i < amt; i++) buf[i] -= obscureValue;
+ }
+ rv = EvaluateAdminConfigScript(buf, amt, aFileName, false, true, isEncoded,
+ !isBinDir);
+ }
+ inStr->Close();
+ free(buf);
+
+ return rv;
+}