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 "nsJSConfigTriggers.h"
7 
8 #include "jsapi.h"
9 #include "nsIXPConnect.h"
10 #include "nsCOMPtr.h"
11 #include "nsString.h"
12 #include "nspr.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/NullPrincipal.h"
16 #include "nsContentUtils.h"
17 #include "nsJSPrincipals.h"
18 #include "nsIScriptError.h"
19 #include "js/Wrapper.h"
20 #include "mozilla/Utf8.h"
21 
22 extern mozilla::LazyLogModule MCD;
23 using mozilla::AutoSafeJSContext;
24 using mozilla::IsUtf8;
25 using mozilla::NullPrincipal;
26 using mozilla::dom::AutoJSAPI;
27 
28 //*****************************************************************************
29 
30 static JS::PersistentRooted<JSObject*> autoconfigSystemSb;
31 static JS::PersistentRooted<JSObject*> autoconfigSb;
32 static bool sandboxEnabled;
33 
CentralizedAdminPrefManagerInit(bool aSandboxEnabled)34 nsresult CentralizedAdminPrefManagerInit(bool aSandboxEnabled) {
35   // If the sandbox is already created, no need to create it again.
36   if (autoconfigSb.initialized()) return NS_OK;
37 
38   sandboxEnabled = aSandboxEnabled;
39 
40   // Grab XPConnect.
41   nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
42 
43   // Grab the system principal.
44   nsCOMPtr<nsIPrincipal> principal;
45   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(
46       getter_AddRefs(principal));
47 
48   // Create a sandbox.
49   AutoSafeJSContext cx;
50   JS::Rooted<JSObject*> sandbox(cx);
51   nsresult rv = xpc->CreateSandbox(cx, principal, sandbox.address());
52   NS_ENSURE_SUCCESS(rv, rv);
53 
54   // Unwrap, store and root the sandbox.
55   NS_ENSURE_STATE(sandbox);
56   autoconfigSystemSb.init(cx, js::UncheckedUnwrap(sandbox));
57 
58   // Create an unprivileged sandbox.
59   principal = NullPrincipal::CreateWithoutOriginAttributes();
60   rv = xpc->CreateSandbox(cx, principal, sandbox.address());
61   NS_ENSURE_SUCCESS(rv, rv);
62 
63   autoconfigSb.init(cx, js::UncheckedUnwrap(sandbox));
64 
65   // Define gSandbox on system sandbox.
66   JSAutoRealm ar(cx, autoconfigSystemSb);
67 
68   JS::Rooted<JS::Value> value(cx, JS::ObjectValue(*sandbox));
69 
70   if (!JS_WrapValue(cx, &value) ||
71       !JS_DefineProperty(cx, autoconfigSystemSb, "gSandbox", value,
72                          JSPROP_ENUMERATE)) {
73     return NS_ERROR_FAILURE;
74   }
75 
76   return NS_OK;
77 }
78 
CentralizedAdminPrefManagerFinish()79 nsresult CentralizedAdminPrefManagerFinish() {
80   if (autoconfigSb.initialized()) {
81     AutoSafeJSContext cx;
82     autoconfigSb.reset();
83     autoconfigSystemSb.reset();
84     JS_MaybeGC(cx);
85   }
86   return NS_OK;
87 }
88 
EvaluateAdminConfigScript(const char * js_buffer,size_t length,const char * filename,bool globalContext,bool callbacks,bool skipFirstLine,bool isPrivileged)89 nsresult EvaluateAdminConfigScript(const char* js_buffer, size_t length,
90                                    const char* filename, bool globalContext,
91                                    bool callbacks, bool skipFirstLine,
92                                    bool isPrivileged) {
93   if (!sandboxEnabled) {
94     isPrivileged = true;
95   }
96   return EvaluateAdminConfigScript(
97       isPrivileged ? autoconfigSystemSb : autoconfigSb, js_buffer, length,
98       filename, globalContext, callbacks, skipFirstLine);
99 }
100 
EvaluateAdminConfigScript(JS::HandleObject sandbox,const char * js_buffer,size_t length,const char * filename,bool globalContext,bool callbacks,bool skipFirstLine)101 nsresult EvaluateAdminConfigScript(JS::HandleObject sandbox,
102                                    const char* js_buffer, size_t length,
103                                    const char* filename, bool globalContext,
104                                    bool callbacks, bool skipFirstLine) {
105   if (skipFirstLine) {
106     /* In order to protect the privacy of the JavaScript preferences file
107      * from loading by the browser, we make the first line unparseable
108      * by JavaScript. We must skip that line here before executing
109      * the JavaScript code.
110      */
111     unsigned int i = 0;
112     while (i < length) {
113       char c = js_buffer[i++];
114       if (c == '\r') {
115         if (js_buffer[i] == '\n') i++;
116         break;
117       }
118       if (c == '\n') break;
119     }
120 
121     length -= i;
122     js_buffer += i;
123   }
124 
125   // Grab XPConnect.
126   nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
127 
128   AutoJSAPI jsapi;
129   if (!jsapi.Init(sandbox)) {
130     return NS_ERROR_UNEXPECTED;
131   }
132   JSContext* cx = jsapi.cx();
133 
134   nsAutoCString script(js_buffer, length);
135   JS::RootedValue v(cx);
136 
137   nsString convertedScript;
138   bool isUTF8 = IsUtf8(script);
139   if (isUTF8) {
140     convertedScript = NS_ConvertUTF8toUTF16(script);
141   } else {
142     nsContentUtils::ReportToConsoleNonLocalized(
143         NS_LITERAL_STRING(
144             "Your AutoConfig file is ASCII. Please convert it to UTF-8."),
145         nsIScriptError::warningFlag, NS_LITERAL_CSTRING("autoconfig"), nullptr);
146     /* If the length is 0, the conversion failed. Fallback to ASCII */
147     convertedScript = NS_ConvertASCIItoUTF16(script);
148   }
149   {
150     JSAutoRealm ar(cx, autoconfigSystemSb);
151     JS::Rooted<JS::Value> value(cx, JS::BooleanValue(isUTF8));
152     if (!JS_DefineProperty(cx, autoconfigSystemSb, "gIsUTF8", value,
153                            JSPROP_ENUMERATE)) {
154       return NS_ERROR_UNEXPECTED;
155     }
156   }
157   nsresult rv =
158       xpc->EvalInSandboxObject(convertedScript, filename, cx, sandbox, &v);
159   NS_ENSURE_SUCCESS(rv, rv);
160 
161   return NS_OK;
162 }
163