Why are Keychain services returning incorrect content for keys?
I am trying to use permalink chain links in an iPhone application. I found that if I created two different keychain items, I would get a different permalink each time (they look like "genp ....... 1", "genp ....... 2", .. .), However, attempts to find items using a permalink always returned the contents of the first item. Why should it be? I have confirmed that my keychain-preserving code is definitely creating new items in each case (rather than updating existing items) and is not getting any errors. And as I said, Keychain Services provides different permalinks for each item.
I was able to solve my immediate problem by looking for key binding elements by attribute rather than permalinks, but it would be easier to use permalinks, so I would appreciate a solution to that problem.
Here's my code:
- (NSString *)keychainItemWithName: (NSString *)name {
NSString *path = [GLApplicationSupportFolder()
stringByAppendingPathComponent: name];
NSData *persistentRef = [NSData dataWithContentsOfFile: path];
if (!persistentRef) {
NSLog(@"no persistent reference for name: %@", name);
return nil;
}
NSArray *refs = [NSArray arrayWithObject: persistentRef];
//get the data
CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(params, kSecMatchItemList, refs);
CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(params, kSecReturnData, kCFBooleanTrue);
CFDataRef item = NULL;
OSStatus result = SecItemCopyMatching(params, (CFTypeRef *)&item);
CFRelease(params);
if (result != errSecSuccess) {
NSLog(@"error %d retrieving keychain reference for name: %@", result, name);
return nil;
}
NSString *token = [[NSString alloc] initWithData: (NSData *)item
encoding: NSUTF8StringEncoding];
CFRelease(item);
return [token autorelease];
}
- (void)setKeychainItem: (NSString *)newToken forName: (NSString *)name {
NSData *tokenData = [newToken dataUsingEncoding: NSUTF8StringEncoding];
//firstly, find out whether the item already exists
NSDictionary *searchAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
name, kSecAttrAccount,
kCFBooleanTrue, kSecReturnAttributes,
nil];
NSDictionary *foundAttrs = nil;
OSStatus searchResult = SecItemCopyMatching((CFDictionaryRef)searchAttributes,
(CFTypeRef *)&foundAttrs);
if (noErr == searchResult) {
NSMutableDictionary *toStore = [foundAttrs mutableCopy];
[toStore setObject: tokenData forKey: (id)kSecValueData];
OSStatus result = SecItemUpdate((CFDictionaryRef)foundAttrs,
(CFDictionaryRef)toStore);
if (result != errSecSuccess) {
NSLog(@"error %d updating keychain", result);
}
[toStore release];
return;
}
//need to create the item.
CFMutableDictionaryRef params = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(params, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(params, kSecAttrAccount, name);
CFDictionaryAddValue(params, kSecReturnPersistentRef, kCFBooleanTrue);
CFDictionaryAddValue(params, kSecValueData, tokenData);
NSData *persistentRef = nil;
OSStatus result = SecItemAdd(params, (CFTypeRef *)&persistentRef);
CFRelease(params);
if (result != errSecSuccess) {
NSLog(@"error %d from keychain services", result);
return;
}
NSString *path = [GLApplicationSupportFolder()
stringByAppendingPathComponent: name];
[persistentRef writeToFile: path atomically: NO];
[persistentRef release];
}
It turns out that using kSecMatchItemList doesn't work at all.
I did it like this:
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword, kSecClass,
persistentRef, (id)kSecValuePersistentRef,
(id)kCFBooleanTrue, kSecReturnAttributes,
(id)kCFBooleanTrue, kSecReturnData,
nil];
NSDictionary *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query,
(CFTypeRef*)&result);
which returned the attributes and data for the permalink. The documentation in the title about converting a "permalink" to a "standard link" doesn't make any sense. Hope this helps.
a source to share