Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/nsSecureBrowserUIImpl.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsSecureBrowserUIImpl.h"
7
8
#include "mozilla/Assertions.h"
9
#include "mozilla/Logging.h"
10
#include "mozilla/Unused.h"
11
#include "nsIChannel.h"
12
#include "nsIDocShell.h"
13
#include "nsIDocShellTreeItem.h"
14
#include "nsIInterfaceRequestorUtils.h"
15
#include "nsISecurityEventSink.h"
16
#include "nsITransportSecurityInfo.h"
17
#include "nsIWebProgress.h"
18
19
using namespace mozilla;
20
21
LazyLogModule gSecureBrowserUILog("nsSecureBrowserUI");
22
23
nsSecureBrowserUIImpl::nsSecureBrowserUIImpl()
24
  : mState(0)
25
0
{
26
0
  MOZ_ASSERT(NS_IsMainThread());
27
0
}
28
29
NS_IMPL_ISUPPORTS(nsSecureBrowserUIImpl,
30
                  nsISecureBrowserUI,
31
                  nsIWebProgressListener,
32
                  nsISupportsWeakReference)
33
34
NS_IMETHODIMP
35
nsSecureBrowserUIImpl::Init(mozIDOMWindowProxy* aWindow)
36
0
{
37
0
  MOZ_ASSERT(NS_IsMainThread());
38
0
  NS_ENSURE_ARG(aWindow);
39
0
40
0
  auto* piwindow = nsPIDOMWindowOuter::From(aWindow);
41
0
  nsIDocShell* docShell = piwindow->GetDocShell();
42
0
43
0
  // The Docshell will own the SecureBrowserUI object
44
0
  if (!docShell) {
45
0
    return NS_ERROR_FAILURE;
46
0
  }
47
0
48
0
  docShell->SetSecurityUI(this);
49
0
50
0
  // hook up to the webprogress notifications.
51
0
  nsCOMPtr<nsIWebProgress> wp(do_GetInterface(docShell));
52
0
  if (!wp) {
53
0
    return NS_ERROR_FAILURE;
54
0
  }
55
0
56
0
  // Save this so we can compare it to the web progress in OnLocationChange.
57
0
  nsresult rv;
58
0
  mWebProgress = do_GetWeakReference(wp, &rv);
59
0
  if (NS_FAILED(rv)) {
60
0
    return rv;
61
0
  }
62
0
63
0
  return wp->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION);
64
0
}
65
66
NS_IMETHODIMP
67
nsSecureBrowserUIImpl::GetState(uint32_t* aState)
68
0
{
69
0
  MOZ_ASSERT(NS_IsMainThread());
70
0
  NS_ENSURE_ARG(aState);
71
0
72
0
  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("GetState %p", this));
73
0
  // With respect to mixed content and tracking protection, we won't know when
74
0
  // the state of our document (or a subdocument) has changed, so we ask the
75
0
  // docShell.
76
0
  CheckForBlockedContent();
77
0
  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  mState: %x", mState));
78
0
79
0
  *aState = mState;
80
0
  return NS_OK;
