Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * The Components.Sandbox object.
9 : */
10 :
11 : #include "AccessCheck.h"
12 : #include "jsfriendapi.h"
13 : #include "js/Proxy.h"
14 : #include "js/StructuredClone.h"
15 : #include "nsContentUtils.h"
16 : #include "nsGlobalWindow.h"
17 : #include "nsIScriptContext.h"
18 : #include "nsIScriptObjectPrincipal.h"
19 : #include "nsIURI.h"
20 : #include "nsJSUtils.h"
21 : #include "nsNetUtil.h"
22 : #include "NullPrincipal.h"
23 : #include "ExpandedPrincipal.h"
24 : #include "WrapperFactory.h"
25 : #include "xpcprivate.h"
26 : #include "xpc_make_class.h"
27 : #include "XPCWrapper.h"
28 : #include "XrayWrapper.h"
29 : #include "Crypto.h"
30 : #include "mozilla/dom/BindingUtils.h"
31 : #include "mozilla/dom/BlobBinding.h"
32 : #include "mozilla/dom/cache/CacheStorage.h"
33 : #include "mozilla/dom/CSSBinding.h"
34 : #include "mozilla/dom/DirectoryBinding.h"
35 : #include "mozilla/dom/IndexedDatabaseManager.h"
36 : #include "mozilla/dom/Fetch.h"
37 : #include "mozilla/dom/FileBinding.h"
38 : #include "mozilla/dom/MessageChannelBinding.h"
39 : #include "mozilla/dom/MessagePortBinding.h"
40 : #include "mozilla/dom/PromiseBinding.h"
41 : #include "mozilla/dom/RequestBinding.h"
42 : #include "mozilla/dom/ResponseBinding.h"
43 : #ifdef MOZ_WEBRTC
44 : #include "mozilla/dom/RTCIdentityProviderRegistrar.h"
45 : #endif
46 : #include "mozilla/dom/FileReaderBinding.h"
47 : #include "mozilla/dom/ScriptSettings.h"
48 : #include "mozilla/dom/TextDecoderBinding.h"
49 : #include "mozilla/dom/TextEncoderBinding.h"
50 : #include "mozilla/dom/UnionConversions.h"
51 : #include "mozilla/dom/URLBinding.h"
52 : #include "mozilla/dom/URLSearchParamsBinding.h"
53 : #include "mozilla/dom/XMLHttpRequest.h"
54 : #include "mozilla/DeferredFinalize.h"
55 :
56 : using namespace mozilla;
57 : using namespace JS;
58 : using namespace xpc;
59 :
60 : using mozilla::dom::DestroyProtoAndIfaceCache;
61 : using mozilla::dom::IndexedDatabaseManager;
62 :
63 : NS_IMPL_CYCLE_COLLECTION_CLASS(SandboxPrivate)
64 :
65 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate)
66 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
67 0 : tmp->UnlinkHostObjectURIs();
68 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
69 :
70 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate)
71 0 : tmp->TraverseHostObjectURIs(cb);
72 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
73 :
74 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(SandboxPrivate)
75 :
76 2425 : NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
77 2332 : NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
78 2272 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
79 2247 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
80 2247 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
81 2247 : NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
82 2222 : NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
83 631 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
84 576 : NS_INTERFACE_MAP_END
85 :
86 : const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
87 :
88 : class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
89 : public nsIXPCScriptable
90 : {
91 : public:
92 : // Aren't macros nice?
93 : NS_DECL_ISUPPORTS
94 : NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
95 : NS_DECL_NSIXPCSCRIPTABLE
96 :
97 : public:
98 : nsXPCComponents_utils_Sandbox();
99 :
100 : private:
101 : virtual ~nsXPCComponents_utils_Sandbox();
102 :
103 : static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
104 : JSContext* cx, HandleObject obj,
105 : const CallArgs& args, bool* _retval);
106 : };
107 :
108 : already_AddRefed<nsIXPCComponents_utils_Sandbox>
109 2 : xpc::NewSandboxConstructor()
110 : {
111 : nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
112 4 : new nsXPCComponents_utils_Sandbox();
113 4 : return sbConstructor.forget();
114 : }
115 :
116 : static bool
117 0 : SandboxDump(JSContext* cx, unsigned argc, Value* vp)
118 : {
119 0 : CallArgs args = CallArgsFromVp(argc, vp);
120 :
121 0 : if (args.length() == 0)
122 0 : return true;
123 :
124 0 : RootedString str(cx, ToString(cx, args[0]));
125 0 : if (!str)
126 0 : return false;
127 :
128 0 : JSAutoByteString utf8str;
129 0 : char* cstr = utf8str.encodeUtf8(cx, str);
130 0 : if (!cstr)
131 0 : return false;
132 :
133 : #if defined(XP_MACOSX)
134 : // Be nice and convert all \r to \n.
135 : char* c = cstr;
136 : char* cEnd = cstr + strlen(cstr);
137 : while (c < cEnd) {
138 : if (*c == '\r')
139 : *c = '\n';
140 : c++;
141 : }
142 : #endif
143 : #ifdef ANDROID
144 : __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
145 : #endif
146 :
147 0 : fputs(cstr, stdout);
148 0 : fflush(stdout);
149 0 : args.rval().setBoolean(true);
150 0 : return true;
151 : }
152 :
153 : static bool
154 0 : SandboxDebug(JSContext* cx, unsigned argc, Value* vp)
155 : {
156 : #ifdef DEBUG
157 0 : return SandboxDump(cx, argc, vp);
158 : #else
159 : return true;
160 : #endif
161 : }
162 :
163 : static bool
164 0 : SandboxImport(JSContext* cx, unsigned argc, Value* vp)
165 : {
166 0 : CallArgs args = CallArgsFromVp(argc, vp);
167 :
168 0 : if (args.length() < 1 || args[0].isPrimitive()) {
169 0 : XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
170 0 : return false;
171 : }
172 :
173 0 : RootedString funname(cx);
174 0 : if (args.length() > 1) {
175 : // Use the second parameter as the function name.
176 0 : funname = ToString(cx, args[1]);
177 0 : if (!funname)
178 0 : return false;
179 : } else {
180 : // NB: funobj must only be used to get the JSFunction out.
181 0 : RootedObject funobj(cx, &args[0].toObject());
182 0 : if (js::IsProxy(funobj)) {
183 0 : funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
184 : }
185 :
186 0 : JSAutoCompartment ac(cx, funobj);
187 :
188 0 : RootedValue funval(cx, ObjectValue(*funobj));
189 0 : JSFunction* fun = JS_ValueToFunction(cx, funval);
190 0 : if (!fun) {
191 0 : XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
192 0 : return false;
193 : }
194 :
195 : // Use the actual function name as the name.
196 0 : funname = JS_GetFunctionId(fun);
197 0 : if (!funname) {
198 0 : XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
199 0 : return false;
200 : }
201 : }
202 0 : JS_MarkCrossZoneIdValue(cx, StringValue(funname));
203 :
204 0 : RootedId id(cx);
205 0 : if (!JS_StringToId(cx, funname, &id))
206 0 : return false;
207 :
208 : // We need to resolve the this object, because this function is used
209 : // unbound and should still work and act on the original sandbox.
210 0 : RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
211 0 : if (!thisObject) {
212 0 : XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
213 0 : return false;
214 : }
215 0 : if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
216 0 : return false;
217 :
218 0 : args.rval().setUndefined();
219 0 : return true;
220 : }
221 :
222 : static bool
223 0 : SandboxCreateCrypto(JSContext* cx, JS::HandleObject obj)
224 : {
225 0 : MOZ_ASSERT(JS_IsGlobalObject(obj));
226 :
227 0 : nsIGlobalObject* native = xpc::NativeGlobal(obj);
228 0 : MOZ_ASSERT(native);
229 :
230 0 : dom::Crypto* crypto = new dom::Crypto();
231 0 : crypto->Init(native);
232 0 : JS::RootedObject wrapped(cx, crypto->WrapObject(cx, nullptr));
233 0 : return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE);
234 : }
235 :
236 : #ifdef MOZ_WEBRTC
237 : static bool
238 0 : SandboxCreateRTCIdentityProvider(JSContext* cx, JS::HandleObject obj)
239 : {
240 0 : MOZ_ASSERT(JS_IsGlobalObject(obj));
241 :
242 0 : nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::NativeGlobal(obj);
243 0 : MOZ_ASSERT(nativeGlobal);
244 :
245 : dom::RTCIdentityProviderRegistrar* registrar =
246 0 : new dom::RTCIdentityProviderRegistrar(nativeGlobal);
247 0 : JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr));
248 0 : return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped, JSPROP_ENUMERATE);
249 : }
250 : #endif
251 :
252 : static bool
253 1 : SetFetchRequestFromValue(JSContext *cx, RequestOrUSVString& request,
254 : const MutableHandleValue& requestOrUrl)
255 : {
256 1 : RequestOrUSVStringArgument requestHolder(request);
257 1 : bool noMatch = true;
258 1 : if (requestOrUrl.isObject() &&
259 0 : !requestHolder.TrySetToRequest(cx, requestOrUrl, noMatch, false)) {
260 0 : return false;
261 : }
262 2 : if (noMatch &&
263 1 : !requestHolder.TrySetToUSVString(cx, requestOrUrl, noMatch)) {
264 0 : return false;
265 : }
266 1 : if (noMatch) {
267 0 : return false;
268 : }
269 1 : return true;
270 : }
271 :
272 : static bool
273 1 : SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
274 : {
275 1 : if (args.length() < 1) {
276 0 : JS_ReportErrorASCII(cx, "fetch requires at least 1 argument");
277 0 : return false;
278 : }
279 :
280 2 : RequestOrUSVString request;
281 1 : if (!SetFetchRequestFromValue(cx, request, args[0])) {
282 0 : JS_ReportErrorASCII(cx, "fetch requires a string or Request in argument 1");
283 0 : return false;
284 : }
285 2 : RootedDictionary<dom::RequestInit> options(cx);
286 1 : if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
287 : "Argument 2 of fetch", false)) {
288 0 : return false;
289 : }
290 2 : nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(scope);
291 1 : if (!global) {
292 0 : return false;
293 : }
294 1 : dom::CallerType callerType = nsContentUtils::IsSystemCaller(cx) ?
295 1 : dom::CallerType::System : dom::CallerType::NonSystem;
296 2 : ErrorResult rv;
297 : RefPtr<dom::Promise> response =
298 2 : FetchRequest(global, Constify(request), Constify(options),
299 2 : callerType, rv);
300 1 : if (rv.MaybeSetPendingException(cx)) {
301 0 : return false;
302 : }
303 :
304 1 : args.rval().setObject(*response->PromiseObj());
305 1 : return true;
306 : }
307 :
308 1 : static bool SandboxFetchPromise(JSContext* cx, unsigned argc, Value* vp)
309 : {
310 1 : CallArgs args = CallArgsFromVp(argc, vp);
311 2 : RootedObject callee(cx, &args.callee());
312 2 : RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
313 1 : if (SandboxFetch(cx, scope, args)) {
314 1 : return true;
315 : }
316 0 : return ConvertExceptionToPromise(cx, scope, args.rval());
317 : }
318 :
319 :
320 : static bool
321 3 : SandboxCreateFetch(JSContext* cx, HandleObject obj)
322 : {
323 3 : MOZ_ASSERT(JS_IsGlobalObject(obj));
324 :
325 6 : return JS_DefineFunction(cx, obj, "fetch", SandboxFetchPromise, 2, 0) &&
326 6 : dom::RequestBinding::GetConstructorObject(cx) &&
327 9 : dom::ResponseBinding::GetConstructorObject(cx) &&
328 6 : dom::HeadersBinding::GetConstructorObject(cx);
329 : }
330 :
331 : static bool
332 0 : SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp)
333 : {
334 0 : CallArgs args = CallArgsFromVp(argc, vp);
335 0 : if (args.length() < 1) {
336 0 : JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
337 0 : return false;
338 : }
339 0 : if (!args[0].isObject()) {
340 0 : args.rval().setBoolean(false);
341 0 : return true;
342 : }
343 :
344 0 : RootedObject obj(cx, &args[0].toObject());
345 0 : obj = js::CheckedUnwrap(obj);
346 0 : NS_ENSURE_TRUE(obj, false);
347 :
348 0 : args.rval().setBoolean(js::IsScriptedProxy(obj));
349 0 : return true;
350 : }
351 :
352 : /*
353 : * Expected type of the arguments and the return value:
354 : * function exportFunction(function funToExport,
355 : * object targetScope,
356 : * [optional] object options)
357 : */
358 : static bool
359 0 : SandboxExportFunction(JSContext* cx, unsigned argc, Value* vp)
360 : {
361 0 : CallArgs args = CallArgsFromVp(argc, vp);
362 0 : if (args.length() < 2) {
363 0 : JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
364 0 : return false;
365 : }
366 :
367 0 : RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
368 0 : return ExportFunction(cx, args[0], args[1], options, args.rval());
369 : }
370 :
371 : static bool
372 0 : SandboxCreateObjectIn(JSContext* cx, unsigned argc, Value* vp)
373 : {
374 0 : CallArgs args = CallArgsFromVp(argc, vp);
375 0 : if (args.length() < 1) {
376 0 : JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
377 0 : return false;
378 : }
379 :
380 0 : RootedObject optionsObj(cx);
381 0 : bool calledWithOptions = args.length() > 1;
382 0 : if (calledWithOptions) {
383 0 : if (!args[1].isObject()) {
384 0 : JS_ReportErrorASCII(cx, "Expected the 2nd argument (options) to be an object");
385 0 : return false;
386 : }
387 0 : optionsObj = &args[1].toObject();
388 : }
389 :
390 0 : CreateObjectInOptions options(cx, optionsObj);
391 0 : if (calledWithOptions && !options.Parse())
392 0 : return false;
393 :
394 0 : return xpc::CreateObjectIn(cx, args[0], options, args.rval());
395 : }
396 :
397 : static bool
398 0 : SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp)
399 : {
400 0 : CallArgs args = CallArgsFromVp(argc, vp);
401 0 : if (args.length() < 2) {
402 0 : JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
403 0 : return false;
404 : }
405 :
406 0 : RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
407 0 : return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
408 : }
409 :
410 : static void
411 0 : sandbox_finalize(js::FreeOp* fop, JSObject* obj)
412 : {
413 : nsIScriptObjectPrincipal* sop =
414 0 : static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
415 0 : if (!sop) {
416 : // sop can be null if CreateSandboxObject fails in the middle.
417 0 : return;
418 : }
419 :
420 0 : static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject(obj);
421 0 : DestroyProtoAndIfaceCache(obj);
422 0 : DeferredFinalize(sop);
423 : }
424 :
425 : static void
426 0 : sandbox_moved(JSObject* obj, const JSObject* old)
427 : {
428 : // Note that this hook can be called before the private pointer is set. In
429 : // this case the SandboxPrivate will not exist yet, so there is nothing to
430 : // do.
431 : nsIScriptObjectPrincipal* sop =
432 0 : static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
433 0 : if (sop)
434 0 : static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
435 0 : }
436 :
437 : static bool
438 0 : writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
439 : JS::MutableHandleValue vp, JS::ObjectOpResult& result)
440 : {
441 0 : RootedObject proto(cx);
442 0 : if (!JS_GetPrototype(cx, obj, &proto))
443 0 : return false;
444 :
445 0 : RootedValue receiver(cx, ObjectValue(*proto));
446 0 : return JS_ForwardSetPropertyTo(cx, proto, id, vp, receiver, result);
447 : }
448 :
449 : static bool
450 0 : writeToProto_getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
451 : JS::MutableHandleValue vp)
452 : {
453 0 : RootedObject proto(cx);
454 0 : if (!JS_GetPrototype(cx, obj, &proto))
455 0 : return false;
456 :
457 0 : return JS_GetPropertyById(cx, proto, id, vp);
458 : }
459 :
460 : struct AutoSkipPropertyMirroring
461 : {
462 50 : explicit AutoSkipPropertyMirroring(CompartmentPrivate* priv) : priv(priv) {
463 50 : MOZ_ASSERT(!priv->skipWriteToGlobalPrototype);
464 50 : priv->skipWriteToGlobalPrototype = true;
465 50 : }
466 100 : ~AutoSkipPropertyMirroring() {
467 50 : MOZ_ASSERT(priv->skipWriteToGlobalPrototype);
468 50 : priv->skipWriteToGlobalPrototype = false;
469 50 : }
470 :
471 : private:
472 : CompartmentPrivate* priv;
473 : };
474 :
475 : // This hook handles the case when writeToGlobalPrototype is set on the
476 : // sandbox. This flag asks that any properties defined on the sandbox global
477 : // also be defined on the sandbox global's prototype. Whenever one of these
478 : // properties is changed (on either side), the change should be reflected on
479 : // both sides. We use this functionality to create sandboxes that are
480 : // essentially "sub-globals" of another global. This is useful for running
481 : // add-ons in a separate compartment while still giving them access to the
482 : // chrome window.
483 : static bool
484 0 : sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
485 : {
486 0 : CompartmentPrivate* priv = CompartmentPrivate::Get(obj);
487 0 : MOZ_ASSERT(priv->writeToGlobalPrototype);
488 :
489 : // Whenever JS_EnumerateStandardClasses is called, it defines the
490 : // "undefined" property, even if it's already defined. We don't want to do
491 : // anything in that case.
492 0 : if (id == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_UNDEFINED))
493 0 : return true;
494 :
495 : // Avoid recursively triggering sandbox_addProperty in the
496 : // JS_DefinePropertyById call below.
497 0 : if (priv->skipWriteToGlobalPrototype)
498 0 : return true;
499 :
500 0 : AutoSkipPropertyMirroring askip(priv);
501 :
502 0 : RootedObject proto(cx);
503 0 : if (!JS_GetPrototype(cx, obj, &proto))
504 0 : return false;
505 :
506 : // After bug 1015790 is fixed, we should be able to remove this unwrapping.
507 0 : RootedObject unwrappedProto(cx, js::UncheckedUnwrap(proto, /* stopAtWindowProxy = */ false));
508 :
509 0 : Rooted<JS::PropertyDescriptor> pd(cx);
510 0 : if (!JS_GetPropertyDescriptorById(cx, proto, id, &pd))
511 0 : return false;
512 :
513 : // This is a little icky. If the property exists and is not configurable,
514 : // then JS_CopyPropertyFrom will throw an exception when we try to do a
515 : // normal assignment since it will think we're trying to remove the
516 : // non-configurability. So we do JS_SetPropertyById in that case.
517 : //
518 : // However, in the case of |const x = 3|, we get called once for
519 : // JSOP_DEFCONST and once for JSOP_SETCONST. The first one creates the
520 : // property as readonly and configurable. The second one changes the
521 : // attributes to readonly and not configurable. If we use JS_SetPropertyById
522 : // for the second call, it will throw an exception because the property is
523 : // readonly. We have to use JS_CopyPropertyFrom since it ignores the
524 : // readonly attribute (as it calls JSObject::defineProperty). See bug
525 : // 1019181.
526 0 : if (pd.object() && !pd.configurable()) {
527 0 : if (!JS_SetPropertyById(cx, proto, id, v))
528 0 : return false;
529 : } else {
530 0 : if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj,
531 : MakeNonConfigurableIntoConfigurable))
532 0 : return false;
533 : }
534 :
535 0 : if (!JS_GetPropertyDescriptorById(cx, obj, id, &pd))
536 0 : return false;
537 0 : unsigned attrs = pd.attributes() & ~(JSPROP_GETTER | JSPROP_SETTER);
538 0 : if (!JS_DefinePropertyById(cx, obj, id, v,
539 : attrs | JSPROP_PROPOP_ACCESSORS | JSPROP_REDEFINE_NONCONFIGURABLE,
540 : JS_PROPERTYOP_GETTER(writeToProto_getProperty),
541 : JS_PROPERTYOP_SETTER(writeToProto_setProperty)))
542 0 : return false;
543 :
544 0 : return true;
545 : }
546 :
547 : #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
548 :
549 : static const js::ClassOps SandboxClassOps = {
550 : nullptr, nullptr, nullptr, nullptr,
551 : nullptr, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass,
552 : JS_MayResolveStandardClass,
553 : sandbox_finalize,
554 : nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
555 : };
556 :
557 : static const js::ClassExtension SandboxClassExtension = {
558 : nullptr, /* weakmapKeyDelegateOp */
559 : sandbox_moved /* objectMovedOp */
560 : };
561 :
562 : static const js::Class SandboxClass = {
563 : "Sandbox",
564 : XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
565 : JSCLASS_FOREGROUND_FINALIZE,
566 : &SandboxClassOps,
567 : JS_NULL_CLASS_SPEC,
568 : &SandboxClassExtension,
569 : JS_NULL_OBJECT_OPS
570 : };
571 :
572 : // Note to whomever comes here to remove addProperty hooks: billm has promised
573 : // to do the work for this class.
574 : static const js::ClassOps SandboxWriteToProtoClassOps = {
575 : sandbox_addProperty, nullptr, nullptr, nullptr,
576 : nullptr, JS_NewEnumerateStandardClasses, JS_ResolveStandardClass,
577 : JS_MayResolveStandardClass,
578 : sandbox_finalize,
579 : nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
580 : };
581 :
582 : static const js::Class SandboxWriteToProtoClass = {
583 : "Sandbox",
584 : XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
585 : JSCLASS_FOREGROUND_FINALIZE,
586 : &SandboxWriteToProtoClassOps,
587 : JS_NULL_CLASS_SPEC,
588 : &SandboxClassExtension,
589 : JS_NULL_OBJECT_OPS
590 : };
591 :
592 : static const JSFunctionSpec SandboxFunctions[] = {
593 : JS_FS("dump", SandboxDump, 1,0),
594 : JS_FS("debug", SandboxDebug, 1,0),
595 : JS_FS("importFunction", SandboxImport, 1,0),
596 : JS_FS_END
597 : };
598 :
599 : bool
600 1471 : xpc::IsSandbox(JSObject* obj)
601 : {
602 1471 : const js::Class* clasp = js::GetObjectClass(obj);
603 1471 : return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
604 : }
605 :
606 : /***************************************************************************/
607 2 : nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
608 : {
609 2 : }
610 :
611 0 : nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
612 : {
613 0 : }
614 :
615 89 : NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
616 89 : NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
617 82 : NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
618 57 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
619 41 : NS_INTERFACE_MAP_END
620 :
621 88 : NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
622 77 : NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
623 :
624 : // We use the nsIXPScriptable macros to generate lots of stuff for us.
625 : #define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox
626 : #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
627 : #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_CALL | \
628 : XPC_SCRIPTABLE_WANT_CONSTRUCT)
629 : #include "xpc_map_end.h" /* This #undef's the above. */
630 :
631 : const xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
632 :
633 : bool
634 0 : xpc::IsSandboxPrototypeProxy(JSObject* obj)
635 : {
636 0 : return js::IsProxy(obj) &&
637 0 : js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
638 : }
639 :
640 : bool
641 0 : xpc::SandboxCallableProxyHandler::call(JSContext* cx, JS::Handle<JSObject*> proxy,
642 : const JS::CallArgs& args) const
643 : {
644 : // We forward the call to our underlying callable.
645 :
646 : // Get our SandboxProxyHandler proxy.
647 0 : RootedObject sandboxProxy(cx, getSandboxProxy(proxy));
648 0 : MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
649 : js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
650 :
651 : // The global of the sandboxProxy is the sandbox global, and the
652 : // target object is the original proto.
653 : RootedObject sandboxGlobal(cx,
654 0 : js::GetGlobalForObjectCrossCompartment(sandboxProxy));
655 0 : MOZ_ASSERT(IsSandbox(sandboxGlobal));
656 :
657 : // If our this object is the sandbox global, we call with this set to the
658 : // original proto instead.
659 : //
660 : // There are two different ways we can compute |this|. If we use
661 : // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
662 : // caller, which may be undefined if a global function was invoked without
663 : // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
664 : // in |vp| will be coerced to the global, which is not the correct
665 : // behavior in ES5 strict mode. And we have no way to compute strictness
666 : // here.
667 : //
668 : // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
669 : // explicit, we can remap it appropriately. If it was implicit, then we
670 : // leave it as undefined, and let the callee sort it out. Since the callee
671 : // is generally in the same compartment as its global (eg the Window's
672 : // compartment, not the Sandbox's), the callee will generally compute the
673 : // correct |this|.
674 : //
675 : // However, this breaks down in the Xray case. If the sandboxPrototype
676 : // is an Xray wrapper, then we'll end up reifying the native methods in
677 : // the Sandbox's scope, which means that they'll compute |this| to be the
678 : // Sandbox, breaking old-style XPC_WN_CallMethod methods.
679 : //
680 : // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
681 : // DOM interface, which means that we don't care about script-enacted
682 : // strictness in the prototype's home compartment. Indeed, since DOM
683 : // methods are always non-strict, we can just assume non-strict semantics
684 : // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
685 : // remap |this|.
686 0 : bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
687 0 : RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
688 0 : if (thisVal == ObjectValue(*sandboxGlobal)) {
689 0 : thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
690 : }
691 :
692 0 : RootedValue func(cx, js::GetProxyPrivate(proxy));
693 0 : return JS::Call(cx, thisVal, func, args, args.rval());
694 : }
695 :
696 : const xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
697 :
698 : /*
699 : * Wrap a callable such that if we're called with oldThisObj as the
700 : * "this" we will instead call it with newThisObj as the this.
701 : */
702 : static JSObject*
703 0 : WrapCallable(JSContext* cx, HandleObject callable, HandleObject sandboxProtoProxy)
704 : {
705 0 : MOZ_ASSERT(JS::IsCallable(callable));
706 : // Our proxy is wrapping the callable. So we need to use the
707 : // callable as the private. We put the given sandboxProtoProxy in
708 : // an extra slot, and our call() hook depends on that.
709 0 : MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
710 : js::GetProxyHandler(sandboxProtoProxy) ==
711 : &xpc::sandboxProxyHandler);
712 :
713 0 : RootedValue priv(cx, ObjectValue(*callable));
714 : // We want to claim to have the same proto as our wrapped callable, so set
715 : // ourselves up with a lazy proto.
716 0 : js::ProxyOptions options;
717 0 : options.setLazyProto(true);
718 0 : JSObject* obj = js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
719 0 : priv, nullptr, options);
720 0 : if (obj) {
721 : js::SetProxyReservedSlot(obj, SandboxCallableProxyHandler::SandboxProxySlot,
722 0 : ObjectValue(*sandboxProtoProxy));
723 : }
724 :
725 0 : return obj;
726 : }
727 :
728 : template<typename Op>
729 0 : bool WrapAccessorFunction(JSContext* cx, Op& op, PropertyDescriptor* desc,
730 : unsigned attrFlag, HandleObject sandboxProtoProxy)
731 : {
732 0 : if (!op) {
733 0 : return true;
734 : }
735 :
736 0 : if (!(desc->attrs & attrFlag)) {
737 0 : XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
738 0 : return false;
739 : }
740 :
741 0 : RootedObject func(cx, JS_FUNC_TO_DATA_PTR(JSObject*, op));
742 0 : func = WrapCallable(cx, func, sandboxProtoProxy);
743 0 : if (!func)
744 0 : return false;
745 0 : op = JS_DATA_TO_FUNC_PTR(Op, func.get());
746 0 : return true;
747 : }
748 :
749 : bool
750 0 : xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext* cx,
751 : JS::Handle<JSObject*> proxy,
752 : JS::Handle<jsid> id,
753 : JS::MutableHandle<PropertyDescriptor> desc) const
754 : {
755 0 : JS::RootedObject obj(cx, wrappedObject(proxy));
756 :
757 0 : MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
758 0 : if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
759 0 : return false;
760 :
761 0 : if (!desc.object())
762 0 : return true; // No property, nothing to do
763 :
764 : // Now fix up the getter/setter/value as needed to be bound to desc->obj.
765 0 : if (!WrapAccessorFunction(cx, desc.getter(), desc.address(),
766 : JSPROP_GETTER, proxy))
767 0 : return false;
768 0 : if (!WrapAccessorFunction(cx, desc.setter(), desc.address(),
769 : JSPROP_SETTER, proxy))
770 0 : return false;
771 0 : if (desc.value().isObject()) {
772 0 : RootedObject val (cx, &desc.value().toObject());
773 0 : if (JS::IsCallable(val)) {
774 0 : val = WrapCallable(cx, val, proxy);
775 0 : if (!val)
776 0 : return false;
777 0 : desc.value().setObject(*val);
778 : }
779 : }
780 :
781 0 : return true;
782 : }
783 :
784 : bool
785 0 : xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
786 : JS::Handle<JSObject*> proxy,
787 : JS::Handle<jsid> id,
788 : JS::MutableHandle<PropertyDescriptor> desc)
789 : const
790 : {
791 0 : if (!getPropertyDescriptor(cx, proxy, id, desc))
792 0 : return false;
793 :
794 0 : if (desc.object() != wrappedObject(proxy))
795 0 : desc.object().set(nullptr);
796 :
797 0 : return true;
798 : }
799 :
800 : /*
801 : * Reuse the BaseProxyHandler versions of the derived traps that are implemented
802 : * in terms of the fundamental traps.
803 : */
804 :
805 : bool
806 0 : xpc::SandboxProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy,
807 : JS::Handle<jsid> id, bool* bp) const
808 : {
809 : // This uses getPropertyDescriptor for backward compatibility with
810 : // the old BaseProxyHandler::has implementation.
811 0 : Rooted<PropertyDescriptor> desc(cx);
812 0 : if (!getPropertyDescriptor(cx, proxy, id, &desc))
813 0 : return false;
814 :
815 0 : *bp = !!desc.object();
816 0 : return true;
817 : }
818 : bool
819 0 : xpc::SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
820 : JS::Handle<jsid> id, bool* bp) const
821 : {
822 0 : return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
823 : }
824 :
825 : bool
826 0 : xpc::SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
827 : JS::Handle<JS::Value> receiver,
828 : JS::Handle<jsid> id,
829 : JS::MutableHandle<Value> vp) const
830 : {
831 : // This uses getPropertyDescriptor for backward compatibility with
832 : // the old BaseProxyHandler::get implementation.
833 0 : Rooted<PropertyDescriptor> desc(cx);
834 0 : if (!getPropertyDescriptor(cx, proxy, id, &desc))
835 0 : return false;
836 0 : desc.assertCompleteIfFound();
837 :
838 0 : if (!desc.object()) {
839 0 : vp.setUndefined();
840 0 : return true;
841 : }
842 :
843 : // Everything after here follows [[Get]] for ordinary objects.
844 0 : if (desc.isDataDescriptor()) {
845 0 : vp.set(desc.value());
846 0 : return true;
847 : }
848 :
849 0 : MOZ_ASSERT(desc.isAccessorDescriptor());
850 0 : RootedObject getter(cx, desc.getterObject());
851 :
852 0 : if (!getter) {
853 0 : vp.setUndefined();
854 0 : return true;
855 : }
856 :
857 0 : return Call(cx, receiver, getter, HandleValueArray::empty(), vp);
858 : }
859 :
860 : bool
861 0 : xpc::SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
862 : JS::Handle<jsid> id,
863 : JS::Handle<Value> v,
864 : JS::Handle<Value> receiver,
865 : JS::ObjectOpResult& result) const
866 : {
867 0 : return BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
868 : }
869 :
870 : bool
871 0 : xpc::SandboxProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
872 : JS::Handle<JSObject*> proxy,
873 : AutoIdVector& props) const
874 : {
875 0 : return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
876 : }
877 :
878 : JSObject*
879 0 : xpc::SandboxProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy) const
880 : {
881 0 : return BaseProxyHandler::enumerate(cx, proxy);
882 : }
883 :
884 : bool
885 36 : xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
886 : {
887 : uint32_t length;
888 36 : bool ok = JS_GetArrayLength(cx, obj, &length);
889 36 : NS_ENSURE_TRUE(ok, false);
890 71 : for (uint32_t i = 0; i < length; i++) {
891 70 : RootedValue nameValue(cx);
892 35 : ok = JS_GetElement(cx, obj, i, &nameValue);
893 35 : NS_ENSURE_TRUE(ok, false);
894 35 : if (!nameValue.isString()) {
895 0 : JS_ReportErrorASCII(cx, "Property names must be strings");
896 0 : return false;
897 : }
898 70 : RootedString nameStr(cx, nameValue.toString());
899 70 : JSAutoByteString name;
900 35 : if (!name.encodeUtf8(cx, nameStr))
901 0 : return false;
902 35 : if (!strcmp(name.ptr(), "CSS")) {
903 0 : CSS = true;
904 35 : } else if (!strcmp(name.ptr(), "indexedDB")) {
905 14 : indexedDB = true;
906 21 : } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
907 4 : XMLHttpRequest = true;
908 17 : } else if (!strcmp(name.ptr(), "TextEncoder")) {
909 0 : TextEncoder = true;
910 17 : } else if (!strcmp(name.ptr(), "TextDecoder")) {
911 0 : TextDecoder = true;
912 17 : } else if (!strcmp(name.ptr(), "URL")) {
913 9 : URL = true;
914 8 : } else if (!strcmp(name.ptr(), "URLSearchParams")) {
915 2 : URLSearchParams = true;
916 6 : } else if (!strcmp(name.ptr(), "atob")) {
917 0 : atob = true;
918 6 : } else if (!strcmp(name.ptr(), "btoa")) {
919 1 : btoa = true;
920 5 : } else if (!strcmp(name.ptr(), "Blob")) {
921 0 : Blob = true;
922 5 : } else if (!strcmp(name.ptr(), "Directory")) {
923 0 : Directory = true;
924 5 : } else if (!strcmp(name.ptr(), "File")) {
925 1 : File = true;
926 4 : } else if (!strcmp(name.ptr(), "crypto")) {
927 0 : crypto = true;
928 : #ifdef MOZ_WEBRTC
929 4 : } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
930 0 : rtcIdentityProvider = true;
931 : #endif
932 4 : } else if (!strcmp(name.ptr(), "fetch")) {
933 3 : fetch = true;
934 1 : } else if (!strcmp(name.ptr(), "caches")) {
935 0 : caches = true;
936 1 : } else if (!strcmp(name.ptr(), "FileReader")) {
937 1 : fileReader = true;
938 0 : } else if (!strcmp(name.ptr(), "MessageChannel")) {
939 0 : messageChannel = true;
940 : } else {
941 0 : JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
942 0 : return false;
943 : }
944 : }
945 36 : return true;
946 : }
947 :
948 : bool
949 45 : xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
950 : {
951 45 : MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
952 : // Properties will be exposed to System automatically but not to Sandboxes
953 : // if |[Exposed=System]| is specified.
954 : // This function holds common properties not exposed automatically but able
955 : // to be requested either in |Cu.importGlobalProperties| or
956 : // |wantGlobalProperties| of a sandbox.
957 45 : if (CSS && !dom::CSSBinding::GetConstructorObject(cx))
958 0 : return false;
959 :
960 49 : if (XMLHttpRequest &&
961 4 : !dom::XMLHttpRequestBinding::GetConstructorObject(cx))
962 0 : return false;
963 :
964 45 : if (TextEncoder &&
965 0 : !dom::TextEncoderBinding::GetConstructorObject(cx))
966 0 : return false;
967 :
968 45 : if (TextDecoder &&
969 0 : !dom::TextDecoderBinding::GetConstructorObject(cx))
970 0 : return false;
971 :
972 54 : if (URL &&
973 9 : !dom::URLBinding::GetConstructorObject(cx))
974 0 : return false;
975 :
976 47 : if (URLSearchParams &&
977 2 : !dom::URLSearchParamsBinding::GetConstructorObject(cx))
978 0 : return false;
979 :
980 45 : if (atob &&
981 0 : !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
982 0 : return false;
983 :
984 46 : if (btoa &&
985 1 : !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
986 0 : return false;
987 :
988 45 : if (Blob &&
989 0 : !dom::BlobBinding::GetConstructorObject(cx))
990 0 : return false;
991 :
992 45 : if (Directory &&
993 0 : !dom::DirectoryBinding::GetConstructorObject(cx))
994 0 : return false;
995 :
996 46 : if (File &&
997 1 : !dom::FileBinding::GetConstructorObject(cx))
998 0 : return false;
999 :
1000 45 : if (crypto && !SandboxCreateCrypto(cx, obj))
1001 0 : return false;
1002 :
1003 : #ifdef MOZ_WEBRTC
1004 45 : if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj))
1005 0 : return false;
1006 : #endif
1007 :
1008 45 : if (fetch && !SandboxCreateFetch(cx, obj))
1009 0 : return false;
1010 :
1011 45 : if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj))
1012 0 : return false;
1013 :
1014 45 : if (fileReader && !dom::FileReaderBinding::GetConstructorObject(cx))
1015 0 : return false;
1016 :
1017 45 : if (messageChannel &&
1018 0 : (!dom::MessageChannelBinding::GetConstructorObject(cx) ||
1019 0 : !dom::MessagePortBinding::GetConstructorObject(cx)))
1020 0 : return false;
1021 :
1022 45 : return true;
1023 : }
1024 :
1025 : bool
1026 20 : xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx, JS::HandleObject obj)
1027 : {
1028 20 : if (indexedDB &&
1029 0 : !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
1030 0 : return false;
1031 :
1032 20 : return Define(cx, obj);
1033 : }
1034 :
1035 : bool
1036 25 : xpc::GlobalProperties::DefineInSandbox(JSContext* cx, JS::HandleObject obj)
1037 : {
1038 25 : MOZ_ASSERT(IsSandbox(obj));
1039 25 : MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
1040 :
1041 39 : if (indexedDB &&
1042 28 : !(IndexedDatabaseManager::ResolveSandboxBinding(cx) &&
1043 14 : IndexedDatabaseManager::DefineIndexedDB(cx, obj)))
1044 0 : return false;
1045 :
1046 25 : return Define(cx, obj);
1047 : }
1048 :
1049 : nsresult
1050 25 : xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prinOrSop,
1051 : SandboxOptions& options)
1052 : {
1053 : // Create the sandbox global object
1054 50 : nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
1055 25 : if (!principal) {
1056 12 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
1057 6 : if (sop) {
1058 0 : principal = sop->GetPrincipal();
1059 : } else {
1060 12 : RefPtr<NullPrincipal> nullPrin = NullPrincipal::Create();
1061 6 : principal = nullPrin;
1062 : }
1063 : }
1064 25 : MOZ_ASSERT(principal);
1065 :
1066 25 : JS::CompartmentOptions compartmentOptions;
1067 :
1068 25 : auto& creationOptions = compartmentOptions.creationOptions();
1069 :
1070 : // XXXjwatt: Consider whether/when sandboxes should be able to see
1071 : // [SecureContext] API (bug 1273687). In that case we'd call
1072 : // creationOptions.setSecureContext(true).
1073 :
1074 25 : if (xpc::SharedMemoryEnabled())
1075 25 : creationOptions.setSharedMemoryAndAtomicsEnabled(true);
1076 :
1077 25 : if (options.sameZoneAs)
1078 0 : creationOptions.setExistingZone(js::UncheckedUnwrap(options.sameZoneAs));
1079 25 : else if (options.freshZone)
1080 0 : creationOptions.setNewZoneInSystemZoneGroup();
1081 : else
1082 25 : creationOptions.setSystemZone();
1083 :
1084 25 : creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
1085 25 : .setTrace(TraceXPCGlobal);
1086 :
1087 : // Try to figure out any addon this sandbox should be associated with.
1088 : // The addon could have been passed in directly, as part of the metadata,
1089 : // or by being constructed from an addon's code.
1090 25 : JSAddonId* addonId = nullptr;
1091 25 : if (options.addonId) {
1092 14 : addonId = JS::NewAddonId(cx, options.addonId);
1093 14 : NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
1094 11 : } else if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
1095 2 : if (JSAddonId* id = JS::AddonIdOfObject(obj))
1096 0 : addonId = id;
1097 : }
1098 :
1099 25 : creationOptions.setAddonId(addonId);
1100 :
1101 25 : compartmentOptions.behaviors().setDiscardSource(options.discardSource);
1102 :
1103 25 : const js::Class* clasp = options.writeToGlobalPrototype
1104 25 : ? &SandboxWriteToProtoClass
1105 25 : : &SandboxClass;
1106 :
1107 50 : RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
1108 50 : principal, compartmentOptions));
1109 25 : if (!sandbox)
1110 0 : return NS_ERROR_FAILURE;
1111 :
1112 25 : CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
1113 25 : priv->allowWaivers = options.allowWaivers;
1114 25 : priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
1115 25 : priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
1116 25 : priv->waiveInterposition = options.waiveInterposition;
1117 :
1118 : // Set up the wantXrays flag, which indicates whether xrays are desired even
1119 : // for same-origin access.
1120 : //
1121 : // This flag has historically been ignored for chrome sandboxes due to
1122 : // quirks in the wrapping implementation that have now been removed. Indeed,
1123 : // same-origin Xrays for chrome->chrome access seems a bit superfluous.
1124 : // Arguably we should just flip the default for chrome and still honor the
1125 : // flag, but such a change would break code in subtle ways for minimal
1126 : // benefit. So we just switch it off here.
1127 25 : priv->wantXrays =
1128 25 : AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
1129 :
1130 : {
1131 50 : JSAutoCompartment ac(cx, sandbox);
1132 :
1133 : nsCOMPtr<nsIScriptObjectPrincipal> sbp =
1134 75 : new SandboxPrivate(principal, sandbox);
1135 :
1136 : // Pass on ownership of sbp to |sandbox|.
1137 25 : JS_SetPrivate(sandbox, sbp.forget().take());
1138 :
1139 : {
1140 : // Don't try to mirror standard class properties, if we're using a
1141 : // mirroring sandbox. (This is meaningless for non-mirroring
1142 : // sandboxes.)
1143 50 : AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
1144 :
1145 : // Ensure |Object.prototype| is instantiated before prototype-
1146 : // splicing below. For write-to-global-prototype behavior, extend
1147 : // this to all builtin properties.
1148 25 : if (options.writeToGlobalPrototype) {
1149 0 : if (!JS_EnumerateStandardClasses(cx, sandbox))
1150 0 : return NS_ERROR_XPC_UNEXPECTED;
1151 : } else {
1152 25 : if (!JS_GetObjectPrototype(cx, sandbox))
1153 0 : return NS_ERROR_XPC_UNEXPECTED;
1154 : }
1155 : }
1156 :
1157 25 : if (options.proto) {
1158 1 : bool ok = JS_WrapObject(cx, &options.proto);
1159 1 : if (!ok)
1160 0 : return NS_ERROR_XPC_UNEXPECTED;
1161 :
1162 : // Now check what sort of thing we've got in |proto|, and figure out
1163 : // if we need a SandboxProxyHandler.
1164 : //
1165 : // Note that, in the case of a window, we can't require that the
1166 : // Sandbox subsumes the prototype, because we have to hold our
1167 : // reference to it via an outer window, and the window may navigate
1168 : // at any time. So we have to handle that case separately.
1169 1 : bool useSandboxProxy = !!WindowOrNull(js::UncheckedUnwrap(options.proto, false));
1170 1 : if (!useSandboxProxy) {
1171 1 : JSObject* unwrappedProto = js::CheckedUnwrap(options.proto, false);
1172 1 : if (!unwrappedProto) {
1173 0 : JS_ReportErrorASCII(cx, "Sandbox must subsume sandboxPrototype");
1174 0 : return NS_ERROR_INVALID_ARG;
1175 : }
1176 1 : const js::Class* unwrappedClass = js::GetObjectClass(unwrappedProto);
1177 2 : useSandboxProxy = IS_WN_CLASS(unwrappedClass) ||
1178 1 : mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass));
1179 : }
1180 :
1181 1 : if (useSandboxProxy) {
1182 : // Wrap it up in a proxy that will do the right thing in terms
1183 : // of this-binding for methods.
1184 0 : RootedValue priv(cx, ObjectValue(*options.proto));
1185 0 : options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
1186 0 : priv, nullptr);
1187 0 : if (!options.proto)
1188 0 : return NS_ERROR_OUT_OF_MEMORY;
1189 : }
1190 :
1191 1 : ok = JS_SplicePrototype(cx, sandbox, options.proto);
1192 1 : if (!ok)
1193 0 : return NS_ERROR_XPC_UNEXPECTED;
1194 : }
1195 :
1196 : // Don't try to mirror the properties that are set below.
1197 50 : AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
1198 :
1199 31 : bool allowComponents = principal == nsXPConnect::SystemPrincipal() ||
1200 31 : nsContentUtils::IsExpandedPrincipal(principal);
1201 40 : if (options.wantComponents && allowComponents &&
1202 15 : !ObjectScope(sandbox)->AttachComponentsObject(cx))
1203 0 : return NS_ERROR_XPC_UNEXPECTED;
1204 :
1205 25 : if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
1206 0 : return NS_ERROR_XPC_UNEXPECTED;
1207 :
1208 25 : if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
1209 0 : return NS_ERROR_XPC_UNEXPECTED;
1210 :
1211 75 : if (options.wantExportHelpers &&
1212 25 : (!JS_DefineFunction(cx, sandbox, "exportFunction", SandboxExportFunction, 3, 0) ||
1213 25 : !JS_DefineFunction(cx, sandbox, "createObjectIn", SandboxCreateObjectIn, 2, 0) ||
1214 25 : !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) ||
1215 25 : !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0)))
1216 0 : return NS_ERROR_XPC_UNEXPECTED;
1217 :
1218 25 : if (!options.globalProperties.DefineInSandbox(cx, sandbox))
1219 0 : return NS_ERROR_XPC_UNEXPECTED;
1220 : }
1221 :
1222 : // We handle the case where the context isn't in a compartment for the
1223 : // benefit of InitSingletonScopes.
1224 25 : vp.setObject(*sandbox);
1225 25 : if (js::GetContextCompartment(cx) && !JS_WrapValue(cx, vp))
1226 0 : return NS_ERROR_UNEXPECTED;
1227 :
1228 : // Set the location information for the new global, so that tools like
1229 : // about:memory may use that information
1230 25 : xpc::SetLocationForGlobal(sandbox, options.sandboxName);
1231 :
1232 25 : xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
1233 :
1234 50 : JSAutoCompartment ac(cx, sandbox);
1235 25 : JS_FireOnNewGlobalObject(cx, sandbox);
1236 :
1237 25 : return NS_OK;
1238 : }
1239 :
1240 : NS_IMETHODIMP
1241 2 : nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
1242 : JSObject* objArg, const CallArgs& args, bool* _retval)
1243 : {
1244 4 : RootedObject obj(cx, objArg);
1245 4 : return CallOrConstruct(wrapper, cx, obj, args, _retval);
1246 : }
1247 :
1248 : NS_IMETHODIMP
1249 14 : nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
1250 : JSObject* objArg, const CallArgs& args, bool* _retval)
1251 : {
1252 28 : RootedObject obj(cx, objArg);
1253 28 : return CallOrConstruct(wrapper, cx, obj, args, _retval);
1254 : }
1255 :
1256 : /*
1257 : * For sandbox constructor the first argument can be a URI string in which case
1258 : * we use the related Codebase Principal for the sandbox.
1259 : */
1260 : bool
1261 0 : ParsePrincipal(JSContext* cx, HandleString codebase, const OriginAttributes& aAttrs,
1262 : nsIPrincipal** principal)
1263 : {
1264 0 : MOZ_ASSERT(principal);
1265 0 : MOZ_ASSERT(codebase);
1266 0 : nsCOMPtr<nsIURI> uri;
1267 0 : nsAutoJSString codebaseStr;
1268 0 : NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
1269 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
1270 0 : if (NS_FAILED(rv)) {
1271 0 : JS_ReportErrorASCII(cx, "Creating URI from string failed");
1272 0 : return false;
1273 : }
1274 :
1275 : // We could allow passing in the app-id and browser-element info to the
1276 : // sandbox constructor. But creating a sandbox based on a string is a
1277 : // deprecated API so no need to add features to it.
1278 : nsCOMPtr<nsIPrincipal> prin =
1279 0 : BasePrincipal::CreateCodebasePrincipal(uri, aAttrs);
1280 0 : prin.forget(principal);
1281 :
1282 0 : if (!*principal) {
1283 0 : JS_ReportErrorASCII(cx, "Creating Principal from URI failed");
1284 0 : return false;
1285 : }
1286 0 : return true;
1287 : }
1288 :
1289 : /*
1290 : * For sandbox constructor the first argument can be a principal object or
1291 : * a script object principal (Document, Window).
1292 : */
1293 : static bool
1294 16 : GetPrincipalOrSOP(JSContext* cx, HandleObject from, nsISupports** out)
1295 : {
1296 16 : MOZ_ASSERT(out);
1297 16 : *out = nullptr;
1298 :
1299 32 : nsCOMPtr<nsISupports> native = xpc::UnwrapReflectorToISupports(from);
1300 :
1301 32 : if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
1302 0 : sop.forget(out);
1303 0 : return true;
1304 : }
1305 :
1306 32 : nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
1307 16 : principal.forget(out);
1308 16 : NS_ENSURE_TRUE(*out, false);
1309 :
1310 16 : return true;
1311 : }
1312 :
1313 : /*
1314 : * The first parameter of the sandbox constructor might be an array of principals, either in string
1315 : * format or actual objects (see GetPrincipalOrSOP)
1316 : */
1317 : static bool
1318 0 : GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj,
1319 : const SandboxOptions& options, nsIExpandedPrincipal** out)
1320 : {
1321 0 : MOZ_ASSERT(out);
1322 : uint32_t length;
1323 :
1324 0 : if (!JS_GetArrayLength(cx, arrayObj, &length))
1325 0 : return false;
1326 0 : if (!length) {
1327 : // We need a whitelist of principals or uri strings to create an
1328 : // expanded principal, if we got an empty array or something else
1329 : // report error.
1330 0 : JS_ReportErrorASCII(cx, "Expected an array of URI strings");
1331 0 : return false;
1332 : }
1333 :
1334 0 : nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
1335 0 : allowedDomains.SetLength(length);
1336 :
1337 : // If an originAttributes option has been specified, we will use that as the
1338 : // OriginAttribute of all of the string arguments passed to this function.
1339 : // Otherwise, we will use the OriginAttributes of a principal or SOP object
1340 : // in the array, if any. If no such object is present, and all we have are
1341 : // strings, then we will use a default OriginAttribute.
1342 : // Otherwise, we will use the origin attributes of the passed object(s). If
1343 : // more than one object is specified, we ensure that the OAs match.
1344 0 : Maybe<OriginAttributes> attrs;
1345 0 : if (options.originAttributes) {
1346 0 : attrs.emplace();
1347 0 : JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
1348 0 : if (!attrs->Init(cx, val)) {
1349 : // The originAttributes option, if specified, must be valid!
1350 0 : JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
1351 0 : return false;
1352 : }
1353 : }
1354 :
1355 : // Now we go over the array in two passes. In the first pass, we ignore
1356 : // strings, and only process objects. Assuming that no originAttributes
1357 : // option has been passed, if we encounter a principal or SOP object, we
1358 : // grab its OA and save it if it's the first OA encountered, otherwise
1359 : // check to make sure that it is the same as the OA found before.
1360 : // In the second pass, we ignore objects, and use the OA found in pass 0
1361 : // (or the previously computed OA if we have obtained it from the options)
1362 : // to construct codebase principals.
1363 : //
1364 : // The effective OA selected above will also be set as the OA of the
1365 : // expanded principal object.
1366 :
1367 : // First pass:
1368 0 : for (uint32_t i = 0; i < length; ++i) {
1369 0 : RootedValue allowed(cx);
1370 0 : if (!JS_GetElement(cx, arrayObj, i, &allowed))
1371 0 : return false;
1372 :
1373 : nsresult rv;
1374 0 : nsCOMPtr<nsIPrincipal> principal;
1375 0 : if (allowed.isObject()) {
1376 : // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
1377 0 : nsCOMPtr<nsISupports> prinOrSop;
1378 0 : RootedObject obj(cx, &allowed.toObject());
1379 0 : if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
1380 0 : return false;
1381 :
1382 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
1383 0 : principal = do_QueryInterface(prinOrSop);
1384 0 : if (sop)
1385 0 : principal = sop->GetPrincipal();
1386 0 : NS_ENSURE_TRUE(principal, false);
1387 :
1388 0 : if (!options.originAttributes) {
1389 : const OriginAttributes prinAttrs =
1390 0 : principal->OriginAttributesRef();
1391 0 : if (attrs.isNothing()) {
1392 0 : attrs.emplace(prinAttrs);
1393 0 : } else if (prinAttrs != attrs.ref()) {
1394 : // If attrs is from a previously encountered principal in the
1395 : // array, we need to ensure that it matches the OA of the
1396 : // principal we have here.
1397 : // If attrs comes from OriginAttributes, we don't need
1398 : // this check.
1399 0 : return false;
1400 : }
1401 : }
1402 :
1403 : // We do not allow ExpandedPrincipals to contain any system principals.
1404 : bool isSystem;
1405 0 : rv = nsXPConnect::SecurityManager()->IsSystemPrincipal(principal, &isSystem);
1406 0 : NS_ENSURE_SUCCESS(rv, false);
1407 0 : if (isSystem) {
1408 0 : JS_ReportErrorASCII(cx, "System principal is not allowed in an expanded principal");
1409 0 : return false;
1410 : }
1411 0 : allowedDomains[i] = principal;
1412 0 : } else if (allowed.isString()) {
1413 : // Skip any string arguments - we handle them in the next pass.
1414 : } else {
1415 : // Don't know what this is.
1416 0 : return false;
1417 : }
1418 : }
1419 :
1420 0 : if (attrs.isNothing()) {
1421 : // If no OriginAttributes was found in the first pass, fall back to a default one.
1422 0 : attrs.emplace();
1423 : }
1424 :
1425 : // Second pass:
1426 0 : for (uint32_t i = 0; i < length; ++i) {
1427 0 : RootedValue allowed(cx);
1428 0 : if (!JS_GetElement(cx, arrayObj, i, &allowed))
1429 0 : return false;
1430 :
1431 0 : nsCOMPtr<nsIPrincipal> principal;
1432 0 : if (allowed.isString()) {
1433 : // In case of string let's try to fetch a codebase principal from it.
1434 0 : RootedString str(cx, allowed.toString());
1435 :
1436 : // attrs here is either a default OriginAttributes in case the
1437 : // originAttributes option isn't specified, and no object in the array
1438 : // provides a principal. Otherwise it's either the forced principal, or
1439 : // the principal found before, so we can use it here.
1440 0 : if (!ParsePrincipal(cx, str, attrs.ref(), getter_AddRefs(principal)))
1441 0 : return false;
1442 0 : NS_ENSURE_TRUE(principal, false);
1443 0 : allowedDomains[i] = principal;
1444 : } else {
1445 0 : MOZ_ASSERT(allowed.isObject());
1446 : }
1447 : }
1448 :
1449 : RefPtr<ExpandedPrincipal> result =
1450 0 : ExpandedPrincipal::Create(allowedDomains, attrs.ref());
1451 0 : result.forget(out);
1452 0 : return true;
1453 : }
1454 :
1455 : /*
1456 : * Helper that tries to get a property from the options object.
1457 : */
1458 : bool
1459 288 : OptionsBase::ParseValue(const char* name, MutableHandleValue prop, bool* aFound)
1460 : {
1461 : bool found;
1462 288 : bool ok = JS_HasProperty(mCx, mObject, name, &found);
1463 288 : NS_ENSURE_TRUE(ok, false);
1464 :
1465 288 : if (aFound)
1466 272 : *aFound = found;
1467 :
1468 288 : if (!found)
1469 224 : return true;
1470 :
1471 64 : return JS_GetProperty(mCx, mObject, name, prop);
1472 : }
1473 :
1474 : /*
1475 : * Helper that tries to get a boolean property from the options object.
1476 : */
1477 : bool
1478 160 : OptionsBase::ParseBoolean(const char* name, bool* prop)
1479 : {
1480 160 : MOZ_ASSERT(prop);
1481 320 : RootedValue value(mCx);
1482 : bool found;
1483 160 : bool ok = ParseValue(name, &value, &found);
1484 160 : NS_ENSURE_TRUE(ok, false);
1485 :
1486 160 : if (!found)
1487 157 : return true;
1488 :
1489 3 : if (!value.isBoolean()) {
1490 0 : JS_ReportErrorASCII(mCx, "Expected a boolean value for property %s", name);
1491 0 : return false;
1492 : }
1493 :
1494 3 : *prop = value.toBoolean();
1495 3 : return true;
1496 : }
1497 :
1498 : /*
1499 : * Helper that tries to get an object property from the options object.
1500 : */
1501 : bool
1502 48 : OptionsBase::ParseObject(const char* name, MutableHandleObject prop)
1503 : {
1504 96 : RootedValue value(mCx);
1505 : bool found;
1506 48 : bool ok = ParseValue(name, &value, &found);
1507 48 : NS_ENSURE_TRUE(ok, false);
1508 :
1509 48 : if (!found)
1510 47 : return true;
1511 :
1512 1 : if (!value.isObject()) {
1513 0 : JS_ReportErrorASCII(mCx, "Expected an object value for property %s", name);
1514 0 : return false;
1515 : }
1516 1 : prop.set(&value.toObject());
1517 1 : return true;
1518 : }
1519 :
1520 : /*
1521 : * Helper that tries to get an object property from the options object.
1522 : */
1523 : bool
1524 16 : OptionsBase::ParseJSString(const char* name, MutableHandleString prop)
1525 : {
1526 32 : RootedValue value(mCx);
1527 : bool found;
1528 16 : bool ok = ParseValue(name, &value, &found);
1529 16 : NS_ENSURE_TRUE(ok, false);
1530 :
1531 16 : if (!found)
1532 2 : return true;
1533 :
1534 14 : if (!value.isString()) {
1535 0 : JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
1536 0 : return false;
1537 : }
1538 14 : prop.set(value.toString());
1539 14 : return true;
1540 : }
1541 :
1542 : /*
1543 : * Helper that tries to get a string property from the options object.
1544 : */
1545 : bool
1546 16 : OptionsBase::ParseString(const char* name, nsCString& prop)
1547 : {
1548 32 : RootedValue value(mCx);
1549 : bool found;
1550 16 : bool ok = ParseValue(name, &value, &found);
1551 16 : NS_ENSURE_TRUE(ok, false);
1552 :
1553 16 : if (!found)
1554 1 : return true;
1555 :
1556 15 : if (!value.isString()) {
1557 0 : JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
1558 0 : return false;
1559 : }
1560 :
1561 15 : char* tmp = JS_EncodeString(mCx, value.toString());
1562 15 : NS_ENSURE_TRUE(tmp, false);
1563 15 : prop.Assign(tmp, strlen(tmp));
1564 15 : js_free(tmp);
1565 15 : return true;
1566 : }
1567 :
1568 : /*
1569 : * Helper that tries to get a string property from the options object.
1570 : */
1571 : bool
1572 0 : OptionsBase::ParseString(const char* name, nsString& prop)
1573 : {
1574 0 : RootedValue value(mCx);
1575 : bool found;
1576 0 : bool ok = ParseValue(name, &value, &found);
1577 0 : NS_ENSURE_TRUE(ok, false);
1578 :
1579 0 : if (!found)
1580 0 : return true;
1581 :
1582 0 : if (!value.isString()) {
1583 0 : JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
1584 0 : return false;
1585 : }
1586 :
1587 0 : nsAutoJSString strVal;
1588 0 : if (!strVal.init(mCx, value.toString()))
1589 0 : return false;
1590 :
1591 0 : prop = strVal;
1592 0 : return true;
1593 : }
1594 :
1595 : /*
1596 : * Helper that tries to get jsid property from the options object.
1597 : */
1598 : bool
1599 0 : OptionsBase::ParseId(const char* name, MutableHandleId prop)
1600 : {
1601 0 : RootedValue value(mCx);
1602 : bool found;
1603 0 : bool ok = ParseValue(name, &value, &found);
1604 0 : NS_ENSURE_TRUE(ok, false);
1605 :
1606 0 : if (!found)
1607 0 : return true;
1608 :
1609 0 : return JS_ValueToId(mCx, value, prop);
1610 : }
1611 :
1612 : /*
1613 : * Helper that tries to get a uint32_t property from the options object.
1614 : */
1615 : bool
1616 16 : OptionsBase::ParseUInt32(const char* name, uint32_t* prop)
1617 : {
1618 16 : MOZ_ASSERT(prop);
1619 32 : RootedValue value(mCx);
1620 : bool found;
1621 16 : bool ok = ParseValue(name, &value, &found);
1622 16 : NS_ENSURE_TRUE(ok, false);
1623 :
1624 16 : if (!found)
1625 16 : return true;
1626 :
1627 0 : if(!JS::ToUint32(mCx, value, prop)) {
1628 0 : JS_ReportErrorASCII(mCx, "Expected a uint32_t value for property %s", name);
1629 0 : return false;
1630 : }
1631 :
1632 0 : return true;
1633 : }
1634 :
1635 : /*
1636 : * Helper that tries to get a list of DOM constructors and other helpers from the options object.
1637 : */
1638 : bool
1639 16 : SandboxOptions::ParseGlobalProperties()
1640 : {
1641 32 : RootedValue value(mCx);
1642 : bool found;
1643 16 : bool ok = ParseValue("wantGlobalProperties", &value, &found);
1644 16 : NS_ENSURE_TRUE(ok, false);
1645 16 : if (!found)
1646 0 : return true;
1647 :
1648 16 : if (!value.isObject()) {
1649 0 : JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
1650 0 : return false;
1651 : }
1652 :
1653 32 : RootedObject ctors(mCx, &value.toObject());
1654 : bool isArray;
1655 16 : if (!JS_IsArrayObject(mCx, ctors, &isArray))
1656 0 : return false;
1657 16 : if (!isArray) {
1658 0 : JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
1659 0 : return false;
1660 : }
1661 :
1662 16 : return globalProperties.Parse(mCx, ctors);
1663 : }
1664 :
1665 : /*
1666 : * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
1667 : */
1668 : bool
1669 16 : SandboxOptions::Parse()
1670 : {
1671 : /* All option names must be ASCII-only. */
1672 80 : bool ok = ParseObject("sandboxPrototype", &proto) &&
1673 32 : ParseBoolean("wantXrays", &wantXrays) &&
1674 32 : ParseBoolean("allowWaivers", &allowWaivers) &&
1675 32 : ParseBoolean("wantComponents", &wantComponents) &&
1676 32 : ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
1677 32 : ParseBoolean("isWebExtensionContentScript", &isWebExtensionContentScript) &&
1678 32 : ParseBoolean("waiveInterposition", &waiveInterposition) &&
1679 48 : ParseString("sandboxName", sandboxName) &&
1680 64 : ParseObject("sameZoneAs", &sameZoneAs) &&
1681 32 : ParseBoolean("freshZone", &freshZone) &&
1682 32 : ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
1683 48 : ParseBoolean("discardSource", &discardSource) &&
1684 64 : ParseJSString("addonId", &addonId) &&
1685 32 : ParseBoolean("writeToGlobalPrototype", &writeToGlobalPrototype) &&
1686 48 : ParseGlobalProperties() &&
1687 64 : ParseValue("metadata", &metadata) &&
1688 96 : ParseUInt32("userContextId", &userContextId) &&
1689 48 : ParseObject("originAttributes", &originAttributes);
1690 16 : if (!ok)
1691 0 : return false;
1692 :
1693 16 : if (freshZone && sameZoneAs) {
1694 0 : JS_ReportErrorASCII(mCx, "Cannot use both sameZoneAs and freshZone");
1695 0 : return false;
1696 : }
1697 :
1698 16 : return true;
1699 : }
1700 :
1701 : static nsresult
1702 16 : AssembleSandboxMemoryReporterName(JSContext* cx, nsCString& sandboxName)
1703 : {
1704 : // Use a default name when the caller did not provide a sandboxName.
1705 16 : if (sandboxName.IsEmpty())
1706 1 : sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
1707 :
1708 16 : nsXPConnect* xpc = nsXPConnect::XPConnect();
1709 : // Get the xpconnect native call context.
1710 16 : nsAXPCNativeCallContext* cc = nullptr;
1711 16 : xpc->GetCurrentNativeCallContext(&cc);
1712 16 : NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
1713 :
1714 : // Get the current source info from xpc.
1715 32 : nsCOMPtr<nsIStackFrame> frame;
1716 16 : xpc->GetCurrentJSStack(getter_AddRefs(frame));
1717 :
1718 : // Append the caller's location information.
1719 16 : if (frame) {
1720 32 : nsString location;
1721 16 : int32_t lineNumber = 0;
1722 16 : frame->GetFilename(cx, location);
1723 16 : frame->GetLineNumber(cx, &lineNumber);
1724 :
1725 16 : sandboxName.AppendLiteral(" (from: ");
1726 16 : sandboxName.Append(NS_ConvertUTF16toUTF8(location));
1727 16 : sandboxName.Append(':');
1728 16 : sandboxName.AppendInt(lineNumber);
1729 16 : sandboxName.Append(')');
1730 : }
1731 :
1732 16 : return NS_OK;
1733 : }
1734 :
1735 : // static
1736 : nsresult
1737 16 : nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
1738 : JSContext* cx, HandleObject obj,
1739 : const CallArgs& args, bool* _retval)
1740 : {
1741 16 : if (args.length() < 1)
1742 0 : return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
1743 :
1744 : nsresult rv;
1745 16 : bool ok = false;
1746 16 : bool calledWithOptions = args.length() > 1;
1747 16 : if (calledWithOptions && !args[1].isObject())
1748 0 : return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1749 :
1750 48 : RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
1751 32 : : nullptr);
1752 :
1753 32 : SandboxOptions options(cx, optionsObject);
1754 16 : if (calledWithOptions && !options.Parse())
1755 0 : return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1756 :
1757 : // Make sure to set up principals on the sandbox before initing classes.
1758 32 : nsCOMPtr<nsIPrincipal> principal;
1759 32 : nsCOMPtr<nsIExpandedPrincipal> expanded;
1760 32 : nsCOMPtr<nsISupports> prinOrSop;
1761 :
1762 16 : if (args[0].isString()) {
1763 0 : RootedString str(cx, args[0].toString());
1764 0 : OriginAttributes attrs;
1765 0 : if (options.originAttributes) {
1766 0 : JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
1767 0 : if (!attrs.Init(cx, val)) {
1768 : // The originAttributes option, if specified, must be valid!
1769 0 : JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
1770 0 : return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1771 : }
1772 : }
1773 0 : attrs.mUserContextId = options.userContextId;
1774 0 : ok = ParsePrincipal(cx, str, attrs, getter_AddRefs(principal));
1775 0 : prinOrSop = principal;
1776 16 : } else if (args[0].isObject()) {
1777 32 : RootedObject obj(cx, &args[0].toObject());
1778 : bool isArray;
1779 16 : if (!JS_IsArrayObject(cx, obj, &isArray)) {
1780 0 : ok = false;
1781 16 : } else if (isArray) {
1782 0 : if (options.userContextId != 0) {
1783 : // We don't support passing a userContextId with an array.
1784 0 : ok = false;
1785 : } else {
1786 0 : ok = GetExpandedPrincipal(cx, obj, options, getter_AddRefs(expanded));
1787 0 : prinOrSop = expanded;
1788 : }
1789 : } else {
1790 16 : ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
1791 : }
1792 0 : } else if (args[0].isNull()) {
1793 : // Null means that we just pass prinOrSop = nullptr, and get an
1794 : // NullPrincipal.
1795 0 : ok = true;
1796 : }
1797 :
1798 16 : if (!ok)
1799 0 : return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1800 :
1801 :
1802 16 : if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
1803 0 : return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1804 :
1805 16 : if (options.metadata.isNullOrUndefined()) {
1806 : // If the caller is running in a sandbox, inherit.
1807 2 : RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
1808 1 : if (IsSandbox(callerGlobal)) {
1809 1 : rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
1810 1 : if (NS_WARN_IF(NS_FAILED(rv)))
1811 0 : return rv;
1812 : }
1813 : }
1814 :
1815 16 : rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
1816 :
1817 16 : if (NS_FAILED(rv))
1818 0 : return ThrowAndFail(rv, cx, _retval);
1819 :
1820 : // We have this crazy behavior where wantXrays=false also implies that the
1821 : // returned sandbox is implicitly waived. We've stopped advertising it, but
1822 : // keep supporting it for now.
1823 16 : if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, args.rval()))
1824 0 : return NS_ERROR_UNEXPECTED;
1825 :
1826 16 : *_retval = true;
1827 16 : return NS_OK;
1828 : }
1829 :
1830 : nsresult
1831 0 : xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg, const nsAString& source,
1832 : const nsACString& filename, int32_t lineNo,
1833 : JSVersion jsVersion, MutableHandleValue rval)
1834 : {
1835 0 : JS_AbortIfWrongThread(cx);
1836 0 : rval.set(UndefinedValue());
1837 :
1838 0 : bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
1839 0 : RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
1840 0 : if (!sandbox || !IsSandbox(sandbox)) {
1841 0 : return NS_ERROR_INVALID_ARG;
1842 : }
1843 :
1844 : nsIScriptObjectPrincipal* sop =
1845 0 : static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(sandbox));
1846 0 : MOZ_ASSERT(sop, "Invalid sandbox passed");
1847 0 : SandboxPrivate* priv = static_cast<SandboxPrivate*>(sop);
1848 0 : nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
1849 0 : NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
1850 :
1851 0 : nsAutoCString filenameBuf;
1852 0 : if (!filename.IsVoid() && filename.Length() != 0) {
1853 0 : filenameBuf.Assign(filename);
1854 : } else {
1855 : // Default to the spec of the principal.
1856 0 : nsresult rv = nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
1857 0 : NS_ENSURE_SUCCESS(rv, rv);
1858 0 : lineNo = 1;
1859 : }
1860 :
1861 : // We create a separate cx to do the sandbox evaluation. Scope it.
1862 0 : RootedValue v(cx, UndefinedValue());
1863 0 : RootedValue exn(cx, UndefinedValue());
1864 0 : bool ok = true;
1865 : {
1866 : // We're about to evaluate script, so make an AutoEntryScript.
1867 : // This is clearly Gecko-specific and not in any spec.
1868 0 : mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
1869 0 : JSContext* sandcx = aes.cx();
1870 0 : JSAutoCompartment ac(sandcx, sandbox);
1871 :
1872 0 : JS::CompileOptions options(sandcx);
1873 0 : options.setFileAndLine(filenameBuf.get(), lineNo)
1874 0 : .setVersion(jsVersion);
1875 0 : MOZ_ASSERT(JS_IsGlobalObject(sandbox));
1876 0 : ok = JS::Evaluate(sandcx, options,
1877 0 : PromiseFlatString(source).get(), source.Length(), &v);
1878 :
1879 : // If the sandbox threw an exception, grab it off the context.
1880 0 : if (aes.HasException()) {
1881 0 : if (!aes.StealException(&exn)) {
1882 0 : return NS_ERROR_OUT_OF_MEMORY;
1883 : }
1884 : }
1885 : }
1886 :
1887 : //
1888 : // Alright, we're back on the caller's cx. If an error occured, try to
1889 : // wrap and set the exception. Otherwise, wrap the return value.
1890 : //
1891 :
1892 0 : if (!ok) {
1893 : // If we end up without an exception, it was probably due to OOM along
1894 : // the way, in which case we thow. Otherwise, wrap it.
1895 0 : if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
1896 0 : return NS_ERROR_OUT_OF_MEMORY;
1897 :
1898 : // Set the exception on our caller's cx.
1899 0 : JS_SetPendingException(cx, exn);
1900 0 : return NS_ERROR_FAILURE;
1901 : }
1902 :
1903 : // Transitively apply Xray waivers if |sb| was waived.
1904 0 : if (waiveXray) {
1905 0 : ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
1906 : } else {
1907 0 : ok = JS_WrapValue(cx, &v);
1908 : }
1909 0 : NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1910 :
1911 : // Whew!
1912 0 : rval.set(v);
1913 0 : return NS_OK;
1914 : }
1915 :
1916 : nsresult
1917 0 : xpc::GetSandboxAddonId(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
1918 : {
1919 0 : MOZ_ASSERT(NS_IsMainThread());
1920 0 : MOZ_ASSERT(IsSandbox(sandbox));
1921 :
1922 0 : JSAddonId* id = JS::AddonIdOfObject(sandbox);
1923 0 : if (!id) {
1924 0 : rval.setNull();
1925 0 : return NS_OK;
1926 : }
1927 :
1928 0 : JS::RootedValue idStr(cx, StringValue(JS::StringOfAddonId(id)));
1929 0 : if (!JS_WrapValue(cx, &idStr))
1930 0 : return NS_ERROR_UNEXPECTED;
1931 :
1932 0 : rval.set(idStr);
1933 0 : return NS_OK;
1934 : }
1935 :
1936 : nsresult
1937 1 : xpc::GetSandboxMetadata(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
1938 : {
1939 1 : MOZ_ASSERT(NS_IsMainThread());
1940 1 : MOZ_ASSERT(IsSandbox(sandbox));
1941 :
1942 2 : RootedValue metadata(cx);
1943 : {
1944 2 : JSAutoCompartment ac(cx, sandbox);
1945 1 : metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
1946 : }
1947 :
1948 1 : if (!JS_WrapValue(cx, &metadata))
1949 0 : return NS_ERROR_UNEXPECTED;
1950 :
1951 1 : rval.set(metadata);
1952 1 : return NS_OK;
1953 : }
1954 :
1955 : nsresult
1956 25 : xpc::SetSandboxMetadata(JSContext* cx, HandleObject sandbox, HandleValue metadataArg)
1957 : {
1958 25 : MOZ_ASSERT(NS_IsMainThread());
1959 25 : MOZ_ASSERT(IsSandbox(sandbox));
1960 :
1961 50 : RootedValue metadata(cx);
1962 :
1963 50 : JSAutoCompartment ac(cx, sandbox);
1964 25 : if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
1965 0 : return NS_ERROR_UNEXPECTED;
1966 :
1967 25 : JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
1968 :
1969 25 : return NS_OK;
1970 : }
|