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