81
0
}
82
83
NS_IMETHODIMP
84
nsSecureBrowserUIImpl::GetSecInfo(nsITransportSecurityInfo** result)
85
0
{
86
0
  MOZ_ASSERT(NS_IsMainThread());
87
0
  NS_ENSURE_ARG_POINTER(result);
88
0
89
0
  *result = mTopLevelSecurityInfo;
90
0
  NS_IF_ADDREF(*result);
91
0
92
0
  return NS_OK;
93
0
}
94
95
NS_IMETHODIMP
96
nsSecureBrowserUIImpl::SetDocShell(nsIDocShell* aDocShell)
97
0
{
98
0
  MOZ_ASSERT(NS_IsMainThread());
99
0
  NS_ENSURE_ARG(aDocShell);
100
0
  nsresult rv;
101
0
  mDocShell = do_GetWeakReference(aDocShell, &rv);
102
0
  return rv;
103
0
}
104
105
// Ask the docShell if we've blocked or loaded any mixed or tracking content.
106
void
107
nsSecureBrowserUIImpl::CheckForBlockedContent()
108
0
{
109
0
  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
110
0
  if (!docShell) {
111
0
    return;
112
0
  }
113
0
114
0
  // For content docShells, the mixed content security state is set on the root
115
0
  // docShell.
116
0
  if (docShell->ItemType() == nsIDocShellTreeItem::typeContent) {
117
0
    nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(docShell));
118
0
    nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
119
0
    Unused << docShellTreeItem->GetSameTypeRootTreeItem(
120
0
      getter_AddRefs(sameTypeRoot));
121
0
    MOZ_ASSERT(
122
0
      sameTypeRoot,
123
0
      "No document shell root tree item from document shell tree item!");
124
0
    docShell = do_QueryInterface(sameTypeRoot);
125
0
    if (!docShell) {
126
0
      return;
127
0
    }
128
0
  }
129
0
130
0
  // Has mixed content been loaded or blocked in nsMixedContentBlocker?
131
0
  // This only applies to secure documents.
132
0
  if (mState & STATE_IS_SECURE) {
133
0
    if (docShell->GetHasMixedActiveContentLoaded()) {
134
0
      mState |= STATE_IS_BROKEN | STATE_LOADED_MIXED_ACTIVE_CONTENT;
135
0
      mState &= ~STATE_IS_SECURE;
136
0
      mState &= ~STATE_SECURE_HIGH;
137
0
    }
138
0
139
0
    if (docShell->GetHasMixedDisplayContentLoaded()) {
140
0
      mState |= STATE_IS_BROKEN | STATE_LOADED_MIXED_DISPLAY_CONTENT;
141
0
      mState &= ~STATE_IS_SECURE;
142
0
      mState &= ~STATE_SECURE_HIGH;
143
0
    }
144
0
145
0
    if (docShell->GetHasMixedActiveContentBlocked()) {
146
0
      mState |= STATE_BLOCKED_MIXED_ACTIVE_CONTENT;
147
0
    }
148
0
149
0
    if (docShell->GetHasMixedDisplayContentBlocked()) {
150
0
      mState |= STATE_BLOCKED_MIXED_DISPLAY_CONTENT;
151
0
    }
152
0
  }
153
0
154
0
  // Has tracking content been blocked or loaded?
155
0
  if (docShell->GetHasTrackingContentBlocked()) {
156
0
    mState |= STATE_BLOCKED_TRACKING_CONTENT;
157
0
  }
158
0
159
0
  if (docShell->GetHasSlowTrackingContentBlocked()) {
160
0
    mState |= STATE_BLOCKED_SLOW_TRACKING_CONTENT;
161
0
  }
162
0
163
0
  if (docShell->GetHasTrackingContentLoaded()) {
164
0
    mState |= STATE_LOADED_TRACKING_CONTENT;
165
0
  }
166
0
167
0
  if (docShell->GetHasCookiesBlockedByPermission()) {
168
0
    mState |= STATE_COOKIES_BLOCKED_BY_PERMISSION;
169
0
  }
170
0
171
0
  if (docShell->GetHasCookiesBlockedDueToTrackers()) {
172
0
    mState |= STATE_COOKIES_BLOCKED_TRACKER;
173
0
  }
174
0
175
0
  if (docShell->GetHasForeignCookiesBeenBlocked()) {
176
0
    mState |= STATE_COOKIES_BLOCKED_FOREIGN;
177
0
  }
178
0
179
0
  if (docShell->GetHasAllCookiesBeenBlocked()) {
180
0
    mState |= STATE_COOKIES_BLOCKED_ALL;
181
0
  }
