ExpandedPrincipal.cpp (13938B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 sw=2 et tw=80: */ 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 #include "ExpandedPrincipal.h" 8 #include "nsIClassInfoImpl.h" 9 #include "nsIObjectInputStream.h" 10 #include "nsReadableUtils.h" 11 #include "mozilla/Base64.h" 12 #include "mozilla/extensions/WebExtensionPolicy.h" 13 #include "mozilla/JSONWriter.h" 14 15 #include "js/JSON.h" 16 #include "ExpandedPrincipalJSONHandler.h" 17 #include "SubsumedPrincipalJSONHandler.h" 18 19 using namespace mozilla; 20 21 NS_IMPL_CLASSINFO(ExpandedPrincipal, nullptr, 0, NS_EXPANDEDPRINCIPAL_CID) 22 NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal, nsIPrincipal, 23 nsIExpandedPrincipal) 24 NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal, nsIPrincipal, 25 nsIExpandedPrincipal) 26 27 ExpandedPrincipal::ExpandedPrincipal( 28 nsTArray<nsCOMPtr<nsIPrincipal>>&& aPrincipals, 29 const nsACString& aOriginNoSuffix, const OriginAttributes& aAttrs) 30 : BasePrincipal(eExpandedPrincipal, aOriginNoSuffix, aAttrs), 31 mPrincipals(std::move(aPrincipals)) {} 32 33 ExpandedPrincipal::~ExpandedPrincipal() = default; 34 35 already_AddRefed<ExpandedPrincipal> ExpandedPrincipal::Create( 36 const nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList, 37 const OriginAttributes& aAttrs) { 38 nsTArray<nsCOMPtr<nsIPrincipal>> principals; 39 for (size_t i = 0; i < aAllowList.Length(); ++i) { 40 principals.AppendElement(aAllowList[i]); 41 } 42 43 nsAutoCString origin; 44 origin.AssignLiteral("[Expanded Principal ["); 45 StringJoinAppend( 46 origin, ", "_ns, principals, 47 [](nsACString& dest, const nsCOMPtr<nsIPrincipal>& principal) { 48 nsAutoCString subOrigin; 49 DebugOnly<nsresult> rv = principal->GetOrigin(subOrigin); 50 MOZ_ASSERT(NS_SUCCEEDED(rv)); 51 dest.Append(subOrigin); 52 }); 53 origin.AppendLiteral("]]"); 54 55 RefPtr<ExpandedPrincipal> ep = 56 new ExpandedPrincipal(std::move(principals), origin, aAttrs); 57 return ep.forget(); 58 } 59 60 NS_IMETHODIMP 61 ExpandedPrincipal::GetDomain(nsIURI** aDomain) { 62 *aDomain = nullptr; 63 return NS_OK; 64 } 65 66 NS_IMETHODIMP 67 ExpandedPrincipal::SetDomain(nsIURI* aDomain) { return NS_OK; } 68 69 bool ExpandedPrincipal::SubsumesInternal( 70 nsIPrincipal* aOther, 71 BasePrincipal::DocumentDomainConsideration aConsideration) { 72 // If aOther is an ExpandedPrincipal too, we break it down into its component 73 // nsIPrincipals, and check subsumes on each one. 74 if (Cast(aOther)->Is<ExpandedPrincipal>()) { 75 auto* expanded = Cast(aOther)->As<ExpandedPrincipal>(); 76 77 for (auto& other : expanded->AllowList()) { 78 // Use SubsumesInternal rather than Subsumes here, since OriginAttribute 79 // checks are only done between non-expanded sub-principals, and we don't 80 // need to incur the extra virtual call overhead. 81 if (!SubsumesInternal(other, aConsideration)) { 82 return false; 83 } 84 } 85 return true; 86 } 87 88 // We're dealing with a regular principal. One of our principals must subsume 89 // it. 90 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) { 91 if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) { 92 return true; 93 } 94 } 95 96 return false; 97 } 98 99 bool ExpandedPrincipal::MayLoadInternal(nsIURI* uri) { 100 for (uint32_t i = 0; i < mPrincipals.Length(); ++i) { 101 if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) { 102 return true; 103 } 104 } 105 106 return false; 107 } 108 109 NS_IMETHODIMP 110 ExpandedPrincipal::GetURI(nsIURI** aURI) { 111 *aURI = nullptr; 112 return NS_OK; 113 } 114 115 const nsTArray<nsCOMPtr<nsIPrincipal>>& ExpandedPrincipal::AllowList() { 116 return mPrincipals; 117 } 118 119 NS_IMETHODIMP 120 ExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) { 121 return NS_ERROR_NOT_AVAILABLE; 122 } 123 124 NS_IMETHODIMP 125 ExpandedPrincipal::GetAddonId(nsAString& aAddonId) { 126 aAddonId.Truncate(); 127 return NS_OK; 128 }; 129 130 bool ExpandedPrincipal::AddonHasPermission(const nsAtom* aPerm) { 131 for (size_t i = 0; i < mPrincipals.Length(); ++i) { 132 if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) { 133 return true; 134 } 135 } 136 return false; 137 } 138 139 bool ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI, 140 bool aExplicit /* = false */) { 141 for (const auto& principal : mPrincipals) { 142 if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) { 143 return true; 144 } 145 } 146 return false; 147 } 148 149 void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy* aCSP) { 150 AssertIsOnMainThread(); 151 mCSP = new nsMainThreadPtrHolder<nsIContentSecurityPolicy>( 152 "ExpandedPrincipal::mCSP", aCSP); 153 } 154 155 NS_IMETHODIMP 156 ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) { 157 AssertIsOnMainThread(); 158 NS_IF_ADDREF(*aCsp = mCSP); 159 return NS_OK; 160 } 161 162 nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) { 163 if (aRequestedURI) { 164 // If a given sub-principal subsumes the given URI, use that principal for 165 // inheritance. In general, this only happens with certain CORS modes, loads 166 // with forced principal inheritance, and creation of XML documents from 167 // XMLHttpRequests or fetch requests. For URIs that normally inherit a 168 // principal (such as data: URIs), we fall back to the last principal in the 169 // allowlist. 170 for (const auto& principal : mPrincipals) { 171 if (Cast(principal)->MayLoadInternal(aRequestedURI)) { 172 return principal; 173 } 174 } 175 } 176 return mPrincipals.LastElement(); 177 } 178 179 nsresult ExpandedPrincipal::GetScriptLocation(nsACString& aStr) { 180 aStr.AssignLiteral("[Expanded Principal ["); 181 for (size_t i = 0; i < mPrincipals.Length(); ++i) { 182 if (i != 0) { 183 aStr.AppendLiteral(", "); 184 } 185 186 nsAutoCString spec; 187 nsresult rv = 188 nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec); 189 NS_ENSURE_SUCCESS(rv, rv); 190 191 aStr.Append(spec); 192 } 193 aStr.AppendLiteral("]]"); 194 return NS_OK; 195 } 196 197 ////////////////////////////////////////// 198 // Methods implementing nsISerializable // 199 ////////////////////////////////////////// 200 201 // We've had way too many issues with unversioned serializations, so 202 // explicitly version this one. 203 static const uint32_t kSerializationVersion = 1; 204 205 NS_IMETHODIMP 206 ExpandedPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) { 207 uint32_t version; 208 nsresult rv = aStream->Read32(&version); 209 if (version != kSerializationVersion) { 210 MOZ_ASSERT(false, 211 "We really need to add handling of the old(?) version here"); 212 return NS_ERROR_UNEXPECTED; 213 } 214 215 uint32_t count; 216 rv = aStream->Read32(&count); 217 if (NS_FAILED(rv)) { 218 return rv; 219 } 220 221 nsTArray<nsCOMPtr<nsIPrincipal>> principals; 222 if (!principals.SetCapacity(count, fallible)) { 223 return NS_ERROR_OUT_OF_MEMORY; 224 } 225 226 for (uint32_t i = 0; i < count; ++i) { 227 nsCOMPtr<nsISupports> read; 228 rv = aStream->ReadObject(true, getter_AddRefs(read)); 229 if (NS_FAILED(rv)) { 230 return rv; 231 } 232 233 nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(read); 234 if (!principal) { 235 return NS_ERROR_UNEXPECTED; 236 } 237 238 principals.AppendElement(std::move(principal)); 239 } 240 241 mPrincipal = ExpandedPrincipal::Create(principals, OriginAttributes()); 242 return NS_OK; 243 } 244 245 nsresult ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) { 246 // Call GetSiteIdentifier on each of our principals and return a new 247 // ExpandedPrincipal. 248 249 nsTArray<nsCOMPtr<nsIPrincipal>> allowlist; 250 for (const auto& principal : mPrincipals) { 251 SiteIdentifier site; 252 nsresult rv = Cast(principal)->GetSiteIdentifier(site); 253 NS_ENSURE_SUCCESS(rv, rv); 254 allowlist.AppendElement(site.GetPrincipal()); 255 } 256 257 RefPtr<ExpandedPrincipal> expandedPrincipal = 258 ExpandedPrincipal::Create(allowlist, OriginAttributesRef()); 259 MOZ_ASSERT(expandedPrincipal, "ExpandedPrincipal::Create returned nullptr?"); 260 261 aSite.Init(expandedPrincipal); 262 return NS_OK; 263 } 264 265 nsresult ExpandedPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) { 266 aWriter.StartArrayProperty(JSONEnumKeyString<eSpecs>(), 267 JSONWriter::CollectionStyle::SingleLineStyle); 268 269 for (const auto& principal : mPrincipals) { 270 aWriter.StartObjectElement(JSONWriter::CollectionStyle::SingleLineStyle); 271 272 nsresult rv = BasePrincipal::Cast(principal)->WriteJSONProperties(aWriter); 273 NS_ENSURE_SUCCESS(rv, rv); 274 275 aWriter.EndObject(); 276 } 277 278 aWriter.EndArray(); 279 280 nsAutoCString suffix; 281 OriginAttributesRef().CreateSuffix(suffix); 282 if (suffix.Length() > 0) { 283 WriteJSONProperty<eSuffix>(aWriter, suffix); 284 } 285 286 return NS_OK; 287 } 288 289 bool ExpandedPrincipalJSONHandler::ProcessSubsumedResult(bool aResult) { 290 if (!aResult) { 291 NS_WARNING("Failed to parse subsumed principal"); 292 mState = State::Error; 293 return false; 294 } 295 return true; 296 } 297 298 bool ExpandedPrincipalJSONHandler::startObject() { 299 if (mSubsumedHandler.isSome()) { 300 return ProcessSubsumedResult(mSubsumedHandler->startObject()); 301 } 302 303 switch (mState) { 304 case State::Init: 305 mState = State::StartObject; 306 break; 307 case State::StartArray: 308 mState = State::SubsumedPrincipal; 309 [[fallthrough]]; 310 case State::SubsumedPrincipal: 311 mSubsumedHandler.emplace(); 312 313 return ProcessSubsumedResult(mSubsumedHandler->startObject()); 314 default: 315 NS_WARNING("Unexpected object value"); 316 mState = State::Error; 317 return false; 318 } 319 320 return true; 321 } 322 323 bool ExpandedPrincipalJSONHandler::propertyName(const JS::Latin1Char* name, 324 size_t length) { 325 if (mSubsumedHandler.isSome()) { 326 return ProcessSubsumedResult(mSubsumedHandler->propertyName(name, length)); 327 } 328 329 switch (mState) { 330 case State::StartObject: 331 case State::AfterPropertyValue: { 332 if (length != 1) { 333 NS_WARNING( 334 nsPrintfCString("Unexpected property name length: %zu", length) 335 .get()); 336 mState = State::Error; 337 return false; 338 } 339 340 char key = char(name[0]); 341 switch (key) { 342 case ExpandedPrincipal::SpecsKey: 343 mState = State::SpecsKey; 344 break; 345 case ExpandedPrincipal::SuffixKey: 346 mState = State::SuffixKey; 347 break; 348 default: 349 NS_WARNING( 350 nsPrintfCString("Unexpected property name: '%c'", key).get()); 351 mState = State::Error; 352 return false; 353 } 354 break; 355 } 356 default: 357 NS_WARNING("Unexpected property name"); 358 mState = State::Error; 359 return false; 360 } 361 362 return true; 363 } 364 365 bool ExpandedPrincipalJSONHandler::endObject() { 366 if (mSubsumedHandler.isSome()) { 367 if (!ProcessSubsumedResult(mSubsumedHandler->endObject())) { 368 return false; 369 } 370 if (mSubsumedHandler->HasAccepted()) { 371 nsCOMPtr<nsIPrincipal> principal = mSubsumedHandler->mPrincipal.forget(); 372 mSubsumedHandler.reset(); 373 mAllowList.AppendElement(principal); 374 } 375 return true; 376 } 377 378 switch (mState) { 379 case State::AfterPropertyValue: 380 mPrincipal = ExpandedPrincipal::Create(mAllowList, mAttrs); 381 MOZ_ASSERT(mPrincipal); 382 383 mState = State::EndObject; 384 break; 385 default: 386 NS_WARNING("Unexpected end of object"); 387 mState = State::Error; 388 return false; 389 } 390 391 return true; 392 } 393 394 bool ExpandedPrincipalJSONHandler::startArray() { 395 switch (mState) { 396 case State::SpecsKey: 397 mState = State::StartArray; 398 break; 399 default: 400 NS_WARNING("Unexpected array value"); 401 mState = State::Error; 402 return false; 403 } 404 405 return true; 406 } 407 408 bool ExpandedPrincipalJSONHandler::endArray() { 409 switch (mState) { 410 case State::SubsumedPrincipal: { 411 mState = State::AfterPropertyValue; 412 break; 413 } 414 default: 415 NS_WARNING("Unexpected end of array"); 416 mState = State::Error; 417 return false; 418 } 419 420 return true; 421 } 422 423 bool ExpandedPrincipalJSONHandler::stringValue(const JS::Latin1Char* str, 424 size_t length) { 425 if (mSubsumedHandler.isSome()) { 426 return ProcessSubsumedResult(mSubsumedHandler->stringValue(str, length)); 427 } 428 429 switch (mState) { 430 case State::SpecsKey: { 431 nsDependentCSubstring specs(reinterpret_cast<const char*>(str), length); 432 433 for (const nsACString& each : specs.Split(',')) { 434 nsAutoCString result; 435 nsresult rv = Base64Decode(each, result); 436 MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode"); 437 if (NS_FAILED(rv)) { 438 mState = State::Error; 439 return false; 440 } 441 442 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result); 443 if (!principal) { 444 mState = State::Error; 445 return false; 446 } 447 mAllowList.AppendElement(principal); 448 } 449 450 mState = State::AfterPropertyValue; 451 break; 452 } 453 case State::SuffixKey: { 454 nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length); 455 if (!mAttrs.PopulateFromSuffix(attrs)) { 456 mState = State::Error; 457 return false; 458 } 459 460 mState = State::AfterPropertyValue; 461 break; 462 } 463 default: 464 NS_WARNING("Unexpected string value"); 465 mState = State::Error; 466 return false; 467 } 468 469 return true; 470 } 471 472 NS_IMETHODIMP 473 ExpandedPrincipal::IsThirdPartyURI(nsIURI* aURI, bool* aRes) { 474 // ExpandedPrincipal for extension content scripts consist of two principals, 475 // the document's principal and the extension's principal. 476 // To make sure that the third-party check behaves like the web page on which 477 // the content script is running, ignore the extension's principal. 478 479 for (const auto& principal : mPrincipals) { 480 if (!Cast(principal)->AddonPolicyCore()) { 481 return Cast(principal)->IsThirdPartyURI(aURI, aRes); 482 } 483 } 484 485 if (mPrincipals.IsEmpty()) { 486 *aRes = true; 487 return NS_OK; 488 } 489 490 return Cast(mPrincipals[0])->IsThirdPartyURI(aURI, aRes); 491 }