NullPrincipal.cpp (11627B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 sts=2 ts=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 /** 8 * This is the principal that has no rights and can't be accessed by 9 * anything other than itself and chrome; null principals are not 10 * same-origin with anything but themselves. 11 */ 12 13 #include "mozilla/dom/BlobURLProtocolHandler.h" 14 #include "mozilla/StaticPrefs_network.h" 15 #include "nsDocShell.h" 16 #include "NullPrincipal.h" 17 #include "DefaultURI.h" 18 #include "nsSimpleURI.h" 19 #include "nsIClassInfoImpl.h" 20 #include "nsNetCID.h" 21 #include "nsError.h" 22 #include "nsEscape.h" 23 #include "ContentPrincipal.h" 24 #include "nsScriptSecurityManager.h" 25 #include "pratom.h" 26 #include "nsIObjectInputStream.h" 27 28 #include "js/JSON.h" 29 #include "NullPrincipalJSONHandler.h" 30 31 using namespace mozilla; 32 33 NS_IMPL_CLASSINFO(NullPrincipal, nullptr, 0, NS_NULLPRINCIPAL_CID) 34 NS_IMPL_QUERY_INTERFACE_CI(NullPrincipal, nsIPrincipal) 35 NS_IMPL_CI_INTERFACE_GETTER(NullPrincipal, nsIPrincipal) 36 37 NullPrincipal::NullPrincipal(nsIURI* aURI, const nsACString& aOriginNoSuffix, 38 const OriginAttributes& aOriginAttributes) 39 : BasePrincipal(eNullPrincipal, aOriginNoSuffix, aOriginAttributes), 40 mURI(aURI) {} 41 42 /* static */ 43 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithInheritedAttributes( 44 nsIPrincipal* aInheritFrom) { 45 MOZ_ASSERT(aInheritFrom); 46 nsCOMPtr<nsIURI> uri = CreateURI(aInheritFrom); 47 return Create(Cast(aInheritFrom)->OriginAttributesRef(), uri); 48 } 49 50 /* static */ 51 already_AddRefed<NullPrincipal> NullPrincipal::Create( 52 const OriginAttributes& aOriginAttributes, nsIURI* aNullPrincipalURI) { 53 nsCOMPtr<nsIURI> uri = aNullPrincipalURI; 54 if (!uri) { 55 uri = NullPrincipal::CreateURI(nullptr); 56 } 57 58 MOZ_RELEASE_ASSERT(uri->SchemeIs(NS_NULLPRINCIPAL_SCHEME)); 59 60 nsAutoCString originNoSuffix; 61 DebugOnly<nsresult> rv = uri->GetSpec(originNoSuffix); 62 MOZ_ASSERT(NS_SUCCEEDED(rv)); 63 64 RefPtr<NullPrincipal> nullPrin = 65 new NullPrincipal(uri, originNoSuffix, aOriginAttributes); 66 return nullPrin.forget(); 67 } 68 69 /* static */ 70 already_AddRefed<NullPrincipal> NullPrincipal::CreateWithoutOriginAttributes() { 71 return NullPrincipal::Create(OriginAttributes(), nullptr); 72 } 73 74 void NullPrincipal::EscapePrecursorQuery(nsACString& aPrecursorQuery) { 75 // origins should not contain existing escape sequences, so set `esc_Forced` 76 // to force any `%` in the input to be escaped in addition to non-ascii, 77 // control characters and DEL. 78 nsCString modified; 79 if (NS_EscapeURLSpan(aPrecursorQuery, esc_Query | esc_Forced, modified)) { 80 aPrecursorQuery.Assign(std::move(modified)); 81 } 82 } 83 84 void NullPrincipal::UnescapePrecursorQuery(nsACString& aPrecursorQuery) { 85 nsCString modified; 86 if (NS_UnescapeURL(aPrecursorQuery.BeginReading(), aPrecursorQuery.Length(), 87 /* aFlags */ 0, modified)) { 88 aPrecursorQuery.Assign(std::move(modified)); 89 } 90 } 91 92 already_AddRefed<nsIURI> NullPrincipal::CreateURI( 93 nsIPrincipal* aPrecursor, const nsID* aNullPrincipalID) { 94 nsCOMPtr<nsIURIMutator> iMutator; 95 if (StaticPrefs::network_url_useDefaultURI()) { 96 iMutator = new mozilla::net::DefaultURI::Mutator(); 97 } else { 98 iMutator = new mozilla::net::nsSimpleURI::Mutator(); 99 } 100 101 nsID uuid = aNullPrincipalID ? *aNullPrincipalID : nsID::GenerateUUID(); 102 103 NS_MutateURI mutator(iMutator); 104 mutator.SetSpec(NS_NULLPRINCIPAL_SCHEME ":"_ns + 105 nsDependentCString(nsIDToCString(uuid).get())); 106 107 // If there's a precursor URI, encode it in the null principal URI's query. 108 if (aPrecursor) { 109 nsAutoCString precursorOrigin; 110 switch (BasePrincipal::Cast(aPrecursor)->Kind()) { 111 case eNullPrincipal: { 112 // If the precursor null principal has a precursor, inherit it. 113 if (nsCOMPtr<nsIURI> nullPrecursorURI = aPrecursor->GetURI()) { 114 MOZ_ALWAYS_SUCCEEDS(nullPrecursorURI->GetQuery(precursorOrigin)); 115 } 116 break; 117 } 118 case eContentPrincipal: { 119 MOZ_ALWAYS_SUCCEEDS(aPrecursor->GetOriginNoSuffix(precursorOrigin)); 120 #ifdef DEBUG 121 nsAutoCString original(precursorOrigin); 122 #endif 123 EscapePrecursorQuery(precursorOrigin); 124 #ifdef DEBUG 125 nsAutoCString unescaped(precursorOrigin); 126 UnescapePrecursorQuery(unescaped); 127 MOZ_ASSERT(unescaped == original, 128 "cannot recover original precursor origin after escape"); 129 #endif 130 break; 131 } 132 133 // For now, we won't track expanded or system principal precursors. We may 134 // want to track expanded principal precursors in the future, but it's 135 // unlikely we'll want to track system principal precursors. 136 case eExpandedPrincipal: 137 case eSystemPrincipal: 138 break; 139 } 140 if (!precursorOrigin.IsEmpty()) { 141 mutator.SetQuery(precursorOrigin); 142 } 143 } 144 145 nsCOMPtr<nsIURI> uri; 146 MOZ_ALWAYS_SUCCEEDS(mutator.Finalize(getter_AddRefs(uri))); 147 return uri.forget(); 148 } 149 150 nsresult NullPrincipal::GetScriptLocation(nsACString& aStr) { 151 return mURI->GetSpec(aStr); 152 } 153 154 /** 155 * nsIPrincipal implementation 156 */ 157 158 NS_IMETHODIMP 159 NullPrincipal::GetURI(nsIURI** aURI) { 160 nsCOMPtr<nsIURI> uri = mURI; 161 uri.forget(aURI); 162 return NS_OK; 163 } 164 NS_IMETHODIMP 165 NullPrincipal::GetIsOriginPotentiallyTrustworthy(bool* aResult) { 166 *aResult = false; 167 return NS_OK; 168 } 169 170 NS_IMETHODIMP 171 NullPrincipal::GetDomain(nsIURI** aDomain) { 172 nsCOMPtr<nsIURI> uri = mURI; 173 uri.forget(aDomain); 174 return NS_OK; 175 } 176 177 NS_IMETHODIMP 178 NullPrincipal::SetDomain(nsIURI* aDomain) { 179 // I think the right thing to do here is to just throw... Silently failing 180 // seems counterproductive. 181 return NS_ERROR_NOT_AVAILABLE; 182 } 183 184 bool NullPrincipal::MayLoadInternal(nsIURI* aURI) { 185 // Also allow the load if we are the principal of the URI being checked. 186 nsCOMPtr<nsIPrincipal> blobPrincipal; 187 if (dom::BlobURLProtocolHandler::GetBlobURLPrincipal( 188 aURI, getter_AddRefs(blobPrincipal))) { 189 MOZ_ASSERT(blobPrincipal); 190 return SubsumesInternal(blobPrincipal, 191 BasePrincipal::ConsiderDocumentDomain); 192 } 193 194 return false; 195 } 196 197 NS_IMETHODIMP 198 NullPrincipal::GetBaseDomain(nsACString& aBaseDomain) { 199 // For a null principal, we use our unique uuid as the base domain. 200 return mURI->GetPathQueryRef(aBaseDomain); 201 } 202 203 NS_IMETHODIMP 204 NullPrincipal::GetAddonId(nsAString& aAddonId) { 205 aAddonId.Truncate(); 206 return NS_OK; 207 }; 208 209 /** 210 * nsISerializable implementation 211 */ 212 NS_IMETHODIMP 213 NullPrincipal::Deserializer::Read(nsIObjectInputStream* aStream) { 214 nsAutoCString spec; 215 nsresult rv = aStream->ReadCString(spec); 216 NS_ENSURE_SUCCESS(rv, rv); 217 218 nsCOMPtr<nsIURI> uri; 219 rv = NS_NewURI(getter_AddRefs(uri), spec); 220 NS_ENSURE_SUCCESS(rv, rv); 221 222 nsAutoCString suffix; 223 rv = aStream->ReadCString(suffix); 224 NS_ENSURE_SUCCESS(rv, rv); 225 226 OriginAttributes attrs; 227 bool ok = attrs.PopulateFromSuffix(suffix); 228 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); 229 230 mPrincipal = NullPrincipal::Create(attrs, uri); 231 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE); 232 233 return NS_OK; 234 } 235 236 nsresult NullPrincipal::WriteJSONInnerProperties(JSONWriter& aWriter) { 237 nsAutoCString principalURI; 238 nsresult rv = mURI->GetSpec(principalURI); 239 NS_ENSURE_SUCCESS(rv, rv); 240 WriteJSONProperty<eSpec>(aWriter, principalURI); 241 242 nsAutoCString suffix; 243 OriginAttributesRef().CreateSuffix(suffix); 244 if (suffix.Length() > 0) { 245 WriteJSONProperty<eSuffix>(aWriter, suffix); 246 } 247 248 return NS_OK; 249 } 250 251 NS_IMETHODIMP 252 NullPrincipal::GetPrecursorPrincipal(nsIPrincipal** aPrincipal) { 253 *aPrincipal = nullptr; 254 255 nsAutoCString query; 256 if (NS_FAILED(mURI->GetQuery(query)) || query.IsEmpty()) { 257 return NS_OK; 258 } 259 UnescapePrecursorQuery(query); 260 261 nsCOMPtr<nsIURI> precursorURI; 262 if (NS_FAILED(NS_NewURI(getter_AddRefs(precursorURI), query))) { 263 MOZ_ASSERT_UNREACHABLE( 264 "Failed to parse precursor from nullprincipal query"); 265 return NS_OK; 266 } 267 268 // If our precursor is another null principal, re-construct it. This can 269 // happen if a null principal without a precursor causes another principal to 270 // be created. 271 if (precursorURI->SchemeIs(NS_NULLPRINCIPAL_SCHEME)) { 272 #ifdef DEBUG 273 nsAutoCString precursorQuery; 274 precursorURI->GetQuery(precursorQuery); 275 MOZ_ASSERT(precursorQuery.IsEmpty(), 276 "Null principal with nested precursors?"); 277 #endif 278 *aPrincipal = 279 NullPrincipal::Create(OriginAttributesRef(), precursorURI).take(); 280 return NS_OK; 281 } 282 283 RefPtr<BasePrincipal> contentPrincipal = 284 BasePrincipal::CreateContentPrincipal(precursorURI, 285 OriginAttributesRef()); 286 // If `CreateContentPrincipal` failed, it will create a new NullPrincipal and 287 // return that instead. We only want to return real content principals here. 288 if (!contentPrincipal || !contentPrincipal->Is<ContentPrincipal>()) { 289 return NS_OK; 290 } 291 contentPrincipal.forget(aPrincipal); 292 return NS_OK; 293 } 294 295 bool NullPrincipalJSONHandler::startObject() { 296 switch (mState) { 297 case State::Init: 298 mState = State::StartObject; 299 break; 300 default: 301 NS_WARNING("Unexpected object value"); 302 mState = State::Error; 303 return false; 304 } 305 306 return true; 307 } 308 309 bool NullPrincipalJSONHandler::propertyName(const JS::Latin1Char* name, 310 size_t length) { 311 switch (mState) { 312 case State::StartObject: 313 case State::AfterPropertyValue: { 314 if (length != 1) { 315 NS_WARNING( 316 nsPrintfCString("Unexpected property name length: %zu", length) 317 .get()); 318 mState = State::Error; 319 return false; 320 } 321 322 char key = char(name[0]); 323 switch (key) { 324 case NullPrincipal::SpecKey: 325 mState = State::SpecKey; 326 break; 327 case NullPrincipal::SuffixKey: 328 mState = State::SuffixKey; 329 break; 330 default: 331 NS_WARNING( 332 nsPrintfCString("Unexpected property name: '%c'", key).get()); 333 mState = State::Error; 334 return false; 335 } 336 break; 337 } 338 default: 339 NS_WARNING("Unexpected property name"); 340 mState = State::Error; 341 return false; 342 } 343 344 return true; 345 } 346 347 bool NullPrincipalJSONHandler::endObject() { 348 switch (mState) { 349 case State::AfterPropertyValue: 350 MOZ_ASSERT(mUri); 351 352 mPrincipal = NullPrincipal::Create(mAttrs, mUri); 353 MOZ_ASSERT(mPrincipal); 354 355 mState = State::EndObject; 356 break; 357 default: 358 NS_WARNING("Unexpected end of object"); 359 mState = State::Error; 360 return false; 361 } 362 363 return true; 364 } 365 366 bool NullPrincipalJSONHandler::stringValue(const JS::Latin1Char* str, 367 size_t length) { 368 switch (mState) { 369 case State::SpecKey: { 370 nsDependentCSubstring spec(reinterpret_cast<const char*>(str), length); 371 nsresult rv = NS_NewURI(getter_AddRefs(mUri), spec); 372 if (NS_FAILED(rv)) { 373 mState = State::Error; 374 return false; 375 } 376 377 mState = State::AfterPropertyValue; 378 break; 379 } 380 case State::SuffixKey: { 381 nsDependentCSubstring attrs(reinterpret_cast<const char*>(str), length); 382 if (!mAttrs.PopulateFromSuffix(attrs)) { 383 mState = State::Error; 384 return false; 385 } 386 387 mState = State::AfterPropertyValue; 388 break; 389 } 390 default: 391 NS_WARNING("Unexpected string value"); 392 mState = State::Error; 393 return false; 394 } 395 396 return true; 397 }