nsReadConfig.cpp (10037B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsReadConfig.h" 7 #include "nsJSConfigTriggers.h" 8 9 #include "mozilla/Logging.h" 10 #include "mozilla/Components.h" 11 #include "mozilla/HelperMacros.h" 12 #include "nsAppDirectoryServiceDefs.h" 13 #include "nsIAppStartup.h" 14 #include "nsIChannel.h" 15 #include "nsContentUtils.h" 16 #include "nsDirectoryServiceDefs.h" 17 #include "nsIFile.h" 18 #include "nsIInputStream.h" 19 #include "nsIObserverService.h" 20 #include "nsIPrefBranch.h" 21 #include "nsIPrefService.h" 22 #include "nsIPromptService.h" 23 #include "nsIStringBundle.h" 24 #include "nsNetUtil.h" 25 #include "nsString.h" 26 #include "nsCRT.h" 27 #include "nspr.h" 28 #include "nsXULAppAPI.h" 29 30 #if defined(MOZ_WIDGET_GTK) 31 # include "mozilla/WidgetUtilsGtk.h" 32 #endif // defined(MOZ_WIDGET_GTK) 33 34 using namespace mozilla; 35 36 extern bool sandboxEnabled; 37 38 extern mozilla::LazyLogModule MCD; 39 40 extern nsresult CentralizedAdminPrefManagerInit(bool aSandboxEnabled); 41 extern nsresult CentralizedAdminPrefManagerFinish(); 42 43 static nsresult DisplayError(void) { 44 nsresult rv; 45 46 nsCOMPtr<nsIPromptService> promptService = 47 do_GetService("@mozilla.org/prompter;1"); 48 if (!promptService) return NS_ERROR_FAILURE; 49 50 nsCOMPtr<nsIStringBundleService> bundleService = 51 do_GetService(NS_STRINGBUNDLE_CONTRACTID); 52 if (!bundleService) return NS_ERROR_FAILURE; 53 54 nsCOMPtr<nsIStringBundle> bundle; 55 bundleService->CreateBundle( 56 "chrome://autoconfig/locale/autoconfig.properties", 57 getter_AddRefs(bundle)); 58 if (!bundle) return NS_ERROR_FAILURE; 59 60 nsAutoString title; 61 rv = bundle->GetStringFromName("readConfigTitle", title); 62 if (NS_FAILED(rv)) return rv; 63 64 nsAutoString err; 65 rv = bundle->GetStringFromName("readConfigMsg", err); 66 if (NS_FAILED(rv)) return rv; 67 68 return promptService->Alert(nullptr, title.get(), err.get()); 69 } 70 71 // nsISupports Implementation 72 73 NS_IMPL_ISUPPORTS(nsReadConfig, nsIObserver) 74 75 nsReadConfig::nsReadConfig() : mRead(false) {} 76 77 nsresult nsReadConfig::Init() { 78 nsresult rv; 79 80 nsCOMPtr<nsIObserverService> observerService = 81 do_GetService("@mozilla.org/observer-service;1", &rv); 82 83 if (observerService) { 84 rv = 85 observerService->AddObserver(this, NS_PREFSERVICE_READ_TOPIC_ID, false); 86 } 87 return (rv); 88 } 89 90 nsReadConfig::~nsReadConfig() { CentralizedAdminPrefManagerFinish(); } 91 92 NS_IMETHODIMP nsReadConfig::Observe(nsISupports* aSubject, const char* aTopic, 93 const char16_t* someData) { 94 nsresult rv = NS_OK; 95 96 if (!nsCRT::strcmp(aTopic, NS_PREFSERVICE_READ_TOPIC_ID)) { 97 rv = readConfigFile(); 98 // Don't show error alerts if the sandbox is enabled, just show 99 // sandbox warning. 100 if (NS_FAILED(rv)) { 101 if (sandboxEnabled) { 102 nsContentUtils::ReportToConsoleNonLocalized( 103 u"Autoconfig is sandboxed by default. See " 104 "https://support.mozilla.org/products/" 105 "firefox-enterprise for more information."_ns, 106 nsIScriptError::warningFlag, "autoconfig"_ns, nullptr); 107 } else { 108 rv = DisplayError(); 109 if (NS_FAILED(rv)) { 110 nsCOMPtr<nsIAppStartup> appStartup = 111 components::AppStartup::Service(); 112 if (appStartup) { 113 bool userAllowedQuit = true; 114 appStartup->Quit(nsIAppStartup::eAttemptQuit, 0, &userAllowedQuit); 115 } 116 } 117 } 118 } 119 } 120 return rv; 121 } 122 123 /** 124 * This is the blocklist for known bad autoconfig files. 125 */ 126 static const char* gBlockedConfigs[] = {"dsengine.cfg"}; 127 128 nsresult nsReadConfig::readConfigFile() { 129 nsresult rv = NS_OK; 130 nsAutoCString lockFileName; 131 nsAutoCString lockVendor; 132 uint32_t fileNameLen = 0; 133 134 nsCOMPtr<nsIPrefBranch> defaultPrefBranch; 135 nsCOMPtr<nsIPrefService> prefService = 136 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); 137 if (NS_FAILED(rv)) return rv; 138 139 rv = 140 prefService->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch)); 141 if (NS_FAILED(rv)) return rv; 142 143 constexpr auto channel = nsLiteralCString{MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL)}; 144 145 bool sandboxEnabled = 146 channel.EqualsLiteral("beta") || channel.EqualsLiteral("release"); 147 148 (void)defaultPrefBranch->GetBoolPref("general.config.sandbox_enabled", 149 &sandboxEnabled); 150 151 rv = defaultPrefBranch->GetCharPref("general.config.filename", lockFileName); 152 153 if (NS_FAILED(rv)) return rv; 154 155 MOZ_LOG(MCD, LogLevel::Debug, 156 ("general.config.filename = %s\n", lockFileName.get())); 157 158 for (size_t index = 0, len = std::size(gBlockedConfigs); index < len; 159 ++index) { 160 if (lockFileName == gBlockedConfigs[index]) { 161 // This is NS_OK because we don't want to show an error to the user 162 return rv; 163 } 164 } 165 166 // This needs to be read only once. 167 // 168 if (!mRead) { 169 // Initiate the new JS Context for Preference management 170 171 rv = CentralizedAdminPrefManagerInit(sandboxEnabled); 172 if (NS_FAILED(rv)) return rv; 173 174 // Open and evaluate function calls to set/lock/unlock prefs 175 rv = openAndEvaluateJSFile("prefcalls.js", 0, false, false); 176 if (NS_FAILED(rv)) return rv; 177 178 mRead = true; 179 } 180 // If the lockFileName is nullptr return ok, because no lockFile will be used 181 182 // Once the config file is read, we should check that the vendor name 183 // is consistent By checking for the vendor name after reading the config 184 // file we allow for the preference to be set (and locked) by the creator 185 // of the cfg file meaning the file can not be renamed (successfully). 186 187 nsCOMPtr<nsIPrefBranch> prefBranch; 188 rv = prefService->GetBranch(nullptr, getter_AddRefs(prefBranch)); 189 NS_ENSURE_SUCCESS(rv, rv); 190 191 int32_t obscureValue = 0; 192 (void)defaultPrefBranch->GetIntPref("general.config.obscure_value", 193 &obscureValue); 194 MOZ_LOG(MCD, LogLevel::Debug, 195 ("evaluating .cfg file %s with obscureValue %d\n", lockFileName.get(), 196 obscureValue)); 197 rv = openAndEvaluateJSFile(lockFileName.get(), obscureValue, true, true); 198 if (NS_FAILED(rv)) { 199 MOZ_LOG(MCD, LogLevel::Debug, 200 ("error evaluating .cfg file %s %" PRIx32 "\n", lockFileName.get(), 201 static_cast<uint32_t>(rv))); 202 return rv; 203 } 204 205 rv = prefBranch->GetCharPref("general.config.filename", lockFileName); 206 if (NS_FAILED(rv)) 207 // There is NO REASON we should ever get here. This is POST reading 208 // of the config file. 209 return NS_ERROR_FAILURE; 210 211 rv = prefBranch->GetCharPref("general.config.vendor", lockVendor); 212 // If vendor is not nullptr, do this check 213 if (NS_SUCCEEDED(rv)) { 214 fileNameLen = strlen(lockFileName.get()); 215 216 // lockVendor and lockFileName should be the same with the addtion of 217 // .cfg to the filename by checking this post reading of the cfg file 218 // this value can be set within the cfg file adding a level of security. 219 220 if (strncmp(lockFileName.get(), lockVendor.get(), fileNameLen - 4) != 0) { 221 return NS_ERROR_FAILURE; 222 } 223 } 224 225 // get the value of the autoconfig url 226 nsAutoCString urlName; 227 rv = prefBranch->GetCharPref("autoadmin.global_config_url", urlName); 228 if (NS_SUCCEEDED(rv) && !urlName.IsEmpty()) { 229 // Instantiating nsAutoConfig object if the pref is present 230 mAutoConfig = new nsAutoConfig(); 231 232 rv = mAutoConfig->Init(); 233 if (NS_WARN_IF(NS_FAILED(rv))) { 234 return rv; 235 } 236 237 mAutoConfig->SetConfigURL(urlName.get()); 238 } 239 240 return NS_OK; 241 } // ReadConfigFile 242 243 nsresult nsReadConfig::openAndEvaluateJSFile(const char* aFileName, 244 int32_t obscureValue, 245 bool isEncoded, bool isBinDir) { 246 nsresult rv; 247 248 nsCOMPtr<nsIInputStream> inStr; 249 if (isBinDir) { 250 nsCOMPtr<nsIFile> jsFile; 251 #if defined(MOZ_WIDGET_GTK) 252 if (!mozilla::widget::IsRunningUnderFlatpakOrSnap()) { 253 #endif // defined(MOZ_WIDGET_GTK) 254 rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(jsFile)); 255 #if defined(MOZ_WIDGET_GTK) 256 } else { 257 rv = NS_GetSpecialDirectory(NS_OS_SYSTEM_CONFIG_DIR, 258 getter_AddRefs(jsFile)); 259 } 260 #endif // defined(MOZ_WIDGET_GTK) 261 if (NS_FAILED(rv)) return rv; 262 263 rv = jsFile->AppendNative(nsDependentCString(aFileName)); 264 if (NS_FAILED(rv)) return rv; 265 266 rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), jsFile); 267 if (NS_FAILED(rv)) return rv; 268 269 } else { 270 nsAutoCString location("resource://gre/defaults/autoconfig/"); 271 location += aFileName; 272 273 nsCOMPtr<nsIURI> uri; 274 rv = NS_NewURI(getter_AddRefs(uri), location); 275 NS_ENSURE_SUCCESS(rv, rv); 276 277 nsCOMPtr<nsIChannel> channel; 278 rv = NS_NewChannel(getter_AddRefs(channel), uri, 279 nsContentUtils::GetSystemPrincipal(), 280 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 281 nsIContentPolicy::TYPE_OTHER); 282 NS_ENSURE_SUCCESS(rv, rv); 283 284 rv = channel->Open(getter_AddRefs(inStr)); 285 NS_ENSURE_SUCCESS(rv, rv); 286 } 287 288 uint64_t fs64; 289 uint32_t amt = 0; 290 rv = inStr->Available(&fs64); 291 if (NS_FAILED(rv)) return rv; 292 // This used to use PR_Malloc(), which doesn't support over 4GB. 293 if (fs64 > UINT32_MAX) return NS_ERROR_FILE_TOO_BIG; 294 uint32_t fs = (uint32_t)fs64; 295 296 char* buf = (char*)malloc(fs * sizeof(char)); 297 if (!buf) return NS_ERROR_OUT_OF_MEMORY; 298 299 rv = inStr->Read(buf, (uint32_t)fs, &amt); 300 NS_ASSERTION((amt == fs), "failed to read the entire configuration file!!"); 301 if (NS_SUCCEEDED(rv)) { 302 if (obscureValue > 0) { 303 // Unobscure file by subtracting some value from every char. 304 for (uint32_t i = 0; i < amt; i++) buf[i] -= obscureValue; 305 } 306 rv = EvaluateAdminConfigScript(buf, amt, aFileName, false, true, isEncoded, 307 !isBinDir); 308 } 309 inStr->Close(); 310 free(buf); 311 312 return rv; 313 }