Home | History | Annotate | Download | only in crypto
      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 
     13 namespace {
     14 
     15 enum 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.
     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);
     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.
     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);
     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 
    109 namespace crypto {
    110 
    111 AppleKeychain::AppleKeychain() {}
    112 
    113 AppleKeychain::~AppleKeychain() {}
    114 
    115 OSStatus AppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList,
    116                                         void* data) const {
    117   free(data);
    118   return noErr;
    119 }
    120 
    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   }
    156 
    157   return status;
    158 }
    159 
    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));
    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