nsJSPrincipals.cpp (11147B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsIPrincipal.h" 7 #include "xpcpublic.h" 8 #include "nsString.h" 9 #include "nsJSPrincipals.h" 10 #include "nsCOMPtr.h" 11 #include "mozilla/BasePrincipal.h" 12 #include "mozilla/StaticPtr.h" 13 #include "mozilla/dom/StructuredCloneTags.h" 14 #include "mozilla/ipc/BackgroundUtils.h" 15 #include "mozilla/ipc/PBackgroundSharedTypes.h" 16 17 using namespace mozilla; 18 using namespace mozilla::dom; 19 using namespace mozilla::ipc; 20 21 NS_IMETHODIMP_(MozExternalRefCountType) 22 nsJSPrincipals::AddRef() { 23 MOZ_ASSERT(int32_t(refcount) >= 0, "illegal refcnt"); 24 nsrefcnt count = ++refcount; 25 NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this)); 26 return count; 27 } 28 29 NS_IMETHODIMP_(MozExternalRefCountType) 30 nsJSPrincipals::Release() { 31 MOZ_ASSERT(0 != refcount, "dup release"); 32 nsrefcnt count = --refcount; 33 NS_LOG_RELEASE(this, count, "nsJSPrincipals"); 34 if (count == 0) { 35 delete this; 36 } 37 38 return count; 39 } 40 41 /* static */ 42 bool nsJSPrincipals::Subsume(JSPrincipals* jsprin, JSPrincipals* other) { 43 bool result; 44 nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes( 45 nsJSPrincipals::get(other), &result); 46 return NS_SUCCEEDED(rv) && result; 47 } 48 49 /* static */ 50 void nsJSPrincipals::Destroy(JSPrincipals* jsprin) { 51 // The JS runtime can call this method during the last GC when 52 // nsScriptSecurityManager is destroyed. So we must not assume here that 53 // the security manager still exists. 54 55 nsJSPrincipals* nsjsprin = nsJSPrincipals::get(jsprin); 56 57 // We need to destroy the nsIPrincipal. We'll do this by adding 58 // to the refcount and calling release 59 60 #ifdef NS_BUILD_REFCNT_LOGGING 61 // The refcount logging considers AddRef-to-1 to indicate creation, 62 // so trick it into thinking it's otherwise, but balance the 63 // Release() we do below. 64 nsjsprin->refcount++; 65 nsjsprin->AddRef(); 66 nsjsprin->refcount--; 67 #else 68 nsjsprin->refcount++; 69 #endif 70 nsjsprin->Release(); 71 } 72 73 #ifdef DEBUG 74 75 // Defined here so one can do principals->dump() in the debugger 76 JS_PUBLIC_API void JSPrincipals::dump() { 77 if (debugToken == nsJSPrincipals::DEBUG_TOKEN) { 78 nsAutoCString str; 79 nsresult rv = static_cast<nsJSPrincipals*>(this)->GetScriptLocation(str); 80 fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this), 81 NS_SUCCEEDED(rv) ? str.get() : "(unknown)"); 82 } else { 83 fprintf(stderr, 84 "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: " 85 "actual=0x%x expected=0x%x\n", 86 this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN)); 87 } 88 } 89 90 #endif 91 92 /* static */ 93 bool nsJSPrincipals::ReadPrincipals(JSContext* aCx, 94 JSStructuredCloneReader* aReader, 95 JSPrincipals** aOutPrincipals) { 96 uint32_t tag; 97 uint32_t unused; 98 if (!JS_ReadUint32Pair(aReader, &tag, &unused)) { 99 return false; 100 } 101 102 if (tag != SCTAG_DOM_NULL_PRINCIPAL && tag != SCTAG_DOM_SYSTEM_PRINCIPAL && 103 tag != SCTAG_DOM_CONTENT_PRINCIPAL && 104 tag != SCTAG_DOM_EXPANDED_PRINCIPAL) { 105 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 106 return false; 107 } 108 109 return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals); 110 } 111 112 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, 113 OriginAttributes& aAttrs, nsACString& aSpec, 114 nsACString& aOriginNoSuffix, 115 nsACString& aBaseDomain) { 116 uint32_t suffixLength, specLength; 117 if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) { 118 return false; 119 } 120 121 nsAutoCString suffix; 122 if (!suffix.SetLength(suffixLength, fallible)) { 123 return false; 124 } 125 126 if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) { 127 return false; 128 } 129 130 if (!aAttrs.PopulateFromSuffix(suffix)) { 131 return false; 132 } 133 134 if (!aSpec.SetLength(specLength, fallible)) { 135 return false; 136 } 137 138 if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) { 139 return false; 140 } 141 142 uint32_t originNoSuffixLength, dummy; 143 if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) { 144 return false; 145 } 146 147 MOZ_ASSERT(dummy == 0); 148 if (dummy != 0) { 149 return false; 150 } 151 152 if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) { 153 return false; 154 } 155 156 if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(), 157 originNoSuffixLength)) { 158 return false; 159 } 160 161 uint32_t baseDomainIsVoid, baseDomainLength; 162 if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) { 163 return false; 164 } 165 166 if (baseDomainIsVoid != 0 && baseDomainIsVoid != 1) { 167 return false; 168 } 169 170 if (baseDomainIsVoid) { 171 if (baseDomainLength != 0) { 172 return false; 173 } 174 175 aBaseDomain.SetIsVoid(true); 176 return true; 177 } 178 179 if (!aBaseDomain.SetLength(baseDomainLength, fallible)) { 180 return false; 181 } 182 183 if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) { 184 return false; 185 } 186 187 return true; 188 } 189 190 static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag, 191 PrincipalInfo& aInfo) { 192 if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) { 193 aInfo = SystemPrincipalInfo(); 194 } else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) { 195 OriginAttributes attrs; 196 nsAutoCString spec; 197 nsAutoCString originNoSuffix; 198 nsAutoCString baseDomain; 199 if (!::ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, 200 baseDomain)) { 201 return false; 202 } 203 aInfo = NullPrincipalInfo(attrs, spec); 204 } else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) { 205 uint32_t length, unused; 206 if (!JS_ReadUint32Pair(aReader, &length, &unused)) { 207 return false; 208 } 209 210 ExpandedPrincipalInfo expanded; 211 212 for (uint32_t i = 0; i < length; i++) { 213 uint32_t tag; 214 if (!JS_ReadUint32Pair(aReader, &tag, &unused)) { 215 return false; 216 } 217 218 PrincipalInfo sub; 219 if (!ReadPrincipalInfo(aReader, tag, sub)) { 220 return false; 221 } 222 expanded.allowlist().AppendElement(sub); 223 } 224 225 aInfo = expanded; 226 } else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) { 227 OriginAttributes attrs; 228 nsAutoCString spec; 229 nsAutoCString originNoSuffix; 230 nsAutoCString baseDomain; 231 if (!::ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, 232 baseDomain)) { 233 return false; 234 } 235 236 #ifdef FUZZING 237 if (originNoSuffix.IsEmpty()) { 238 return false; 239 } 240 #endif 241 242 MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty()); 243 244 // XXX: Do we care about mDomain for structured clone? 245 aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(), 246 baseDomain); 247 } else { 248 #ifdef FUZZING 249 return false; 250 #else 251 MOZ_CRASH("unexpected principal structured clone tag"); 252 #endif 253 } 254 255 return true; 256 } 257 258 /* static */ 259 bool nsJSPrincipals::ReadPrincipalInfo(JSStructuredCloneReader* aReader, 260 PrincipalInfo& aInfo) { 261 uint32_t tag, unused; 262 if (!JS_ReadUint32Pair(aReader, &tag, &unused)) { 263 return false; 264 } 265 return ::ReadPrincipalInfo(aReader, tag, aInfo); 266 } 267 268 /* static */ 269 bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx, 270 JSStructuredCloneReader* aReader, 271 uint32_t aTag, 272 JSPrincipals** aOutPrincipals) { 273 MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL || 274 aTag == SCTAG_DOM_SYSTEM_PRINCIPAL || 275 aTag == SCTAG_DOM_CONTENT_PRINCIPAL || 276 aTag == SCTAG_DOM_EXPANDED_PRINCIPAL); 277 278 PrincipalInfo info; 279 if (!::ReadPrincipalInfo(aReader, aTag, info)) { 280 return false; 281 } 282 283 auto principalOrErr = PrincipalInfoToPrincipal(info); 284 if (NS_WARN_IF(principalOrErr.isErr())) { 285 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 286 return false; 287 } 288 289 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 290 291 *aOutPrincipals = get(principal.forget().take()); 292 return true; 293 } 294 295 static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter, 296 const OriginAttributes& aAttrs, 297 const nsCString& aSpec, 298 const nsCString& aOriginNoSuffix, 299 const nsCString& aBaseDomain) { 300 nsAutoCString suffix; 301 aAttrs.CreateSuffix(suffix); 302 303 if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) && 304 JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) && 305 JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) && 306 JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) && 307 JS_WriteBytes(aWriter, aOriginNoSuffix.get(), 308 aOriginNoSuffix.Length()))) { 309 return false; 310 } 311 312 if (aBaseDomain.IsVoid()) { 313 return JS_WriteUint32Pair(aWriter, 1, 0); 314 } 315 316 return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) && 317 JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length()); 318 } 319 320 /* static */ 321 bool nsJSPrincipals::WritePrincipalInfo(JSStructuredCloneWriter* aWriter, 322 const PrincipalInfo& aInfo) { 323 if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) { 324 const NullPrincipalInfo& nullInfo = aInfo; 325 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) && 326 ::WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(), 327 ""_ns, ""_ns); 328 } 329 if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) { 330 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0); 331 } 332 if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) { 333 const ExpandedPrincipalInfo& expanded = aInfo; 334 if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) || 335 !JS_WriteUint32Pair(aWriter, expanded.allowlist().Length(), 0)) { 336 return false; 337 } 338 339 for (uint32_t i = 0; i < expanded.allowlist().Length(); i++) { 340 if (!WritePrincipalInfo(aWriter, expanded.allowlist()[i])) { 341 return false; 342 } 343 } 344 return true; 345 } 346 347 MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo); 348 const ContentPrincipalInfo& cInfo = aInfo; 349 return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) && 350 ::WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(), 351 cInfo.originNoSuffix(), cInfo.baseDomain()); 352 } 353 354 bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) { 355 PrincipalInfo info; 356 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) { 357 xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); 358 return false; 359 } 360 361 return WritePrincipalInfo(aWriter, info); 362 } 363 364 bool nsJSPrincipals::isSystemOrAddonPrincipal() { 365 JS::AutoSuppressGCAnalysis suppress; 366 return this->IsSystemPrincipal() || 367 this->GetIsAddonOrExpandedAddonPrincipal(); 368 }