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