Bug 1423714 - Add an API to retrieve a password from Keychain by account and service. r=mstange a=lizzard
authorMatthew Noorenberghe <mozilla@noorenberghe.ca>
Wed, 18 Sep 2019 04:47:10 +0000
changeset 555344 882e32d712a008d2baf12147816dc473289e5ea4
parent 555343 13a09bfb08af5287e25eee0807a957aa5291b618
child 555345 6b26012e920beba837ccf21edc5785791fe3c148
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, lizzard
bugs1423714
milestone70.0
Bug 1423714 - Add an API to retrieve a password from Keychain by account and service. r=mstange a=lizzard Differential Revision: https://phabricator.services.mozilla.com/D45968
browser/components/migration/components.conf
browser/components/migration/moz.build
browser/components/migration/nsIKeychainMigrationUtils.idl
browser/components/migration/nsKeychainMigrationUtils.h
browser/components/migration/nsKeychainMigrationUtils.mm
widget/cocoa/nsCocoaUtils.h
widget/cocoa/nsCocoaUtils.mm
--- a/browser/components/migration/components.conf
+++ b/browser/components/migration/components.conf
@@ -84,9 +84,15 @@ if XP_WIN:
 if XP_MACOSX:
     Classes += [
         {
             'cid': '{4b609ecf-60b2-4655-9df4-dc149e474da1}',
             'contract_ids': ['@mozilla.org/profile/migrator;1?app=browser&type=safari'],
             'jsm': 'resource:///modules/SafariProfileMigrator.jsm',
             'constructor': 'SafariProfileMigrator',
         },
+        {
+            'cid': '{647bf80c-cd35-4ce6-b904-fd586b97ae48}',
+            'contract_ids': ['@mozilla.org/profile/migrator/keychainmigrationutils;1'],
+            'type': 'nsKeychainMigrationUtils',
+            'headers': ['nsKeychainMigrationUtils.h'],
+        },
     ]
--- a/browser/components/migration/moz.build
+++ b/browser/components/migration/moz.build
@@ -36,19 +36,29 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         '360seProfileMigrator.jsm',
         'EdgeProfileMigrator.jsm',
         'ESEDBReader.jsm',
         'IEProfileMigrator.jsm',
         'MSMigrationUtils.jsm',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    EXPORTS += [
+        'nsKeychainMigrationUtils.h',
+    ]
     EXTRA_JS_MODULES += [
         'SafariProfileMigrator.jsm',
     ]
