Reputation: 3829
From this question, I know you can use SecKeychainFindGenericPassword
without a username value. It will still return a keychain item for the given service. But how do I then get the username?
Get the Username(s) stored in Keychain, using only the ServiceName? OR: Where are you supposed to store the Username?
//SecKeychainFindGenericPassword
//Finds the first generic password based on the attributes passed.
OSStatus SecKeychainFindGenericPassword (
CFTypeRef keychainOrArray,
UInt32 serviceNameLength,
const char *serviceName,
UInt32 accountNameLength,
const char *accountName,
UInt32 *passwordLength,
void **passwordData,
SecKeychainItemRef *itemRef
);
My code looks like this:
UInt32 passwordLength = 0;
char *password = nil;
SecKeychainItemRef item = nil;
OSStatus returnStatus = SecKeychainFindGenericPassword(NULL, 9, @"MyAppName", 0, NULL, &passwordLength, (void **)&password, &item);
//Get password
NSString *passwordString = [[[NSString alloc] initWithData:[NSData dataWithBytes:password length:passwordLength] encoding:NSUTF8StringEncoding] autorelease];
SecKeychainItemFreeContent(NULL, password);
//Get username (not yet working)
UInt32 attributeTags[1];
*attributeTags = 'acct';// TODO: populate with the kSecAttrAccount constant (NOT a string cast to a UInt32)
//kSecAccountItemAttr = 'acct',
UInt32 formatConstants[1];
*formatConstants = 0; // TODO: populate with a format constant found in "cssmtype.h". I'm picking "0" = "string" in list below...
//SecKeychainAttributeInfo doc says: "A pointer to the first attribute format in the array. Attribute formats are of type CSSM_DB_ATTRIBUTE_FORMAT."
/* //found in "cssmtype.h"
typedef uint32 CSSM_DB_ATTRIBUTE_FORMAT, *CSSM_DB_ATTRIBUTE_FORMAT_PTR;
enum {
CSSM_DB_ATTRIBUTE_FORMAT_STRING = 0,
CSSM_DB_ATTRIBUTE_FORMAT_SINT32 = 1,
CSSM_DB_ATTRIBUTE_FORMAT_UINT32 = 2,
CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM = 3,
CSSM_DB_ATTRIBUTE_FORMAT_REAL = 4,
CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE = 5,
CSSM_DB_ATTRIBUTE_FORMAT_BLOB = 6,
CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32 = 7,
CSSM_DB_ATTRIBUTE_FORMAT_COMPLEX = 8
};
*/
struct SecKeychainAttributeInfo attributeInfo, 1, *attributeTags, *formatConstants; //ERROR: "Expected ';' at end of declaration list.
//OR:
/*
struct SecKeychainAttributeInfo
{
UInt32 1; //ERROR: "Expected ';' at end of declaration list.
UInt32 *attributeTags;
UInt32 *formatConstants;
}attributeInfo;
*/
/*
SecKeychainAttributeInfo *attributeInfo; //TODO: "change it to hold the structure directly"
attributeInfo->count = 1;
attributeInfo->tag = attributeTags;
attributeInfo->format = formatConstants;
*/
SecKeychainAttributeList *attributeList = nil;
OSStatus attributeStatus = SecKeychainItemCopyAttributesAndData(item, &attributeInfo, NULL, &attributeList, 0, NULL);
if (attributeStatus != noErr)
{
if (_logsErrors)
NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(attributeStatus));
return nil;
}
for (int i = 0; i < attributeList->count; i ++)
{
SecKeychainAttribute attr = attributeList->attr[i];
NSLog(@"%08x %@", attr.tag, [NSData dataWithBytes:attr.data length:attr.length]);
}
How do I get an NSString value of the username contained in that SecKeychainItemRef
?
Upvotes: 4
Views: 2677
Reputation: 3829
+ (EMGenericKeychainItem *)genericKeychainItemForService:(NSString *)serviceName
{
if (!serviceName)
return nil;
const char *serviceNameCString = [serviceName UTF8String];
UInt32 passwordLength = 0;
char *password = nil;
SecKeychainItemRef item = nil;
OSStatus returnStatus = SecKeychainFindGenericPassword(NULL, strlen(serviceNameCString), serviceNameCString, 0, NULL, &passwordLength, (void **)&password, &item);
UInt32 attributeTags[1];
*attributeTags = kSecAccountItemAttr;
UInt32 formatConstants[1];
*formatConstants = CSSM_DB_ATTRIBUTE_FORMAT_STRING; //From "cssmtype.h" under "CSSM_DB_ATTRIBUTE_FORMAT".
struct SecKeychainAttributeInfo
{
UInt32 count;
UInt32 *tag;
UInt32 *format;
}attributeInfo;
attributeInfo.count = 1;
attributeInfo.tag = attributeTags;
attributeInfo.format = formatConstants;
SecKeychainAttributeList *attributeList = nil;
OSStatus attributeStatus = SecKeychainItemCopyAttributesAndData(item, &attributeInfo, NULL, &attributeList, 0, NULL);
if (attributeStatus != noErr || !item)
{
if (_logsErrors)
NSLog(@"Error (%@) - %s", NSStringFromSelector(_cmd), GetMacOSStatusErrorString(returnStatus));
return nil;
}
SecKeychainAttribute accountNameAttribute = attributeList->attr[0];
NSString* accountName = [[[NSString alloc] initWithData:[NSData dataWithBytes:accountNameAttribute.data length:accountNameAttribute.length] encoding:NSUTF8StringEncoding] autorelease];
NSString *passwordString = [[[NSString alloc] initWithData:[NSData dataWithBytes:password length:passwordLength] encoding:NSUTF8StringEncoding] autorelease];
SecKeychainItemFreeContent(NULL, password);
return [EMGenericKeychainItem _genericKeychainItemWithCoreKeychainItem:item forServiceName:serviceName username:accountName password:passwordString];
}
Upvotes: 5
Reputation: 96333
Use the SecKeychainItemCopyAttributesAndData
function to obtain the account name and any other attributes you're interested in.
Upvotes: 0