Coverage Report

Created: 2018-09-25 14:53

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