Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/ThirdPartyUtil.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 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 "ThirdPartyUtil.h"
8
#include "nsNetCID.h"
9
#include "nsNetUtil.h"
10
#include "nsIChannel.h"
11
#include "nsIServiceManager.h"
12
#include "nsIHttpChannelInternal.h"
13
#include "nsIDOMWindow.h"
14
#include "nsILoadContext.h"
15
#include "nsIPrincipal.h"
16
#include "nsIScriptObjectPrincipal.h"
17
#include "nsIURI.h"
18
#include "nsThreadUtils.h"
19
#include "mozilla/Logging.h"
20
#include "nsPIDOMWindow.h"
21
22
NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil)
23
24
//
25
// MOZ_LOG=thirdPartyUtil:5
26
//
27
static mozilla::LazyLogModule gThirdPartyLog("thirdPartyUtil");
28
#undef LOG
29
0
#define LOG(args)     MOZ_LOG(gThirdPartyLog, mozilla::LogLevel::Debug, args)
30
31
nsresult
32
ThirdPartyUtil::Init()
33
0
{
34
0
  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
35
0
36
0
  nsresult rv;
37
0
  mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
38
0
39
0
  return rv;
40
0
}
41
42
// Determine if aFirstDomain is a different base domain to aSecondURI; or, if
43
// the concept of base domain does not apply, determine if the two hosts are not
44
// string-identical.
45
nsresult
46
ThirdPartyUtil::IsThirdPartyInternal(const nsCString& aFirstDomain,
47
                                     nsIURI* aSecondURI,
48
                                     bool* aResult)
49
0
{
50
0
  if (!aSecondURI) {
51
0
    return NS_ERROR_INVALID_ARG;
52
0
  }
53
0
54
0
  // Get the base domain for aSecondURI.
55
0
  nsCString secondDomain;
56
0
  nsresult rv = GetBaseDomain(aSecondURI, secondDomain);
57
0
  LOG(("ThirdPartyUtil::IsThirdPartyInternal %s =? %s", aFirstDomain.get(), secondDomain.get()));
58
0
  if (NS_FAILED(rv))
59
0
    return rv;
60
0
61
0
  // Check strict equality.
62
0
  *aResult = aFirstDomain != secondDomain;
63
0
  return NS_OK;
64
0
}
65
66
// Get the URI associated with a window.
67
NS_IMETHODIMP
68
ThirdPartyUtil::GetURIFromWindow(mozIDOMWindowProxy* aWin, nsIURI** result)
69
0
{
70
0
  nsresult rv;
71
0
  nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aWin);
72
0
  if (!scriptObjPrin) {
73
0
    return NS_ERROR_INVALID_ARG;
74
0
  }
75
0
76
0
  nsIPrincipal* prin = scriptObjPrin->GetPrincipal();
77
0
  if (!prin) {
78
0
    return NS_ERROR_INVALID_ARG;
79
0
  }
80
0
81
0
  if (prin->GetIsNullPrincipal()) {
82
0
    LOG(("ThirdPartyUtil::GetURIFromWindow can't use null principal\n"));
83
0
    return NS_ERROR_INVALID_ARG;
84
0
  }
85
0
86
0
  rv = prin->GetURI(result);
87
0
  return rv;
88
0
}
89
90
// Determine if aFirstURI is third party with respect to aSecondURI. See docs
91
// for mozIThirdPartyUtil.
92
NS_IMETHODIMP
93
ThirdPartyUtil::IsThirdPartyURI(nsIURI* aFirstURI,
94
                                nsIURI* aSecondURI,
95
                                bool* aResult)
96
0
{
97
0
  NS_ENSURE_ARG(aFirstURI);
98
0
  NS_ENSURE_ARG(aSecondURI);
99
0
  NS_ASSERTION(aResult, "null outparam pointer");
100
0
101
0
  nsCString firstHost;
102
0
  nsresult rv = GetBaseDomain(aFirstURI, firstHost);
103
0
  if (NS_FAILED(rv))
104
0
    return rv;
105
0
106
0
  return IsThirdPartyInternal(firstHost, aSecondURI, aResult);
107
0
}
108
109
// Determine if any URI of the window hierarchy of aWindow is foreign with
110
// respect to aSecondURI. See docs for mozIThirdPartyUtil.
111
NS_IMETHODIMP
112
ThirdPartyUtil::IsThirdPartyWindow(mozIDOMWindowProxy* aWindow,
113
                                   nsIURI* aURI,
114
                                   bool* aResult)