182
0
}
183
184
// Helper function to get the securityInfo from a channel as a
185
// nsITransportSecurityInfo. The out parameter will be set to null if there is
186
// no securityInfo set.
187
static void
188
GetSecurityInfoFromChannel(nsIChannel* channel,
189
                           nsITransportSecurityInfo** securityInfoOut)
190
0
{
191
0
  MOZ_ASSERT(channel);
192
0
  MOZ_ASSERT(securityInfoOut);
193
0
194
0
  NS_ENSURE_TRUE_VOID(channel);
195
0
  NS_ENSURE_TRUE_VOID(securityInfoOut);
196
0
197
0
  *securityInfoOut = nullptr;
198
0
199
0
  nsCOMPtr<nsISupports> securityInfoSupports;
200
0
  nsresult rv = channel->GetSecurityInfo(getter_AddRefs(securityInfoSupports));
201
0
  // GetSecurityInfo may return an error, but it's not necessarily fatal - the
202
0
  // underlying channel may simply not have a securityInfo.
203
0
  if (NS_FAILED(rv)) {
204
0
    return;
205
0
  }
206
0
  nsCOMPtr<nsITransportSecurityInfo> securityInfo(
207
0
    do_QueryInterface(securityInfoSupports));
208
0
  securityInfo.forget(securityInfoOut);
209
0
}
210
211
nsresult
212
nsSecureBrowserUIImpl::UpdateStateAndSecurityInfo(nsIChannel* channel,
213
                                                  nsIURI* uri)
214
0
{
215
0
  MOZ_ASSERT(channel);
216
0
  MOZ_ASSERT(uri);
217
0
218
0
  NS_ENSURE_ARG(channel);
219
0
  NS_ENSURE_ARG(uri);
220
0
221
0
  mState = STATE_IS_INSECURE;
222
0
  mTopLevelSecurityInfo = nullptr;
223
0
224
0
  nsCOMPtr<nsITransportSecurityInfo> securityInfo;
225
0
  GetSecurityInfoFromChannel(channel, getter_AddRefs(securityInfo));
226
0
  if (securityInfo) {
227
0
    MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
228
0
            ("  we have a security info %p", securityInfo.get()));
229
0
230
0
    nsresult rv = securityInfo->GetSecurityState(&mState);
231
0
    if (NS_FAILED(rv)) {
232
0
      return rv;
233
0
    }
234
0
    // If the security state is STATE_IS_INSECURE, the TLS handshake never
235
0
    // completed. Don't set any further state.
236
0
    if (mState == STATE_IS_INSECURE) {
237
0
      return NS_OK;
238
0
    }
239
0
240
0
    mTopLevelSecurityInfo = securityInfo;
241
0
    MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
242
0
            ("  set mTopLevelSecurityInfo"));
243
0
    bool isEV;
244
0
    rv = mTopLevelSecurityInfo->GetIsExtendedValidation(&isEV);
245
0
    if (NS_FAILED(rv)) {
246
0
      return rv;
247
0
    }
248
0
    if (isEV) {
249
0
      MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug, ("  is EV"));
250
0
      mState |= STATE_IDENTITY_EV_TOPLEVEL;
251
0
    }
252
0
  }
253
0
  return NS_OK;
254
0
}
255
256
// We receive this notification for the nsIWebProgress we added ourselves to
257
// (i.e. the window we were passed in Init, which should be the top-level
258
// window or whatever corresponds to an <iframe mozbrowser> element). In some
259
// cases, we also receive it from nsIWebProgress instances that are children of
260
// that nsIWebProgress. We ignore notifications from children because they don't
261
// change the top-level state (if children load mixed or tracking content, the
262
// docShell will know and will tell us in GetState when we call
263
// CheckForBlockedContent).
264
// When we receive a notification from the top-level nsIWebProgress, we extract
265
// any relevant security information and set our state accordingly. We then call
266
// OnSecurityChange to notify any downstream listeners of the security state.
267
NS_IMETHODIMP
268
nsSecureBrowserUIImpl::OnLocationChange(nsIWebProgress* aWebProgress,
269
                                        nsIRequest* aRequest,
270
                                        nsIURI* aLocation,
271
                                        uint32_t aFlags)
