Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
7 /*
8 * The Components.Sandbox object.
9 */
11 #include "AccessCheck.h"
12 #include "jsfriendapi.h"
13 #include "jsproxy.h"
14 #include "js/OldDebugAPI.h"
15 #include "js/StructuredClone.h"
16 #include "nsContentUtils.h"
17 #include "nsCxPusher.h"
18 #include "nsGlobalWindow.h"
19 #include "nsIScriptContext.h"
20 #include "nsIScriptObjectPrincipal.h"
21 #include "nsIScriptSecurityManager.h"
22 #include "nsIURI.h"
23 #include "nsJSUtils.h"
24 #include "nsNetUtil.h"
25 #include "nsPrincipal.h"
26 #include "nsXMLHttpRequest.h"
27 #include "WrapperFactory.h"
28 #include "xpcprivate.h"
29 #include "XPCQuickStubs.h"
30 #include "XPCWrapper.h"
31 #include "XrayWrapper.h"
32 #include "mozilla/dom/BindingUtils.h"
33 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
34 #include "mozilla/dom/PromiseBinding.h"
35 #include "mozilla/dom/TextDecoderBinding.h"
36 #include "mozilla/dom/TextEncoderBinding.h"
37 #include "mozilla/dom/URLBinding.h"
39 using namespace mozilla;
40 using namespace JS;
41 using namespace js;
42 using namespace xpc;
44 using mozilla::dom::DestroyProtoAndIfaceCache;
45 using mozilla::dom::indexedDB::IndexedDatabaseManager;
47 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(SandboxPrivate)
48 NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
49 NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
50 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
51 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
52 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
53 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
54 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
55 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
56 NS_INTERFACE_MAP_END
58 const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
60 class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
61 public nsIXPCScriptable
62 {
63 public:
64 // Aren't macros nice?
65 NS_DECL_ISUPPORTS
66 NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
67 NS_DECL_NSIXPCSCRIPTABLE
69 public:
70 nsXPCComponents_utils_Sandbox();
71 virtual ~nsXPCComponents_utils_Sandbox();
73 private:
74 static nsresult CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
75 JSContext *cx, HandleObject obj,
76 const CallArgs &args, bool *_retval);
77 };
79 already_AddRefed<nsIXPCComponents_utils_Sandbox>
80 xpc::NewSandboxConstructor()
81 {
82 nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
83 new nsXPCComponents_utils_Sandbox();
84 return sbConstructor.forget();
85 }
87 static bool
88 SandboxDump(JSContext *cx, unsigned argc, jsval *vp)
89 {
90 CallArgs args = CallArgsFromVp(argc, vp);
92 if (args.length() == 0)
93 return true;
95 RootedString str(cx, ToString(cx, args[0]));
96 if (!str)
97 return false;
99 size_t length;
100 const jschar *chars = JS_GetStringCharsZAndLength(cx, str, &length);
101 if (!chars)
102 return false;
104 nsDependentString wstr(chars, length);
105 char *cstr = ToNewUTF8String(wstr);
106 if (!cstr)
107 return false;
109 #if defined(XP_MACOSX)
110 // Be nice and convert all \r to \n.
111 char *c = cstr, *cEnd = cstr + strlen(cstr);
112 while (c < cEnd) {
113 if (*c == '\r')
114 *c = '\n';
115 c++;
116 }
117 #endif
118 #ifdef ANDROID
119 __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
120 #endif
122 fputs(cstr, stdout);
123 fflush(stdout);
124 NS_Free(cstr);
125 args.rval().setBoolean(true);
126 return true;
127 }
129 static bool
130 SandboxDebug(JSContext *cx, unsigned argc, jsval *vp)
131 {
132 #ifdef DEBUG
133 return SandboxDump(cx, argc, vp);
134 #else
135 return true;
136 #endif
137 }
139 static bool
140 SandboxImport(JSContext *cx, unsigned argc, Value *vp)
141 {
142 CallArgs args = CallArgsFromVp(argc, vp);
144 if (args.length() < 1 || args[0].isPrimitive()) {
145 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
146 return false;
147 }
149 RootedString funname(cx);
150 if (args.length() > 1) {
151 // Use the second parameter as the function name.
152 funname = ToString(cx, args[1]);
153 if (!funname)
154 return false;
155 } else {
156 // NB: funobj must only be used to get the JSFunction out.
157 RootedObject funobj(cx, &args[0].toObject());
158 if (js::IsProxy(funobj)) {
159 funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
160 }
162 JSAutoCompartment ac(cx, funobj);
164 RootedValue funval(cx, ObjectValue(*funobj));
165 JSFunction *fun = JS_ValueToFunction(cx, funval);
166 if (!fun) {
167 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
168 return false;
169 }
171 // Use the actual function name as the name.
172 funname = JS_GetFunctionId(fun);
173 if (!funname) {
174 XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
175 return false;
176 }
177 }
179 RootedId id(cx);
180 if (!JS_StringToId(cx, funname, &id))
181 return false;
183 // We need to resolve the this object, because this function is used
184 // unbound and should still work and act on the original sandbox.
185 RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
186 if (!thisObject) {
187 XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
188 return false;
189 }
190 if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
191 return false;
193 args.rval().setUndefined();
194 return true;
195 }
197 static bool
198 CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp)
199 {
200 CallArgs args = CallArgsFromVp(argc, vp);
202 nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
203 if (!ssm)
204 return false;
206 nsIPrincipal *subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
207 if (!subjectPrincipal)
208 return false;
210 RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
211 MOZ_ASSERT(global);
213 nsIScriptObjectPrincipal *sop =
214 static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(global));
215 nsCOMPtr<nsIGlobalObject> iglobal = do_QueryInterface(sop);
217 nsCOMPtr<nsIXMLHttpRequest> xhr = new nsXMLHttpRequest();
218 nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr);
219 if (NS_FAILED(rv))
220 return false;
222 rv = nsContentUtils::WrapNative(cx, xhr, args.rval());
223 if (NS_FAILED(rv))
224 return false;
226 return true;
227 }
229 static bool
230 IsProxy(JSContext *cx, unsigned argc, jsval *vp)
231 {
232 CallArgs args = CallArgsFromVp(argc, vp);
233 if (args.length() < 1) {
234 JS_ReportError(cx, "Function requires at least 1 argument");
235 return false;
236 }
237 if (!args[0].isObject()) {
238 args.rval().setBoolean(false);
239 return true;
240 }
242 RootedObject obj(cx, &args[0].toObject());
243 obj = js::CheckedUnwrap(obj);
244 NS_ENSURE_TRUE(obj, false);
246 args.rval().setBoolean(js::IsScriptedProxy(obj));
247 return true;
248 }
250 namespace xpc {
252 bool
253 ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions,
254 MutableHandleValue rval)
255 {
256 bool hasOptions = !voptions.isUndefined();
257 if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) {
258 JS_ReportError(cx, "Invalid argument");
259 return false;
260 }
262 RootedObject funObj(cx, &vfunction.toObject());
263 RootedObject targetScope(cx, &vscope.toObject());
264 ExportOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
265 if (hasOptions && !options.Parse())
266 return false;
268 // We can only export functions to scopes those are transparent for us,
269 // so if there is a security wrapper around targetScope we must throw.
270 targetScope = CheckedUnwrap(targetScope);
271 if (!targetScope) {
272 JS_ReportError(cx, "Permission denied to export function into scope");
273 return false;
274 }
276 if (js::IsScriptedProxy(targetScope)) {
277 JS_ReportError(cx, "Defining property on proxy object is not allowed");
278 return false;
279 }
281 {
282 // We need to operate in the target scope from here on, let's enter
283 // its compartment.
284 JSAutoCompartment ac(cx, targetScope);
286 // Unwrapping to see if we have a callable.
287 funObj = UncheckedUnwrap(funObj);
288 if (!JS_ObjectIsCallable(cx, funObj)) {
289 JS_ReportError(cx, "First argument must be a function");
290 return false;
291 }
293 RootedId id(cx, options.defineAs);
294 if (JSID_IS_VOID(id)) {
295 // If there wasn't any function name specified,
296 // copy the name from the function being imported.
297 JSFunction *fun = JS_GetObjectFunction(funObj);
298 RootedString funName(cx, JS_GetFunctionId(fun));
299 if (!funName)
300 funName = JS_InternString(cx, "");
302 if (!JS_StringToId(cx, funName, &id))
303 return false;
304 }
305 MOZ_ASSERT(JSID_IS_STRING(id));
307 // The function forwarder will live in the target compartment. Since
308 // this function will be referenced from its private slot, to avoid a
309 // GC hazard, we must wrap it to the same compartment.
310 if (!JS_WrapObject(cx, &funObj))
311 return false;
313 // And now, let's create the forwarder function in the target compartment
314 // for the function the be exported.
315 if (!NewFunctionForwarder(cx, id, funObj, /* doclone = */ true, rval)) {
316 JS_ReportError(cx, "Exporting function failed");
317 return false;
318 }
320 // We have the forwarder function in the target compartment. If
321 // defineAs was set, we also need to define it as a property on
322 // the target.
323 if (!JSID_IS_VOID(options.defineAs)) {
324 if (!JS_DefinePropertyById(cx, targetScope, id, rval,
325 JS_PropertyStub, JS_StrictPropertyStub,
326 JSPROP_ENUMERATE)) {
327 return false;
328 }
329 }
330 }
332 // Finally we have to re-wrap the exported function back to the caller compartment.
333 if (!JS_WrapValue(cx, rval))
334 return false;
336 return true;
337 }
339 /*
340 * Expected type of the arguments and the return value:
341 * function exportFunction(function funToExport,
342 * object targetScope,
343 * [optional] object options)
344 */
345 static bool
346 ExportFunction(JSContext *cx, unsigned argc, jsval *vp)
347 {
348 CallArgs args = CallArgsFromVp(argc, vp);
349 if (args.length() < 2) {
350 JS_ReportError(cx, "Function requires at least 2 arguments");
351 return false;
352 }
354 RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
355 return ExportFunction(cx, args[0], args[1], options, args.rval());
356 }
357 } /* namespace xpc */
359 static bool
360 GetFilenameAndLineNumber(JSContext *cx, nsACString &filename, unsigned &lineno)
361 {
362 JS::AutoFilename scriptFilename;
363 if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineno)) {
364 if (const char *cfilename = scriptFilename.get()) {
365 filename.Assign(nsDependentCString(cfilename));
366 return true;
367 }
368 }
369 return false;
370 }
372 bool
373 xpc::IsReflector(JSObject *obj)
374 {
375 return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj);
376 }
378 enum ForwarderCloneTags {
379 SCTAG_BASE = JS_SCTAG_USER_MIN,
380 SCTAG_REFLECTOR
381 };
383 static JSObject *
384 CloneNonReflectorsRead(JSContext *cx, JSStructuredCloneReader *reader, uint32_t tag,
385 uint32_t data, void *closure)
386 {
387 MOZ_ASSERT(closure, "Null pointer!");
388 AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
389 if (tag == SCTAG_REFLECTOR) {
390 MOZ_ASSERT(!data);
392 size_t idx;
393 if (JS_ReadBytes(reader, &idx, sizeof(size_t))) {
394 RootedObject reflector(cx, reflectors->handleAt(idx));
395 MOZ_ASSERT(reflector, "No object pointer?");
396 MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!");
398 if (!JS_WrapObject(cx, &reflector))
399 return nullptr;
400 MOZ_ASSERT(WrapperFactory::IsXrayWrapper(reflector) ||
401 IsReflector(reflector));
403 return reflector;
404 }
405 }
407 JS_ReportError(cx, "CloneNonReflectorsRead error");
408 return nullptr;
409 }
411 static bool
412 CloneNonReflectorsWrite(JSContext *cx, JSStructuredCloneWriter *writer,
413 Handle<JSObject *> obj, void *closure)
414 {
415 MOZ_ASSERT(closure, "Null pointer!");
417 // We need to maintain a list of reflectors to make sure all these objects
418 // are properly rooter. Only their indices will be serialized.
419 AutoObjectVector *reflectors = static_cast<AutoObjectVector *>(closure);
420 if (IsReflector(obj)) {
421 if (!reflectors->append(obj))
422 return false;
424 size_t idx = reflectors->length()-1;
425 if (JS_WriteUint32Pair(writer, SCTAG_REFLECTOR, 0) &&
426 JS_WriteBytes(writer, &idx, sizeof(size_t))) {
427 return true;
428 }
429 }
431 JS_ReportError(cx, "CloneNonReflectorsWrite error");
432 return false;
433 }
435 static const JSStructuredCloneCallbacks gForwarderStructuredCloneCallbacks = {
436 CloneNonReflectorsRead,
437 CloneNonReflectorsWrite,
438 nullptr,
439 nullptr,
440 nullptr,
441 nullptr
442 };
444 /*
445 * This is a special structured cloning, that clones only non-reflectors.
446 * The function assumes the cx is already entered the compartment we want
447 * to clone to, and that if val is an object is from the compartment we
448 * clone from.
449 */
450 static bool
451 CloneNonReflectors(JSContext *cx, MutableHandleValue val)
452 {
453 JSAutoStructuredCloneBuffer buffer;
454 AutoObjectVector rootedReflectors(cx);
455 {
456 // For parsing val we have to enter its compartment.
457 // (unless it's a primitive)
458 Maybe<JSAutoCompartment> ac;
459 if (val.isObject()) {
460 ac.construct(cx, &val.toObject());
461 } else if (val.isString() && !JS_WrapValue(cx, val)) {
462 return false;
463 }
465 if (!buffer.write(cx, val,
466 &gForwarderStructuredCloneCallbacks,
467 &rootedReflectors))
468 {
469 return false;
470 }
471 }
473 // Now recreate the clones in the target compartment.
474 if (!buffer.read(cx, val,
475 &gForwarderStructuredCloneCallbacks,
476 &rootedReflectors))
477 {
478 return false;
479 }
481 return true;
482 }
484 namespace xpc {
486 bool
487 EvalInWindow(JSContext *cx, const nsAString &source, HandleObject scope, MutableHandleValue rval)
488 {
489 // If we cannot unwrap we must not eval in it.
490 RootedObject targetScope(cx, CheckedUnwrap(scope));
491 if (!targetScope) {
492 JS_ReportError(cx, "Permission denied to eval in target scope");
493 return false;
494 }
496 // Make sure that we have a window object.
497 RootedObject inner(cx, CheckedUnwrap(targetScope, /* stopAtOuter = */ false));
498 nsCOMPtr<nsIGlobalObject> global;
499 nsCOMPtr<nsPIDOMWindow> window;
500 if (!JS_IsGlobalObject(inner) ||
501 !(global = GetNativeForGlobal(inner)) ||
502 !(window = do_QueryInterface(global)))
503 {
504 JS_ReportError(cx, "Second argument must be a window");
505 return false;
506 }
508 nsCOMPtr<nsIScriptContext> context =
509 (static_cast<nsGlobalWindow*>(window.get()))->GetScriptContext();
510 if (!context) {
511 JS_ReportError(cx, "Script context needed");
512 return false;
513 }
515 nsCString filename;
516 unsigned lineNo;
517 if (!GetFilenameAndLineNumber(cx, filename, lineNo)) {
518 // Default values for non-scripted callers.
519 filename.Assign("Unknown");
520 lineNo = 0;
521 }
523 RootedObject cxGlobal(cx, JS::CurrentGlobalOrNull(cx));
524 {
525 // CompileOptions must be created from the context
526 // we will execute this script in.
527 JSContext *wndCx = context->GetNativeContext();
528 AutoCxPusher pusher(wndCx);
529 JS::CompileOptions compileOptions(wndCx);
530 compileOptions.setFileAndLine(filename.get(), lineNo);
532 // We don't want the JS engine to automatically report
533 // uncaught exceptions.
534 nsJSUtils::EvaluateOptions evaluateOptions;
535 evaluateOptions.setReportUncaught(false);
537 nsresult rv = nsJSUtils::EvaluateString(wndCx,
538 source,
539 targetScope,
540 compileOptions,
541 evaluateOptions,
542 rval);
544 if (NS_FAILED(rv)) {
545 // If there was an exception we get it as a return value, if
546 // the evaluation failed for some other reason, then a default
547 // exception is raised.
548 MOZ_ASSERT(!JS_IsExceptionPending(wndCx),
549 "Exception should be delivered as return value.");
550 if (rval.isUndefined()) {
551 MOZ_ASSERT(rv == NS_ERROR_OUT_OF_MEMORY);
552 return false;
553 }
555 // If there was an exception thrown we should set it
556 // on the calling context.
557 RootedValue exn(wndCx, rval);
558 // First we should reset the return value.
559 rval.set(UndefinedValue());
561 // Then clone the exception.
562 JSAutoCompartment ac(wndCx, cxGlobal);
563 if (CloneNonReflectors(wndCx, &exn))
564 js::SetPendingExceptionCrossContext(cx, exn);
566 return false;
567 }
568 }
570 // Let's clone the return value back to the callers compartment.
571 if (!CloneNonReflectors(cx, rval)) {
572 rval.set(UndefinedValue());
573 return false;
574 }
576 return true;
577 }
579 /*
580 * Expected type of the arguments:
581 * value evalInWindow(string script,
582 * object window)
583 */
584 static bool
585 EvalInWindow(JSContext *cx, unsigned argc, jsval *vp)
586 {
587 CallArgs args = CallArgsFromVp(argc, vp);
588 if (args.length() < 2) {
589 JS_ReportError(cx, "Function requires two arguments");
590 return false;
591 }
593 if (!args[0].isString() || !args[1].isObject()) {
594 JS_ReportError(cx, "Invalid arguments");
595 return false;
596 }
598 RootedString srcString(cx, args[0].toString());
599 RootedObject targetScope(cx, &args[1].toObject());
601 nsDependentJSString srcDepString;
602 if (!srcDepString.init(cx, srcString)) {
603 JS_ReportError(cx, "Source string is invalid");
604 return false;
605 }
607 return EvalInWindow(cx, srcDepString, targetScope, args.rval());
608 }
610 static bool
611 CreateObjectIn(JSContext *cx, unsigned argc, jsval *vp)
612 {
613 CallArgs args = CallArgsFromVp(argc, vp);
614 if (args.length() < 1) {
615 JS_ReportError(cx, "Function requires at least 1 argument");
616 return false;
617 }
619 RootedObject optionsObj(cx);
620 bool calledWithOptions = args.length() > 1;
621 if (calledWithOptions) {
622 if (!args[1].isObject()) {
623 JS_ReportError(cx, "Expected the 2nd argument (options) to be an object");
624 return false;
625 }
626 optionsObj = &args[1].toObject();
627 }
629 CreateObjectInOptions options(cx, optionsObj);
630 if (calledWithOptions && !options.Parse())
631 return false;
633 return xpc::CreateObjectIn(cx, args[0], options, args.rval());
634 }
636 static bool
637 CloneInto(JSContext *cx, unsigned argc, jsval *vp)
638 {
639 CallArgs args = CallArgsFromVp(argc, vp);
640 if (args.length() < 2) {
641 JS_ReportError(cx, "Function requires at least 2 arguments");
642 return false;
643 }
645 RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
646 return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
647 }
649 } /* namespace xpc */
651 static bool
652 sandbox_enumerate(JSContext *cx, HandleObject obj)
653 {
654 return JS_EnumerateStandardClasses(cx, obj);
655 }
657 static bool
658 sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
659 {
660 bool resolved;
661 return JS_ResolveStandardClass(cx, obj, id, &resolved);
662 }
664 static void
665 sandbox_finalize(JSFreeOp *fop, JSObject *obj)
666 {
667 nsIScriptObjectPrincipal *sop =
668 static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
669 MOZ_ASSERT(sop);
670 static_cast<SandboxPrivate *>(sop)->ForgetGlobalObject();
671 NS_IF_RELEASE(sop);
672 DestroyProtoAndIfaceCache(obj);
673 }
675 static bool
676 sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
677 {
678 if (type == JSTYPE_OBJECT) {
679 vp.set(OBJECT_TO_JSVAL(obj));
680 return true;
681 }
683 return JS_ConvertStub(cx, obj, type, vp);
684 }
686 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
688 static const JSClass SandboxClass = {
689 "Sandbox",
690 XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
691 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
692 sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
693 nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
694 };
696 static const JSFunctionSpec SandboxFunctions[] = {
697 JS_FS("dump", SandboxDump, 1,0),
698 JS_FS("debug", SandboxDebug, 1,0),
699 JS_FS("importFunction", SandboxImport, 1,0),
700 JS_FS_END
701 };
703 bool
704 xpc::IsSandbox(JSObject *obj)
705 {
706 return GetObjectJSClass(obj) == &SandboxClass;
707 }
709 /***************************************************************************/
710 nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
711 {
712 }
714 nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
715 {
716 }
718 NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
719 NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
720 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
721 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
722 NS_INTERFACE_MAP_END
724 NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
725 NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
727 // We use the nsIXPScriptable macros to generate lots of stuff for us.
728 #define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox
729 #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
730 #define XPC_MAP_WANT_CALL
731 #define XPC_MAP_WANT_CONSTRUCT
732 #define XPC_MAP_FLAGS 0
733 #include "xpc_map_end.h" /* This #undef's the above. */
735 xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
737 bool
738 xpc::IsSandboxPrototypeProxy(JSObject *obj)
739 {
740 return js::IsProxy(obj) &&
741 js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
742 }
744 bool
745 xpc::SandboxCallableProxyHandler::call(JSContext *cx, JS::Handle<JSObject*> proxy,
746 const JS::CallArgs &args)
747 {
748 // We forward the call to our underlying callable.
750 // The parent of our proxy is the SandboxProxyHandler proxy
751 RootedObject sandboxProxy(cx, JS_GetParent(proxy));
752 MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
753 js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
755 // The parent of the sandboxProxy is the sandbox global, and the
756 // target object is the original proto.
757 RootedObject sandboxGlobal(cx, JS_GetParent(sandboxProxy));
758 MOZ_ASSERT(js::GetObjectJSClass(sandboxGlobal) == &SandboxClass);
760 // If our this object is the sandbox global, we call with this set to the
761 // original proto instead.
762 //
763 // There are two different ways we can compute |this|. If we use
764 // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
765 // caller, which may be undefined if a global function was invoked without
766 // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
767 // in |vp| will be coerced to the global, which is not the correct
768 // behavior in ES5 strict mode. And we have no way to compute strictness
769 // here.
770 //
771 // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
772 // explicit, we can remap it appropriately. If it was implicit, then we
773 // leave it as undefined, and let the callee sort it out. Since the callee
774 // is generally in the same compartment as its global (eg the Window's
775 // compartment, not the Sandbox's), the callee will generally compute the
776 // correct |this|.
777 //
778 // However, this breaks down in the Xray case. If the sandboxPrototype
779 // is an Xray wrapper, then we'll end up reifying the native methods in
780 // the Sandbox's scope, which means that they'll compute |this| to be the
781 // Sandbox, breaking old-style XPC_WN_CallMethod methods.
782 //
783 // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
784 // DOM interface, which means that we don't care about script-enacted
785 // strictness in the prototype's home compartment. Indeed, since DOM
786 // methods are always non-strict, we can just assume non-strict semantics
787 // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
788 // remap |this|.
789 bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
790 RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
791 if (thisVal == ObjectValue(*sandboxGlobal)) {
792 thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
793 }
795 RootedValue func(cx, js::GetProxyPrivate(proxy));
796 return JS::Call(cx, thisVal, func, args, args.rval());
797 }
799 xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
801 /*
802 * Wrap a callable such that if we're called with oldThisObj as the
803 * "this" we will instead call it with newThisObj as the this.
804 */
805 static JSObject*
806 WrapCallable(JSContext *cx, JSObject *callable, JSObject *sandboxProtoProxy)
807 {
808 MOZ_ASSERT(JS_ObjectIsCallable(cx, callable));
809 // Our proxy is wrapping the callable. So we need to use the
810 // callable as the private. We use the given sandboxProtoProxy as
811 // the parent, and our call() hook depends on that.
812 MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
813 js::GetProxyHandler(sandboxProtoProxy) ==
814 &xpc::sandboxProxyHandler);
816 RootedValue priv(cx, ObjectValue(*callable));
817 js::ProxyOptions options;
818 options.selectDefaultClass(true);
819 return js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
820 priv, nullptr,
821 sandboxProtoProxy, options);
822 }
824 template<typename Op>
825 bool BindPropertyOp(JSContext *cx, Op &op, JSPropertyDescriptor *desc, HandleId id,
826 unsigned attrFlag, HandleObject sandboxProtoProxy)
827 {
828 if (!op) {
829 return true;
830 }
832 RootedObject func(cx);
833 if (desc->attrs & attrFlag) {
834 // Already an object
835 func = JS_FUNC_TO_DATA_PTR(JSObject *, op);
836 } else {
837 // We have an actual property op. For getters, we use 0
838 // args, for setters we use 1 arg.
839 uint32_t args = (attrFlag == JSPROP_GETTER) ? 0 : 1;
840 RootedObject obj(cx, desc->obj);
841 func = GeneratePropertyOp(cx, obj, id, args, op);
842 if (!func)
843 return false;
844 }
845 func = WrapCallable(cx, func, sandboxProtoProxy);
846 if (!func)
847 return false;
848 op = JS_DATA_TO_FUNC_PTR(Op, func.get());
849 desc->attrs |= attrFlag;
850 return true;
851 }
853 extern bool
854 XPC_WN_Helper_GetProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
855 extern bool
856 XPC_WN_Helper_SetProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp);
858 bool
859 xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext *cx,
860 JS::Handle<JSObject*> proxy,
861 JS::Handle<jsid> id,
862 JS::MutableHandle<JSPropertyDescriptor> desc)
863 {
864 JS::RootedObject obj(cx, wrappedObject(proxy));
866 MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
867 if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
868 return false;
870 if (!desc.object())
871 return true; // No property, nothing to do
873 // Now fix up the getter/setter/value as needed to be bound to desc->obj
874 // Don't mess with holder_get and holder_set, though, because those rely on
875 // the "vp is prefilled with the value in the slot" behavior that property
876 // ops can in theory rely on, but our property op forwarder doesn't know how
877 // to make that happen. Since we really only need to rebind the DOM methods
878 // here, not rebindings holder_get and holder_set is OK.
879 //
880 // Similarly, don't mess with XPC_WN_Helper_GetProperty and
881 // XPC_WN_Helper_SetProperty, for the same reasons: that could confuse our
882 // access to expandos when we're not doing Xrays.
883 if (desc.getter() != xpc::holder_get &&
884 desc.getter() != XPC_WN_Helper_GetProperty &&
885 !BindPropertyOp(cx, desc.getter(), desc.address(), id, JSPROP_GETTER, proxy))
886 return false;
887 if (desc.setter() != xpc::holder_set &&
888 desc.setter() != XPC_WN_Helper_SetProperty &&
889 !BindPropertyOp(cx, desc.setter(), desc.address(), id, JSPROP_SETTER, proxy))
890 return false;
891 if (desc.value().isObject()) {
892 JSObject* val = &desc.value().toObject();
893 if (JS_ObjectIsCallable(cx, val)) {
894 val = WrapCallable(cx, val, proxy);
895 if (!val)
896 return false;
897 desc.value().setObject(*val);
898 }
899 }
901 return true;
902 }
904 bool
905 xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext *cx,
906 JS::Handle<JSObject*> proxy,
907 JS::Handle<jsid> id,
908 JS::MutableHandle<JSPropertyDescriptor> desc)
909 {
910 if (!getPropertyDescriptor(cx, proxy, id, desc))
911 return false;
913 if (desc.object() != wrappedObject(proxy))
914 desc.object().set(nullptr);
916 return true;
917 }
919 /*
920 * Reuse the BaseProxyHandler versions of the derived traps that are implemented
921 * in terms of the fundamental traps.
922 */
924 bool
925 xpc::SandboxProxyHandler::has(JSContext *cx, JS::Handle<JSObject*> proxy,
926 JS::Handle<jsid> id, bool *bp)
927 {
928 return BaseProxyHandler::has(cx, proxy, id, bp);
929 }
930 bool
931 xpc::SandboxProxyHandler::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
932 JS::Handle<jsid> id, bool *bp)
933 {
934 return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
935 }
937 bool
938 xpc::SandboxProxyHandler::get(JSContext *cx, JS::Handle<JSObject*> proxy,
939 JS::Handle<JSObject*> receiver,
940 JS::Handle<jsid> id,
941 JS::MutableHandle<Value> vp)
942 {
943 return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
944 }
946 bool
947 xpc::SandboxProxyHandler::set(JSContext *cx, JS::Handle<JSObject*> proxy,
948 JS::Handle<JSObject*> receiver,
949 JS::Handle<jsid> id,
950 bool strict,
951 JS::MutableHandle<Value> vp)
952 {
953 return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
954 }
956 bool
957 xpc::SandboxProxyHandler::keys(JSContext *cx, JS::Handle<JSObject*> proxy,
958 AutoIdVector &props)
959 {
960 return BaseProxyHandler::keys(cx, proxy, props);
961 }
963 bool
964 xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
965 unsigned flags, JS::MutableHandle<Value> vp)
966 {
967 return BaseProxyHandler::iterate(cx, proxy, flags, vp);
968 }
970 bool
971 xpc::GlobalProperties::Parse(JSContext *cx, JS::HandleObject obj)
972 {
973 MOZ_ASSERT(JS_IsArrayObject(cx, obj));
975 uint32_t length;
976 bool ok = JS_GetArrayLength(cx, obj, &length);
977 NS_ENSURE_TRUE(ok, false);
978 bool promise = Promise;
979 for (uint32_t i = 0; i < length; i++) {
980 RootedValue nameValue(cx);
981 ok = JS_GetElement(cx, obj, i, &nameValue);
982 NS_ENSURE_TRUE(ok, false);
983 if (!nameValue.isString()) {
984 JS_ReportError(cx, "Property names must be strings");
985 return false;
986 }
987 JSAutoByteString name(cx, nameValue.toString());
988 NS_ENSURE_TRUE(name, false);
989 if (promise && !strcmp(name.ptr(), "-Promise")) {
990 Promise = false;
991 } else if (!strcmp(name.ptr(), "indexedDB")) {
992 indexedDB = true;
993 } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
994 XMLHttpRequest = true;
995 } else if (!strcmp(name.ptr(), "TextEncoder")) {
996 TextEncoder = true;
997 } else if (!strcmp(name.ptr(), "TextDecoder")) {
998 TextDecoder = true;
999 } else if (!strcmp(name.ptr(), "URL")) {
1000 URL = true;
1001 } else if (!strcmp(name.ptr(), "atob")) {
1002 atob = true;
1003 } else if (!strcmp(name.ptr(), "btoa")) {
1004 btoa = true;
1005 } else {
1006 JS_ReportError(cx, "Unknown property name: %s", name.ptr());
1007 return false;
1008 }
1009 }
1010 return true;
1011 }
1013 bool
1014 xpc::GlobalProperties::Define(JSContext *cx, JS::HandleObject obj)
1015 {
1016 if (Promise && !dom::PromiseBinding::GetConstructorObject(cx, obj))
1017 return false;
1019 if (indexedDB && AccessCheck::isChrome(obj) &&
1020 !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
1021 return false;
1023 if (XMLHttpRequest &&
1024 !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
1025 return false;
1027 if (TextEncoder &&
1028 !dom::TextEncoderBinding::GetConstructorObject(cx, obj))
1029 return false;
1031 if (TextDecoder &&
1032 !dom::TextDecoderBinding::GetConstructorObject(cx, obj))
1033 return false;
1035 if (URL &&
1036 !dom::URLBinding::GetConstructorObject(cx, obj))
1037 return false;
1039 if (atob &&
1040 !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
1041 return false;
1043 if (btoa &&
1044 !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
1045 return false;
1047 return true;
1048 }
1050 nsresult
1051 xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prinOrSop,
1052 SandboxOptions& options)
1053 {
1054 // Create the sandbox global object
1055 nsresult rv;
1056 nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
1057 if (NS_FAILED(rv))
1058 return NS_ERROR_XPC_UNEXPECTED;
1060 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
1061 if (!principal) {
1062 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
1063 if (sop) {
1064 principal = sop->GetPrincipal();
1065 } else {
1066 principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
1067 MOZ_ASSERT(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance");
1069 if (!principal || NS_FAILED(rv)) {
1070 if (NS_SUCCEEDED(rv)) {
1071 rv = NS_ERROR_FAILURE;
1072 }
1074 return rv;
1075 }
1076 }
1077 MOZ_ASSERT(principal);
1078 }
1080 JS::CompartmentOptions compartmentOptions;
1081 if (options.sameZoneAs)
1082 compartmentOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
1083 else
1084 compartmentOptions.setZone(JS::SystemZone);
1086 compartmentOptions.setInvisibleToDebugger(options.invisibleToDebugger)
1087 .setDiscardSource(options.discardSource)
1088 .setTrace(TraceXPCGlobal);
1090 RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, &SandboxClass,
1091 principal, compartmentOptions));
1092 if (!sandbox)
1093 return NS_ERROR_FAILURE;
1095 // Set up the wantXrays flag, which indicates whether xrays are desired even
1096 // for same-origin access.
1097 //
1098 // This flag has historically been ignored for chrome sandboxes due to
1099 // quirks in the wrapping implementation that have now been removed. Indeed,
1100 // same-origin Xrays for chrome->chrome access seems a bit superfluous.
1101 // Arguably we should just flip the default for chrome and still honor the
1102 // flag, but such a change would break code in subtle ways for minimal
1103 // benefit. So we just switch it off here.
1104 xpc::GetCompartmentPrivate(sandbox)->wantXrays =
1105 AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
1107 {
1108 JSAutoCompartment ac(cx, sandbox);
1110 if (options.proto) {
1111 bool ok = JS_WrapObject(cx, &options.proto);
1112 if (!ok)
1113 return NS_ERROR_XPC_UNEXPECTED;
1115 if (xpc::WrapperFactory::IsXrayWrapper(options.proto) && !options.wantXrays) {
1116 RootedValue v(cx, ObjectValue(*options.proto));
1117 if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v))
1118 return NS_ERROR_FAILURE;
1119 options.proto = &v.toObject();
1120 }
1122 // Now check what sort of thing we've got in |proto|
1123 JSObject *unwrappedProto = js::UncheckedUnwrap(options.proto, false);
1124 const js::Class *unwrappedClass = js::GetObjectClass(unwrappedProto);
1125 if (IS_WN_CLASS(unwrappedClass) ||
1126 mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) {
1127 // Wrap it up in a proxy that will do the right thing in terms
1128 // of this-binding for methods.
1129 RootedValue priv(cx, ObjectValue(*options.proto));
1130 options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
1131 priv, nullptr, sandbox);
1132 if (!options.proto)
1133 return NS_ERROR_OUT_OF_MEMORY;
1134 }
1136 ok = JS_SetPrototype(cx, sandbox, options.proto);
1137 if (!ok)
1138 return NS_ERROR_XPC_UNEXPECTED;
1139 }
1141 nsCOMPtr<nsIScriptObjectPrincipal> sbp =
1142 new SandboxPrivate(principal, sandbox);
1144 // Pass on ownership of sbp to |sandbox|.
1145 JS_SetPrivate(sandbox, sbp.forget().take());
1147 bool allowComponents = nsContentUtils::IsSystemPrincipal(principal) ||
1148 nsContentUtils::IsExpandedPrincipal(principal);
1149 if (options.wantComponents && allowComponents &&
1150 !GetObjectScope(sandbox)->AttachComponentsObject(cx))
1151 return NS_ERROR_XPC_UNEXPECTED;
1153 if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
1154 return NS_ERROR_XPC_UNEXPECTED;
1156 if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
1157 return NS_ERROR_XPC_UNEXPECTED;
1159 if (options.wantExportHelpers &&
1160 (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) ||
1161 !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0) ||
1162 !JS_DefineFunction(cx, sandbox, "createObjectIn", CreateObjectIn, 2, 0) ||
1163 !JS_DefineFunction(cx, sandbox, "cloneInto", CloneInto, 3, 0) ||
1164 !JS_DefineFunction(cx, sandbox, "isProxy", IsProxy, 1, 0)))
1165 return NS_ERROR_XPC_UNEXPECTED;
1167 if (!options.globalProperties.Define(cx, sandbox))
1168 return NS_ERROR_XPC_UNEXPECTED;
1169 }
1172 // We have this crazy behavior where wantXrays=false also implies that the
1173 // returned sandbox is implicitly waived. We've stopped advertising it, but
1174 // keep supporting it for now.
1175 vp.setObject(*sandbox);
1176 if (options.wantXrays && !JS_WrapValue(cx, vp))
1177 return NS_ERROR_UNEXPECTED;
1178 if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, vp))
1179 return NS_ERROR_UNEXPECTED;
1181 // Set the location information for the new global, so that tools like
1182 // about:memory may use that information
1183 xpc::SetLocationForGlobal(sandbox, options.sandboxName);
1185 xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
1187 JS_FireOnNewGlobalObject(cx, sandbox);
1189 return NS_OK;
1190 }
1192 /* bool call(in nsIXPConnectWrappedNative wrapper,
1193 * in JSContextPtr cx,
1194 * in JSObjectPtr obj,
1195 * in uint32_t argc,
1196 * in JSValPtr argv,
1197 * in JSValPtr vp);
1198 */
1199 NS_IMETHODIMP
1200 nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
1201 JSObject *objArg, const CallArgs &args, bool *_retval)
1202 {
1203 RootedObject obj(cx, objArg);
1204 return CallOrConstruct(wrapper, cx, obj, args, _retval);
1205 }
1207 /* bool construct(in nsIXPConnectWrappedNative wrapper,
1208 * in JSContextPtr cx,
1209 * in JSObjectPtr obj,
1210 * in uint32_t argc,
1211 * in JSValPtr argv,
1212 * in JSValPtr vp);
1213 */
1214 NS_IMETHODIMP
1215 nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
1216 JSObject *objArg, const CallArgs &args, bool *_retval)
1217 {
1218 RootedObject obj(cx, objArg);
1219 return CallOrConstruct(wrapper, cx, obj, args, _retval);
1220 }
1222 /*
1223 * For sandbox constructor the first argument can be a URI string in which case
1224 * we use the related Codebase Principal for the sandbox.
1225 */
1226 bool
1227 ParsePrincipal(JSContext *cx, HandleString codebase, nsIPrincipal **principal)
1228 {
1229 MOZ_ASSERT(principal);
1230 MOZ_ASSERT(codebase);
1231 nsCOMPtr<nsIURI> uri;
1232 nsDependentJSString codebaseStr;
1233 NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
1234 nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
1235 if (NS_FAILED(rv)) {
1236 JS_ReportError(cx, "Creating URI from string failed");
1237 return false;
1238 }
1240 nsCOMPtr<nsIScriptSecurityManager> secman =
1241 do_GetService(kScriptSecurityManagerContractID);
1242 NS_ENSURE_TRUE(secman, false);
1244 // We could allow passing in the app-id and browser-element info to the
1245 // sandbox constructor. But creating a sandbox based on a string is a
1246 // deprecated API so no need to add features to it.
1247 rv = secman->GetNoAppCodebasePrincipal(uri, principal);
1248 if (NS_FAILED(rv) || !*principal) {
1249 JS_ReportError(cx, "Creating Principal from URI failed");
1250 return false;
1251 }
1252 return true;
1253 }
1255 /*
1256 * For sandbox constructor the first argument can be a principal object or
1257 * a script object principal (Document, Window).
1258 */
1259 static bool
1260 GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out)
1261 {
1262 MOZ_ASSERT(out);
1263 *out = nullptr;
1265 nsXPConnect* xpc = nsXPConnect::XPConnect();
1266 nsISupports* native = xpc->GetNativeOfWrapper(cx, from);
1268 if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
1269 sop.forget(out);
1270 return true;
1271 }
1273 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
1274 principal.forget(out);
1275 NS_ENSURE_TRUE(*out, false);
1277 return true;
1278 }
1280 /*
1281 * The first parameter of the sandbox constructor might be an array of principals, either in string
1282 * format or actual objects (see GetPrincipalOrSOP)
1283 */
1284 static bool
1285 GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out)
1286 {
1287 MOZ_ASSERT(out);
1288 uint32_t length;
1290 if (!JS_IsArrayObject(cx, arrayObj) ||
1291 !JS_GetArrayLength(cx, arrayObj, &length) ||
1292 !length)
1293 {
1294 // We need a whitelist of principals or uri strings to create an
1295 // expanded principal, if we got an empty array or something else
1296 // report error.
1297 JS_ReportError(cx, "Expected an array of URI strings");
1298 return false;
1299 }
1301 nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
1302 allowedDomains.SetLength(length);
1303 nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
1304 NS_ENSURE_TRUE(ssm, false);
1306 for (uint32_t i = 0; i < length; ++i) {
1307 RootedValue allowed(cx);
1308 if (!JS_GetElement(cx, arrayObj, i, &allowed))
1309 return false;
1311 nsresult rv;
1312 nsCOMPtr<nsIPrincipal> principal;
1313 if (allowed.isString()) {
1314 // In case of string let's try to fetch a codebase principal from it.
1315 RootedString str(cx, allowed.toString());
1316 if (!ParsePrincipal(cx, str, getter_AddRefs(principal)))
1317 return false;
1319 } else if (allowed.isObject()) {
1320 // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
1321 nsCOMPtr<nsISupports> prinOrSop;
1322 RootedObject obj(cx, &allowed.toObject());
1323 if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
1324 return false;
1326 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
1327 principal = do_QueryInterface(prinOrSop);
1328 if (sop)
1329 principal = sop->GetPrincipal();
1330 }
1331 NS_ENSURE_TRUE(principal, false);
1333 // We do not allow ExpandedPrincipals to contain any system principals.
1334 bool isSystem;
1335 rv = ssm->IsSystemPrincipal(principal, &isSystem);
1336 NS_ENSURE_SUCCESS(rv, false);
1337 if (isSystem) {
1338 JS_ReportError(cx, "System principal is not allowed in an expanded principal");
1339 return false;
1340 }
1341 allowedDomains[i] = principal;
1342 }
1344 nsCOMPtr<nsIExpandedPrincipal> result = new nsExpandedPrincipal(allowedDomains);
1345 result.forget(out);
1346 return true;
1347 }
1349 /*
1350 * Helper that tries to get a property from the options object.
1351 */
1352 bool
1353 OptionsBase::ParseValue(const char *name, MutableHandleValue prop, bool *aFound)
1354 {
1355 bool found;
1356 bool ok = JS_HasProperty(mCx, mObject, name, &found);
1357 NS_ENSURE_TRUE(ok, false);
1359 if (aFound)
1360 *aFound = found;
1362 if (!found)
1363 return true;
1365 return JS_GetProperty(mCx, mObject, name, prop);
1366 }
1368 /*
1369 * Helper that tries to get a boolean property from the options object.
1370 */
1371 bool
1372 OptionsBase::ParseBoolean(const char *name, bool *prop)
1373 {
1374 MOZ_ASSERT(prop);
1375 RootedValue value(mCx);
1376 bool found;
1377 bool ok = ParseValue(name, &value, &found);
1378 NS_ENSURE_TRUE(ok, false);
1380 if (!found)
1381 return true;
1383 if (!value.isBoolean()) {
1384 JS_ReportError(mCx, "Expected a boolean value for property %s", name);
1385 return false;
1386 }
1388 *prop = value.toBoolean();
1389 return true;
1390 }
1392 /*
1393 * Helper that tries to get an object property from the options object.
1394 */
1395 bool
1396 OptionsBase::ParseObject(const char *name, MutableHandleObject prop)
1397 {
1398 RootedValue value(mCx);
1399 bool found;
1400 bool ok = ParseValue(name, &value, &found);
1401 NS_ENSURE_TRUE(ok, false);
1403 if (!found)
1404 return true;
1406 if (!value.isObject()) {
1407 JS_ReportError(mCx, "Expected an object value for property %s", name);
1408 return false;
1409 }
1410 prop.set(&value.toObject());
1411 return true;
1412 }
1414 /*
1415 * Helper that tries to get a string property from the options object.
1416 */
1417 bool
1418 OptionsBase::ParseString(const char *name, nsCString &prop)
1419 {
1420 RootedValue value(mCx);
1421 bool found;
1422 bool ok = ParseValue(name, &value, &found);
1423 NS_ENSURE_TRUE(ok, false);
1425 if (!found)
1426 return true;
1428 if (!value.isString()) {
1429 JS_ReportError(mCx, "Expected a string value for property %s", name);
1430 return false;
1431 }
1433 char *tmp = JS_EncodeString(mCx, value.toString());
1434 NS_ENSURE_TRUE(tmp, false);
1435 prop.Adopt(tmp, strlen(tmp));
1436 return true;
1437 }
1439 /*
1440 * Helper that tries to get a string property from the options object.
1441 */
1442 bool
1443 OptionsBase::ParseString(const char *name, nsString &prop)
1444 {
1445 RootedValue value(mCx);
1446 bool found;
1447 bool ok = ParseValue(name, &value, &found);
1448 NS_ENSURE_TRUE(ok, false);
1450 if (!found)
1451 return true;
1453 if (!value.isString()) {
1454 JS_ReportError(mCx, "Expected a string value for property %s", name);
1455 return false;
1456 }
1458 nsDependentJSString strVal;
1459 strVal.init(mCx, value.toString());
1460 prop = strVal;
1461 return true;
1462 }
1464 /*
1465 * Helper that tries to get jsid property from the options object.
1466 */
1467 bool
1468 OptionsBase::ParseId(const char *name, MutableHandleId prop)
1469 {
1470 RootedValue value(mCx);
1471 bool found;
1472 bool ok = ParseValue(name, &value, &found);
1473 NS_ENSURE_TRUE(ok, false);
1475 if (!found)
1476 return true;
1478 return JS_ValueToId(mCx, value, prop);
1479 }
1481 /*
1482 * Helper that tries to get a list of DOM constructors and other helpers from the options object.
1483 */
1484 bool
1485 SandboxOptions::ParseGlobalProperties()
1486 {
1487 RootedValue value(mCx);
1488 bool found;
1489 bool ok = ParseValue("wantGlobalProperties", &value, &found);
1490 NS_ENSURE_TRUE(ok, false);
1491 if (!found)
1492 return true;
1494 if (!value.isObject()) {
1495 JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
1496 return false;
1497 }
1499 RootedObject ctors(mCx, &value.toObject());
1500 if (!JS_IsArrayObject(mCx, ctors)) {
1501 JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
1502 return false;
1503 }
1505 return globalProperties.Parse(mCx, ctors);
1506 }
1508 /*
1509 * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
1510 */
1511 bool
1512 SandboxOptions::Parse()
1513 {
1514 return ParseObject("sandboxPrototype", &proto) &&
1515 ParseBoolean("wantXrays", &wantXrays) &&
1516 ParseBoolean("wantComponents", &wantComponents) &&
1517 ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
1518 ParseString("sandboxName", sandboxName) &&
1519 ParseObject("sameZoneAs", &sameZoneAs) &&
1520 ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
1521 ParseBoolean("discardSource", &discardSource) &&
1522 ParseGlobalProperties() &&
1523 ParseValue("metadata", &metadata);
1524 }
1526 static nsresult
1527 AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName)
1528 {
1529 // Use a default name when the caller did not provide a sandboxName.
1530 if (sandboxName.IsEmpty())
1531 sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
1533 nsXPConnect* xpc = nsXPConnect::XPConnect();
1534 // Get the xpconnect native call context.
1535 nsAXPCNativeCallContext *cc = nullptr;
1536 xpc->GetCurrentNativeCallContext(&cc);
1537 NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
1539 // Get the current source info from xpc.
1540 nsCOMPtr<nsIStackFrame> frame;
1541 xpc->GetCurrentJSStack(getter_AddRefs(frame));
1543 // Append the caller's location information.
1544 if (frame) {
1545 nsString location;
1546 int32_t lineNumber = 0;
1547 frame->GetFilename(location);
1548 frame->GetLineNumber(&lineNumber);
1550 sandboxName.AppendLiteral(" (from: ");
1551 sandboxName.Append(NS_ConvertUTF16toUTF8(location));
1552 sandboxName.AppendLiteral(":");
1553 sandboxName.AppendInt(lineNumber);
1554 sandboxName.AppendLiteral(")");
1555 }
1557 return NS_OK;
1558 }
1560 // static
1561 nsresult
1562 nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
1563 JSContext *cx, HandleObject obj,
1564 const CallArgs &args, bool *_retval)
1565 {
1566 if (args.length() < 1)
1567 return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
1569 nsresult rv;
1570 bool ok = false;
1572 // Make sure to set up principals on the sandbox before initing classes.
1573 nsCOMPtr<nsIPrincipal> principal;
1574 nsCOMPtr<nsIExpandedPrincipal> expanded;
1575 nsCOMPtr<nsISupports> prinOrSop;
1577 if (args[0].isString()) {
1578 RootedString str(cx, args[0].toString());
1579 ok = ParsePrincipal(cx, str, getter_AddRefs(principal));
1580 prinOrSop = principal;
1581 } else if (args[0].isObject()) {
1582 RootedObject obj(cx, &args[0].toObject());
1583 if (JS_IsArrayObject(cx, obj)) {
1584 ok = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
1585 prinOrSop = expanded;
1586 } else {
1587 ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
1588 }
1589 }
1591 if (!ok)
1592 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1594 bool calledWithOptions = args.length() > 1;
1595 if (calledWithOptions && !args[1].isObject())
1596 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1598 RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
1599 : nullptr);
1601 SandboxOptions options(cx, optionsObject);
1602 if (calledWithOptions && !options.Parse())
1603 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1605 if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
1606 return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
1608 if (options.metadata.isNullOrUndefined()) {
1609 // If the caller is running in a sandbox, inherit.
1610 RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
1611 if (IsSandbox(callerGlobal)) {
1612 rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
1613 if (NS_WARN_IF(NS_FAILED(rv)))
1614 return rv;
1615 }
1616 }
1618 rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
1620 if (NS_FAILED(rv))
1621 return ThrowAndFail(rv, cx, _retval);
1623 *_retval = true;
1624 return NS_OK;
1625 }
1627 class ContextHolder : public nsIScriptObjectPrincipal
1628 {
1629 public:
1630 ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal);
1631 virtual ~ContextHolder();
1633 JSContext * GetJSContext()
1634 {
1635 return mJSContext;
1636 }
1638 nsIPrincipal * GetPrincipal() { return mPrincipal; }
1640 NS_DECL_ISUPPORTS
1642 private:
1643 JSContext* mJSContext;
1644 nsCOMPtr<nsIPrincipal> mPrincipal;
1645 };
1647 NS_IMPL_ISUPPORTS(ContextHolder, nsIScriptObjectPrincipal)
1649 ContextHolder::ContextHolder(JSContext *aOuterCx,
1650 HandleObject aSandbox,
1651 nsIPrincipal *aPrincipal)
1652 : mJSContext(JS_NewContext(JS_GetRuntime(aOuterCx), 1024)),
1653 mPrincipal(aPrincipal)
1654 {
1655 if (mJSContext) {
1656 bool isChrome;
1657 DebugOnly<nsresult> rv = XPCWrapper::GetSecurityManager()->
1658 IsSystemPrincipal(mPrincipal, &isChrome);
1659 MOZ_ASSERT(NS_SUCCEEDED(rv));
1661 JS::ContextOptionsRef(mJSContext).setDontReportUncaught(true)
1662 .setPrivateIsNSISupports(true);
1663 js::SetDefaultObjectForContext(mJSContext, aSandbox);
1664 JS_SetContextPrivate(mJSContext, this);
1665 }
1666 }
1668 ContextHolder::~ContextHolder()
1669 {
1670 if (mJSContext)
1671 JS_DestroyContextNoGC(mJSContext);
1672 }
1674 nsresult
1675 xpc::EvalInSandbox(JSContext *cx, HandleObject sandboxArg, const nsAString& source,
1676 const nsACString& filename, int32_t lineNo,
1677 JSVersion jsVersion, bool returnStringOnly, MutableHandleValue rval)
1678 {
1679 JS_AbortIfWrongThread(JS_GetRuntime(cx));
1680 rval.set(UndefinedValue());
1682 bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
1683 RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
1684 if (!sandbox || js::GetObjectJSClass(sandbox) != &SandboxClass) {
1685 return NS_ERROR_INVALID_ARG;
1686 }
1688 nsIScriptObjectPrincipal *sop =
1689 (nsIScriptObjectPrincipal*)xpc_GetJSPrivate(sandbox);
1690 MOZ_ASSERT(sop, "Invalid sandbox passed");
1691 nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
1692 NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
1694 nsAutoCString filenameBuf;
1695 if (!filename.IsVoid()) {
1696 filenameBuf.Assign(filename);
1697 } else {
1698 // Default to the spec of the principal.
1699 nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
1700 lineNo = 1;
1701 }
1703 // We create a separate cx to do the sandbox evaluation. Scope it.
1704 RootedValue v(cx, UndefinedValue());
1705 RootedValue exn(cx, UndefinedValue());
1706 bool ok = true;
1707 {
1708 // Make a special cx for the sandbox and push it.
1709 // NB: As soon as the RefPtr goes away, the cx goes away. So declare
1710 // it first so that it disappears last.
1711 nsRefPtr<ContextHolder> sandcxHolder = new ContextHolder(cx, sandbox, prin);
1712 JSContext *sandcx = sandcxHolder->GetJSContext();
1713 if (!sandcx) {
1714 JS_ReportError(cx, "Can't prepare context for evalInSandbox");
1715 return NS_ERROR_OUT_OF_MEMORY;
1716 }
1717 nsCxPusher pusher;
1718 pusher.Push(sandcx);
1719 JSAutoCompartment ac(sandcx, sandbox);
1721 JS::CompileOptions options(sandcx);
1722 options.setFileAndLine(filenameBuf.get(), lineNo);
1723 if (jsVersion != JSVERSION_DEFAULT)
1724 options.setVersion(jsVersion);
1725 JS::RootedObject rootedSandbox(sandcx, sandbox);
1726 ok = JS::Evaluate(sandcx, rootedSandbox, options,
1727 PromiseFlatString(source).get(), source.Length(), &v);
1728 if (ok && returnStringOnly && !v.isUndefined()) {
1729 JSString *str = ToString(sandcx, v);
1730 ok = !!str;
1731 v = ok ? JS::StringValue(str) : JS::UndefinedValue();
1732 }
1734 // If the sandbox threw an exception, grab it off the context.
1735 if (JS_GetPendingException(sandcx, &exn)) {
1736 MOZ_ASSERT(!ok);
1737 JS_ClearPendingException(sandcx);
1738 if (returnStringOnly) {
1739 // The caller asked for strings only, convert the
1740 // exception into a string.
1741 JSString *str = ToString(sandcx, exn);
1742 exn = str ? JS::StringValue(str) : JS::UndefinedValue();
1743 }
1744 }
1745 }
1747 //
1748 // Alright, we're back on the caller's cx. If an error occured, try to
1749 // wrap and set the exception. Otherwise, wrap the return value.
1750 //
1752 if (!ok) {
1753 // If we end up without an exception, it was probably due to OOM along
1754 // the way, in which case we thow. Otherwise, wrap it.
1755 if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
1756 return NS_ERROR_OUT_OF_MEMORY;
1758 // Set the exception on our caller's cx.
1759 JS_SetPendingException(cx, exn);
1760 return NS_ERROR_FAILURE;
1761 }
1763 // Transitively apply Xray waivers if |sb| was waived.
1764 if (waiveXray) {
1765 ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
1766 } else {
1767 ok = JS_WrapValue(cx, &v);
1768 }
1769 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1771 // Whew!
1772 rval.set(v);
1773 return NS_OK;
1774 }
1776 static bool
1777 NonCloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
1778 {
1779 CallArgs args = CallArgsFromVp(argc, vp);
1781 RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
1782 MOZ_ASSERT(v.isObject(), "weird function");
1784 RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
1785 if (!obj) {
1786 return false;
1787 }
1788 return JS_CallFunctionValue(cx, obj, v, args, args.rval());
1789 }
1791 /*
1792 * Forwards the call to the exported function. Clones all the non reflectors, ignores
1793 * the |this| argument.
1794 */
1795 static bool
1796 CloningFunctionForwarder(JSContext *cx, unsigned argc, Value *vp)
1797 {
1798 CallArgs args = CallArgsFromVp(argc, vp);
1800 RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
1801 NS_ASSERTION(v.isObject(), "weird function");
1802 RootedObject origFunObj(cx, UncheckedUnwrap(&v.toObject()));
1803 {
1804 JSAutoCompartment ac(cx, origFunObj);
1805 // Note: only the arguments are cloned not the |this| or the |callee|.
1806 // Function forwarder does not use those.
1807 for (unsigned i = 0; i < args.length(); i++) {
1808 if (!CloneNonReflectors(cx, args[i])) {
1809 return false;
1810 }
1811 }
1813 // JS API does not support any JSObject to JSFunction conversion,
1814 // so let's use JS_CallFunctionValue instead.
1815 RootedValue functionVal(cx, ObjectValue(*origFunObj));
1817 if (!JS_CallFunctionValue(cx, JS::NullPtr(), functionVal, args, args.rval()))
1818 return false;
1819 }
1821 // Return value must be wrapped.
1822 return JS_WrapValue(cx, args.rval());
1823 }
1825 bool
1826 xpc::NewFunctionForwarder(JSContext *cx, HandleId id, HandleObject callable, bool doclone,
1827 MutableHandleValue vp)
1828 {
1829 JSFunction *fun = js::NewFunctionByIdWithReserved(cx, doclone ? CloningFunctionForwarder :
1830 NonCloningFunctionForwarder,
1831 0,0, JS::CurrentGlobalOrNull(cx), id);
1833 if (!fun)
1834 return false;
1836 JSObject *funobj = JS_GetFunctionObject(fun);
1837 js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
1838 vp.setObject(*funobj);
1839 return true;
1840 }
1842 bool
1843 xpc::NewFunctionForwarder(JSContext *cx, HandleObject callable, bool doclone,
1844 MutableHandleValue vp)
1845 {
1846 RootedId emptyId(cx);
1847 RootedValue emptyStringValue(cx, JS_GetEmptyStringValue(cx));
1848 if (!JS_ValueToId(cx, emptyStringValue, &emptyId))
1849 return false;
1851 return NewFunctionForwarder(cx, emptyId, callable, doclone, vp);
1852 }
1855 nsresult
1856 xpc::GetSandboxMetadata(JSContext *cx, HandleObject sandbox, MutableHandleValue rval)
1857 {
1858 MOZ_ASSERT(NS_IsMainThread());
1859 MOZ_ASSERT(IsSandbox(sandbox));
1861 RootedValue metadata(cx);
1862 {
1863 JSAutoCompartment ac(cx, sandbox);
1864 metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
1865 }
1867 if (!JS_WrapValue(cx, &metadata))
1868 return NS_ERROR_UNEXPECTED;
1870 rval.set(metadata);
1871 return NS_OK;
1872 }
1874 nsresult
1875 xpc::SetSandboxMetadata(JSContext *cx, HandleObject sandbox, HandleValue metadataArg)
1876 {
1877 MOZ_ASSERT(NS_IsMainThread());
1878 MOZ_ASSERT(IsSandbox(sandbox));
1880 RootedValue metadata(cx);
1882 JSAutoCompartment ac(cx, sandbox);
1883 if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
1884 return NS_ERROR_UNEXPECTED;
1886 JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
1888 return NS_OK;
1889 }