115
0
{
116
0
  NS_ENSURE_ARG(aWindow);
117
0
  NS_ASSERTION(aResult, "null outparam pointer");
118
0
119
0
  bool result;
120
0
121
0
  // Get the URI of the window, and its base domain.
122
0
  nsresult rv;
123
0
  nsCOMPtr<nsIURI> currentURI;
124
0
  rv = GetURIFromWindow(aWindow, getter_AddRefs(currentURI));
125
0
  if (NS_FAILED(rv))
126
0
    return rv;
127
0
128
0
  nsCString bottomDomain;
129
0
  rv = GetBaseDomain(currentURI, bottomDomain);
130
0
  if (NS_FAILED(rv))
131
0
    return rv;
132
0
133
0
  if (aURI) {
134
0
    // Determine whether aURI is foreign with respect to currentURI.
135
0
    rv = IsThirdPartyInternal(bottomDomain, aURI, &result);
136
0
    if (NS_FAILED(rv))
137
0
      return rv;
138
0
139
0
    if (result) {
140
0
      *aResult = true;
141
0
      return NS_OK;
142
0
    }
143
0
  }
144
0
145
0
  nsCOMPtr<nsPIDOMWindowOuter> current = nsPIDOMWindowOuter::From(aWindow), parent;
146
0
  nsCOMPtr<nsIURI> parentURI;
147
0
  do {
148
0
    // We use GetScriptableParent rather than GetParent because we consider
149
0
    // <iframe mozbrowser> to be a top-level frame.
150
0
    parent = current->GetScriptableParent();
151
0
    if (SameCOMIdentity(parent, current)) {
152
0
      // We're at the topmost content window. We already know the answer.
153
0
      *aResult = false;
154
0
      return NS_OK;
155
0
    }
156
0
157
0
    rv = GetURIFromWindow(parent, getter_AddRefs(parentURI));
158
0
    NS_ENSURE_SUCCESS(rv, rv);
159
0
160
0
    rv = IsThirdPartyInternal(bottomDomain, parentURI, &result);
161
0
    if (NS_FAILED(rv))
162
0
      return rv;
163
0
164
0
    if (result) {
165
0
      *aResult = true;
166
0
      return NS_OK;
167
0
    }
168
0
169
0
    current = parent;
170
0
    currentURI = parentURI;
171
0
  } while (1);
172
0
173
0
  MOZ_ASSERT_UNREACHABLE("should've returned");
174
0
  return NS_ERROR_UNEXPECTED;
175
0
}
176
177
// Determine if the URI associated with aChannel or any URI of the window
178
// hierarchy associated with the channel is foreign with respect to aSecondURI.
179
// See docs for mozIThirdPartyUtil.
180
NS_IMETHODIMP
181
ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel,
182
                                    nsIURI* aURI,
183
                                    bool* aResult)
