Index: chrome/browser/mac/keychain_reauthorize.mm |
diff --git a/chrome/browser/mac/keychain_reauthorize.mm b/chrome/browser/mac/keychain_reauthorize.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..aa611a2447992a6885a142af0f3e7ccf77436de6 |
--- /dev/null |
+++ b/chrome/browser/mac/keychain_reauthorize.mm |
@@ -0,0 +1,137 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/mac/keychain_reauthorize.h" |
+ |
+#import <Foundation/Foundation.h> |
+#include <Security/Security.h> |
+ |
+#include <string.h> |
+ |
+#include <vector> |
+ |
+#include "base/logging.h" |
+#include "base/mac/foundation_util.h" |
+#include "base/mac/mac_logging.h" |
+#include "base/mac/scoped_cftyperef.h" |
+#include "base/metrics/histogram_macros.h" |
+#include "base/scoped_generic.h" |
+#include "components/os_crypt/keychain_password_mac.h" |
+#include "crypto/apple_keychain.h" |
+ |
+namespace chrome { |
+ |
+namespace { |
+ |
+struct VectorScramblerTraits { |
+ static std::vector<uint8_t>* InvalidValue() { return nullptr; } |
+ |
+ static void Free(std::vector<uint8_t>* buf) { |
+ memset(buf->data(), 0x11, buf->size()); |
+ delete buf; |
+ } |
+}; |
+ |
+typedef base::ScopedGeneric<std::vector<uint8_t>*, VectorScramblerTraits> |
+ ScopedVectorScrambler; |
+ |
+// Reauthorizes the Safe Storage keychain item, which protects the randomly |
+// generated password that encrypts the user's saved passwords. This reads out |
+// the keychain item, deletes it, and re-adds it to the keychain. This works |
+// because the keychain uses an app's designated requirement as the ACL for |
+// reading an item. Chrome will be signed with a designated requirement that |
+// accepts both the old and new certificates. |
+bool KeychainReauthorize() { |
+ base::ScopedCFTypeRef<SecKeychainItemRef> storage_item; |
+ UInt32 pw_length = 0; |
+ void* password_data = nullptr; |
+ |
+ crypto::AppleKeychain keychain; |
+ OSStatus error = keychain.FindGenericPassword( |
+ nullptr, strlen(KeychainPassword::service_name), |
+ KeychainPassword::service_name, strlen(KeychainPassword::account_name), |
+ KeychainPassword::account_name, &pw_length, &password_data, |
+ storage_item.InitializeInto()); |
+ |
+ if (error != noErr) { |
+ OSSTATUS_LOG(ERROR, error) |
+ << "KeychainReauthorize failed. Cannot retrieve item."; |
+ return false; |
+ } |
+ |
+ ScopedVectorScrambler password; |
+ password.reset(new std::vector<uint8_t>( |
+ static_cast<uint8_t*>(password_data), |
+ static_cast<uint8_t*>(password_data) + pw_length)); |
+ memset(password_data, 0x11, pw_length); |
+ keychain.ItemFreeContent(nullptr, password_data); |
+ |
+ error = keychain.ItemDelete(storage_item); |
+ if (error != noErr) { |
+ OSSTATUS_LOG(ERROR, error) |
+ << "KeychainReauthorize failed. Cannot delete item."; |
+ return false; |
+ } |
+ |
+ error = keychain.AddGenericPassword( |
+ NULL, strlen(KeychainPassword::service_name), |
+ KeychainPassword::service_name, strlen(KeychainPassword::account_name), |
+ KeychainPassword::account_name, password.get()->size(), |
+ password.get()->data(), nullptr); |
+ |
+ if (error != noErr) { |
+ OSSTATUS_LOG(ERROR, error) << "Failed to re-add storage password."; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) { |
+ NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults]; |
+ int pref_value = [user_defaults integerForKey:pref_key]; |
+ |
+ if (pref_value >= max_tries) |
+ return; |
+ |
+ NSString* success_pref_key = [pref_key stringByAppendingString:@"Success"]; |
+ BOOL success_value = [user_defaults boolForKey:success_pref_key]; |
+ if (success_value) |
+ return; |
+ |
+ if (pref_value > 0) { |
+ // Logs the number of previous tries that didn't complete. |
+ if (base::mac::AmIBundled()) { |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("OSX.KeychainReauthorizeIfNeeded", |
+ pref_value); |
+ } else { |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("OSX.KeychainReauthorizeIfNeededAtUpdate", |
+ pref_value); |
+ } |
+ } |
+ |
+ ++pref_value; |
+ [user_defaults setInteger:pref_value forKey:pref_key]; |
+ [user_defaults synchronize]; |
+ |
+ bool success = KeychainReauthorize(); |
+ |
+ if (!success) |
+ return; |
+ |
+ [user_defaults setBool:YES forKey:success_pref_key]; |
+ [user_defaults synchronize]; |
+ |
+ // Logs the try number (1, 2) that succeeded. |
+ if (base::mac::AmIBundled()) { |
+ UMA_HISTOGRAM_SPARSE_SLOWLY("OSX.KeychainReauthorizeIfNeededSuccess", |
+ pref_value); |
+ } else { |
+ UMA_HISTOGRAM_SPARSE_SLOWLY( |
+ "OSX.KeychainReauthorizeIfNeededAtUpdateSuccess", pref_value); |
+ } |
+} |
+ |
+} // namespace chrome |