Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/credentialmanagement/CredentialsContainer.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 "mozilla/dom/CredentialsContainer.h"
8
#include "mozilla/dom/Promise.h"
9
#include "mozilla/dom/WebAuthnManager.h"
10
#include "nsContentUtils.h"
11
#include "nsFocusManager.h"
12
#include "nsIDocShell.h"
13
14
namespace mozilla {
15
namespace dom {
16
17
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CredentialsContainer, mParent, mManager)
18
NS_IMPL_CYCLE_COLLECTING_ADDREF(CredentialsContainer)
19
NS_IMPL_CYCLE_COLLECTING_RELEASE(CredentialsContainer)
20
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CredentialsContainer)
21
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
22
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
23
0
NS_INTERFACE_MAP_END
24
25
already_AddRefed<Promise>
26
CreateAndReject(nsPIDOMWindowInner* aParent, ErrorResult& aRv)
27
0
{
28
0
  MOZ_ASSERT(aParent);
29
0
30
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aParent);
31
0
  if (NS_WARN_IF(!global)) {
32
0
    aRv.Throw(NS_ERROR_FAILURE);
33
0
    return nullptr;
34
0
  }
35
0
36
0
  RefPtr<Promise> promise = Promise::Create(global, aRv);
37
0
  if (NS_WARN_IF(aRv.Failed())) {
38
0
    return nullptr;
39
0
  }
40
0
41
0
  promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
42
0
  return promise.forget();
43
0
}
44
45
static bool
46
IsInActiveTab(nsPIDOMWindowInner* aParent)
47
0
{
48
0
  // Returns whether aParent is an inner window somewhere in the active tab.
49
0
  // The active tab is the selected (i.e. visible) tab in the focused window.
50
0
  MOZ_ASSERT(aParent);
51
0
52
0
  nsCOMPtr<nsIDocument> doc(aParent->GetExtantDoc());
53
0
  if (NS_WARN_IF(!doc)) {
54
0
    return false;
55
0
  }
56
0
57
0
  nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
58
0
  if (!docShell) {
59
0
    return false;
60
0
  }
61
0
62
0
  bool isActive = false;
63
0
  docShell->GetIsActive(&isActive);
64
0
  if (!isActive) {
65
0
    return false;
66
0
  }
67
0
68
0
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
69
0
  docShell->GetRootTreeItem(getter_AddRefs(rootItem));
70
0
  if (!rootItem) {
71
0
    return false;
72
0
  }
73
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
74
0
  if (!rootWin) {
75
0
    return false;
76
0
  }
77
0
78
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
79
0
  if (!fm) {
80
0
    return false;
81
0
  }
82
0
83
0
  nsCOMPtr<mozIDOMWindowProxy> activeWindow;
84
0
  fm->GetActiveWindow(getter_AddRefs(activeWindow));
85
0
  return activeWindow == rootWin;
86
0
}
87
88
static bool
89
IsSameOriginWithAncestors(nsPIDOMWindowInner* aParent)
90
0
{
91
0
  // This method returns true if aParent is either not in a frame / iframe, or
92
0
  // is in a frame or iframe and all ancestors for aParent are the same origin.
93
0
  // This is useful for Credential Management because we need to prohibit
94
0
  // iframes, but not break mochitests (which use iframes to embed the tests).
95
0
  MOZ_ASSERT(aParent);
96
0
97
0
  if (aParent->IsTopInnerWindow()) {
98
0
    // Not in a frame or iframe
99
0
    return true;
100
0
  }
101
0
102
0
  // We're in some kind of frame, so let's get the parent and start checking
103
0
  // the same origin policy
104
0
  nsINode* node = nsContentUtils::GetCrossDocParentNode(aParent->GetExtantDoc());
105
0
  if (NS_WARN_IF(!node)) {
106
0
    // This is a sanity check, since there has to be a parent. Fail safe.
107
0
    return false;
108
0
  }
109
0
110
0
  // Check that all ancestors are the same origin, repeating until we find a
111
0
  // null parent
112
0
  do {
113
0
    nsresult rv = nsContentUtils::CheckSameOrigin(aParent->GetExtantDoc(), node);
114
0
    if (NS_FAILED(rv)) {
115
0
      // same-origin policy is violated
116
0
      return false;
117
0
    }
118
0
119
0
    node = nsContentUtils::GetCrossDocParentNode(node);
120
0
  } while (node);
121
0
122
0
  return true;
123
0
}
124
125
CredentialsContainer::CredentialsContainer(nsPIDOMWindowInner* aParent) :
126
  mParent(aParent)
127
0
{
128
0
  MOZ_ASSERT(aParent);
129
0
}
130
131
CredentialsContainer::~CredentialsContainer()
132
0
{}
133
134
void
135
CredentialsContainer::EnsureWebAuthnManager()
136
0
{
137
0
  MOZ_ASSERT(NS_IsMainThread());
138
0
139
0
  if (!mManager) {
140
0
    mManager = new WebAuthnManager(mParent);
141
0
  }
142
0
}
143
144
JSObject*
145
CredentialsContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
146
0
{
147
0
  return CredentialsContainer_Binding::Wrap(aCx, this, aGivenProto);
148
0
}
149
150
already_AddRefed<Promise>
151
CredentialsContainer::Get(const CredentialRequestOptions& aOptions,
152
                          ErrorResult& aRv)
153
0
{
154
0
  if (!IsSameOriginWithAncestors(mParent) || !IsInActiveTab(mParent)) {
155
0
    return CreateAndReject(mParent, aRv);
156
0
  }
157
0
158
0
  EnsureWebAuthnManager();
159
0
  return mManager->GetAssertion(aOptions.mPublicKey, aOptions.mSignal);
160
0
}
161
162
already_AddRefed<Promise>
163
CredentialsContainer::Create(const CredentialCreationOptions& aOptions,
164
                             ErrorResult& aRv)
165
0
{
166
0
  if (!IsSameOriginWithAncestors(mParent) || !IsInActiveTab(mParent)) {
167
0
    return CreateAndReject(mParent, aRv);
168
0
  }
169
0
170
0
  EnsureWebAuthnManager();
171
0
  return mManager->MakeCredential(aOptions.mPublicKey, aOptions.mSignal);
172
0
}
173
174
already_AddRefed<Promise>
175
CredentialsContainer::Store(const Credential& aCredential, ErrorResult& aRv)
176
0
{
177
0
  if (!IsSameOriginWithAncestors(mParent) || !IsInActiveTab(mParent)) {
178
0
    return CreateAndReject(mParent, aRv);
179
0
  }
180
0
181
0
  EnsureWebAuthnManager();
182
0
  return mManager->Store(aCredential);
183
0
}
184
185
already_AddRefed<Promise>
186
CredentialsContainer::PreventSilentAccess(ErrorResult& aRv)
187
0
{
188
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
189
0
  if (NS_WARN_IF(!global)) {
190
0
    aRv.Throw(NS_ERROR_FAILURE);
191
0
    return nullptr;
192
0
  }
193
0
194
0
  RefPtr<Promise> promise = Promise::Create(global, aRv);
195
0
  if (NS_WARN_IF(aRv.Failed())) {
196
0
    return nullptr;
197
0
  }
198
0
199
0
  promise->MaybeResolveWithUndefined();
200
0
  return promise.forget();
201
0
}
202
203
} // namespace dom
204
} // namespace mozilla