diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /extensions/pref/autoconfig/src/nsReadConfig.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extensions/pref/autoconfig/src/nsReadConfig.cpp')
-rw-r--r-- | extensions/pref/autoconfig/src/nsReadConfig.cpp | 312 |
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; +} |