views:

372

answers:

2

I'm trying to get the attributes of a keychain item. This code should look up all the available attributes, then print off their tags and contents.

According to the docs I should be seeing tags like 'cdat', but instead they just look like an index (i.e., the first tag is 0, next is 1). This makes it pretty useless since I can't tell which attribute is the one I'm looking for.

 SecItemClass itemClass;
 SecKeychainItemCopyAttributesAndData(itemRef, NULL, &itemClass, NULL, NULL, NULL);

 SecKeychainRef keychainRef;
 SecKeychainItemCopyKeychain(itemRef, &keychainRef);

 SecKeychainAttributeInfo *attrInfo;
 SecKeychainAttributeInfoForItemID(keychainRef, itemClass, &attrInfo);

 SecKeychainAttributeList *attributes;
 SecKeychainItemCopyAttributesAndData(itemRef, attrInfo, NULL, &attributes, 0, NULL);

 for (int i = 0; i < attributes->count; i ++)
 {
  SecKeychainAttribute attr = attributes->attr[i];
  NSLog(@"%08x %@", attr.tag, [NSData dataWithBytes:attr.data length:attr.length]);
 }

 SecKeychainFreeAttributeInfo(attrInfo);
 SecKeychainItemFreeAttributesAndData(attributes, NULL);
 CFRelease(itemRef);
 CFRelease(keychainRef);
A: 

I think the documentation leads to a bit of confusion.

The numbers I'm seeing appear to be keychain item attribute constants for keys.

However, SecKeychainItemCopyAttributesAndData returns a SecKeychainAttributeList struct, which contains an array of SecKeychainAttributes. From TFD:

tag A 4-byte attribute tag. See “Keychain Item Attribute Constants” for valid attribute types.

The attribute constants (of the non-"for keys" variety) are the 4-char values I expected to see.

rgov
A: 

There are two things you should be doing here. Firstly, you need to handle "generic" itemClasses after the call to SecKeychainAttributeInfoForItemID...

switch (itemClass)
{
    case kSecInternetPasswordItemClass:
        itemClass = CSSM_DL_DB_RECORD_INTERNET_PASSWORD;
        break;
    case kSecGenericPasswordItemClass:
        itemClass = CSSM_DL_DB_RECORD_GENERIC_PASSWORD;
        break;
    case kSecAppleSharePasswordItemClass:
        itemClass = CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD;
        break;
    default:
        // No action required
}

Second, you need to convert the attr.tag from a FourCharCode to a string, i.e.

NSLog(@"%c%c%c%c %@",
    ((char *)&attr.tag)[3],
    ((char *)&attr.tag)[2],
    ((char *)&attr.tag)[1],
    ((char *)&attr.tag)[0],
    [[[NSString alloc]
        initWithData:[NSData dataWithBytes:attr.data length:attr.length]
        encoding:NSUTF8StringEncoding]
    autorelease]]);

Notice that I've also output the data as a string -- it almost always is UTF8 encoded data.

Matt Gallagher