Fix small race in the sandbox
[chromium-blink-merge.git] / crypto / apple_keychain_ios.mm
blob1a4c9d5442f9808c0205166323ee0a0296e221e5
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/memory/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   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   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   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   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::mac::ScopedCFTypeRef<CFDictionaryRef> query(
130       CreateGenericPasswordQuery(serviceNameLength,
131                                  serviceName,
132                                  accountNameLength,
133                                  accountName));
134   // Check that there is not already a password.
135   OSStatus status = SecItemCopyMatching(query, NULL);
136   if (status == errSecItemNotFound) {
137     // A new entry must be created.
138     base::mac::ScopedCFTypeRef<CFDictionaryRef> keychain_data(
139         CreateKeychainData(serviceNameLength,
140                            serviceName,
141                            accountNameLength,
142                            accountName,
143                            passwordLength,
144                            passwordData,
145                            kKeychainActionCreate));
146     status = SecItemAdd(keychain_data, NULL);
147   } else if (status == noErr) {
148     // The entry must be updated.
149     base::mac::ScopedCFTypeRef<CFDictionaryRef> keychain_data(
150         CreateKeychainData(serviceNameLength,
151                            serviceName,
152                            accountNameLength,
153                            accountName,
154                            passwordLength,
155                            passwordData,
156                            kKeychainActionUpdate));
157     status = SecItemUpdate(query, keychain_data);
158   }
160   return status;
163 OSStatus AppleKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
164                                             UInt32 serviceNameLength,
165                                             const char* serviceName,
166                                             UInt32 accountNameLength,
167                                             const char* accountName,
168                                             UInt32* passwordLength,
169                                             void** passwordData,
170                                             SecKeychainItemRef* itemRef) const {
171   DCHECK((passwordData && passwordLength) ||
172          (!passwordData && !passwordLength));
173   base::mac::ScopedCFTypeRef<CFDictionaryRef> query(
174       CreateGenericPasswordQuery(serviceNameLength,
175                                  serviceName,
176                                  accountNameLength,
177                                  accountName));
179   // Get the keychain item containing the password.
180   CFTypeRef resultRef = NULL;
181   OSStatus status = SecItemCopyMatching(query, &resultRef);
182   base::mac::ScopedCFTypeRef<CFTypeRef> result(resultRef);
184   if (status != noErr) {
185     if (passwordData) {
186       *passwordData = NULL;
187       *passwordLength = 0;
188     }
189     return status;
190   }
192   if (passwordData) {
193     CFDataRef data = base::mac::CFCast<CFDataRef>(result);
194     NSUInteger length = CFDataGetLength(data);
195     *passwordData = malloc(length * sizeof(UInt8));
196     CFDataGetBytes(data, CFRangeMake(0, length), (UInt8*)*passwordData);
197     *passwordLength = length;
198   }
199   return status;
202 }  // namespace crypto