Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/clearsitedata/ClearSiteData.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 file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ClearSiteData.h"
8
9
#include "mozilla/OriginAttributes.h"
10
#include "mozilla/Preferences.h"
11
#include "mozilla/Services.h"
12
#include "mozilla/StaticPrefs.h"
13
#include "mozilla/Unused.h"
14
#include "nsASCIIMask.h"
15
#include "nsCharSeparatedTokenizer.h"
16
#include "nsContentUtils.h"
17
#include "nsIClearDataService.h"
18
#include "nsIHttpChannel.h"
19
#include "nsIHttpProtocolHandler.h"
20
#include "nsIObserverService.h"
21
#include "nsIPrincipal.h"
22
#include "nsIScriptError.h"
23
#include "nsNetUtil.h"
24
25
using namespace mozilla;
26
27
namespace {
28
29
StaticRefPtr<ClearSiteData> gClearSiteData;
30
31
} // anonymous
32
33
// This object is used to suspend/resume the channel.
34
class ClearSiteData::PendingCleanupHolder final
35
  : public nsIClearDataCallback
36
{
37
public:
38
  NS_DECL_ISUPPORTS
39
40
  explicit PendingCleanupHolder(nsIHttpChannel* aChannel)
41
    : mChannel(aChannel)
42
    , mPendingOp(false)
43
0
  {}
44
45
  nsresult
46
  Start()
47
0
  {
48
0
    MOZ_ASSERT(!mPendingOp);
49
0
    nsresult rv = mChannel->Suspend();
50
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
51
0
      return rv;
52
0
    }
53
0
54
0
    mPendingOp = true;
55
0
    return NS_OK;
56
0
  }
57
58
  // This method must be called after any Start() call.
59
  void
60
  BrowsingContextsReloadNeeded(const nsACString& aOrigin)
61
0
  {
62
0
    mContextsReloadOrigin = aOrigin;
63
0
    MaybeBrowsingContextsReload();
64
0
  }
65
66
  // nsIClearDataCallback interface
67
68
  NS_IMETHOD
69
  OnDataDeleted(uint32_t aFailedFlags) override
70
0
  {
71
0
    MOZ_ASSERT(mPendingOp);
72
0
    mPendingOp = false;
73
0
74
0
    mChannel->Resume();
75
0
    mChannel = nullptr;
76
0
77
0
    MaybeBrowsingContextsReload();
78
0
    return NS_OK;
79
0
  }
80
81
private:
82
  ~PendingCleanupHolder()
83
0
  {
84
0
    if (mPendingOp) {
85
0
      mChannel->Resume();
86
0
    }
87
0
  }
88
89
  void
90
  MaybeBrowsingContextsReload()
91
0
  {
92
0
    if (mPendingOp || mContextsReloadOrigin.IsEmpty()) {
93
0
      return;
94
0
    }
95
0
96
0
    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
97
0
    if (NS_WARN_IF(!obs)) {
98
0
      return;
99
0
    }
100
0
101
0
    NS_ConvertUTF8toUTF16 origin(mContextsReloadOrigin);
102
0
    nsresult rv = obs->NotifyObservers(nullptr, "clear-site-data-reload-needed",
103
0
                                       origin.get());
104
0
    Unused << NS_WARN_IF(NS_FAILED(rv));
105
0
  }
106
107
  nsCOMPtr<nsIHttpChannel> mChannel;
108
  bool mPendingOp;
109
  nsCString mContextsReloadOrigin;
110
};
111
112
0
NS_INTERFACE_MAP_BEGIN(ClearSiteData::PendingCleanupHolder)
113
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearDataCallback)
114
0
  NS_INTERFACE_MAP_ENTRY(nsIClearDataCallback)
115
0
NS_INTERFACE_MAP_END
116
117
NS_IMPL_ADDREF(ClearSiteData::PendingCleanupHolder)
118
NS_IMPL_RELEASE(ClearSiteData::PendingCleanupHolder)
119
120
/* static */ void
121
ClearSiteData::Initialize()
122
3
{
123
3
  MOZ_ASSERT(!gClearSiteData);
124
3
  MOZ_ASSERT(NS_IsMainThread());
125
3
126
3
  if (!XRE_IsParentProcess()) {
127
0
    return;
128
0
  }
129
3
130
3
  RefPtr<ClearSiteData> service = new ClearSiteData();
131
3
132
3
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
133
3
  if (NS_WARN_IF(!obs)) {
134
0
    return;
135
0
  }
136
3
137
3
  obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
138
3
  obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
139
3
  gClearSiteData = service;
140
3
}
141
142
/* static */ void
143
ClearSiteData::Shutdown()
144
0
{
145
0
  MOZ_ASSERT(NS_IsMainThread());
146
0
147
0
  if (!gClearSiteData) {
148
0
    return;
149
0
  }
150
0
151
0
  RefPtr<ClearSiteData> service = gClearSiteData;
152
0
  gClearSiteData = nullptr;
153
0
154
0
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
155
0
  if (NS_WARN_IF(!obs)) {
156
0
    return;
157
0
  }
158
0
159
0
  obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
160
0
  obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
161
0
}
162
163
3
ClearSiteData::ClearSiteData() = default;
164
0
ClearSiteData::~ClearSiteData() = default;
165
166
NS_IMETHODIMP
167
ClearSiteData::Observe(nsISupports* aSubject, const char* aTopic,
168
                       const char16_t* aData)