+    SOURCES += [
+        'nsKeychainMigrationUtils.mm',
+    ]
+    XPIDL_SOURCES += [
+        'nsIKeychainMigrationUtils.idl',
+    ]
+
 
 XPCOM_MANIFESTS += [
     'components.conf',
 ]
 
 FINAL_LIBRARY = 'browsercomps'
 
 with Files('**'):
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/nsIKeychainMigrationUtils.idl
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(647bf80c-cd35-4ce6-b904-fd586b97ae48)]
+interface nsIKeychainMigrationUtils : nsISupports
+{
+  ACString getGenericPassword(in ACString aServiceName, in ACString aAccountName);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/nsKeychainMigrationUtils.h
@@ -0,0 +1,23 @@
+/* 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/. */
+
+#ifndef nsKeychainMigrationUtils_h__
+#define nsKeychainMigrationUtils_h__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "nsIKeychainMigrationUtils.h"
+
+class nsKeychainMigrationUtils : public nsIKeychainMigrationUtils {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIKEYCHAINMIGRATIONUTILS
+
+  nsKeychainMigrationUtils(){};
+
+ protected:
+  virtual ~nsKeychainMigrationUtils(){};
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/nsKeychainMigrationUtils.mm
@@ -0,0 +1,62 @@
+/* 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 "nsKeychainMigrationUtils.h"
+
+#include <Security/Security.h>
+
+#include "mozilla/Logging.h"
+
+#include "nsCocoaUtils.h"
+#include "nsString.h"
+
+using namespace mozilla;
+
+LazyLogModule gKeychainUtilsLog("keychainmigrationutils");
+
+NS_IMPL_ISUPPORTS(nsKeychainMigrationUtils, nsIKeychainMigrationUtils)
+
+NS_IMETHODIMP
+nsKeychainMigrationUtils::GetGenericPassword(const nsACString& aServiceName,
+                                             const nsACString& aAccountName, nsACString& aKey) {
+  // To retrieve a secret, we create a CFDictionary of the form:
+  // { class: generic password,
+  //   service: the given service name
+  //   account: the given account name,
+  //   match limit: match one,
+  //   return attributes: true,
+  //   return data: true }
+  // This searches for and returns the attributes and data for the secret
+  // matching the given service and account names. We then extract the data
+  // (i.e. the secret) and return it.
+  NSDictionary* searchDictionary = @{
+    (__bridge NSString*)kSecClass : (__bridge NSString*)kSecClassGenericPassword,
+    (__bridge NSString*)kSecAttrService : nsCocoaUtils::ToNSString(aServiceName),
+    (__bridge NSString*)kSecAttrAccount : nsCocoaUtils::ToNSString(aAccountName),
+    (__bridge NSString*)kSecMatchLimit : (__bridge NSString*)kSecMatchLimitOne,
+    (__bridge NSString*)kSecReturnAttributes : @YES,
+    (__bridge NSString*)kSecReturnData : @YES
+  };
+
+  CFTypeRef item;
+  // https://developer.apple.com/documentation/security/1398306-secitemcopymatching
+  OSStatus rv = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, &item);
+  if (rv != errSecSuccess) {
+    MOZ_LOG(gKeychainUtilsLog, LogLevel::Debug, ("SecItemCopyMatching failed: %d", rv));
+    return NS_ERROR_FAILURE;
+  }
+  NSDictionary* resultDict = [(__bridge NSDictionary*)item autorelease];
+  NSData* secret = [resultDict objectForKey:(__bridge NSString*)kSecValueData];
+  if (!secret) {
+    MOZ_LOG(gKeychainUtilsLog, LogLevel::Debug, ("objectForKey failed"));
+    return NS_ERROR_FAILURE;
+  }
+  if ([secret length] != 0) {
+    // We assume that the data is UTF-8 encoded since that seems to be common and
+    // Keychain Access shows it with that encoding.
+    aKey.Assign(reinterpret_cast<const char*>([secret bytes]), [secret length]);
+  }
+
+  return NS_OK;
+}
--- a/widget/cocoa/nsCocoaUtils.h
+++ b/widget/cocoa/nsCocoaUtils.h
@@ -292,16 +292,21 @@ class nsCocoaUtils {
   static void GetStringForNSString(const NSString* aSrc, nsAString& aDist);
 
   /**
    * Makes NSString instance for aString.
    */
   static NSString* ToNSString(const nsAString& aString);
 
   /**
+   * Makes NSString instance for aCString.
+   */
+  static NSString* ToNSString(const nsACString& aCString);
+
+  /**
    * Returns NSRect for aGeckoRect.
    * Just copies values between the two types; it does no coordinate-system
    * conversion, so both rects must have the same coordinate origin/direction.
    */
   static void GeckoRectToNSRect(const nsIntRect& aGeckoRect, NSRect& aOutCocoaRect);
 
   /**
    * Returns Gecko rect for aCocoaRect.
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -579,16 +579,26 @@ NSString* nsCocoaUtils::ToNSString(const
   if (aString.IsEmpty()) {
     return [NSString string];
   }
   return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aString.BeginReading())
                                  length:aString.Length()];
 }
 
 // static
+NSString* nsCocoaUtils::ToNSString(const nsACString& aCString) {
+  if (aCString.IsEmpty()) {
+    return [NSString string];
+  }
+  return [[[NSString alloc] initWithBytes:aCString.BeginReading()
+                                   length:aCString.Length()
+                                 encoding:NSUTF8StringEncoding] autorelease];
+}
+
+// static
 void nsCocoaUtils::GeckoRectToNSRect(const nsIntRect& aGeckoRect, NSRect& aOutCocoaRect) {
   aOutCocoaRect.origin.x = aGeckoRect.x;
   aOutCocoaRect.origin.y = aGeckoRect.y;
   aOutCocoaRect.size.width = aGeckoRect.width;
   aOutCocoaRect.size.height = aGeckoRect.height;
 }
 
 // static