• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "crypto/apple_keychain.h"
6
7#import <Foundation/Foundation.h>
8
9#include "base/mac/foundation_util.h"
10#include "base/mac/scoped_cftyperef.h"
11#include "base/mac/scoped_nsobject.h"
12
13namespace {
14
15enum KeychainAction {
16  kKeychainActionCreate,
17  kKeychainActionUpdate
18};
19
20// Creates a dictionary that can be used to query the keystore.
21// Ownership follows the Create rule.
22CFDictionaryRef 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);
33
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));
41
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));
49
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;
54}
55
56// Creates a dictionary conatining the data to save into the keychain.
57// Ownership follows the Create rule.
58CFDictionaryRef 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);
70
71  // Set the password.
72  NSData* password = [NSData dataWithBytes:passwordData length:passwordLength];
73  CFDictionarySetValue(keychain_data, kSecValueData,
74                       base::mac::NSToCFCast(password));
75
76  // If this is not a creation, no structural information is needed.
77  if (action != kKeychainActionCreate)
78    return keychain_data;
79
80  // Set the type of the data.
81  CFDictionarySetValue(keychain_data, kSecClass, kSecClassGenericPassword);
82
83  // Only allow access when the device has been unlocked.
84  CFDictionarySetValue(keychain_data,
85                       kSecAttrAccessible,
86                       kSecAttrAccessibleWhenUnlocked);
87
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));
95
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));
103
104  return keychain_data;
105}
106
107}  // namespace
108
109namespace crypto {
110
111AppleKeychain::AppleKeychain() {}
112
113AppleKeychain::~AppleKeychain() {}
114
115OSStatus AppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList,
116                                        void* data) const {
117  free(data);
118  return noErr;
119}
120
121OSStatus 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  }
156
157  return status;
158}
159
160OSStatus 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));
172
173  // Get the keychain item containing the password.
174  CFTypeRef resultRef = NULL;
175  OSStatus status = SecItemCopyMatching(query, &resultRef);
176  base::ScopedCFTypeRef<CFTypeRef> result(resultRef);
177
178  if (status != noErr) {
179    if (passwordData) {
180      *passwordData = NULL;
181      *passwordLength = 0;
182    }
183    return status;
184  }
185
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;
194}
195
196}  // namespace crypto
197