apple_keychain_ios.mm 7.0 KB

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