nsAboutProtocolHandler.cpp (12797B)
1 /* -*- Mode: C++; tab-width: 2; 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 "base/basictypes.h" 7 8 #include "nsAboutProtocolHandler.h" 9 #include "nsIURI.h" 10 #include "nsIAboutModule.h" 11 #include "nsContentUtils.h" 12 #include "nsString.h" 13 #include "nsNetCID.h" 14 #include "nsAboutProtocolUtils.h" 15 #include "nsError.h" 16 #include "nsNetUtil.h" 17 #include "nsIObjectInputStream.h" 18 #include "nsIObjectOutputStream.h" 19 #include "nsIWritablePropertyBag2.h" 20 #include "nsIChannel.h" 21 #include "nsIScriptError.h" 22 #include "nsIClassInfoImpl.h" 23 24 #include "mozilla/ipc/URIUtils.h" 25 26 namespace mozilla { 27 namespace net { 28 29 static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID); 30 31 static bool IsSafeToLinkForUntrustedContent(nsIURI* aURI) { 32 nsAutoCString path; 33 aURI->GetPathQueryRef(path); 34 35 int32_t f = path.FindChar('#'); 36 if (f >= 0) { 37 path.SetLength(f); 38 } 39 40 f = path.FindChar('?'); 41 if (f >= 0) { 42 path.SetLength(f); 43 } 44 45 ToLowerCase(path); 46 47 // The about modules for these URL types have the 48 // URI_SAFE_FOR_UNTRUSTED_CONTENT and MAKE_LINKABLE flags set. 49 return path.EqualsLiteral("blank") || path.EqualsLiteral("logo") || 50 path.EqualsLiteral("srcdoc"); 51 } 52 53 //////////////////////////////////////////////////////////////////////////////// 54 55 NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler, 56 nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference) 57 58 //////////////////////////////////////////////////////////////////////////////// 59 // nsIProtocolHandler methods: 60 61 NS_IMETHODIMP 62 nsAboutProtocolHandler::GetScheme(nsACString& result) { 63 result.AssignLiteral("about"); 64 return NS_OK; 65 } 66 67 NS_IMETHODIMP 68 nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags) { 69 // First use the default (which is "unsafe for content"): 70 *aFlags = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | 71 URI_SCHEME_NOT_SELF_LINKABLE; 72 73 // Now try to see if this URI overrides the default: 74 nsCOMPtr<nsIAboutModule> aboutMod; 75 nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod)); 76 if (NS_FAILED(rv)) { 77 // Swallow this and just tell the consumer the default: 78 return NS_OK; 79 } 80 uint32_t aboutModuleFlags = 0; 81 rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags); 82 // This should never happen, so pass back the error: 83 NS_ENSURE_SUCCESS(rv, rv); 84 85 // Secure (https) pages can load safe about pages without becoming 86 // mixed content. 87 if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) { 88 *aFlags |= URI_IS_POTENTIALLY_TRUSTWORTHY; 89 // about: pages can only be loaded by unprivileged principals 90 // if they are marked as LINKABLE 91 if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) { 92 // Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE. 93 *aFlags &= ~URI_DANGEROUS_TO_LOAD; 94 *aFlags |= URI_LOADABLE_BY_ANYONE; 95 } 96 } 97 return NS_OK; 98 } 99 100 // static 101 nsresult nsAboutProtocolHandler::CreateNewURI(const nsACString& aSpec, 102 const char* aCharset, 103 nsIURI* aBaseURI, 104 nsIURI** aResult) { 105 *aResult = nullptr; 106 nsresult rv; 107 108 // Use a simple URI to parse out some stuff first 109 nsCOMPtr<nsIURI> url; 110 rv = NS_MutateURI(new nsSimpleURI::Mutator()).SetSpec(aSpec).Finalize(url); 111 112 if (NS_FAILED(rv)) { 113 return rv; 114 } 115 116 if (IsSafeToLinkForUntrustedContent(url)) { 117 // We need to indicate that this baby is safe. Use an inner URI that 118 // no one but the security manager will see. Make sure to preserve our 119 // path, in case someone decides to hardcode checks for particular 120 // about: URIs somewhere. 121 nsAutoCString spec; 122 rv = url->GetPathQueryRef(spec); 123 NS_ENSURE_SUCCESS(rv, rv); 124 125 spec.InsertLiteral("moz-safe-about:", 0); 126 127 nsCOMPtr<nsIURI> inner; 128 rv = NS_NewURI(getter_AddRefs(inner), spec); 129 NS_ENSURE_SUCCESS(rv, rv); 130 131 rv = NS_MutateURI(new nsNestedAboutURI::Mutator()) 132 .Apply(&nsINestedAboutURIMutator::InitWithBase, inner, aBaseURI) 133 .SetSpec(aSpec) 134 .Finalize(url); 135 NS_ENSURE_SUCCESS(rv, rv); 136 } 137 138 url.swap(*aResult); 139 return NS_OK; 140 } 141 142 NS_IMETHODIMP 143 nsAboutProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, 144 nsIChannel** result) { 145 NS_ENSURE_ARG_POINTER(uri); 146 147 // about:what you ask? 148 nsCOMPtr<nsIAboutModule> aboutMod; 149 nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod)); 150 151 nsAutoCString path; 152 if (NS_SUCCEEDED(NS_GetAboutModuleName(uri, path)) && 153 path.EqualsLiteral("srcdoc")) { 154 // about:srcdoc is meant to be unresolvable, yet is included in the 155 // about lookup tables so that it can pass security checks when used in 156 // a srcdoc iframe. To ensure that it stays unresolvable, we pretend 157 // that it doesn't exist. 158 return NS_ERROR_MALFORMED_URI; 159 } 160 161 if (NS_FAILED(rv)) { 162 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) { 163 // This looks like an about: we don't know about. Convert 164 // this to an invalid URI error. 165 return NS_ERROR_MALFORMED_URI; 166 } 167 168 return rv; 169 } 170 171 uint32_t flags = 0; 172 if (NS_FAILED(aboutMod->GetURIFlags(uri, &flags))) { 173 return NS_ERROR_FAILURE; 174 } 175 176 bool safeForUntrustedContent = 177 (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0; 178 179 MOZ_DIAGNOSTIC_ASSERT( 180 safeForUntrustedContent || 181 (flags & (nsIAboutModule::URI_CAN_LOAD_IN_CHILD | 182 nsIAboutModule::URI_MUST_LOAD_IN_CHILD)) == 0, 183 "Only unprivileged content should be loaded in child processes. (Did " 184 "you forget to add URI_SAFE_FOR_UNTRUSTED_CONTENT to your about: " 185 "page?)"); 186 187 // The standard return case: 188 rv = aboutMod->NewChannel(uri, aLoadInfo, result); 189 if (NS_FAILED(rv)) { 190 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) { 191 // This looks like an about: we don't know about. Convert 192 // this to an invalid URI error. 193 return NS_ERROR_MALFORMED_URI; 194 } 195 196 return rv; 197 } 198 199 // Not all implementations of nsIAboutModule::NewChannel() 200 // set the LoadInfo on the newly created channel yet, as 201 // an interim solution we set the LoadInfo here if not 202 // available on the channel. Bug 1087720 203 nsCOMPtr<nsILoadInfo> loadInfo = (*result)->LoadInfo(); 204 if (aLoadInfo != loadInfo) { 205 NS_ASSERTION(false, 206 "nsIAboutModule->newChannel(aURI, aLoadInfo) needs to " 207 "set LoadInfo"); 208 AutoTArray<nsString, 2> params = { 209 u"nsIAboutModule->newChannel(aURI)"_ns, 210 u"nsIAboutModule->newChannel(aURI, aLoadInfo)"_ns}; 211 nsContentUtils::ReportToConsole( 212 nsIScriptError::warningFlag, "Security by Default"_ns, 213 nullptr, // aDocument 214 nsContentUtils::eNECKO_PROPERTIES, "APIDeprecationWarning", params); 215 (*result)->SetLoadInfo(aLoadInfo); 216 } 217 218 // If this URI is safe for untrusted content, enforce that its 219 // principal be based on the channel's originalURI by setting the 220 // owner to null. 221 // Note: this relies on aboutMod's newChannel implementation 222 // having set the proper originalURI, which probably isn't ideal. 223 if (safeForUntrustedContent) { 224 (*result)->SetOwner(nullptr); 225 } 226 227 RefPtr<nsNestedAboutURI> aboutURI; 228 if (NS_SUCCEEDED( 229 uri->QueryInterface(kNestedAboutURICID, getter_AddRefs(aboutURI))) && 230 aboutURI->GetBaseURI()) { 231 nsCOMPtr<nsIWritablePropertyBag2> writableBag = do_QueryInterface(*result); 232 if (writableBag) { 233 writableBag->SetPropertyAsInterface(u"baseURI"_ns, 234 aboutURI->GetBaseURI()); 235 } 236 } 237 238 return NS_OK; 239 } 240 241 NS_IMETHODIMP 242 nsAboutProtocolHandler::AllowPort(int32_t port, const char* scheme, 243 bool* _retval) { 244 // don't override anything. 245 *_retval = false; 246 return NS_OK; 247 } 248 249 //////////////////////////////////////////////////////////////////////////////// 250 // Safe about protocol handler impl 251 252 NS_IMPL_ISUPPORTS(nsSafeAboutProtocolHandler, nsIProtocolHandler, 253 nsISupportsWeakReference) 254 255 // nsIProtocolHandler methods: 256 257 NS_IMETHODIMP 258 nsSafeAboutProtocolHandler::GetScheme(nsACString& result) { 259 result.AssignLiteral("moz-safe-about"); 260 return NS_OK; 261 } 262 263 NS_IMETHODIMP 264 nsSafeAboutProtocolHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo, 265 nsIChannel** result) { 266 *result = nullptr; 267 return NS_ERROR_NOT_AVAILABLE; 268 } 269 270 NS_IMETHODIMP 271 nsSafeAboutProtocolHandler::AllowPort(int32_t port, const char* scheme, 272 bool* _retval) { 273 // don't override anything. 274 *_retval = false; 275 return NS_OK; 276 } 277 278 //////////////////////////////////////////////////////////// 279 // nsNestedAboutURI implementation 280 281 NS_IMPL_CLASSINFO(nsNestedAboutURI, nullptr, nsIClassInfo::THREADSAFE, 282 NS_NESTEDABOUTURI_CID); 283 // Empty CI getter. We only need nsIClassInfo for Serialization 284 NS_IMPL_CI_INTERFACE_GETTER0(nsNestedAboutURI) 285 286 NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI) 287 if (aIID.Equals(kNestedAboutURICID)) { 288 foundInterface = static_cast<nsIURI*>(this); 289 } else 290 NS_IMPL_QUERY_CLASSINFO(nsNestedAboutURI) 291 NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI) 292 293 // nsISerializable 294 295 NS_IMETHODIMP 296 nsNestedAboutURI::Read(nsIObjectInputStream* aStream) { 297 MOZ_ASSERT_UNREACHABLE("Use nsIURIMutator.read() instead"); 298 return NS_ERROR_NOT_IMPLEMENTED; 299 } 300 301 nsresult nsNestedAboutURI::ReadPrivate(nsIObjectInputStream* aStream) { 302 nsresult rv = nsSimpleNestedURI::ReadPrivate(aStream); 303 if (NS_FAILED(rv)) return rv; 304 305 bool haveBase; 306 rv = aStream->ReadBoolean(&haveBase); 307 if (NS_FAILED(rv)) return rv; 308 309 if (haveBase) { 310 nsCOMPtr<nsISupports> supports; 311 rv = aStream->ReadObject(true, getter_AddRefs(supports)); 312 if (NS_FAILED(rv)) return rv; 313 314 mBaseURI = do_QueryInterface(supports, &rv); 315 if (NS_FAILED(rv)) return rv; 316 } 317 318 return NS_OK; 319 } 320 321 NS_IMETHODIMP 322 nsNestedAboutURI::Write(nsIObjectOutputStream* aStream) { 323 nsresult rv = nsSimpleNestedURI::Write(aStream); 324 if (NS_FAILED(rv)) return rv; 325 326 rv = aStream->WriteBoolean(mBaseURI != nullptr); 327 if (NS_FAILED(rv)) return rv; 328 329 if (mBaseURI) { 330 // A previous iteration of this code wrote out mBaseURI as nsISupports 331 // and then read it in as nsIURI, which is non-kosher when mBaseURI 332 // implements more than just a single line of interfaces and the 333 // canonical nsISupports* isn't the one a static_cast<> of mBaseURI 334 // would produce. For backwards compatibility with existing 335 // serializations we continue to write mBaseURI as nsISupports but 336 // switch to reading it as nsISupports, with a post-read QI to get to 337 // nsIURI. 338 rv = aStream->WriteCompoundObject(mBaseURI, NS_GET_IID(nsISupports), true); 339 if (NS_FAILED(rv)) return rv; 340 } 341 342 return NS_OK; 343 } 344 345 NS_IMETHODIMP_(void) 346 nsNestedAboutURI::Serialize(mozilla::ipc::URIParams& aParams) { 347 using namespace mozilla::ipc; 348 349 NestedAboutURIParams params; 350 URIParams nestedParams; 351 352 nsSimpleNestedURI::Serialize(nestedParams); 353 params.nestedParams() = nestedParams; 354 355 if (mBaseURI) { 356 SerializeURI(mBaseURI, params.baseURI()); 357 } 358 359 aParams = params; 360 } 361 362 bool nsNestedAboutURI::Deserialize(const mozilla::ipc::URIParams& aParams) { 363 using namespace mozilla::ipc; 364 365 if (aParams.type() != URIParams::TNestedAboutURIParams) { 366 NS_ERROR("Received unknown parameters from the other process!"); 367 return false; 368 } 369 370 const NestedAboutURIParams& params = aParams.get_NestedAboutURIParams(); 371 if (!nsSimpleNestedURI::Deserialize(params.nestedParams())) { 372 return false; 373 } 374 375 mBaseURI = nullptr; 376 if (params.baseURI()) { 377 mBaseURI = DeserializeURI(*params.baseURI()); 378 } 379 return true; 380 } 381 382 // nsSimpleURI 383 /* virtual */ already_AddRefed<nsSimpleURI> nsNestedAboutURI::StartClone() { 384 NS_ENSURE_TRUE(mInnerURI, nullptr); 385 386 RefPtr<nsNestedAboutURI> url = new nsNestedAboutURI(mInnerURI, mBaseURI); 387 388 return url.forget(); 389 } 390 391 // Queries this list of interfaces. If none match, it queries mURI. 392 NS_IMPL_NSIURIMUTATOR_ISUPPORTS(nsNestedAboutURI::Mutator, nsIURISetters, 393 nsIURIMutator, nsISerializable, 394 nsINestedAboutURIMutator) 395 396 NS_IMETHODIMP 397 nsNestedAboutURI::Mutate(nsIURIMutator** aMutator) { 398 RefPtr<nsNestedAboutURI::Mutator> mutator = new nsNestedAboutURI::Mutator(); 399 nsresult rv = mutator->InitFromURI(this); 400 if (NS_FAILED(rv)) { 401 return rv; 402 } 403 mutator.forget(aMutator); 404 return NS_OK; 405 } 406 407 } // namespace net 408 } // namespace mozilla