169
0
{
170
0
  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
171
0
    Shutdown();
172
0
    return NS_OK;
173
0
  }
174
0
175
0
  MOZ_ASSERT(!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC));
176
0
177
0
  // Pref disabled.
178
0
  if (!StaticPrefs::dom_clearSiteData_enabled()) {
179
0
    return NS_OK;
180
0
  }
181
0
182
0
  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
183
0
  if (NS_WARN_IF(!channel)) {
184
0
    return NS_OK;
185
0
  }
186
0
187
0
  ClearDataFromChannel(channel);
188
0
  return NS_OK;
189
0
}
190
191
void
192
ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel)
193
0
{
194
0
  nsresult rv;
195
0
  nsCOMPtr<nsIURI> uri;
196
0
197
0
  // We want to use the final URI to check if Clear-Site-Data should be allowed
198
0
  // or not.
199
0
  rv = aChannel->GetURI(getter_AddRefs(uri));
200
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
201
0
    return;
202
0
  }
203
0
204
0
  if (!IsSecureURI(uri)) {
205
0
    return;
206
0
  }
207
0
208
0
  uint32_t flags = ParseHeader(aChannel, uri);
209
0
  if (flags == 0) {
210
0
    // Nothing to do.
211
0
    return;
212
0
  }
213
0
214
0
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
215
0
  if (NS_WARN_IF(!ssm)) {
216
0
    return;
217
0
  }
218
0
219
0
  nsCOMPtr<nsIPrincipal> principal;
220
0
  rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
221
0
  if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
222
0
    return;
223
0
  }
224
0
225
0
  int32_t cleanFlags = 0;
226
0
  RefPtr<PendingCleanupHolder> holder = new PendingCleanupHolder(aChannel);
227
0
228
0
  if (flags & eCache) {
229
0
    LogOpToConsole(aChannel, uri, eCache);
230
0
    cleanFlags |= nsIClearDataService::CLEAR_ALL_CACHES;
231
0
  }
232
0
233
0
  if (flags & eCookies) {
234
0
    LogOpToConsole(aChannel, uri, eCookies);
235
0
    cleanFlags |= nsIClearDataService::CLEAR_COOKIES;
236
0
  }
237
0
238
0
  if (flags & eStorage) {
239
0
    LogOpToConsole(aChannel, uri, eStorage);
240
0
    cleanFlags |= nsIClearDataService::CLEAR_DOM_STORAGES;
241
0
  }
242
0
243
0
  if (cleanFlags) {
244
0
    nsCOMPtr<nsIClearDataService> csd =
245
0
      do_GetService("@mozilla.org/clear-data-service;1");
246
0
    MOZ_ASSERT(csd);
247
0
248
0
    rv = holder->Start();
249
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
250
0
      return;
251
0
    }
252
0
253
0
    rv = csd->DeleteDataFromPrincipal(principal,
254
0
                                      false /* user request */,
255
0
                                      cleanFlags, holder);
256
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
257
0
      return;
258
0
    }
259
0
  }
260
0
261
0
  if (flags & eExecutionContexts) {
262
0
    LogOpToConsole(aChannel, uri, eExecutionContexts);
263
0
    BrowsingContextsReload(holder, principal);
264
0
  }
265
0
}
266
267
bool
268
ClearSiteData::IsSecureURI(nsIURI* aURI) const
269
0
{
270
0
  MOZ_ASSERT(aURI);
271
0
272
0
  bool prioriAuthenticated = false;
273
0
  if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(aURI,
274
0
                                               nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
275
0
                                               &prioriAuthenticated)))) {
276
0
    return false;
277
0
  }
278
0
279
0
  return prioriAuthenticated;
280
0
}
281
282
uint32_t
283
ClearSiteData::ParseHeader(nsIHttpChannel* aChannel, nsIURI* aURI) const
284
0
{
285
0
  MOZ_ASSERT(aChannel);
286
0
287
0
  nsAutoCString headerValue;
288
0
  nsresult rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("Clear-Site-Data"),
289
0
                                            headerValue);
290
0
  if (NS_FAILED(rv)) {
291
0
    return 0;
292
0
  }
293
0
294
0
  uint32_t flags = 0;
295
0
296
0
  nsCCharSeparatedTokenizer token(headerValue, ',');
