Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / crypto / apple_keychain_ios.mm
blob74cf129ce1fd02790f390f051347bb1d60d192d9
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.
5 #include "crypto/apple_keychain.h"
7 #import <Foundation/Foundation.h>
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/scoped_cftyperef.h"
11 #include "base/mac/scoped_nsobject.h"
13 namespace {
15 enum KeychainAction {
16   kKeychainActionCreate,
17   kKeychainActionUpdate
20 // Creates a dictionary that can be used to query the keystore.
21 // Ownership follows the Create rule.
22 CFDictionaryRef CreateGenericPasswordQuery(UInt32 serviceNameLength,
23                                            const char* serviceName,
24                                            UInt32 accountNameLength,
25                                            const char* accountName) {
26   CFMutableDictionaryRef query =
27       CFDictionaryCreateMutable(NULL,
28                                 5,
29                                 &kCFTypeDictionaryKeyCallBacks,
30                                 &kCFTypeDictionaryValueCallBacks);
31   // Type of element is generic password.
32   CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
34   // Set the service name.
35   base::scoped_nsobject<NSString> service_name_ns(
36       [[NSString alloc] initWithBytes:serviceName
37                                length:serviceNameLength
38                              encoding:NSUTF8StringEncoding]);
39   CFDictionarySetValue(query, kSecAttrService,
40                        base::mac::NSToCFCast(service_name_ns));
42   // Set the account name.
43   base::scoped_nsobject<NSString> account_name_ns(
44       [[NSString alloc] initWithBytes:accountName
45                                length:accountNameLength
46                              encoding:NSUTF8StringEncoding]);
47   CFDictionarySetValue(query, kSecAttrAccount,
48                        base::mac::NSToCFCast(account_name_ns));
50   // Use the proper search constants, return only the data of the first match.
51   CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne);
52   CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
53   return query;
56 // Creates a dictionary conatining the data to save into the keychain.
57 // Ownership follows the Create rule.
58 CFDictionaryRef CreateKeychainData(UInt32 serviceNameLength,
59                                    const char* serviceName,
60                                    UInt32 accountNameLength,
61                                    const char* accountName,
62                                    UInt32 passwordLength,
63                                    const void* passwordData,
64                                    KeychainAction action) {
65   CFMutableDictionaryRef keychain_data =
66       CFDictionaryCreateMutable(NULL,
67                                 0,
68                                 &kCFTypeDictionaryKeyCallBacks,
69                                 &kCFTypeDictionaryValueCallBacks);
71   // Set the password.
72   NSData* password = [NSData dataWithBytes:passwordData length:passwordLength];
73   CFDictionarySetValue(keychain_data, kSecValueData,
74                        base::mac::NSToCFCast(password));
76   // If this is not a creation, no structural information is needed.
77   if (action != kKeychainActionCreate)
78     return keychain_data;
80   // Set the type of the data.
81   CFDictionarySetValue(keychain_data, kSecClass, kSecClassGenericPassword);
83   // Only allow access when the device has been unlocked.
84   CFDictionarySetValue(keychain_data,
85                        kSecAttrAccessible,
86                        kSecAttrAccessibleWhenUnlocked);
88   // Set the service name.
89   base::scoped_nsobject<NSString> service_name_ns(
90       [[NSString alloc] initWithBytes:serviceName
91                                length:serviceNameLength
92                              encoding:NSUTF8StringEncoding]);
93   CFDictionarySetValue(keychain_data, kSecAttrService,
94                        base::mac::NSToCFCast(service_name_ns));
96   // Set the account name.
97   base::scoped_nsobject<NSString> account_name_ns(
98       [[NSString alloc] initWithBytes:accountName
99                                length:accountNameLength
100                              encoding:NSUTF8StringEncoding]);
101   CFDictionarySetValue(keychain_data, kSecAttrAccount,
102                        base::mac::NSToCFCast(account_name_ns));
104   return keychain_data;
107 }  // namespace
109 namespace crypto {
111 AppleKeychain::AppleKeychain() {}
113 AppleKeychain::~AppleKeychain() {}
115 OSStatus AppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList,
116                                         void* data) const {
117   free(data);
118   return noErr;
121 OSStatus AppleKeychain::AddGenericPassword(SecKeychainRef keychain,
122                                            UInt32 serviceNameLength,
123                                            const char* serviceName,
124                                            UInt32 accountNameLength,
125                                            const char* accountName,
126                                            UInt32 passwordLength,
127                                            const void* passwordData,
128                                            SecKeychainItemRef* itemRef) const {
129   base::ScopedCFTypeRef<CFDictionaryRef> query(CreateGenericPasswordQuery(
130       serviceNameLength, serviceName, accountNameLength, accountName));
131   // Check that there is not already a password.
132   OSStatus status = SecItemCopyMatching(query, NULL);
133   if (status == errSecItemNotFound) {
134     // A new entry must be created.
135     base::ScopedCFTypeRef<CFDictionaryRef> keychain_data(
136         CreateKeychainData(serviceNameLength,
137                            serviceName,
138                            accountNameLength,
139                            accountName,
140                            passwordLength,
141                            passwordData,
142                            kKeychainActionCreate));
143     status = SecItemAdd(keychain_data, NULL);
144   } else if (status == noErr) {
145     // The entry must be updated.
146     base::ScopedCFTypeRef<CFDictionaryRef> keychain_data(
147         CreateKeychainData(serviceNameLength,
148                            serviceName,
149                            accountNameLength,
150                            accountName,
151                            passwordLength,
152                            passwordData,
153                            kKeychainActionUpdate));
154     status = SecItemUpdate(query, keychain_data);
155   }
157   return status;
160 OSStatus AppleKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
161                                             UInt32 serviceNameLength,
162                                             const char* serviceName,
163                                             UInt32 accountNameLength,
164                                             const char* accountName,
165                                             UInt32* passwordLength,
166                                             void** passwordData,
167                                             SecKeychainItemRef* itemRef) const {
168   DCHECK((passwordData && passwordLength) ||
169          (!passwordData && !passwordLength));
170   base::ScopedCFTypeRef<CFDictionaryRef> query(CreateGenericPasswordQuery(
171       serviceNameLength, serviceName, accountNameLength, accountName));
173   // Get the keychain item containing the password.
174   CFTypeRef resultRef = NULL;
175   OSStatus status = SecItemCopyMatching(query, &resultRef);
176   base::ScopedCFTypeRef<CFTypeRef> result(resultRef);
178   if (status != noErr) {
179     if (passwordData) {
180       *passwordData = NULL;
181       *passwordLength = 0;
182     }
183     return status;
184   }
186   if (passwordData) {
187     CFDataRef data = base::mac::CFCast<CFDataRef>(result);
188     NSUInteger length = CFDataGetLength(data);
189     *passwordData = malloc(length * sizeof(UInt8));
190     CFDataGetBytes(data, CFRangeMake(0, length), (UInt8*)*passwordData);
191     *passwordLength = length;
192   }
193   return status;
196 }  // namespace crypto