SecItemCopyMatching returns errSecSuccess but the result pointer is nullptr

Hey, guys!
I am using C++ to write some MacOS code.
  1. I have added the data to the keychain with the SecItemAdd Here is the code snipped.

Code Block
CFStringRef DictKeys[] = {kSecClass, kSecAttrService, kSecAttrType, kSecAttrAccount, kSecValueData, kSecAttrDescription};
CFTypeRef DictValues[] = {kSecClassGenericPassword, ServiceRef, TypeRef, AccountRef, ValueRef, DescriptionRef};
/* Create a dictionary object that holds key-value parameters to store with the new credentials entry. */
CFDictionaryRef KeychainRef = CFDictionaryCreate(kCFAllocatorDefault, (const void **)DictKeys, (const void **)DictValues, 6, nullptr, nullptr);
if (KeychainRef != nullptr)
{
/* Create a new credentials entry in the default keychain of the currently logged in local user. */
const OSStatus Status = SecItemAdd(KeychainRef, nullptr);
Result = OSStatusToResult(Status);
}

It actually works and I can see the entry in the keychain.

2. I call the SecItemCopyMatching to get the entry. You can find the code snipped below.
Code Block
CFMutableDictionaryRef Query = CreateSearchQuery(ServiceRef, TypeRef, AccountRef);
if (Query != nullptr)
{
CFDataRef DataRef = nullptr;
const OSStatus Status = SecItemCopyMatching(Query, (CFTypeRef *)DataRef);
/* Here Status = errSecSuccess but DataRef = nullptr */
if (Status == errSecSuccess && DataRef != nullptr)
{
/* Some code here ... */
}

Here is the implementation of CreateSearchQuery
Code Block
CFMutableDictionaryRef CreateSearchQuery(CFStringRef ServiceRef, CFNumberRef TypeRef, CFStringRef AccountRef)
{
CFMutableDictionaryRef Query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr);
if (Query)
{
CFDictionaryAddValue(Query, kSecClass, kSecClassGenericPassword);
CFDictionaryAddValue(Query, kSecMatchLimit, kSecMatchLimitOne);
CFDictionaryAddValue(Query, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
CFDictionaryAddValue(Query, kSecReturnData, kCFBooleanTrue);
CFDictionaryAddValue(Query, kSecAttrService, ServiceRef);
CFDictionaryAddValue(Query, kSecAttrType, TypeRef);
CFDictionaryAddValue(Query, kSecAttrAccount, AccountRef);
}
return Query;
}


I have confirmed that ServiceRef, TypeRef and AccountRef have the same values as when SecItemAdd was called. Also It do returns non-empty Query

I think it worth to notice, that SecItemDelete successfully deletes the entry with the same Query.
MacOS version = 10.15.6
First things first, your CFDictionary code is really broken. When you pass NULL to the key and value callbacks parameters, the dictionary acts “as if a valid structure of version 0 with all fields NULL had been passed in”. This means you get no retains or releases of the keys or values! If you’re working with CF or Obj-C objects, you should instead be passing in kCFTypeDictionaryKeyCallBacks and kCFTypeDictionaryValueCallBacks. These will retain the keys and values as expected.

Better yet, switch to Objective-C++ and then you can radically simplify your code. For example:

Code Block
OSStatus status = SecItemAdd( (!!bridge CFDictionaryRef) @{
(!!bridge NSString *) kSecClass: (!!bridge NSString *) kSecClassGenericPassword,
(!!bridge NSString *) kSecAttrService: ServiceRef,
(!!bridge NSString *) kSecAttrType: TypeRef,
(!!bridge NSString *) kSecAttrAccount: AccountRef,
(!!bridge NSString *) kSecValueData: ValueRef,
(!!bridge NSString *) kSecAttrDescription: DescriptionRef,
}, NULL);


IMPORTANT There’s a bug in DevForums that causes it to munge double underscore pairs (r. 66512566) so I've replaced all of them with !! in this code listing.



As to your actual problem, it’s hard to be sure without seeing all the values in play but my general advice here is that you simplify your query dictionary. To start, you should remove any attributes that don’t contribute to item uniqueness. In this case that means kSecAttrType. For a list of attributes that do contribute to item uniqueness, see the docs for errSecDuplicateItem.

Next, remove the kSecMatchLimit property. This isn’t helping — the call defaults to a limit of 1 — and it may be causing problems because by setting a match limit you change how the call returns items (an array versus a single record).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
SecItemCopyMatching returns errSecSuccess but the result pointer is nullptr
 
 
Q