blob: 1992e4595302d766fe01c47b7dc101f6f63a56fc [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/keychain_wrapper.h"
#import "remoting/ios/domain/host_info.h"
static const UInt8 kKeychainItemIdentifier[] = "org.chromium.RemoteDesktop\0";
@interface KeychainWrapper () {
NSMutableDictionary* _keychainData;
NSMutableDictionary* _userInfoQuery;
}
@end
@implementation KeychainWrapper
- (id)init {
if ((self = [super init])) {
OSStatus keychainErr = noErr;
_userInfoQuery = [[NSMutableDictionary alloc] init];
[_userInfoQuery setObject:(__bridge id)kSecClassGenericPassword
forKey:(__bridge id)kSecClass];
NSData* keychainItemID =
[NSData dataWithBytes:kKeychainItemIdentifier
length:strlen((const char*)kKeychainItemIdentifier)];
[_userInfoQuery setObject:keychainItemID
forKey:(__bridge id)kSecAttrGeneric];
[_userInfoQuery setObject:(__bridge id)kSecMatchLimitOne
forKey:(__bridge id)kSecMatchLimit];
[_userInfoQuery setObject:(__bridge id)kCFBooleanTrue
forKey:(__bridge id)kSecReturnAttributes];
CFMutableDictionaryRef outDictionary = nil;
keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)_userInfoQuery,
(CFTypeRef*)&outDictionary);
if (keychainErr == noErr) {
_keychainData = [self
secItemFormatToDictionary:(__bridge_transfer NSMutableDictionary*)
outDictionary];
} else if (keychainErr == errSecItemNotFound) {
[self resetKeychainItem];
if (outDictionary) {
CFRelease(outDictionary);
_keychainData = nil;
}
} else {
NSLog(@"Serious error.");
if (outDictionary) {
CFRelease(outDictionary);
_keychainData = nil;
}
}
}
return self;
}
- (void)setRefreshToken:(NSString*)refreshToken {
[self setObject:refreshToken forKey:(__bridge id)kSecValueData];
}
- (NSString*)refreshToken {
return [self objectForKey:(__bridge id)kSecValueData];
}
// Implement the mySetObject:forKey method, which writes attributes to the
// keychain:
- (void)setObject:(id)inObject forKey:(id)key {
if (inObject == nil)
return;
id currentObject = [_keychainData objectForKey:key];
if (![currentObject isEqual:inObject]) {
[_keychainData setObject:inObject forKey:key];
[self writeToKeychain];
}
}
// Implement the myObjectForKey: method, which reads an attribute value from a
// dictionary:
- (id)objectForKey:(id)key {
return [_keychainData objectForKey:key];
}
- (void)resetKeychainItem {
if (!_keychainData) {
_keychainData = [[NSMutableDictionary alloc] init];
} else if (_keychainData) {
NSMutableDictionary* tmpDictionary =
[self dictionaryToSecItemFormat:_keychainData];
OSStatus errorcode = SecItemDelete((__bridge CFDictionaryRef)tmpDictionary);
if (errorcode == errSecItemNotFound) {
// this is ok.
} else if (errorcode != noErr) {
NSLog(@"Problem deleting current keychain item.");
}
}
[_keychainData setObject:@"gaia_refresh_token"
forKey:(__bridge id)kSecAttrLabel];
[_keychainData setObject:@"Gaia fresh token"
forKey:(__bridge id)kSecAttrDescription];
[_keychainData setObject:@"" forKey:(__bridge id)kSecValueData];
}
- (NSMutableDictionary*)dictionaryToSecItemFormat:
(NSDictionary*)dictionaryToConvert {
NSMutableDictionary* returnDictionary =
[NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
NSData* keychainItemID =
[NSData dataWithBytes:kKeychainItemIdentifier
length:strlen((const char*)kKeychainItemIdentifier)];
[returnDictionary setObject:keychainItemID
forKey:(__bridge id)kSecAttrGeneric];
[returnDictionary setObject:(__bridge id)kSecClassGenericPassword
forKey:(__bridge id)kSecClass];
NSString* passwordString =
[dictionaryToConvert objectForKey:(__bridge id)kSecValueData];
[returnDictionary
setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding]
forKey:(__bridge id)kSecValueData];
return returnDictionary;
}
- (NSMutableDictionary*)secItemFormatToDictionary:
(NSDictionary*)dictionaryToConvert {
NSMutableDictionary* returnDictionary =
[NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert];
[returnDictionary setObject:(__bridge id)kCFBooleanTrue
forKey:(__bridge id)kSecReturnData];
[returnDictionary setObject:(__bridge id)kSecClassGenericPassword
forKey:(__bridge id)kSecClass];
CFDataRef passwordData = NULL;
OSStatus keychainError = noErr;
keychainError = SecItemCopyMatching(
(__bridge CFDictionaryRef)returnDictionary, (CFTypeRef*)&passwordData);
if (keychainError == noErr) {
[returnDictionary removeObjectForKey:(__bridge id)kSecReturnData];
NSString* password = [[NSString alloc]
initWithBytes:[(__bridge_transfer NSData*)passwordData bytes]
length:[(__bridge NSData*)passwordData length]
encoding:NSUTF8StringEncoding];
[returnDictionary setObject:password forKey:(__bridge id)kSecValueData];
} else if (keychainError == errSecItemNotFound) {
NSLog(@"Nothing was found in the keychain.");
if (passwordData) {
CFRelease(passwordData);
passwordData = nil;
}
} else {
NSLog(@"Serious error.\n");
if (passwordData) {
CFRelease(passwordData);
passwordData = nil;
}
}
return returnDictionary;
}
- (void)writeToKeychain {
CFDictionaryRef attributes = nil;
NSMutableDictionary* updateItem = nil;
if (SecItemCopyMatching((__bridge CFDictionaryRef)_userInfoQuery,
(CFTypeRef*)&attributes) == noErr) {
updateItem = [NSMutableDictionary
dictionaryWithDictionary:(__bridge_transfer NSDictionary*)attributes];
[updateItem setObject:[_userInfoQuery objectForKey:(__bridge id)kSecClass]
forKey:(__bridge id)kSecClass];
NSMutableDictionary* tempCheck =
[self dictionaryToSecItemFormat:_keychainData];
[tempCheck removeObjectForKey:(__bridge id)kSecClass];
OSStatus errorcode = SecItemUpdate((__bridge CFDictionaryRef)updateItem,
(__bridge CFDictionaryRef)tempCheck);
if (errorcode != noErr) {
NSLog(@"Couldn't update the Keychain Item. %d", (int)errorcode);
}
} else {
OSStatus errorcode =
SecItemAdd((__bridge CFDictionaryRef)
[self dictionaryToSecItemFormat:_keychainData],
NULL);
if (errorcode != noErr) {
NSLog(@"Couldn't add the Keychain Item. %d", (int)errorcode);
}
if (attributes) {
CFRelease(attributes);
attributes = nil;
}
}
}
@end