Search code examples
cxcodemacossecuritykeychain

EXC_BAD_ACCESS While trying to access OS X Keychain


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:

  1. Using the SecItemCopyMatching API that uses a query based approach to access the keychain.
  2. Using the SecKeychainFindGenericPassword.

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;
}

Solution

  • 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)