272
0
{
273
0
  MOZ_ASSERT(NS_IsMainThread());
274
0
275
0
  NS_ENSURE_ARG(aWebProgress);
276
0
  NS_ENSURE_ARG(aLocation);
277
0
278
0
  MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
279
0
          ("%p OnLocationChange: %p %p %s %x", this, aWebProgress, aRequest,
280
0
           aLocation->GetSpecOrDefault().get(), aFlags));
281
0
282
0
  // Filter out events from children. See comment at the top of this function.
283
0
  // It would be nice if the attribute isTopLevel worked for this, but that
284
0
  // filters out events for <iframe mozbrowser> elements, which means they don't
285
0
  // get OnSecurityChange events from this implementation. Instead, we check to
286
0
  // see if the web progress we were handed here is the same one as we were
287
0
  // initialized with.
288
0
  nsCOMPtr<nsIWebProgress> originalWebProgress = do_QueryReferent(mWebProgress);
289
0
  if (aWebProgress != originalWebProgress) {
290
0
    return NS_OK;
291
0
  }
292
0
293
0
  // Clear any state that varies by location.
294
0
  if (!(aFlags & LOCATION_CHANGE_SAME_DOCUMENT)) {
295
0
    mState = 0;
296
0
    mTopLevelSecurityInfo = nullptr;
297
0
  }
298
0
299
0
  // NB: aRequest may be null. It may also not be QI-able to nsIChannel.
300
0
  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
301
0
  if (channel) {
302
0
    MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
303
0
            ("  we have a channel %p", channel.get()));
304
0
    nsresult rv = UpdateStateAndSecurityInfo(channel, aLocation);
305
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
306
0
      return rv;
307
0
    }
308
0
309
0
    nsCOMPtr<nsISecurityEventSink> eventSink;
310
0
    NS_QueryNotificationCallbacks(channel, eventSink);
311
0
    if (NS_WARN_IF(!eventSink)) {
312
0
      return NS_ERROR_INVALID_ARG;
313
0
    }
314
0
    MOZ_LOG(gSecureBrowserUILog, LogLevel::Debug,
315
0
            ("  calling OnSecurityChange %p %x\n", aRequest, mState));
316
0
    Unused << eventSink->OnSecurityChange(aRequest, mState);
317
0
  }
318
0
319
0
  return NS_OK;
320
0
}
321
322
NS_IMETHODIMP
323
nsSecureBrowserUIImpl::OnStateChange(nsIWebProgress*,
324
                                     nsIRequest*,
325
                                     uint32_t,
326
                                     nsresult)
327
0
{
328
0
  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
329
0
  return NS_OK;
330
0
}
331
332
NS_IMETHODIMP
333
nsSecureBrowserUIImpl::OnProgressChange(nsIWebProgress*,
334
                                        nsIRequest*,
335
                                        int32_t,
336
                                        int32_t,
337
                                        int32_t,
338
                                        int32_t)
339
0
{
340
0
  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
341
0
  return NS_OK;
342
0
}
343
344
NS_IMETHODIMP
345
nsSecureBrowserUIImpl::OnStatusChange(nsIWebProgress*,
346
                                      nsIRequest*,
347
                                      nsresult,
348
                                      const char16_t*)
349
0
{
350
0
  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
351
0
  return NS_OK;
352
0
}
353
354
nsresult
355
nsSecureBrowserUIImpl::OnSecurityChange(nsIWebProgress*, nsIRequest*, uint32_t)
356
0
{
357
0
  MOZ_ASSERT_UNREACHABLE("Should have been excluded in AddProgressListener()");
358
0
  return NS_OK;
359
0
}