184
0
{
185
0
  LOG(("ThirdPartyUtil::IsThirdPartyChannel [channel=%p]", aChannel));
186
0
  NS_ENSURE_ARG(aChannel);
187
0
  NS_ASSERTION(aResult, "null outparam pointer");
188
0
189
0
  nsresult rv;
190
0
  bool doForce = false;
191
0
  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
192
0
    do_QueryInterface(aChannel);
193
0
  if (httpChannelInternal) {
194
0
    uint32_t flags;
195
0
    rv = httpChannelInternal->GetThirdPartyFlags(&flags);
196
0
    NS_ENSURE_SUCCESS(rv, rv);
197
0
198
0
    doForce = (flags & nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
199
0
200
0
    // If aURI was not supplied, and we're forcing, then we're by definition
201
0
    // not foreign. If aURI was supplied, we still want to check whether it's
202
0
    // foreign with respect to the channel URI. (The forcing only applies to
203
0
    // whatever window hierarchy exists above the channel.)
204
0
    if (doForce && !aURI) {
205
0
      *aResult = false;
206
0
      return NS_OK;
207
0
    }
208
0
  }
209
0
210
0
  bool parentIsThird = false;
211
0
212
0
  // Obtain the URI from the channel, and its base domain.
213
0
  nsCOMPtr<nsIURI> channelURI;
214
0
  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
215
0
  if (NS_FAILED(rv))
216
0
    return rv;
217
0
218
0
  nsCString channelDomain;
219
0
  rv = GetBaseDomain(channelURI, channelDomain);
220
0
  if (NS_FAILED(rv))
221
0
    return rv;
222
0
223
0
  if (!doForce) {
224
0
    if (nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo()) {
225
0
      parentIsThird = loadInfo->GetIsInThirdPartyContext();
226
0
      if (!parentIsThird &&
227
0
          loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
228
0
        // Check if the channel itself is third-party to its own requestor.
229
0
        // Unforunately, we have to go through the loading principal.
230
0
        nsCOMPtr<nsIURI> parentURI;
231
0
        loadInfo->LoadingPrincipal()->GetURI(getter_AddRefs(parentURI));
232
0
        rv = IsThirdPartyInternal(channelDomain, parentURI, &parentIsThird);
233
0
        if (NS_FAILED(rv))
234
0
          return rv;
235
0
      }
236
0
    } else {
237
0
      NS_WARNING("Found channel with no loadinfo, assuming third-party request");
238
0
      parentIsThird = true;
239
0
    }
240
0
  }
241
0
242
0
  // If we're not comparing to a URI, we have our answer. Otherwise, if
243
0
  // parentIsThird, we're not forcing and we know that we're a third-party
244
0
  // request.
245
0
  if (!aURI || parentIsThird) {
246
0
    *aResult = parentIsThird;
247
0
    return NS_OK;
248
0
  }
249
0
250
0
  // Determine whether aURI is foreign with respect to channelURI.
251
0
  return IsThirdPartyInternal(channelDomain, aURI, aResult);
252
0
}
253
254
NS_IMETHODIMP
255
ThirdPartyUtil::GetTopWindowForChannel(nsIChannel* aChannel, mozIDOMWindowProxy** aWin)
256
0
{
257
0
  NS_ENSURE_ARG(aWin);
258
0
259
0
  // Find the associated window and its parent window.
260
0
  nsCOMPtr<nsILoadContext> ctx;
261
0
  NS_QueryNotificationCallbacks(aChannel, ctx);
262
0
  if (!ctx) {
263
0
    return NS_ERROR_INVALID_ARG;
264
0
  }
265
0
266
0
  nsCOMPtr<mozIDOMWindowProxy> window;
267
0
  ctx->GetAssociatedWindow(getter_AddRefs(window));
268
0
  if (!window) {
269
0
    return NS_ERROR_INVALID_ARG;
270
0
  }
271
0
272
0
  nsCOMPtr<nsPIDOMWindowOuter> top = nsPIDOMWindowOuter::From(window)->GetTop();
273
0
  top.forget(aWin);
274
0
  return NS_OK;
275
0
}
276
277
// Get the base domain for aHostURI; e.g. for "www.bbc.co.uk", this would be
278
// "bbc.co.uk". Only properly-formed URI's are tolerated, though a trailing
279
// dot may be present. If aHostURI is an IP address, an alias such as
280
// 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
281
// be the exact host. The result of this function should only be used in exact
282
// string comparisons, since substring comparisons will not be valid for the
283
// special cases elided above.
284
NS_IMETHODIMP
285
ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI,
286
                              nsACString& aBaseDomain)
287
0
{
288
0
  if (!aHostURI) {
289
0
    return NS_ERROR_INVALID_ARG;
290
0
  }
291
0
292
0
  // Get the base domain. this will fail if the host contains a leading dot,
293
0
  // more than one trailing dot, or is otherwise malformed.
294
0
  nsresult rv = mTLDService->GetBaseDomain(aHostURI, 0, aBaseDomain);
295
0
  if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
296
0
      rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
297
0
    // aHostURI is either an IP address, an alias such as 'localhost', an eTLD
298
0
    // such as 'co.uk', or the empty string. Uses the normalized host in such
299
0
    // cases.
300
0
    rv = aHostURI->GetAsciiHost(aBaseDomain);
301
0
  }
302
0
  NS_ENSURE_SUCCESS(rv, rv);
303
0
304
0
  // aHostURI (and thus aBaseDomain) may be the string '.'. If so, fail.
305
0
  if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
306
0
    return NS_ERROR_INVALID_ARG;
307
0
308
0
  // Reject any URIs without a host that aren't file:// URIs. This makes it the
309
0
  // only way we can get a base domain consisting of the empty string, which
310
0
  // means we can safely perform foreign tests on such URIs where "not foreign"
311
0
  // means "the involved URIs are all file://".
312
0
  if (aBaseDomain.IsEmpty()) {
313
0
    bool isFileURI = false;
314
0
    aHostURI->SchemeIs("file", &isFileURI);
315
0
    if (!isFileURI) {
316
0
     return NS_ERROR_INVALID_ARG;
317
0
    }
318
0
  }
319
0
320
0
  return NS_OK;
321
0
}