OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/mac/keychain_reauthorize.h" | |
6 | |
7 #import <Foundation/Foundation.h> | |
8 #include <Security/Security.h> | |
9 | |
10 #include <string.h> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/mac/foundation_util.h" | |
14 #include "base/mac/scoped_cftyperef.h" | |
15 #include "base/metrics/histogram.h" | |
16 #include "base/metrics/histogram_macros.h" | |
17 #include "base/scoped_generic.h" | |
18 #include "components/os_crypt/keychain_password_mac.h" | |
19 #include "crypto/apple_keychain.h" | |
20 | |
21 namespace chrome { | |
22 | |
23 namespace { | |
24 | |
25 // Reauthorizes the Safe Storage keychain item, which protects the randomly | |
26 // generated password that encrypts the user's saved passwords. This reads out | |
27 // the keychain item, deletes it, and re-adds it to the keychain. This works | |
28 // because the keychain uses an apps designated requirement as the default ACL | |
Mark Mentovai
2017/03/01 22:00:30
“default ACL for”→“ACL for reading”
Mark Mentovai
2017/03/01 22:00:31
app’s
Greg K
2017/03/02 01:28:27
Done.
Greg K
2017/03/02 01:28:27
Done.
| |
29 // for an item. We are signing Chrome with the current certificate (so it can | |
Mark Mentovai
2017/03/01 22:00:31
Don’t say “we.” Also, the bit about signing will b
Greg K
2017/03/02 01:28:27
Done.
| |
30 // access the item), but using a designated requirement that accepts the current | |
31 // and new certificates as valid. | |
32 bool KeychainReauthorize() { | |
33 base::ScopedCFTypeRef<SecKeychainItemRef> storage_item; | |
34 UInt32 pw_length = 0; | |
35 void* password_data = nullptr; | |
36 | |
37 crypto::AppleKeychain keychain; | |
38 OSStatus error = keychain.FindGenericPassword( | |
39 NULL, strlen(KeychainPassword::service_name), | |
Mark Mentovai
2017/03/01 22:00:31
Use nullptr now in new code. Hooray for C++11!
Greg K
2017/03/02 01:28:27
Done.
| |
40 KeychainPassword::service_name, strlen(KeychainPassword::account_name), | |
Mark Mentovai
2017/03/01 22:00:31
If git cl format formatted this, then it’s fine be
Greg K
2017/03/02 01:28:27
Done.
| |
41 KeychainPassword::account_name, &pw_length, &password_data, | |
42 storage_item.InitializeInto()); | |
43 | |
44 std::string password = | |
Mark Mentovai
2017/03/01 22:00:30
One of the sanitizers may not like this if !passwo
Greg K
2017/03/02 01:28:27
Done.
| |
45 std::string(static_cast<char*>(password_data), pw_length); | |
46 if (password_data) { | |
47 memset(password_data, 0xAA, pw_length); | |
Mark Mentovai
2017/03/01 22:00:31
This is good. memset_s() would be even better. But
Greg K
2017/03/02 01:28:27
memset_s requires some LIBC extension, but let's d
| |
48 keychain.ItemFreeContent(nullptr, password_data); | |
49 } | |
50 | |
51 if (error != noErr) | |
52 return false; | |
Mark Mentovai
2017/03/01 22:00:30
Wanna say something about this?
Greg K
2017/03/02 01:28:27
Done.
| |
53 | |
54 if (keychain.ItemDelete(storage_item) != noErr) { | |
55 LOG(ERROR) << "KeychainReauthorize failed. Cannot delete item."; | |
Mark Mentovai
2017/03/01 22:00:31
Aha! Whenever you’re logging something about an OS
| |
56 return false; | |
57 } | |
58 | |
59 error = keychain.AddGenericPassword( | |
Mark Mentovai
2017/03/01 22:00:31
I’m pretty sure that the answer is no, but we can’
Greg K
2017/03/02 01:28:27
Definitely good to test, but it failed.
| |
60 NULL, strlen(KeychainPassword::service_name), | |
61 KeychainPassword::service_name, strlen(KeychainPassword::account_name), | |
62 KeychainPassword::account_name, password.size(), password.data(), NULL); | |
63 | |
64 if (error != noErr) { | |
65 LOG(ERROR) << "Failed to re-add storage password."; | |
66 return false; | |
67 } | |
68 return true; | |
69 } | |
70 | |
71 } // namespace | |
72 | |
73 void KeychainReauthorizeIfNeeded(NSString* pref_key, int max_tries) { | |
74 NSUserDefaults* user_defaults = [NSUserDefaults standardUserDefaults]; | |
75 [user_defaults synchronize]; | |
Mark Mentovai
2017/03/01 22:00:31
I don’t believe that this one will do anything, be
Greg K
2017/03/02 01:28:27
Done.
| |
76 int pref_value = [user_defaults integerForKey:pref_key]; | |
77 | |
78 if (pref_value >= max_tries) | |
79 return; | |
80 | |
81 BOOL success_value = | |
82 [user_defaults boolForKey:[pref_key stringByAppendingString:@"Success"]]; | |
83 if (success_value) | |
84 return; | |
85 | |
86 if (pref_value > 0) { | |
87 // Logs the number of previous tries that didn't complete. | |
88 if (base::mac::AmIBundled()) { | |
89 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeeded", pref_value); | |
90 } else { | |
91 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededAtUpdate", | |
92 pref_value); | |
93 } | |
94 } | |
95 | |
96 bool success = KeychainReauthorize(); | |
97 | |
98 ++pref_value; | |
Mark Mentovai
2017/03/01 22:00:31
The comment in the header was explicit that this w
Greg K
2017/03/02 01:28:27
That makes sense, I didn't think about that.
| |
99 [user_defaults setInteger:pref_value forKey:pref_key]; | |
100 [user_defaults synchronize]; | |
101 | |
102 if (!success) | |
103 return; | |
104 | |
105 NSString* success_pref_key = [pref_key stringByAppendingString:@"Success"]; | |
Mark Mentovai
2017/03/01 22:00:31
You already computed this above, on line 82. You s
Greg K
2017/03/02 01:28:27
Done.
| |
106 [user_defaults setBool:YES forKey:success_pref_key]; | |
107 [user_defaults synchronize]; | |
108 | |
109 // Logs the try number (1, 2) that succeeded. | |
110 if (base::mac::AmIBundled()) { | |
111 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededSuccess", pref_value); | |
112 } else { | |
113 UMA_HISTOGRAM_COUNTS("OSX.KeychainReauthorizeIfNeededAtUpdateSuccess", | |
114 pref_value); | |
115 } | |
116 } | |
117 | |
118 } // namespace chrome | |
OLD | NEW |