Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 : #include "mozilla/ArrayUtils.h"
8 :
9 : #include "nsAboutProtocolHandler.h"
10 : #include "nsIURI.h"
11 : #include "nsIAboutModule.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 "nsAutoPtr.h"
20 : #include "nsIWritablePropertyBag2.h"
21 : #include "nsIChannel.h"
22 : #include "nsIScriptError.h"
23 :
24 : namespace mozilla {
25 : namespace net {
26 :
27 : static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
28 : static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID);
29 :
30 4 : static bool IsSafeForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
31 : uint32_t flags;
32 4 : nsresult rv = aModule->GetURIFlags(aURI, &flags);
33 4 : NS_ENSURE_SUCCESS(rv, false);
34 :
35 4 : return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0;
36 : }
37 :
38 28 : static bool IsSafeToLinkForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) {
39 : uint32_t flags;
40 28 : nsresult rv = aModule->GetURIFlags(aURI, &flags);
41 28 : NS_ENSURE_SUCCESS(rv, false);
42 :
43 28 : return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) && (flags & nsIAboutModule::MAKE_LINKABLE);
44 : }
45 : ////////////////////////////////////////////////////////////////////////////////
46 :
47 592 : NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler,
48 : nsIProtocolHandlerWithDynamicFlags, nsISupportsWeakReference)
49 :
50 : ////////////////////////////////////////////////////////////////////////////////
51 : // nsIProtocolHandler methods:
52 :
53 : NS_IMETHODIMP
54 0 : nsAboutProtocolHandler::GetScheme(nsACString &result)
55 : {
56 0 : result.AssignLiteral("about");
57 0 : return NS_OK;
58 : }
59 :
60 : NS_IMETHODIMP
61 0 : nsAboutProtocolHandler::GetDefaultPort(int32_t *result)
62 : {
63 0 : *result = -1; // no port for about: URLs
64 0 : return NS_OK;
65 : }
66 :
67 : NS_IMETHODIMP
68 41 : nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
69 : {
70 41 : *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | URI_SCHEME_NOT_SELF_LINKABLE;
71 41 : return NS_OK;
72 : }
73 :
74 : NS_IMETHODIMP
75 41 : nsAboutProtocolHandler::GetFlagsForURI(nsIURI* aURI, uint32_t* aFlags)
76 : {
77 : // First use the default (which is "unsafe for content"):
78 41 : GetProtocolFlags(aFlags);
79 :
80 : // Now try to see if this URI overrides the default:
81 82 : nsCOMPtr<nsIAboutModule> aboutMod;
82 41 : nsresult rv = NS_GetAboutModule(aURI, getter_AddRefs(aboutMod));
83 41 : if (NS_FAILED(rv)) {
84 : // Swallow this and just tell the consumer the default:
85 0 : return NS_OK;
86 : }
87 41 : uint32_t aboutModuleFlags = 0;
88 41 : rv = aboutMod->GetURIFlags(aURI, &aboutModuleFlags);
89 : // This should never happen, so pass back the error:
90 41 : NS_ENSURE_SUCCESS(rv, rv);
91 :
92 : // Secure (https) pages can load safe about pages without becoming
93 : // mixed content.
94 41 : if (aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) {
95 20 : *aFlags |= URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
96 : // about: pages can only be loaded by unprivileged principals
97 : // if they are marked as LINKABLE
98 20 : if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
99 : // Replace URI_DANGEROUS_TO_LOAD with URI_LOADABLE_BY_ANYONE.
100 20 : *aFlags &= ~URI_DANGEROUS_TO_LOAD;
101 20 : *aFlags |= URI_LOADABLE_BY_ANYONE;
102 : }
103 : }
104 41 : return NS_OK;
105 : }
106 :
107 : NS_IMETHODIMP
108 30 : nsAboutProtocolHandler::NewURI(const nsACString &aSpec,
109 : const char *aCharset, // ignore charset info
110 : nsIURI *aBaseURI,
111 : nsIURI **result)
112 : {
113 30 : *result = nullptr;
114 : nsresult rv;
115 :
116 : // Use a simple URI to parse out some stuff first
117 60 : nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
118 30 : if (NS_FAILED(rv)) return rv;
119 :
120 30 : rv = url->SetSpec(aSpec);
121 30 : if (NS_FAILED(rv)) {
122 0 : return rv;
123 : }
124 :
125 : // Unfortunately, people create random about: URIs that don't correspond to
126 : // about: modules... Since those URIs will never open a channel, might as
127 : // well consider them unsafe for better perf, and just in case.
128 30 : bool isSafe = false;
129 :
130 60 : nsCOMPtr<nsIAboutModule> aboutMod;
131 30 : rv = NS_GetAboutModule(url, getter_AddRefs(aboutMod));
132 30 : if (NS_SUCCEEDED(rv)) {
133 28 : isSafe = IsSafeToLinkForUntrustedContent(aboutMod, url);
134 : }
135 :
136 30 : if (isSafe) {
137 : // We need to indicate that this baby is safe. Use an inner URI that
138 : // no one but the security manager will see. Make sure to preserve our
139 : // path, in case someone decides to hardcode checks for particular
140 : // about: URIs somewhere.
141 42 : nsAutoCString spec;
142 21 : rv = url->GetPath(spec);
143 21 : NS_ENSURE_SUCCESS(rv, rv);
144 :
145 21 : spec.Insert("moz-safe-about:", 0);
146 :
147 42 : nsCOMPtr<nsIURI> inner;
148 21 : rv = NS_NewURI(getter_AddRefs(inner), spec);
149 21 : NS_ENSURE_SUCCESS(rv, rv);
150 :
151 42 : nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI);
152 21 : NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY);
153 :
154 : // Take a ref to it in the COMPtr we plan to return
155 21 : url = outer;
156 :
157 21 : rv = outer->SetSpec(aSpec);
158 21 : NS_ENSURE_SUCCESS(rv, rv);
159 : }
160 :
161 : // We don't want to allow mutation, since it would allow safe and
162 : // unsafe URIs to change into each other...
163 30 : NS_TryToSetImmutable(url);
164 30 : url.swap(*result);
165 30 : return NS_OK;
166 : }
167 :
168 : NS_IMETHODIMP
169 4 : nsAboutProtocolHandler::NewChannel2(nsIURI* uri,
170 : nsILoadInfo* aLoadInfo,
171 : nsIChannel** result)
172 : {
173 4 : NS_ENSURE_ARG_POINTER(uri);
174 :
175 : // about:what you ask?
176 8 : nsCOMPtr<nsIAboutModule> aboutMod;
177 4 : nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod));
178 :
179 8 : nsAutoCString path;
180 4 : nsresult rv2 = NS_GetAboutModuleName(uri, path);
181 4 : if (NS_SUCCEEDED(rv2) && path.EqualsLiteral("srcdoc")) {
182 : // about:srcdoc is meant to be unresolvable, yet is included in the
183 : // about lookup tables so that it can pass security checks when used in
184 : // a srcdoc iframe. To ensure that it stays unresolvable, we pretend
185 : // that it doesn't exist.
186 0 : rv = NS_ERROR_FACTORY_NOT_REGISTERED;
187 : }
188 :
189 4 : if (NS_SUCCEEDED(rv)) {
190 : // The standard return case:
191 4 : rv = aboutMod->NewChannel(uri, aLoadInfo, result);
192 4 : if (NS_SUCCEEDED(rv)) {
193 : // Not all implementations of nsIAboutModule::NewChannel()
194 : // set the LoadInfo on the newly created channel yet, as
195 : // an interim solution we set the LoadInfo here if not
196 : // available on the channel. Bug 1087720
197 8 : nsCOMPtr<nsILoadInfo> loadInfo = (*result)->GetLoadInfo();
198 4 : if (aLoadInfo != loadInfo) {
199 0 : if (loadInfo) {
200 0 : NS_ASSERTION(false,
201 : "nsIAboutModule->newChannel(aURI, aLoadInfo) needs to set LoadInfo");
202 : const char16_t* params[] = {
203 : u"nsIAboutModule->newChannel(aURI)",
204 : u"nsIAboutModule->newChannel(aURI, aLoadInfo)"
205 0 : };
206 0 : nsContentUtils::ReportToConsole(
207 : nsIScriptError::warningFlag,
208 0 : NS_LITERAL_CSTRING("Security by Default"),
209 : nullptr, // aDocument
210 : nsContentUtils::eNECKO_PROPERTIES,
211 : "APIDeprecationWarning",
212 0 : params, mozilla::ArrayLength(params));
213 : }
214 0 : (*result)->SetLoadInfo(aLoadInfo);
215 : }
216 :
217 : // If this URI is safe for untrusted content, enforce that its
218 : // principal be based on the channel's originalURI by setting the
219 : // owner to null.
220 : // Note: this relies on aboutMod's newChannel implementation
221 : // having set the proper originalURI, which probably isn't ideal.
222 4 : if (IsSafeForUntrustedContent(aboutMod, uri)) {
223 4 : (*result)->SetOwner(nullptr);
224 : }
225 :
226 8 : RefPtr<nsNestedAboutURI> aboutURI;
227 8 : nsresult rv2 = uri->QueryInterface(kNestedAboutURICID,
228 8 : getter_AddRefs(aboutURI));
229 4 : if (NS_SUCCEEDED(rv2) && aboutURI->GetBaseURI()) {
230 : nsCOMPtr<nsIWritablePropertyBag2> writableBag =
231 4 : do_QueryInterface(*result);
232 2 : if (writableBag) {
233 2 : writableBag->
234 6 : SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
235 6 : aboutURI->GetBaseURI());
236 : }
237 : }
238 : }
239 4 : return rv;
240 : }
241 :
242 : // mumble...
243 :
244 0 : if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) {
245 : // This looks like an about: we don't know about. Convert
246 : // this to an invalid URI error.
247 0 : rv = NS_ERROR_MALFORMED_URI;
248 : }
249 :
250 0 : return rv;
251 : }
252 :
253 : NS_IMETHODIMP
254 0 : nsAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
255 : {
256 0 : return NewChannel2(uri, nullptr, result);
257 : }
258 :
259 : NS_IMETHODIMP
260 0 : nsAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
261 : {
262 : // don't override anything.
263 0 : *_retval = false;
264 0 : return NS_OK;
265 : }
266 :
267 : ////////////////////////////////////////////////////////////////////////////////
268 : // Safe about protocol handler impl
269 :
270 489 : NS_IMPL_ISUPPORTS(nsSafeAboutProtocolHandler, nsIProtocolHandler, nsISupportsWeakReference)
271 :
272 : // nsIProtocolHandler methods:
273 :
274 : NS_IMETHODIMP
275 0 : nsSafeAboutProtocolHandler::GetScheme(nsACString &result)
276 : {
277 0 : result.AssignLiteral("moz-safe-about");
278 0 : return NS_OK;
279 : }
280 :
281 : NS_IMETHODIMP
282 0 : nsSafeAboutProtocolHandler::GetDefaultPort(int32_t *result)
283 : {
284 0 : *result = -1; // no port for moz-safe-about: URLs
285 0 : return NS_OK;
286 : }
287 :
288 : NS_IMETHODIMP
289 42 : nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result)
290 : {
291 42 : *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT;
292 42 : return NS_OK;
293 : }
294 :
295 : NS_IMETHODIMP
296 27 : nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec,
297 : const char *aCharset, // ignore charset info
298 : nsIURI *aBaseURI,
299 : nsIURI **result)
300 : {
301 : nsresult rv;
302 :
303 54 : nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv);
304 27 : if (NS_FAILED(rv)) return rv;
305 :
306 27 : rv = url->SetSpec(aSpec);
307 27 : if (NS_FAILED(rv)) {
308 0 : return rv;
309 : }
310 :
311 27 : NS_TryToSetImmutable(url);
312 :
313 27 : *result = nullptr;
314 27 : url.swap(*result);
315 27 : return rv;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : nsSafeAboutProtocolHandler::NewChannel2(nsIURI* uri,
320 : nsILoadInfo* aLoadInfo,
321 : nsIChannel** result)
322 : {
323 0 : *result = nullptr;
324 0 : return NS_ERROR_NOT_AVAILABLE;
325 : }
326 :
327 : NS_IMETHODIMP
328 0 : nsSafeAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
329 : {
330 0 : *result = nullptr;
331 0 : return NS_ERROR_NOT_AVAILABLE;
332 : }
333 :
334 : NS_IMETHODIMP
335 0 : nsSafeAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
336 : {
337 : // don't override anything.
338 0 : *_retval = false;
339 0 : return NS_OK;
340 : }
341 :
342 : ////////////////////////////////////////////////////////////
343 : // nsNestedAboutURI implementation
344 758 : NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI)
345 758 : if (aIID.Equals(kNestedAboutURICID))
346 4 : foundInterface = static_cast<nsIURI*>(this);
347 : else
348 754 : NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI)
349 :
350 : // nsISerializable
351 : NS_IMETHODIMP
352 0 : nsNestedAboutURI::Read(nsIObjectInputStream* aStream)
353 : {
354 0 : nsresult rv = nsSimpleNestedURI::Read(aStream);
355 0 : if (NS_FAILED(rv)) return rv;
356 :
357 : bool haveBase;
358 0 : rv = aStream->ReadBoolean(&haveBase);
359 0 : if (NS_FAILED(rv)) return rv;
360 :
361 0 : if (haveBase) {
362 0 : nsCOMPtr<nsISupports> supports;
363 0 : rv = aStream->ReadObject(true, getter_AddRefs(supports));
364 0 : if (NS_FAILED(rv)) return rv;
365 :
366 0 : mBaseURI = do_QueryInterface(supports, &rv);
367 0 : if (NS_FAILED(rv)) return rv;
368 : }
369 :
370 0 : return NS_OK;
371 : }
372 :
373 : NS_IMETHODIMP
374 0 : nsNestedAboutURI::Write(nsIObjectOutputStream* aStream)
375 : {
376 0 : nsresult rv = nsSimpleNestedURI::Write(aStream);
377 0 : if (NS_FAILED(rv)) return rv;
378 :
379 0 : rv = aStream->WriteBoolean(mBaseURI != nullptr);
380 0 : if (NS_FAILED(rv)) return rv;
381 :
382 0 : if (mBaseURI) {
383 : // A previous iteration of this code wrote out mBaseURI as nsISupports
384 : // and then read it in as nsIURI, which is non-kosher when mBaseURI
385 : // implements more than just a single line of interfaces and the
386 : // canonical nsISupports* isn't the one a static_cast<> of mBaseURI
387 : // would produce. For backwards compatibility with existing
388 : // serializations we continue to write mBaseURI as nsISupports but
389 : // switch to reading it as nsISupports, with a post-read QI to get to
390 : // nsIURI.
391 : rv = aStream->WriteCompoundObject(mBaseURI, NS_GET_IID(nsISupports),
392 0 : true);
393 0 : if (NS_FAILED(rv)) return rv;
394 : }
395 :
396 0 : return NS_OK;
397 : }
398 :
399 : // nsSimpleURI
400 : /* virtual */ nsSimpleURI*
401 11 : nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
402 : const nsACString& aNewRef)
403 : {
404 : // Sadly, we can't make use of nsSimpleNestedURI::StartClone here.
405 : // However, this function is expected to exactly match that function,
406 : // aside from the "new ns***URI()" call.
407 11 : NS_ENSURE_TRUE(mInnerURI, nullptr);
408 :
409 22 : nsCOMPtr<nsIURI> innerClone;
410 : nsresult rv;
411 11 : if (aRefHandlingMode == eHonorRef) {
412 8 : rv = mInnerURI->Clone(getter_AddRefs(innerClone));
413 3 : } else if (aRefHandlingMode == eReplaceRef) {
414 0 : rv = mInnerURI->CloneWithNewRef(aNewRef, getter_AddRefs(innerClone));
415 : } else {
416 3 : rv = mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone));
417 : }
418 :
419 11 : if (NS_FAILED(rv)) {
420 0 : return nullptr;
421 : }
422 :
423 22 : nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI);
424 11 : SetRefOnClone(url, aRefHandlingMode, aNewRef);
425 11 : url->SetMutable(false);
426 :
427 11 : return url;
428 : }
429 :
430 : // nsIClassInfo
431 : NS_IMETHODIMP
432 0 : nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
433 : {
434 0 : *aClassIDNoAlloc = kNestedAboutURICID;
435 0 : return NS_OK;
436 : }
437 :
438 : } // namespace net
439 : } // namespace mozilla
|