Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "mozilla/dom/U2FTokenManager.h"
8 : #include "mozilla/dom/U2FTokenTransport.h"
9 : #include "mozilla/dom/U2FHIDTokenManager.h"
10 : #include "mozilla/dom/U2FSoftTokenManager.h"
11 : #include "mozilla/dom/WebAuthnTransactionParent.h"
12 : #include "mozilla/MozPromise.h"
13 : #include "mozilla/dom/WebAuthnUtil.h"
14 : #include "mozilla/ClearOnShutdown.h"
15 : #include "mozilla/Unused.h"
16 : #include "hasht.h"
17 : #include "nsICryptoHash.h"
18 : #include "pkix/Input.h"
19 : #include "pkixutil.h"
20 :
21 : // Not named "security.webauth.u2f_softtoken_counter" because setting that
22 : // name causes the window.u2f object to disappear until preferences get
23 : // reloaded, as its pref is a substring!
24 : #define PREF_U2F_NSSTOKEN_COUNTER "security.webauth.softtoken_counter"
25 : #define PREF_WEBAUTHN_SOFTTOKEN_ENABLED "security.webauth.webauthn_enable_softtoken"
26 : #define PREF_WEBAUTHN_USBTOKEN_ENABLED "security.webauth.webauthn_enable_usbtoken"
27 :
28 : namespace mozilla {
29 : namespace dom {
30 :
31 : /***********************************************************************
32 : * Statics
33 : **********************************************************************/
34 :
35 : class U2FPrefManager;
36 :
37 : namespace {
38 : static mozilla::LazyLogModule gU2FTokenManagerLog("u2fkeymanager");
39 3 : StaticRefPtr<U2FTokenManager> gU2FTokenManager;
40 3 : StaticRefPtr<U2FPrefManager> gPrefManager;
41 : }
42 :
43 : class U2FPrefManager final : public nsIObserver
44 : {
45 : private:
46 1 : U2FPrefManager() :
47 1 : mPrefMutex("U2FPrefManager Mutex")
48 : {
49 1 : UpdateValues();
50 1 : }
51 0 : ~U2FPrefManager() = default;
52 :
53 : public:
54 : NS_DECL_ISUPPORTS
55 :
56 1 : static U2FPrefManager* GetOrCreate()
57 : {
58 1 : MOZ_ASSERT(NS_IsMainThread());
59 1 : if (!gPrefManager) {
60 1 : gPrefManager = new U2FPrefManager();
61 1 : Preferences::AddStrongObserver(gPrefManager, PREF_WEBAUTHN_SOFTTOKEN_ENABLED);
62 1 : Preferences::AddStrongObserver(gPrefManager, PREF_U2F_NSSTOKEN_COUNTER);
63 1 : Preferences::AddStrongObserver(gPrefManager, PREF_WEBAUTHN_USBTOKEN_ENABLED);
64 1 : ClearOnShutdown(&gPrefManager, ShutdownPhase::ShutdownThreads);
65 : }
66 1 : return gPrefManager;
67 : }
68 :
69 0 : static U2FPrefManager* Get()
70 : {
71 0 : return gPrefManager;
72 : }
73 :
74 0 : bool GetSoftTokenEnabled()
75 : {
76 0 : MutexAutoLock lock(mPrefMutex);
77 0 : return mSoftTokenEnabled;
78 : }
79 :
80 0 : int GetSoftTokenCounter()
81 : {
82 0 : MutexAutoLock lock(mPrefMutex);
83 0 : return mSoftTokenCounter;
84 : }
85 :
86 0 : bool GetUsbTokenEnabled()
87 : {
88 0 : MutexAutoLock lock(mPrefMutex);
89 0 : return mUsbTokenEnabled;
90 : }
91 :
92 : NS_IMETHODIMP
93 0 : Observe(nsISupports* aSubject,
94 : const char* aTopic,
95 : const char16_t* aData) override
96 : {
97 0 : UpdateValues();
98 0 : return NS_OK;
99 : }
100 : private:
101 1 : void UpdateValues() {
102 1 : MOZ_ASSERT(NS_IsMainThread());
103 2 : MutexAutoLock lock(mPrefMutex);
104 1 : mSoftTokenEnabled = Preferences::GetBool(PREF_WEBAUTHN_SOFTTOKEN_ENABLED);
105 1 : mSoftTokenCounter = Preferences::GetUint(PREF_U2F_NSSTOKEN_COUNTER);
106 1 : mUsbTokenEnabled = Preferences::GetBool(PREF_WEBAUTHN_USBTOKEN_ENABLED);
107 1 : }
108 :
109 : Mutex mPrefMutex;
110 : bool mSoftTokenEnabled;
111 : int mSoftTokenCounter;
112 : bool mUsbTokenEnabled;
113 : };
114 :
115 25 : NS_IMPL_ISUPPORTS(U2FPrefManager, nsIObserver);
116 :
117 : /***********************************************************************
118 : * U2FManager Implementation
119 : **********************************************************************/
120 :
121 1 : U2FTokenManager::U2FTokenManager()
122 : : mTransactionParent(nullptr)
123 1 : , mTransactionId(0)
124 : {
125 1 : MOZ_ASSERT(XRE_IsParentProcess());
126 : // Create on the main thread to make sure ClearOnShutdown() works.
127 1 : MOZ_ASSERT(NS_IsMainThread());
128 : // Create the preference manager while we're initializing.
129 1 : U2FPrefManager::GetOrCreate();
130 1 : }
131 :
132 0 : U2FTokenManager::~U2FTokenManager()
133 : {
134 0 : MOZ_ASSERT(NS_IsMainThread());
135 0 : }
136 :
137 : //static
138 : void
139 3 : U2FTokenManager::Initialize()
140 : {
141 3 : if (!XRE_IsParentProcess()) {
142 2 : return;
143 : }
144 1 : MOZ_ASSERT(NS_IsMainThread());
145 1 : MOZ_ASSERT(!gU2FTokenManager);
146 1 : gU2FTokenManager = new U2FTokenManager();
147 1 : ClearOnShutdown(&gU2FTokenManager);
148 : }
149 :
150 : //static
151 : U2FTokenManager*
152 0 : U2FTokenManager::Get()
153 : {
154 0 : MOZ_ASSERT(XRE_IsParentProcess());
155 : // We should only be accessing this on the background thread
156 0 : MOZ_ASSERT(!NS_IsMainThread());
157 0 : return gU2FTokenManager;
158 : }
159 :
160 : void
161 0 : U2FTokenManager::MaybeAbortTransaction(uint64_t aTransactionId,
162 : const nsresult& aError)
163 : {
164 0 : if (mTransactionId != aTransactionId) {
165 0 : return;
166 : }
167 :
168 0 : AbortTransaction(aError);
169 : }
170 :
171 : void
172 0 : U2FTokenManager::AbortTransaction(const nsresult& aError)
173 : {
174 0 : Unused << mTransactionParent->SendCancel(aError);
175 0 : ClearTransaction();
176 0 : }
177 :
178 : void
179 0 : U2FTokenManager::MaybeClearTransaction(WebAuthnTransactionParent* aParent)
180 : {
181 : // Only clear if we've been requested to do so by our current transaction
182 : // parent.
183 0 : if (mTransactionParent == aParent) {
184 0 : ClearTransaction();
185 : }
186 0 : }
187 :
188 : void
189 0 : U2FTokenManager::ClearTransaction()
190 : {
191 0 : mTransactionParent = nullptr;
192 : // Drop managers at the end of all transactions
193 0 : mTokenManagerImpl = nullptr;
194 : // Drop promises.
195 0 : mRegisterPromise = nullptr;
196 0 : mSignPromise = nullptr;
197 : // Increase in case we're called by the WebAuthnTransactionParent.
198 0 : mTransactionId++;
199 0 : }
200 :
201 : RefPtr<U2FTokenTransport>
202 0 : U2FTokenManager::GetTokenManagerImpl()
203 : {
204 0 : if (mTokenManagerImpl) {
205 0 : return mTokenManagerImpl;
206 : }
207 :
208 0 : auto pm = U2FPrefManager::Get();
209 0 : bool useSoftToken = pm->GetSoftTokenEnabled();
210 0 : bool useUsbToken = pm->GetUsbTokenEnabled();
211 :
212 : // At least one token type must be enabled.
213 : // We currently don't support soft and USB tokens enabled at
214 : // the same time as the softtoken would always win the race to register.
215 : // We could support it for signing though...
216 0 : if (!(useSoftToken ^ useUsbToken)) {
217 0 : return nullptr;
218 : }
219 :
220 0 : if (useSoftToken) {
221 0 : return new U2FSoftTokenManager(pm->GetSoftTokenCounter());
222 : }
223 :
224 : // TODO Use WebAuthnRequest to aggregate results from all transports,
225 : // once we have multiple HW transport types.
226 0 : return new U2FHIDTokenManager();
227 : }
228 :
229 : void
230 0 : U2FTokenManager::Register(WebAuthnTransactionParent* aTransactionParent,
231 : const WebAuthnTransactionInfo& aTransactionInfo)
232 : {
233 0 : MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));
234 0 : MOZ_ASSERT(U2FPrefManager::Get());
235 :
236 0 : uint64_t tid = ++mTransactionId;
237 0 : mTransactionParent = aTransactionParent;
238 0 : mTokenManagerImpl = GetTokenManagerImpl();
239 :
240 0 : if (!mTokenManagerImpl) {
241 0 : AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
242 0 : return;
243 : }
244 :
245 : // Check if all the supplied parameters are syntactically well-formed and
246 : // of the correct length. If not, return an error code equivalent to
247 : // UnknownError and terminate the operation.
248 :
249 0 : if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
250 0 : (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
251 0 : AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
252 0 : return;
253 : }
254 :
255 0 : mRegisterPromise = mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
256 : aTransactionInfo.RpIdHash(),
257 0 : aTransactionInfo.ClientDataHash());
258 :
259 : mRegisterPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
260 0 : [tid](U2FRegisterResult&& aResult) {
261 0 : U2FTokenManager* mgr = U2FTokenManager::Get();
262 0 : mgr->MaybeConfirmRegister(tid, aResult);
263 0 : },
264 0 : [tid](nsresult rv) {
265 0 : MOZ_ASSERT(NS_FAILED(rv));
266 0 : U2FTokenManager* mgr = U2FTokenManager::Get();
267 0 : mgr->MaybeAbortTransaction(tid, rv);
268 0 : });
269 : }
270 :
271 : void
272 0 : U2FTokenManager::MaybeConfirmRegister(uint64_t aTransactionId,
273 : U2FRegisterResult& aResult)
274 : {
275 0 : if (mTransactionId != aTransactionId) {
276 0 : return;
277 : }
278 :
279 0 : nsTArray<uint8_t> registration;
280 0 : aResult.ConsumeRegistration(registration);
281 :
282 0 : Unused << mTransactionParent->SendConfirmRegister(registration);
283 0 : ClearTransaction();
284 : }
285 :
286 : void
287 0 : U2FTokenManager::Sign(WebAuthnTransactionParent* aTransactionParent,
288 : const WebAuthnTransactionInfo& aTransactionInfo)
289 : {
290 0 : MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign"));
291 0 : MOZ_ASSERT(U2FPrefManager::Get());
292 :
293 0 : uint64_t tid = ++mTransactionId;
294 0 : mTransactionParent = aTransactionParent;
295 0 : mTokenManagerImpl = GetTokenManagerImpl();
296 :
297 0 : if (!mTokenManagerImpl) {
298 0 : AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
299 0 : return;
300 : }
301 :
302 0 : if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
303 0 : (aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
304 0 : AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
305 0 : return;
306 : }
307 :
308 0 : mSignPromise = mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
309 : aTransactionInfo.RpIdHash(),
310 0 : aTransactionInfo.ClientDataHash());
311 :
312 : mSignPromise->Then(GetCurrentThreadSerialEventTarget(), __func__,
313 0 : [tid](U2FSignResult&& aResult) {
314 0 : U2FTokenManager* mgr = U2FTokenManager::Get();
315 0 : mgr->MaybeConfirmSign(tid, aResult);
316 0 : },
317 0 : [tid](nsresult rv) {
318 0 : MOZ_ASSERT(NS_FAILED(rv));
319 0 : U2FTokenManager* mgr = U2FTokenManager::Get();
320 0 : mgr->MaybeAbortTransaction(tid, rv);
321 0 : });
322 : }
323 :
324 : void
325 0 : U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId,
326 : U2FSignResult& aResult)
327 : {
328 0 : if (mTransactionId != aTransactionId) {
329 0 : return;
330 : }
331 :
332 0 : nsTArray<uint8_t> keyHandle;
333 0 : aResult.ConsumeKeyHandle(keyHandle);
334 0 : nsTArray<uint8_t> signature;
335 0 : aResult.ConsumeSignature(signature);
336 :
337 0 : Unused << mTransactionParent->SendConfirmSign(keyHandle, signature);
338 0 : ClearTransaction();
339 : }
340 :
341 : void
342 0 : U2FTokenManager::Cancel(WebAuthnTransactionParent* aParent)
343 : {
344 0 : if (mTransactionParent == aParent) {
345 0 : mTokenManagerImpl->Cancel();
346 0 : ClearTransaction();
347 : }
348 0 : }
349 :
350 : }
351 : }
|