/*
- * Copyright (c) 2006-2015 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2017 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <Security/SecBase.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecCertificate.h>
+#include <Security/SecCertificatePriv.h>
#include <sys/param.h>
#include "cssmdatetime.h"
#include "SecItem.h"
#include <login/SessionAgentCom.h>
#include <login/SessionAgentStatusCom.h>
+#include <os/activity.h>
const uint8_t kUUIDStringLength = 36;
OSStatus SecItemValidateAppleApplicationGroupAccess(CFStringRef group);
CFDictionaryRef SecItemCopyTranslatedAttributes(CFDictionaryRef inOSXDict, CFTypeRef itemClass,
bool iOSOut, bool pruneMatch, bool pruneSync, bool pruneReturn, bool pruneData, bool pruneAccess);
+
+bool _SecItemParsePersistentRef(CFDataRef persistent_ref, CFStringRef *return_class,
+ long long int *return_rowid, CFDictionaryRef *return_token_attrs);
}
static Boolean SecItemSynchronizable(CFDictionaryRef query);
// now we can make the result array
attrList->count = (UInt32)count;
- attrList->attr = (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count);
+ attrList->attr = (count > 0) ? (SecKeychainAttribute*) malloc(sizeof(SecKeychainAttribute) * count) : NULL;
// fill out the array
int resultPointer = 0;
break;
default:
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%d"), keyRecordValue);
+ retainString = false;
break;
}
if (stringRef) {
break;
default:
stringRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%u"), (unsigned int)keyRecordValue);
+ retainString = false;
break;
}
if (stringRef) {
}
// create SecTrustedApplicationRef for current application/tool
- CFReleaseSafe(currentAppRef);
+ CFReleaseNull(currentAppRef);
status = SecTrustedApplicationCreateFromPath(NULL, ¤tAppRef);
require_noerr(status, finish);
require_quiet(currentAppRef != NULL, finish);
}
// update item
status = SecKeychainItemModifyContent(itemToUpdate,
- (changeAttrList->count == 0) ? NULL : changeAttrList,
+ (!changeAttrList || changeAttrList->count == 0) ? NULL : changeAttrList,
(theData != NULL) ? (UInt32)CFDataGetLength(theData) : 0,
(theData != NULL) ? CFDataGetBytePtrVoid(theData) : NULL);
require_noerr(status, update_failed);
(itemClass == kSecInternetPasswordItemClass || itemClass == kSecGenericPasswordItemClass)) {
// if we got a cryptographic failure updating a password item, it needs to be replaced
status = _ReplaceKeychainItem(itemToUpdate,
- (changeAttrList->count == 0) ? NULL : changeAttrList,
+ (!changeAttrList || changeAttrList->count == 0) ? NULL : changeAttrList,
theData);
}
if (itemToUpdate)
CFTypeRef keyClass; // value for kSecAttrKeyClass (may be NULL)
CFTypeRef service; // value for kSecAttrService (may be NULL)
CFTypeRef issuer; // value for kSecAttrIssuer (may be NULL)
+ CFTypeRef matchIssuers; // value for kSecMatchIssuers (may be NULL)
CFTypeRef serialNumber; // value for kSecAttrSerialNumber (may be NULL)
CFTypeRef search; // search reference for this query (SecKeychainSearchRef or SecIdentitySearchRef)
CFTypeRef assumedKeyClass; // if no kSecAttrKeyClass provided, holds the current class we're searching for
if (itemParams->keyClass) CFRelease(itemParams->keyClass);
if (itemParams->service) CFRelease(itemParams->service);
if (itemParams->issuer) CFRelease(itemParams->issuer);
+ if (itemParams->matchIssuers) CFRelease(itemParams->matchIssuers);
if (itemParams->serialNumber) CFRelease(itemParams->serialNumber);
if (itemParams->search) CFRelease(itemParams->search);
if (itemParams->access) CFRelease(itemParams->access);
{
OSStatus status;
CFTypeRef value = NULL;
+ CFDictionaryRef policyDict = NULL;
SecItemParams *itemParams = (SecItemParams *)calloc(1, sizeof(struct SecItemParams));
require_action(itemParams != NULL, error_exit, status = errSecAllocate);
require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
}
+ // kSecMatchIssuers is only permitted with identities.
+ // Convert the input issuers to normalized form.
+ require_noerr(status = _ValidateDictionaryEntry(dict, kSecMatchIssuers, (const void **)&itemParams->matchIssuers, CFArrayGetTypeID(), NULL), error_exit);
+ if (itemParams->matchIssuers) {
+ require_action(itemParams->returnIdentity, error_exit, status = errSecParam);
+ CFMutableArrayRef canonical_issuers = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ CFArrayRef issuers = (CFArrayRef)itemParams->matchIssuers;
+ if (canonical_issuers) {
+ CFIndex i, count = CFArrayGetCount(issuers);
+ for (i = 0; i < count; i++) {
+ CFTypeRef issuer_data = CFArrayGetValueAtIndex(issuers, i);
+ CFDataRef issuer_canonical = NULL;
+ if (CFDataGetTypeID() == CFGetTypeID(issuer_data))
+ issuer_canonical = SecDistinguishedNameCopyNormalizedSequence((CFDataRef)issuer_data);
+ if (issuer_canonical) {
+ CFArrayAppendValue(canonical_issuers, issuer_canonical);
+ CFRelease(issuer_canonical);
+ }
+ }
+ if (CFArrayGetCount(canonical_issuers) > 0) {
+ CFAssignRetained(itemParams->matchIssuers, canonical_issuers);
+ } else
+ CFRelease(canonical_issuers);
+ }
+ }
+
itemParams->keyUsage = _CssmKeyUsageFromQuery(dict);
itemParams->trustedOnly = CFDictionaryGetValueIfPresent(dict, kSecMatchTrustedOnly, (const void **)&value) && value && CFEqual(kCFBooleanTrue, value);
itemParams->issuerAndSNToMatch = (itemParams->issuer != NULL && itemParams->serialNumber != NULL);
require_noerr(status, error_exit);
}
- // if we already have an item list (to add or find items in), we don't need an item class, attribute list or a search reference
+ // if we already have an item list (to add or find items in), we don't need a search reference
if (itemParams->useItems) {
if (itemParams->itemClass == 0) {
itemParams->itemClass = _ItemClassFromItemList(itemParams->useItems);
}
- status = errSecSuccess;
+
+ // build a SecKeychainAttributeList from the query dictionary for the specified item class
+ if (itemParams->itemClass != 0) {
+ status = _CreateSecKeychainAttributeListFromDictionary(dict, itemParams->itemClass, &itemParams->attrList);
+ } else {
+ status = errSecSuccess;
+ }
goto error_exit; // all done here
}
// if policy is a SMIME policy, copy email address in policy into emailAddrToMatch parameter
if(itemParams->policy) {
- CFDictionaryRef policyDict = SecPolicyCopyProperties(itemParams->policy);
+ policyDict = SecPolicyCopyProperties(itemParams->policy);
CFStringRef oidStr = (CFStringRef) CFDictionaryGetValue(policyDict, kSecPolicyOid);
if(oidStr && CFStringCompare(kSecPolicyAppleSMIME,oidStr,0) == 0) {
require_noerr(status = _ValidateDictionaryEntry(policyDict, kSecPolicyName, (const void **)&itemParams->emailAddrToMatch, CFStringGetTypeID(), NULL), error_exit);
}
- CFRelease(policyDict);
+ CFReleaseNull(policyDict);
}
// create a search reference (either a SecKeychainSearchRef or a SecIdentitySearchRef)
}
error_exit:
+ CFReleaseNull(policyDict);
if (status) {
_FreeSecItemParams(itemParams);
itemParams = NULL;
return status;
}
+static bool items_matching_issuer_parent(CFDataRef issuer, CFArrayRef issuers, int recurse) {
+ if (!issuers || CFArrayGetCount(issuers) == 0) { return false; }
+
+ /* We found a match, we're done. */
+ if (CFArrayContainsValue(issuers, CFRangeMake(0, CFArrayGetCount(issuers)), issuer)) { return true; }
+
+ /* Prevent infinite recursion */
+ if (recurse <= 0) { return false; }
+ recurse--;
+
+ /* Query for parents */
+ CFMutableDictionaryRef query = NULL;
+ CFTypeRef parents = NULL;
+ bool found = false;
+
+ require_quiet(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 4, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks), out);
+ CFDictionaryAddValue(query, kSecClass, kSecClassCertificate);
+ CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+ CFDictionaryAddValue(query, kSecAttrSubject, issuer);
+ CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
+ require_noerr_quiet(SecItemCopyMatching(query, &parents), out);
+
+ if (parents && CFArrayGetTypeID() == CFGetTypeID(parents)) {
+ CFIndex i, count = CFArrayGetCount((CFArrayRef)parents);
+ for (i = 0; i < count; i++) {
+ SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex((CFArrayRef)parents, i);
+ CFDataRef cert_issuer = SecCertificateCopyNormalizedIssuerSequence(cert);
+ if (CFEqual(cert_issuer, issuer)) {
+ // Self-issued cert, don't look for parents.
+ CFReleaseNull(cert_issuer);
+ continue;
+ }
+ found = items_matching_issuer_parent(cert_issuer, issuers, recurse);
+ CFReleaseNull(cert_issuer);
+ if (found) { break; }
+ }
+ } else if (parents && SecCertificateGetTypeID() == CFGetTypeID(parents)) {
+ SecCertificateRef cert = (SecCertificateRef)parents;
+ CFDataRef cert_issuer = SecCertificateCopyNormalizedIssuerSequence(cert);
+ require_action_quiet(!CFEqual(cert_issuer, issuer), out, CFReleaseNull(cert_issuer));
+ found = items_matching_issuer_parent(cert_issuer, issuers, recurse);
+ CFReleaseNull(cert_issuer);
+ }
+
+out:
+ CFReleaseNull(query);
+ CFReleaseNull(parents);
+ return found;
+}
+
+static OSStatus
+_FilterWithIssuers(CFArrayRef issuers, SecCertificateRef cert)
+{
+ if (!issuers || CFArrayGetCount(issuers) == 0) return errSecParam;
+ if (!cert) return errSecParam;
+
+ OSStatus status = errSecInternalError;
+
+ /* kSecMatchIssuers matches certificates where ANY certificate in the chain has this issuer.
+ * So we now need to recursively query the keychain for this cert's parents to determine if
+ * they match. (This is why we limited the use of this key in _CreateSecItemParamsFromDictionary.) */
+ CFDataRef issuer = SecCertificateCopyNormalizedIssuerSequence(cert);
+ if (items_matching_issuer_parent(issuer, issuers, 10)) {
+ status = errSecSuccess;
+ }
+
+ CFReleaseNull(issuer);
+ return status;
+}
+
static SecKeychainItemRef
CopyResolvedKeychainItem(CFTypeRef item)
{
OSStatus status;
CFTypeRef search = (params) ? params->search : NULL;
CFTypeID typeID = (search) ? CFGetTypeID(search) : 0;
- if (typeID == SecIdentitySearchGetTypeID()) {
+
+ if (search && typeID == SecIdentitySearchGetTypeID()) {
status = SecIdentitySearchCopyNext((SecIdentitySearchRef)search, (SecIdentityRef*)item);
}
- else if (typeID == SecKeychainSearchGetTypeID()) {
+ else if (search && typeID == SecKeychainSearchGetTypeID()) {
status = SecKeychainSearchCopyNext((SecKeychainSearchRef)search, (SecKeychainItemRef*)item);
// Check if we need to refresh the search for the next key class
while (status == errSecItemNotFound && params->assumedKeyClass != NULL)
status = UpdateKeychainSearchAndCopyNext(params, item);
}
- else if (typeID == 0 && (params->useItems || params->itemList)) {
+ else if (typeID == 0 && params && (params->useItems || params->itemList)) {
// No search available, but there is an item list available.
// Return the next candidate item from the caller's item list
CFArrayRef itemList = (params->useItems) ? params->useItems : params->itemList;
}
// certificate item is trusted on this system
}
+ if (itemParams->matchIssuers) {
+ status = _FilterWithIssuers((CFArrayRef)itemParams->matchIssuers, (SecCertificateRef) *item);
+ if (status) goto filterOut;
+ // certificate item has one of the issuers
+ }
}
if (itemParams->itemList) {
Boolean foundMatch = FALSE;
static Boolean SecItemIsIOSPersistentReference(CFTypeRef value)
{
if (value) {
- /* Synchronizable persistent ref consists of the sqlite rowid and 4-byte class value */
- const CFIndex kSynchronizablePersistentRefLength = sizeof(int64_t) + 4;
- return (CFGetTypeID(value) == CFDataGetTypeID() &&
- CFDataGetLength((CFDataRef)value) == kSynchronizablePersistentRefLength);
+ return ::_SecItemParsePersistentRef((CFDataRef)value, NULL, NULL, NULL);
}
return false;
}
CFReleaseNull(gParentCertCacheList);
}
-static CFArrayRef parentCacheRead(SecCertificateRef certificate) {
+static CFArrayRef CF_RETURNS_RETAINED parentCacheRead(SecCertificateRef certificate) {
CFArrayRef parents = NULL;
CFIndex ix;
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
}
/*
- * SecItemCopyParentCertificates returns an array of zero of more possible
+ * SecItemCopyParentCertificates_osx returns an array of zero of more possible
* issuer certificates for the provided certificate. No cryptographic validation
* of the signature is performed in this function; its purpose is only to
* provide a list of candidate certificates.
*/
CFArrayRef
-SecItemCopyParentCertificates(SecCertificateRef certificate, void *context)
+SecItemCopyParentCertificates_osx(SecCertificateRef certificate, void *context)
{
#pragma unused (context) /* for now; in future this can reference a container object */
/* Check for parents in keychain cache */
}
if ((status != errSecSuccess) && (status != errSecItemNotFound)) {
- secitemlog(LOG_WARNING, "SecItemCopyParentCertificates: %d", (int)status);
+ secitemlog(LOG_WARNING, "SecItemCopyParentCertificates_osx: %d", (int)status);
}
CFRelease(query);
}
}
}
- } else if (resultType == CFDataGetTypeID()) {
+ } else if (results && resultType == CFDataGetTypeID()) {
SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)results);
if (cert) {
CFArrayAppendValue(result, cert);
OSStatus
SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
{
- secitemlog(LOG_NOTICE, "SecItemCopyMatching");
+ os_activity_t activity = os_activity_create("SecItemCopyMatching", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+ os_activity_scope(activity);
+ os_release(activity);
+
if (!query) {
return errSecParam;
}
OSStatus
SecItemAdd(CFDictionaryRef attributes, CFTypeRef *result)
{
- secitemlog(LOG_NOTICE, "SecItemAdd");
+ os_activity_t activity = os_activity_create("SecItemAdd", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+ os_activity_scope(activity);
+ os_release(activity);
+
if (!attributes) {
return errSecParam;
}
OSStatus
SecItemUpdate(CFDictionaryRef query, CFDictionaryRef attributesToUpdate)
{
- secitemlog(LOG_NOTICE, "SecItemUpdate");
+ os_activity_t activity = os_activity_create("SecItemUpdate", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+ os_activity_scope(activity);
+ os_release(activity);
+
if (!query || !attributesToUpdate) {
return errSecParam;
}
OSStatus
SecItemDelete(CFDictionaryRef query)
{
- secitemlog(LOG_NOTICE, "SecItemDelete");
+ os_activity_t activity = os_activity_create("SecItemDelete", OS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT);
+ os_activity_scope(activity);
+ os_release(activity);
+
if (!query) {
return errSecParam;
}