OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 "components/password_manager/core/browser/login_database.h" |
| 6 |
| 7 #import <Security/Security.h> |
| 8 |
| 9 #include "base/base64.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/mac/mac_logging.h" |
| 12 #include "base/mac/scoped_cftyperef.h" |
| 13 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/metrics/sparse_histogram.h" |
| 15 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/strings/utf_string_conversions.h" |
| 17 |
| 18 using base::ScopedCFTypeRef; |
| 19 using autofill::PasswordForm; |
| 20 |
| 21 namespace password_manager { |
| 22 |
| 23 // On iOS, the LoginDatabase uses Keychain API to store passwords. The |
| 24 // "encrypted" version of the password is a unique ID (UUID) that is |
| 25 // stored as an attribute along with the password in the keychain. |
| 26 // A side effect of this approach is that the same password saved multiple |
| 27 // times will have different "encrypted" values. |
| 28 |
| 29 // TODO(ios): Use |Encryptor| to encrypt the login database. b/6976257 |
| 30 |
| 31 LoginDatabase::EncryptionResult LoginDatabase::EncryptedString( |
| 32 const base::string16& plain_text, |
| 33 std::string* cipher_text) { |
| 34 if (plain_text.size() == 0) { |
| 35 *cipher_text = std::string(); |
| 36 return ENCRYPTION_RESULT_SUCCESS; |
| 37 } |
| 38 |
| 39 ScopedCFTypeRef<CFUUIDRef> uuid(CFUUIDCreate(NULL)); |
| 40 ScopedCFTypeRef<CFStringRef> item_ref(CFUUIDCreateString(NULL, uuid)); |
| 41 ScopedCFTypeRef<CFMutableDictionaryRef> attributes( |
| 42 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, |
| 43 &kCFTypeDictionaryValueCallBacks)); |
| 44 CFDictionarySetValue(attributes, kSecClass, kSecClassGenericPassword); |
| 45 |
| 46 // It does not matter which attribute we use to identify the keychain |
| 47 // item as long as it uniquely identifies it. We are arbitrarily choosing the |
| 48 // |kSecAttrAccount| attribute for this purpose. |
| 49 CFDictionarySetValue(attributes, kSecAttrAccount, item_ref); |
| 50 std::string plain_text_utf8 = UTF16ToUTF8(plain_text); |
| 51 ScopedCFTypeRef<CFDataRef> data( |
| 52 CFDataCreate(NULL, reinterpret_cast<const UInt8*>(plain_text_utf8.data()), |
| 53 plain_text_utf8.size())); |
| 54 CFDictionarySetValue(attributes, kSecValueData, data); |
| 55 |
| 56 // Only allow access when the device has been unlocked. |
| 57 CFDictionarySetValue(attributes, kSecAttrAccessible, |
| 58 kSecAttrAccessibleWhenUnlocked); |
| 59 |
| 60 OSStatus status = SecItemAdd(attributes, NULL); |
| 61 if (status != errSecSuccess) { |
| 62 NOTREACHED() << "Unable to save password in keychain: " << status; |
| 63 if (status == errSecDuplicateItem || status == errSecDecode) |
| 64 return ENCRYPTION_RESULT_ITEM_FAILURE; |
| 65 else |
| 66 return ENCRYPTION_RESULT_SERVICE_FAILURE; |
| 67 } |
| 68 |
| 69 *cipher_text = base::SysCFStringRefToUTF8(item_ref); |
| 70 return ENCRYPTION_RESULT_SUCCESS; |
| 71 } |
| 72 |
| 73 LoginDatabase::EncryptionResult LoginDatabase::DecryptedString( |
| 74 const std::string& cipher_text, |
| 75 base::string16* plain_text) { |
| 76 if (cipher_text.size() == 0) { |
| 77 *plain_text = base::string16(); |
| 78 return ENCRYPTION_RESULT_SUCCESS; |
| 79 } |
| 80 |
| 81 ScopedCFTypeRef<CFStringRef> item_ref( |
| 82 base::SysUTF8ToCFStringRef(cipher_text)); |
| 83 ScopedCFTypeRef<CFMutableDictionaryRef> query( |
| 84 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, |
| 85 &kCFTypeDictionaryValueCallBacks)); |
| 86 CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); |
| 87 |
| 88 // We are using the account attribute to store item references. |
| 89 CFDictionarySetValue(query, kSecAttrAccount, item_ref); |
| 90 CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); |
| 91 |
| 92 CFDataRef data; |
| 93 OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&data); |
| 94 if (status != errSecSuccess) { |
| 95 OSSTATUS_LOG(INFO, status) << "Failed to retrieve password from keychain"; |
| 96 if (status == errSecItemNotFound || status == errSecDecode) |
| 97 return ENCRYPTION_RESULT_ITEM_FAILURE; |
| 98 else |
| 99 return ENCRYPTION_RESULT_SERVICE_FAILURE; |
| 100 } |
| 101 |
| 102 const size_t size = CFDataGetLength(data); |
| 103 scoped_ptr<UInt8[]> buffer(new UInt8[size]); |
| 104 CFDataGetBytes(data, CFRangeMake(0, size), buffer.get()); |
| 105 CFRelease(data); |
| 106 |
| 107 *plain_text = base::UTF8ToUTF16( |
| 108 std::string(static_cast<char*>(static_cast<void*>(buffer.get())), |
| 109 static_cast<size_t>(size))); |
| 110 return ENCRYPTION_RESULT_SUCCESS; |
| 111 } |
| 112 |
| 113 void LoginDatabase::DeleteEncryptedPassword(const PasswordForm& form) { |
| 114 std::string cipher_text = GetEncryptedPassword(form); |
| 115 if (cipher_text.size() == 0) |
| 116 return; |
| 117 |
| 118 ScopedCFTypeRef<CFStringRef> item_ref( |
| 119 base::SysUTF8ToCFStringRef(cipher_text)); |
| 120 ScopedCFTypeRef<CFMutableDictionaryRef> query( |
| 121 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, |
| 122 &kCFTypeDictionaryValueCallBacks)); |
| 123 CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); |
| 124 |
| 125 // We are using the account attribute to store item references. |
| 126 CFDictionarySetValue(query, kSecAttrAccount, item_ref); |
| 127 |
| 128 OSStatus status = SecItemDelete(query); |
| 129 if (status != errSecSuccess && status != errSecItemNotFound) { |
| 130 NOTREACHED() << "Unable to remove password from keychain: " << status; |
| 131 } |
| 132 } |
| 133 |
| 134 } // namespace password_manager |
OLD | NEW |