297
0
  while (token.hasMoreTokens()) {
298
0
    auto value = token.nextToken();
299
0
    value.StripTaggedASCII(mozilla::ASCIIMask::MaskWhitespace());
300
0
301
0
    if (value.EqualsLiteral("\"cache\"")) {
302
0
      flags |= eCache;
303
0
      continue;
304
0
    }
305
0
306
0
    if (value.EqualsLiteral("\"cookies\"")) {
307
0
      flags |= eCookies;
308
0
      continue;
309
0
    }
310
0
311
0
    if (value.EqualsLiteral("\"storage\"")) {
312
0
      flags |= eStorage;
313
0
      continue;
314
0
    }
315
0
316
0
    if (value.EqualsLiteral("\"executionContexts\"")) {
317
0
      flags |= eExecutionContexts;
318
0
      continue;
319
0
    }
320
0
321
0
    if (value.EqualsLiteral("\"*\"")) {
322
0
      flags = eCache | eCookies | eStorage | eExecutionContexts;
323
0
      break;
324
0
    }
325
0
326
0
    LogErrorToConsole(aChannel, aURI, value);
327
0
  }
328
0
329
0
  return flags;
330
0
}
331
332
void
333
ClearSiteData::LogOpToConsole(nsIHttpChannel* aChannel, nsIURI* aURI,
334
                              Type aType) const
335
0
{
336
0
  nsAutoString type;
337
0
  TypeToString(aType, type);
338
0
339
0
  nsTArray<nsString> params;
340
0
  params.AppendElement(type);
341
0
342
0
  LogToConsoleInternal(aChannel, aURI, "RunningClearSiteDataValue", params);
343
0
}
344
345
void
346
ClearSiteData::LogErrorToConsole(nsIHttpChannel* aChannel,
347
                                 nsIURI* aURI,
348
                                 const nsACString& aUnknownType) const
349
0
{
350
0
  nsTArray<nsString> params;
351
0
  params.AppendElement(NS_ConvertUTF8toUTF16(aUnknownType));
352
0
353
0
  LogToConsoleInternal(aChannel, aURI, "UnknownClearSiteDataValue", params);
354
0
}
355
356
void
357
ClearSiteData::LogToConsoleInternal(nsIHttpChannel* aChannel, nsIURI* aURI,
358
                                    const char* aMsg,
359
                                    const nsTArray<nsString>& aParams) const
360
0
{
361
0
  MOZ_ASSERT(aChannel);
362
0
  MOZ_ASSERT(aURI);
363
0
364
0
  uint64_t windowID = 0;
365
0
366
0
  nsresult rv = aChannel->GetTopLevelContentWindowId(&windowID);
367
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
368
0
    return;
369
0
  }
370
0
371
0
  if (!windowID) {
372
0
    nsCOMPtr<nsILoadGroup> loadGroup;
373
0
    nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
374
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
375
0
      return;
376
0
    }
377
0
378
0
    if (loadGroup) {
379
0
      windowID = nsContentUtils::GetInnerWindowID(loadGroup);
380
0
    }
381
0
  }
382
0
383
0
  nsAutoString localizedMsg;
384
0
  rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
385
0
                                             aMsg, aParams, localizedMsg);
386
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
387
0
    return;
388
0
  }
389
0
390
0
  rv = nsContentUtils::ReportToConsoleByWindowID(localizedMsg,
391
0
                                                 nsIScriptError::infoFlag,
392
0
                                                 NS_LITERAL_CSTRING("Clear-Site-Data"),
393
0
                                                 windowID,
394
0
                                                 aURI);
395
0
  Unused << NS_WARN_IF(NS_FAILED(rv));
396
0
}
397
398
void
399
ClearSiteData::TypeToString(Type aType, nsAString& aStr) const
400
0
{
401
0
  switch (aType) {
402
0
  case eCache:
403
0
    aStr.AssignLiteral("cache");
404
0
    break;
405
0
406
0
  case eCookies:
407
0
    aStr.AssignLiteral("cookies");
408
0
    break;
409
0
410
0
  case eStorage:
411
0
    aStr.AssignLiteral("storage");
412
0
    break;
413
0
414
0
  case eExecutionContexts:
415
0
    aStr.AssignLiteral("executionContexts");
416
0
    break;
417
0
418
0
  default:
419
0
    MOZ_CRASH("Unknown type.");
420
0
  }
421
0
}
422
423
void
424
ClearSiteData::BrowsingContextsReload(PendingCleanupHolder* aHolder,
425
                                      nsIPrincipal* aPrincipal) const
426
0
{
427
0
  nsAutoCString origin;
428
0
  nsresult rv = aPrincipal->GetOrigin(origin);
429
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
430
0
    return;
431
0
  }
432
0
433
0
  aHolder->BrowsingContextsReloadNeeded(origin);
434
0
}
435
436
0
NS_INTERFACE_MAP_BEGIN(ClearSiteData)
437
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
438
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
439
0
NS_INTERFACE_MAP_END
440
441
NS_IMPL_ADDREF(ClearSiteData)
442
NS_IMPL_RELEASE(ClearSiteData)