1*6777b538SAndroid Build Coastguard Worker// Copyright 2012 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker// Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker// found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker#include "crypto/apple_keychain.h" 6*6777b538SAndroid Build Coastguard Worker 7*6777b538SAndroid Build Coastguard Worker#import <Foundation/Foundation.h> 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Worker#include "base/apple/bridging.h" 10*6777b538SAndroid Build Coastguard Worker#include "base/apple/foundation_util.h" 11*6777b538SAndroid Build Coastguard Worker#include "base/apple/scoped_cftyperef.h" 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard Workerusing base::apple::CFToNSPtrCast; 14*6777b538SAndroid Build Coastguard Workerusing base::apple::NSToCFOwnershipCast; 15*6777b538SAndroid Build Coastguard Worker 16*6777b538SAndroid Build Coastguard Workernamespace { 17*6777b538SAndroid Build Coastguard Worker 18*6777b538SAndroid Build Coastguard Workerenum KeychainAction { 19*6777b538SAndroid Build Coastguard Worker kKeychainActionCreate, 20*6777b538SAndroid Build Coastguard Worker kKeychainActionUpdate 21*6777b538SAndroid Build Coastguard Worker}; 22*6777b538SAndroid Build Coastguard Worker 23*6777b538SAndroid Build Coastguard WorkerNSString* StringWithBytesAndLength(const char* bytes, UInt32 length) { 24*6777b538SAndroid Build Coastguard Worker return [[NSString alloc] initWithBytes:bytes 25*6777b538SAndroid Build Coastguard Worker length:length 26*6777b538SAndroid Build Coastguard Worker encoding:NSUTF8StringEncoding]; 27*6777b538SAndroid Build Coastguard Worker} 28*6777b538SAndroid Build Coastguard Worker 29*6777b538SAndroid Build Coastguard Worker// Creates a dictionary that can be used to query the keystore. 30*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFDictionaryRef> MakeGenericPasswordQuery( 31*6777b538SAndroid Build Coastguard Worker UInt32 serviceNameLength, 32*6777b538SAndroid Build Coastguard Worker const char* serviceName, 33*6777b538SAndroid Build Coastguard Worker UInt32 accountNameLength, 34*6777b538SAndroid Build Coastguard Worker const char* accountName) { 35*6777b538SAndroid Build Coastguard Worker NSDictionary* query = @{ 36*6777b538SAndroid Build Coastguard Worker // Type of element is generic password. 37*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword), 38*6777b538SAndroid Build Coastguard Worker 39*6777b538SAndroid Build Coastguard Worker // Set the service name. 40*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrService) : 41*6777b538SAndroid Build Coastguard Worker StringWithBytesAndLength(serviceName, serviceNameLength), 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Worker // Set the account name. 44*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrAccount) : 45*6777b538SAndroid Build Coastguard Worker StringWithBytesAndLength(accountName, accountNameLength), 46*6777b538SAndroid Build Coastguard Worker 47*6777b538SAndroid Build Coastguard Worker // Use the proper search constants, return only the data of the first match. 48*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecMatchLimit) : CFToNSPtrCast(kSecMatchLimitOne), 49*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecReturnData) : @YES, 50*6777b538SAndroid Build Coastguard Worker }; 51*6777b538SAndroid Build Coastguard Worker return base::apple::ScopedCFTypeRef<CFDictionaryRef>( 52*6777b538SAndroid Build Coastguard Worker NSToCFOwnershipCast(query)); 53*6777b538SAndroid Build Coastguard Worker} 54*6777b538SAndroid Build Coastguard Worker 55*6777b538SAndroid Build Coastguard Worker// Creates a dictionary containing the data to save into the keychain. 56*6777b538SAndroid Build Coastguard Workerbase::apple::ScopedCFTypeRef<CFDictionaryRef> MakeKeychainData( 57*6777b538SAndroid Build Coastguard Worker UInt32 serviceNameLength, 58*6777b538SAndroid Build Coastguard Worker const char* serviceName, 59*6777b538SAndroid Build Coastguard Worker UInt32 accountNameLength, 60*6777b538SAndroid Build Coastguard Worker const char* accountName, 61*6777b538SAndroid Build Coastguard Worker UInt32 passwordLength, 62*6777b538SAndroid Build Coastguard Worker const void* passwordData, 63*6777b538SAndroid Build Coastguard Worker KeychainAction action) { 64*6777b538SAndroid Build Coastguard Worker NSData* password = [NSData dataWithBytes:passwordData length:passwordLength]; 65*6777b538SAndroid Build Coastguard Worker 66*6777b538SAndroid Build Coastguard Worker NSDictionary* keychain_data; 67*6777b538SAndroid Build Coastguard Worker 68*6777b538SAndroid Build Coastguard Worker if (action != kKeychainActionCreate) { 69*6777b538SAndroid Build Coastguard Worker // If this is not a creation, no structural information is needed, only the 70*6777b538SAndroid Build Coastguard Worker // password. 71*6777b538SAndroid Build Coastguard Worker keychain_data = @{ 72*6777b538SAndroid Build Coastguard Worker // Set the password. 73*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecValueData) : password, 74*6777b538SAndroid Build Coastguard Worker }; 75*6777b538SAndroid Build Coastguard Worker } else { 76*6777b538SAndroid Build Coastguard Worker keychain_data = @{ 77*6777b538SAndroid Build Coastguard Worker // Set the password. 78*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecValueData) : password, 79*6777b538SAndroid Build Coastguard Worker 80*6777b538SAndroid Build Coastguard Worker // Set the type of the data. 81*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecClass) : CFToNSPtrCast(kSecClassGenericPassword), 82*6777b538SAndroid Build Coastguard Worker 83*6777b538SAndroid Build Coastguard Worker // Only allow access when the device has been unlocked. 84*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrAccessible) : 85*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrAccessibleWhenUnlocked), 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker // Set the service name. 88*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrService) : 89*6777b538SAndroid Build Coastguard Worker StringWithBytesAndLength(serviceName, serviceNameLength), 90*6777b538SAndroid Build Coastguard Worker 91*6777b538SAndroid Build Coastguard Worker // Set the account name. 92*6777b538SAndroid Build Coastguard Worker CFToNSPtrCast(kSecAttrAccount) : 93*6777b538SAndroid Build Coastguard Worker StringWithBytesAndLength(accountName, accountNameLength), 94*6777b538SAndroid Build Coastguard Worker }; 95*6777b538SAndroid Build Coastguard Worker } 96*6777b538SAndroid Build Coastguard Worker 97*6777b538SAndroid Build Coastguard Worker return base::apple::ScopedCFTypeRef<CFDictionaryRef>( 98*6777b538SAndroid Build Coastguard Worker NSToCFOwnershipCast(keychain_data)); 99*6777b538SAndroid Build Coastguard Worker} 100*6777b538SAndroid Build Coastguard Worker 101*6777b538SAndroid Build Coastguard Worker} // namespace 102*6777b538SAndroid Build Coastguard Worker 103*6777b538SAndroid Build Coastguard Workernamespace crypto { 104*6777b538SAndroid Build Coastguard Worker 105*6777b538SAndroid Build Coastguard WorkerAppleKeychain::AppleKeychain() = default; 106*6777b538SAndroid Build Coastguard Worker 107*6777b538SAndroid Build Coastguard WorkerAppleKeychain::~AppleKeychain() = default; 108*6777b538SAndroid Build Coastguard Worker 109*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::ItemFreeContent(void* data) const { 110*6777b538SAndroid Build Coastguard Worker free(data); 111*6777b538SAndroid Build Coastguard Worker return noErr; 112*6777b538SAndroid Build Coastguard Worker} 113*6777b538SAndroid Build Coastguard Worker 114*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::AddGenericPassword( 115*6777b538SAndroid Build Coastguard Worker UInt32 serviceNameLength, 116*6777b538SAndroid Build Coastguard Worker const char* serviceName, 117*6777b538SAndroid Build Coastguard Worker UInt32 accountNameLength, 118*6777b538SAndroid Build Coastguard Worker const char* accountName, 119*6777b538SAndroid Build Coastguard Worker UInt32 passwordLength, 120*6777b538SAndroid Build Coastguard Worker const void* passwordData, 121*6777b538SAndroid Build Coastguard Worker AppleSecKeychainItemRef* itemRef) const { 122*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> query = 123*6777b538SAndroid Build Coastguard Worker MakeGenericPasswordQuery(serviceNameLength, serviceName, 124*6777b538SAndroid Build Coastguard Worker accountNameLength, accountName); 125*6777b538SAndroid Build Coastguard Worker // Check that there is not already a password. 126*6777b538SAndroid Build Coastguard Worker OSStatus status = SecItemCopyMatching(query.get(), /*result=*/nullptr); 127*6777b538SAndroid Build Coastguard Worker if (status == errSecItemNotFound) { 128*6777b538SAndroid Build Coastguard Worker // A new entry must be created. 129*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> keychain_data = 130*6777b538SAndroid Build Coastguard Worker MakeKeychainData(serviceNameLength, serviceName, accountNameLength, 131*6777b538SAndroid Build Coastguard Worker accountName, passwordLength, passwordData, 132*6777b538SAndroid Build Coastguard Worker kKeychainActionCreate); 133*6777b538SAndroid Build Coastguard Worker status = SecItemAdd(keychain_data.get(), /*result=*/nullptr); 134*6777b538SAndroid Build Coastguard Worker } else if (status == noErr) { 135*6777b538SAndroid Build Coastguard Worker // The entry must be updated. 136*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> keychain_data = 137*6777b538SAndroid Build Coastguard Worker MakeKeychainData(serviceNameLength, serviceName, accountNameLength, 138*6777b538SAndroid Build Coastguard Worker accountName, passwordLength, passwordData, 139*6777b538SAndroid Build Coastguard Worker kKeychainActionUpdate); 140*6777b538SAndroid Build Coastguard Worker status = SecItemUpdate(query.get(), keychain_data.get()); 141*6777b538SAndroid Build Coastguard Worker } 142*6777b538SAndroid Build Coastguard Worker 143*6777b538SAndroid Build Coastguard Worker return status; 144*6777b538SAndroid Build Coastguard Worker} 145*6777b538SAndroid Build Coastguard Worker 146*6777b538SAndroid Build Coastguard WorkerOSStatus AppleKeychain::FindGenericPassword( 147*6777b538SAndroid Build Coastguard Worker UInt32 serviceNameLength, 148*6777b538SAndroid Build Coastguard Worker const char* serviceName, 149*6777b538SAndroid Build Coastguard Worker UInt32 accountNameLength, 150*6777b538SAndroid Build Coastguard Worker const char* accountName, 151*6777b538SAndroid Build Coastguard Worker UInt32* passwordLength, 152*6777b538SAndroid Build Coastguard Worker void** passwordData, 153*6777b538SAndroid Build Coastguard Worker AppleSecKeychainItemRef* itemRef) const { 154*6777b538SAndroid Build Coastguard Worker DCHECK((passwordData && passwordLength) || 155*6777b538SAndroid Build Coastguard Worker (!passwordData && !passwordLength)); 156*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFDictionaryRef> query = 157*6777b538SAndroid Build Coastguard Worker MakeGenericPasswordQuery(serviceNameLength, serviceName, 158*6777b538SAndroid Build Coastguard Worker accountNameLength, accountName); 159*6777b538SAndroid Build Coastguard Worker 160*6777b538SAndroid Build Coastguard Worker // Get the keychain item containing the password. 161*6777b538SAndroid Build Coastguard Worker base::apple::ScopedCFTypeRef<CFTypeRef> result; 162*6777b538SAndroid Build Coastguard Worker OSStatus status = SecItemCopyMatching(query.get(), result.InitializeInto()); 163*6777b538SAndroid Build Coastguard Worker 164*6777b538SAndroid Build Coastguard Worker if (status != noErr) { 165*6777b538SAndroid Build Coastguard Worker if (passwordData) { 166*6777b538SAndroid Build Coastguard Worker *passwordData = nullptr; 167*6777b538SAndroid Build Coastguard Worker *passwordLength = 0; 168*6777b538SAndroid Build Coastguard Worker } 169*6777b538SAndroid Build Coastguard Worker return status; 170*6777b538SAndroid Build Coastguard Worker } 171*6777b538SAndroid Build Coastguard Worker 172*6777b538SAndroid Build Coastguard Worker if (passwordData) { 173*6777b538SAndroid Build Coastguard Worker CFDataRef data = base::apple::CFCast<CFDataRef>(result.get()); 174*6777b538SAndroid Build Coastguard Worker NSUInteger length = CFDataGetLength(data); 175*6777b538SAndroid Build Coastguard Worker *passwordData = malloc(length * sizeof(UInt8)); 176*6777b538SAndroid Build Coastguard Worker CFDataGetBytes(data, CFRangeMake(0, length), (UInt8*)*passwordData); 177*6777b538SAndroid Build Coastguard Worker *passwordLength = length; 178*6777b538SAndroid Build Coastguard Worker } 179*6777b538SAndroid Build Coastguard Worker return status; 180*6777b538SAndroid Build Coastguard Worker} 181*6777b538SAndroid Build Coastguard Worker 182*6777b538SAndroid Build Coastguard Worker} // namespace crypto 183