I wrote a sample code (with help received earlier) to add and retrieve a password from OS X keychain. I am able to successfully add the password but when I try to retrieve it I get a EXC_BAD_ACCESS (code=EXC_I386_GPFLT). I tried doing this two ways:
The BAD ACCESS error happens only withe first approach, the second one succeeds. I am trying to use the first approach so that I can ensure that I am using the SecKeychainItemFreeContent to clean up once I am done.
Note - this is a sample code and hence I haven't put any checks for return values. Though I have been keeping an eye on them in the debugger and see no errors there.
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
int main(int argc, const char * argv[])
{
char acc_name[20];
char password[20];
CFStringRef keys[3];
printf("Enter account name - ");
scanf("%s", acc_name);
printf("\nEnter password - ");
scanf("%s", password);
keys[0] = kSecClass;
keys[1] = kSecAttrAccount;
keys[2] = kSecValueData;
CFTypeRef values[3];
values[0] = kSecClassGenericPassword;
values[1] = CFStringCreateWithCString(kCFAllocatorDefault, acc_name, kCFStringEncodingUTF8);
values[2] = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8);
CFDictionaryRef query;
query = CFDictionaryCreate(kCFAllocatorDefault, (const void**) keys, (const void**) values, 3, NULL, NULL);
OSStatus result = SecItemAdd(query, NULL);
printf("%d\n", result);
printf("Retrieve\n");
SecKeychainItemRef pitem = NULL;
SecKeychainItemRef kch_ref = NULL;
CFStringRef qkeys[6];
qkeys[0] = kSecClass;
qkeys[1] = kSecAttrAccount;
qkeys[2] = kSecMatchLimit;
qkeys[3] = kSecReturnAttributes;
qkeys[4] = kSecReturnData;
qkeys[5] = kSecReturnRef;
CFTypeRef qvalues[6];
qvalues[0] = kSecClassGenericPassword;
qvalues[1] = CFStringCreateWithCString(kCFAllocatorDefault, acc_name, kCFStringEncodingUTF8);
qvalues[2] = kSecMatchLimitOne;
qvalues[3] = kCFBooleanTrue;
qvalues[4] = kCFBooleanTrue;
qvalues[5] = kCFBooleanTrue;
unsigned int plength = 0;
char *pdata = NULL;
unsigned int plength2 = 0;
void *pdata2 = NULL;
CFDictionaryRef extract_query = CFDictionaryCreate(kCFAllocatorDefault, (const void **)qkeys, (const void **)qvalues, 6, NULL, NULL);
result = SecItemCopyMatching(extract_query, (CFTypeRef *)&kch_ref);
SecKeychainItemCopyAttributesAndData(kch_ref, NULL, NULL, NULL, &plength2, &pdata2); // <-- EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
//result = SecKeychainFindGenericPassword(NULL, 0, NULL, (uint32)strlen(acc_name), acc_name, &plength, (void **)&pdata, &pitem);
if (result)
{
//return error;
}
printf("password - %s\n", pdata);
return 0;
}
You seem to be passing a bad parameter to SecKeychainItemCopyAttributesAndData
.
In your query dictionary, you specify three different return types: kSecReturnAttributes
, kSecReturnData
, and kSecReturnRef
. The documentation for SecItemCopyMatching
has this to say:
Use the keys found in Item Return Result Keys to indicate whether you seek the item’s attributes, the item’s data, a reference to the data, a persistent reference to the data, or a combination of these. When you specify more than one return type, the search returns a dictionary containing each of the types you request. When your search allows multiple results, they’re all returned together in an array of items.
So, the type returned by SecItemCopyMatching
(since you limit to one result) will be a CFDictionary
containing the item data, item attributes and a reference to the item.
You then pass it to SecKeychainItemCopyAttributesAndData
, but the documentation for the first parameter says:
itemRef
A reference to the keychain item from which you wish to retrieve data or attributes.
If you modify your query dictionary to only include the kSecReturnRef
return type (removing kSecReturnAttributes
and kSecReturnData
), your code will work.
(Or, extract the reference from the dictionary that SecItemCopyMatching
is returning and pass that to SecKeychainItemCopyAttributesAndData
)