Bug 1505106 - Support TouchID/FaceID for Reauthentication r=spohl
authorJ.C. Jones <jjones@mozilla.com>
Wed, 28 Nov 2018 18:35:02 +0000
changeset 507958 2b2777f72b09079f43018c04b1cbed49b959dff1
parent 507957 1e338cf25fc326d7502faae4a56aa73e4231d994
child 507959 f56dfb58f1c9a0e632df1448eb46f5bf30a2c70b
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersspohl
bugs1505106, 1499846
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1505106 - Support TouchID/FaceID for Reauthentication r=spohl In Bug 1499846 we added support for OSX to do Keychain-based reauthentication. On newer versions of OSX, it's possible to instead do TouchID/FaceID for bio- metric reauthentication, with a fallback to Keychain. This implements that functionality. There's no C++ interface to access the LocalAuthentication framework, so it adds an Objective-C method called by the existing OSReauthenticator methods to perform its work. Differential Revision: https://phabricator.services.mozilla.com/D11700
security/manager/ssl/OSReauthenticator.cpp
security/manager/ssl/OSReauthenticator.h
security/manager/ssl/OSReauthenticatorDarwin.mm
security/manager/ssl/moz.build
--- a/security/manager/ssl/OSReauthenticator.cpp
+++ b/security/manager/ssl/OSReauthenticator.cpp
@@ -211,67 +211,16 @@ ReauthenticateUserWindows(const nsACStri
         reauthenticated = true;
         break;
     }
   }
   return NS_OK;
 }
 #endif // XP_WIN
 
-#ifdef XP_MACOSX
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/Security.h>
-
-static nsresult
-ReauthenticateUserMacOS(const nsACString& aPrompt,
-              /* out */ bool& aReauthenticated)
-{
-  // The idea here is that we ask to be authorized to unlock the user's session.
-  // This should cause a prompt to come up for the user asking them for their
-  // password. If they correctly enter it, we'll return a successful result. If
-  // they cancel the prompt or otherwise fail to provide their password, we
-  // return a failing result.
-  AuthorizationItem authorizationItems[] = {
-    { "system.login.screensaver", 0, NULL, 0 },
-  };
-  AuthorizationRights authorizationRights = {
-    ArrayLength(authorizationItems),
-    authorizationItems,
-  };
-  const nsCString& promptFlat = PromiseFlatCString(aPrompt);
-  // All "kAuthorization..." constants come from the MacOS SDK.
-  AuthorizationItem environmentItems[] =  {
-    { kAuthorizationEnvironmentPrompt,
-      promptFlat.Length(),
-      (void*)promptFlat.get(),
-      0
-    },
-  };
-  AuthorizationEnvironment environment = {
-    ArrayLength(environmentItems),
-    environmentItems,
-  };
-  AuthorizationFlags flags = kAuthorizationFlagDefaults |
-                             kAuthorizationFlagInteractionAllowed |
-                             kAuthorizationFlagExtendRights |
-                             kAuthorizationFlagPreAuthorize |
-                             kAuthorizationFlagDestroyRights;
-  AuthorizationRef authorizationRef = nullptr;
-  OSStatus result = AuthorizationCreate(&authorizationRights,
-                                        &environment,
-                                        flags,
-                                        &authorizationRef);
-  aReauthenticated = result == errAuthorizationSuccess;
-  if (authorizationRef) {
-    AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
-  }
-  return NS_OK;
-}
-#endif // XP_MACOSX
-
 static nsresult
 ReauthenticateUser(const nsACString& prompt, /* out */ bool& reauthenticated)
 {
   reauthenticated = false;
 #if defined(XP_WIN)
   return ReauthenticateUserWindows(prompt, reauthenticated);
 #elif defined(XP_MACOSX)
   return ReauthenticateUserMacOS(prompt, reauthenticated);
--- a/security/manager/ssl/OSReauthenticator.h
+++ b/security/manager/ssl/OSReauthenticator.h
@@ -18,9 +18,15 @@ class OSReauthenticator : public nsIOSRe
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOSREAUTHENTICATOR
 
 private:
   virtual ~OSReauthenticator() = default;
 };
 
+#ifdef XP_MACOSX
+nsresult
+ReauthenticateUserMacOS(const nsACString& aPrompt,
+              /* out */ bool& aReauthenticated);
+#endif // XP_MACOSX
+
 #endif // OSReauthenticator_h
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/OSReauthenticatorDarwin.mm
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "OSReauthenticator.h"
+
+#include "nsCocoaUtils.h"
+
+using namespace mozilla;
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <LocalAuthentication/LocalAuthentication.h>
+
+nsresult
+ReauthenticateUserMacOS(const nsACString& aPrompt,
+              /* out */ bool& aReauthenticated)
+{
+  // The idea here is that we ask to be authorized to unlock the user's session.
+  // This should cause a prompt to come up for the user asking them for their
+  // password. If they correctly enter it, we'll set aReauthenticated to true.
+
+  LAContext* context = [[LAContext alloc] init];
+  NSString* prompt = nsCocoaUtils::ToNSString(NS_ConvertUTF8toUTF16(aPrompt));
+
+  dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+
+  __block BOOL biometricSuccess; // mark variable r/w across the block
+
+  // Note: This is an async callback in an already-async Promise chain.
+  [context evaluatePolicy:LAPolicyDeviceOwnerAuthentication
+           localizedReason:prompt
+           reply:^(BOOL success, NSError *error) {
+    dispatch_async(dispatch_get_main_queue(), ^{
+      // error is not particularly useful in this context, and we have no
+      // mechanism to really return it. We could use it to set the nsresult,
+      // but this is a best-effort mechanism and there's no particular case for
+      // propagating up XPCOM.
+      biometricSuccess = success;
+      dispatch_semaphore_signal(sema);
+    });
+  }];
+
+  // What we want to do here is convert this into a blocking call, since
+  // our calling methods expect us to block and set aReauthenticated on return.
+  dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+  dispatch_release(sema);
+  sema = NULL;
+
+  aReauthenticated = biometricSuccess;
+
+  [context release];
+  return NS_OK;
+}
--- a/security/manager/ssl/moz.build
+++ b/security/manager/ssl/moz.build
@@ -146,19 +146,21 @@ if CONFIG['MOZ_LIB_SECRET']:
         'LibSecret.cpp',
     ]
     CFLAGS += CONFIG['MOZ_LIB_SECRET_CFLAGS']
     CXXFLAGS += CONFIG['MOZ_LIB_SECRET_CFLAGS']
 
 if CONFIG['OS_ARCH'] == 'Darwin':
     UNIFIED_SOURCES += [
         'KeychainSecret.cpp',
+        'OSReauthenticatorDarwin.mm',
     ]
     OS_LIBS += [
-        '-framework Security'
+        '-framework LocalAuthentication',
+        '-framework Security',
     ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'credui'
     ]
     UNIFIED_SOURCES += [
         'CredentialManagerSecret.cpp',