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.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "FilteringWrapper.h" |
michael@0 | 8 | #include "AccessCheck.h" |
michael@0 | 9 | #include "ChromeObjectWrapper.h" |
michael@0 | 10 | #include "XrayWrapper.h" |
michael@0 | 11 | |
michael@0 | 12 | #include "jsapi.h" |
michael@0 | 13 | |
michael@0 | 14 | using namespace JS; |
michael@0 | 15 | using namespace js; |
michael@0 | 16 | |
michael@0 | 17 | namespace xpc { |
michael@0 | 18 | |
michael@0 | 19 | template <typename Base, typename Policy> |
michael@0 | 20 | FilteringWrapper<Base, Policy>::FilteringWrapper(unsigned flags) : Base(flags) |
michael@0 | 21 | { |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | template <typename Base, typename Policy> |
michael@0 | 25 | FilteringWrapper<Base, Policy>::~FilteringWrapper() |
michael@0 | 26 | { |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | template <typename Policy> |
michael@0 | 30 | static bool |
michael@0 | 31 | Filter(JSContext *cx, HandleObject wrapper, AutoIdVector &props) |
michael@0 | 32 | { |
michael@0 | 33 | size_t w = 0; |
michael@0 | 34 | RootedId id(cx); |
michael@0 | 35 | for (size_t n = 0; n < props.length(); ++n) { |
michael@0 | 36 | id = props[n]; |
michael@0 | 37 | if (Policy::check(cx, wrapper, id, Wrapper::GET)) |
michael@0 | 38 | props[w++] = id; |
michael@0 | 39 | else if (JS_IsExceptionPending(cx)) |
michael@0 | 40 | return false; |
michael@0 | 41 | } |
michael@0 | 42 | props.resize(w); |
michael@0 | 43 | return true; |
michael@0 | 44 | } |
michael@0 | 45 | |
michael@0 | 46 | template <typename Policy> |
michael@0 | 47 | static bool |
michael@0 | 48 | FilterSetter(JSContext *cx, JSObject *wrapper, jsid id, JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 49 | { |
michael@0 | 50 | bool setAllowed = Policy::check(cx, wrapper, id, Wrapper::SET); |
michael@0 | 51 | if (!setAllowed) { |
michael@0 | 52 | if (JS_IsExceptionPending(cx)) |
michael@0 | 53 | return false; |
michael@0 | 54 | desc.setSetter(nullptr); |
michael@0 | 55 | } |
michael@0 | 56 | return true; |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | template <typename Base, typename Policy> |
michael@0 | 60 | bool |
michael@0 | 61 | FilteringWrapper<Base, Policy>::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, |
michael@0 | 62 | HandleId id, |
michael@0 | 63 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 64 | { |
michael@0 | 65 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
michael@0 | 66 | if (!Base::getPropertyDescriptor(cx, wrapper, id, desc)) |
michael@0 | 67 | return false; |
michael@0 | 68 | return FilterSetter<Policy>(cx, wrapper, id, desc); |
michael@0 | 69 | } |
michael@0 | 70 | |
michael@0 | 71 | template <typename Base, typename Policy> |
michael@0 | 72 | bool |
michael@0 | 73 | FilteringWrapper<Base, Policy>::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, |
michael@0 | 74 | HandleId id, |
michael@0 | 75 | JS::MutableHandle<JSPropertyDescriptor> desc) |
michael@0 | 76 | { |
michael@0 | 77 | assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
michael@0 | 78 | if (!Base::getOwnPropertyDescriptor(cx, wrapper, id, desc)) |
michael@0 | 79 | return false; |
michael@0 | 80 | return FilterSetter<Policy>(cx, wrapper, id, desc); |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | template <typename Base, typename Policy> |
michael@0 | 84 | bool |
michael@0 | 85 | FilteringWrapper<Base, Policy>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, |
michael@0 | 86 | AutoIdVector &props) |
michael@0 | 87 | { |
michael@0 | 88 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
michael@0 | 89 | return Base::getOwnPropertyNames(cx, wrapper, props) && |
michael@0 | 90 | Filter<Policy>(cx, wrapper, props); |
michael@0 | 91 | } |
michael@0 | 92 | |
michael@0 | 93 | template <typename Base, typename Policy> |
michael@0 | 94 | bool |
michael@0 | 95 | FilteringWrapper<Base, Policy>::enumerate(JSContext *cx, HandleObject wrapper, |
michael@0 | 96 | AutoIdVector &props) |
michael@0 | 97 | { |
michael@0 | 98 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
michael@0 | 99 | return Base::enumerate(cx, wrapper, props) && |
michael@0 | 100 | Filter<Policy>(cx, wrapper, props); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | template <typename Base, typename Policy> |
michael@0 | 104 | bool |
michael@0 | 105 | FilteringWrapper<Base, Policy>::keys(JSContext *cx, HandleObject wrapper, |
michael@0 | 106 | AutoIdVector &props) |
michael@0 | 107 | { |
michael@0 | 108 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
michael@0 | 109 | return Base::keys(cx, wrapper, props) && |
michael@0 | 110 | Filter<Policy>(cx, wrapper, props); |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | template <typename Base, typename Policy> |
michael@0 | 114 | bool |
michael@0 | 115 | FilteringWrapper<Base, Policy>::iterate(JSContext *cx, HandleObject wrapper, |
michael@0 | 116 | unsigned flags, MutableHandleValue vp) |
michael@0 | 117 | { |
michael@0 | 118 | assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
michael@0 | 119 | // We refuse to trigger the iterator hook across chrome wrappers because |
michael@0 | 120 | // we don't know how to censor custom iterator objects. Instead we trigger |
michael@0 | 121 | // the default proxy iterate trap, which will ask enumerate() for the list |
michael@0 | 122 | // of (censored) ids. |
michael@0 | 123 | return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | template <typename Base, typename Policy> |
michael@0 | 127 | bool |
michael@0 | 128 | FilteringWrapper<Base, Policy>::nativeCall(JSContext *cx, JS::IsAcceptableThis test, |
michael@0 | 129 | JS::NativeImpl impl, JS::CallArgs args) |
michael@0 | 130 | { |
michael@0 | 131 | if (Policy::allowNativeCall(cx, test, impl)) |
michael@0 | 132 | return Base::Permissive::nativeCall(cx, test, impl, args); |
michael@0 | 133 | return Base::Restrictive::nativeCall(cx, test, impl, args); |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | template <typename Base, typename Policy> |
michael@0 | 137 | bool |
michael@0 | 138 | FilteringWrapper<Base, Policy>::defaultValue(JSContext *cx, HandleObject obj, |
michael@0 | 139 | JSType hint, MutableHandleValue vp) |
michael@0 | 140 | { |
michael@0 | 141 | return Base::defaultValue(cx, obj, hint, vp); |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | // With our entirely-opaque wrapper, the DefaultValue algorithm throws, |
michael@0 | 145 | // causing spurious exceptions. Manually implement something benign. |
michael@0 | 146 | template<> |
michael@0 | 147 | bool |
michael@0 | 148 | FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque> |
michael@0 | 149 | ::defaultValue(JSContext *cx, HandleObject obj, |
michael@0 | 150 | JSType hint, MutableHandleValue vp) |
michael@0 | 151 | { |
michael@0 | 152 | JSString *str = JS_NewStringCopyZ(cx, "[Opaque]"); |
michael@0 | 153 | if (!str) |
michael@0 | 154 | return false; |
michael@0 | 155 | vp.set(JS::StringValue(str)); |
michael@0 | 156 | return true; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | |
michael@0 | 160 | template <typename Base, typename Policy> |
michael@0 | 161 | bool |
michael@0 | 162 | FilteringWrapper<Base, Policy>::enter(JSContext *cx, HandleObject wrapper, |
michael@0 | 163 | HandleId id, Wrapper::Action act, bool *bp) |
michael@0 | 164 | { |
michael@0 | 165 | // This is a super ugly hacky to get around Xray Resolve wonkiness. |
michael@0 | 166 | // |
michael@0 | 167 | // Basically, XPCWN Xrays sometimes call into the Resolve hook of the |
michael@0 | 168 | // scriptable helper, and pass the wrapper itself as the object upon which |
michael@0 | 169 | // the resolve is happening. Then, special handling happens in |
michael@0 | 170 | // XrayWrapper::defineProperty to detect the resolve and redefine the |
michael@0 | 171 | // property on the holder. Really, we should just pass the holder itself to |
michael@0 | 172 | // NewResolve, but there's too much code in nsDOMClassInfo that assumes this |
michael@0 | 173 | // isn't the case (in particular, code expects to be able to look up |
michael@0 | 174 | // properties on the object, which doesn't work for the holder). Given that |
michael@0 | 175 | // these hooks are going away eventually with the new DOM bindings, let's |
michael@0 | 176 | // just hack around this for now. |
michael@0 | 177 | if (XrayUtils::IsXrayResolving(cx, wrapper, id)) { |
michael@0 | 178 | *bp = true; |
michael@0 | 179 | return true; |
michael@0 | 180 | } |
michael@0 | 181 | if (!Policy::check(cx, wrapper, id, act)) { |
michael@0 | 182 | *bp = JS_IsExceptionPending(cx) ? false : Policy::deny(act, id); |
michael@0 | 183 | return false; |
michael@0 | 184 | } |
michael@0 | 185 | *bp = true; |
michael@0 | 186 | return true; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | #define XOW FilteringWrapper<SecurityXrayXPCWN, CrossOriginAccessiblePropertiesOnly> |
michael@0 | 190 | #define DXOW FilteringWrapper<SecurityXrayDOM, CrossOriginAccessiblePropertiesOnly> |
michael@0 | 191 | #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque> |
michael@0 | 192 | #define NNXOWC FilteringWrapper<CrossCompartmentSecurityWrapper, OpaqueWithCall> |
michael@0 | 193 | #define GO FilteringWrapper<CrossCompartmentSecurityWrapper, GentlyOpaque> |
michael@0 | 194 | template<> XOW XOW::singleton(0); |
michael@0 | 195 | template<> DXOW DXOW::singleton(0); |
michael@0 | 196 | template<> NNXOW NNXOW::singleton(0); |
michael@0 | 197 | template<> NNXOWC NNXOWC::singleton(0); |
michael@0 | 198 | |
michael@0 | 199 | template<> GO GO::singleton(0); |
michael@0 | 200 | |
michael@0 | 201 | template class XOW; |
michael@0 | 202 | template class DXOW; |
michael@0 | 203 | template class NNXOW; |
michael@0 | 204 | template class NNXOWC; |
michael@0 | 205 | template class ChromeObjectWrapperBase; |
michael@0 | 206 | template class GO; |
michael@0 | 207 | } |