#include <login/SessionAgentCom.h>
#include <login/SessionAgentStatusCom.h>
#include <os/activity.h>
+#include <CoreFoundation/CFPriv.h>
const uint8_t kUUIDStringLength = 36;
}
static Boolean SecItemSynchronizable(CFDictionaryRef query);
+static CFArrayRef _CopyMatchingIssuers(CFArrayRef issuers);
static void secitemlog(int priority, const char *format, ...)
{
// make storage to extract the dictionary items
CFIndex itemsInDictionary = CFDictionaryGetCount(dictionaryRef);
- CFTypeRef keys[itemsInDictionary];
- CFTypeRef values[itemsInDictionary];
+ std::vector<CFTypeRef> keys(itemsInDictionary);
+ std::vector<CFTypeRef> values(itemsInDictionary);
- CFTypeRef *keysPtr = keys;
- CFTypeRef *valuesPtr = values;
-
- CFDictionaryGetKeysAndValues(dictionaryRef, keys, values);
+ CFDictionaryGetKeysAndValues(dictionaryRef, keys.data(), values.data());
// count the number of items we are interested in
CFIndex count = 0;
// since this is one of those nasty order n^2 loops, we cache as much stuff as possible so that
// we don't pay the price for this twice
- SecKeychainAttrType tags[itemsInDictionary];
- ItemRepresentation types[itemsInDictionary];
+ std::vector<SecKeychainAttrType> tags(itemsInDictionary);
+ std::vector<ItemRepresentation> types(itemsInDictionary);
for (i = 0; i < itemsInDictionary; ++i)
{
- CFTypeRef key = keysPtr[i];
+ CFTypeRef key = keys[i];
int j;
for (j = 0; j < infoNumItems; ++j)
if (j >= infoNumItems)
{
// if we got here, we aren't interested in this item.
- valuesPtr[i] = NULL;
+ values[i] = NULL;
}
}
// we have to clone the data pointer. The caller will need to make sure to throw these away
// with _FreeAttrList when it is done...
- attrList->attr[resultPointer].data = CloneDataByType(types[i], valuesPtr[i], attrList->attr[resultPointer].length);
+ attrList->attr[resultPointer].data = CloneDataByType(types[i], values[i], attrList->attr[resultPointer].length);
resultPointer += 1;
}
}
for (i=0, newCount=0; i < attrList->count; i++) {
if (attrList->attr[i].length > 0) {
newAttrList.attr[newCount++] = attrList->attr[i];
- #if 0
- // debugging code to log item attributes
- SecKeychainAttrType tag = attrList->attr[i].tag;
- SecKeychainAttrType htag=(SecKeychainAttrType)OSSwapConstInt32(tag);
- char tmp[sizeof(SecKeychainAttrType) + 1];
- char tmpdata[attrList->attr[i].length + 1];
- memcpy(tmp, &htag, sizeof(SecKeychainAttrType));
- tmp[sizeof(SecKeychainAttrType)]=0;
- memcpy(tmpdata, attrList->attr[i].data, attrList->attr[i].length);
- tmpdata[attrList->attr[i].length]=0;
- secitemlog(priority, "item attr '%s' = %d bytes: \"%s\"",
- tmp, (int)attrList->attr[i].length, tmpdata);
- #endif
}
}
newAttrList.count = newCount;
require_action(!(itemParams->itemClass == 0 && !itemParams->useItems), error_exit, status = errSecItemClassMissing);
}
- // kSecMatchIssuers is only permitted with identities.
+ // kSecMatchIssuers is only permitted with identities or certificates.
// 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);
+ CFTypeRef allowCerts = CFDictionaryGetValue(itemParams->query, kSecUseCertificatesWithMatchIssuers);
+ require_action(itemParams->returnIdentity || (allowCerts && CFEqual(allowCerts, kCFBooleanTrue)), error_exit, status = errSecParam);
CFMutableArrayRef canonical_issuers = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
CFArrayRef issuers = (CFArrayRef)itemParams->matchIssuers;
if (canonical_issuers) {
can_target_osx = can_target_ios = true;
// Check no-legacy flag.
- CFTypeRef value = CFDictionaryGetValue(query, kSecAttrNoLegacy);
- if (value != NULL) {
- can_target_ios = readNumber(value) != 0;
+ // it's iOS or bust if we're on MZ!
+ CFTypeRef noLegacy = NULL;
+ if (_CFMZEnabled()) {
+ noLegacy = kCFBooleanTrue;
+ }
+ else {
+ noLegacy = CFDictionaryGetValue(query, kSecAttrNoLegacy);
+ }
+
+ if (noLegacy != NULL) {
+ can_target_ios = readNumber(noLegacy) != 0;
can_target_osx = !can_target_ios;
return errSecSuccess;
}
// Check whether the query contains kSecValueRef and modify can_ flags according to the kind and type of the value.
- value = CFDictionaryGetValue(query, kSecValueRef);
+ CFTypeRef value = CFDictionaryGetValue(query, kSecValueRef);
if (value != NULL) {
CFTypeID typeID = CFGetTypeID(value);
if (typeID == SecKeyGetTypeID()) {
can_target_ios = can_target_ios && !CFDictionaryContainsKey(query, *osx_only_items[i]);
}
+ // Absence of all of kSecItemClass, kSecValuePersistentRef, and kSecValueRef means that the query can't target iOS
+ if(CFDictionaryGetValue(query, kSecClass) == NULL &&
+ CFDictionaryGetValue(query, kSecValuePersistentRef) == NULL &&
+ CFDictionaryGetValue(query, kSecValueRef) == NULL) {
+ can_target_ios = false;
+ }
+
return (can_target_ios || can_target_osx) ? errSecSuccess : errSecParam;
}
return status;
}
-static Mutex gParentCertCacheLock;
+static Mutex& gParentCertCacheLock() {
+ static Mutex fParentCertCacheLock;
+ return fParentCertCacheLock;
+}
static CFMutableDictionaryRef gParentCertCache;
static CFMutableArrayRef gParentCertCacheList;
#define PARENT_CACHE_SIZE 100
void SecItemParentCachePurge() {
- StLock<Mutex> _(gParentCertCacheLock);
+ StLock<Mutex> _(gParentCertCacheLock());
CFReleaseNull(gParentCertCache);
CFReleaseNull(gParentCertCacheList);
}
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
if (!digest) return NULL;
- StLock<Mutex> _(gParentCertCacheLock);
+ StLock<Mutex> _(gParentCertCacheLock());
if (gParentCertCache && gParentCertCacheList) {
if (0 <= (ix = CFArrayGetFirstIndexOfValue(gParentCertCacheList,
CFRangeMake(0, CFArrayGetCount(gParentCertCacheList)),
CFDataRef digest = SecCertificateGetSHA1Digest(certificate);
if (!digest) return;
- StLock<Mutex> _(gParentCertCacheLock);
+ StLock<Mutex> _(gParentCertCacheLock());
if (!gParentCertCache || !gParentCertCacheList) {
CFReleaseNull(gParentCertCache);
gParentCertCache = makeCFMutableDictionary();
CFDictionaryAddValue(gParentCertCache, digest, parents);
if (PARENT_CACHE_SIZE <= CFArrayGetCount(gParentCertCacheList)) {
// Remove least recently used cache entry.
+ CFDictionaryRemoveValue(gParentCertCache, CFArrayGetValueAtIndex(gParentCertCacheList, 0));
CFArrayRemoveValueAtIndex(gParentCertCacheList, 0);
}
CFArrayAppendValue(gParentCertCacheList, digest);
#pragma unused (context) /* for now; in future this can reference a container object */
/* Certificates are unique by issuer and serial number. */
+ CFDataRef serialNumber = SecCertificateCopySerialNumberData(certificate, NULL);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
- CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate, NULL);
CFDataRef normalizedIssuer = SecCertificateCopyNormalizedIssuerContent(certificate, NULL);
#else
- CFDataRef serialNumber = SecCertificateCopySerialNumber(certificate);
CFDataRef normalizedIssuer = SecCertificateGetNormalizedIssuerContent(certificate);
CFRetainSafe(normalizedIssuer);
#endif
* which won't work once the item is updated.
*/
CFDictionaryRemoveValue(result, kSecAttrModificationDate);
+
+ /* Find all intermediate certificates in OSX keychain and append them in to the kSecMatchIssuers.
+ * This is required because secd cannot do query in to the OSX keychain
+ */
+ CFTypeRef matchIssuers = CFDictionaryGetValue(result, kSecMatchIssuers);
+ if (matchIssuers && CFGetTypeID(matchIssuers) == CFArrayGetTypeID()) {
+ CFArrayRef newMatchIssuers = _CopyMatchingIssuers((CFArrayRef)matchIssuers);
+ if (newMatchIssuers) {
+ CFDictionarySetValue(result, kSecMatchIssuers, newMatchIssuers);
+ CFRelease(newMatchIssuers);
+ }
+ }
}
else {
/* iOS doesn't add the class attribute, so we must do it here. */
} /* extern "C" */
+static CFArrayRef
+_CopyMatchingIssuers(CFArrayRef matchIssuers) {
+ CFMutableArrayRef result = NULL;
+ CFMutableDictionaryRef query = NULL;
+ CFMutableDictionaryRef policyProperties = NULL;
+ SecPolicyRef policy = NULL;
+ CFTypeRef matchedCertificates = NULL;
+
+ require_quiet(policyProperties = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), out);
+ CFDictionarySetValue(policyProperties, kSecPolicyKU_KeyCertSign, kCFBooleanTrue);
+ require_quiet(policy = SecPolicyCreateWithProperties(kSecPolicyAppleX509Basic, policyProperties), out);
+
+ require_quiet(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), out);
+ CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
+ CFDictionarySetValue(query, kSecMatchIssuers, matchIssuers);
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+ CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
+ CFDictionarySetValue(query, kSecUseCertificatesWithMatchIssuers, kCFBooleanTrue);
+ CFDictionarySetValue(query, kSecMatchPolicy, policy);
+
+ if (SecItemCopyMatching_osx(query, &matchedCertificates) == errSecSuccess && CFGetTypeID(matchedCertificates) == CFArrayGetTypeID()) {
+ require_quiet(result = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, (CFArrayRef)matchedCertificates), out);
+ for(CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)matchedCertificates); ++i) {
+ CFDictionaryRef attributes = (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)matchedCertificates, i);
+ CFTypeRef subject = CFDictionaryGetValue(attributes, kSecAttrSubject);
+ if (!CFArrayContainsValue(result, CFRangeMake(0, CFArrayGetCount(result)), subject)) {
+ CFArrayAppendValue(result, subject);
+ }
+ }
+ }
+
+out:
+ CFReleaseSafe(query);
+ CFReleaseSafe(policyProperties);
+ CFReleaseSafe(policy);
+ CFReleaseSafe(matchedCertificates);
+
+ return result;
+}
+
static OSStatus
SecItemMergeResults(bool can_target_ios, OSStatus status_ios, CFTypeRef result_ios,
bool can_target_osx, OSStatus status_osx, CFTypeRef result_osx,
}
}
-static bool
-ShouldTryUnlockKeybag(CFDictionaryRef query, OSErr status)
-{
- static __typeof(SASSessionStateForUser) *soft_SASSessionStateForUser = NULL;
- static dispatch_once_t onceToken;
- static void *framework;
-
- if (status != errSecInteractionNotAllowed)
- return false;
-
- // If the query disabled authUI, respect it.
- CFTypeRef authUI = NULL;
- if (query) {
- authUI = CFDictionaryGetValue(query, kSecUseAuthenticationUI);
- if (authUI == NULL) {
- authUI = CFDictionaryGetValue(query, kSecUseNoAuthenticationUI);
- authUI = (authUI != NULL && CFEqual(authUI, kCFBooleanTrue)) ? kSecUseAuthenticationUIFail : NULL;
- }
- }
- if (authUI && !CFEqual(authUI, kSecUseAuthenticationUIAllow))
- return false;
-
- dispatch_once(&onceToken, ^{
- framework = dlopen("/System/Library/PrivateFrameworks/login.framework/login", RTLD_LAZY);
- if (framework == NULL)
- return;
- soft_SASSessionStateForUser = (__typeof(soft_SASSessionStateForUser)) dlsym(framework, "SASSessionStateForUser");
- });
-
- if (soft_SASSessionStateForUser == NULL)
- return false;
-
- SessionAgentState sessionState = soft_SASSessionStateForUser(getuid());
- if(sessionState != kSA_state_desktopshowing)
- return false;
-
- return true;
-}
-
OSStatus
SecItemCopyMatching(CFDictionaryRef query, CFTypeRef *result)
{
}
else {
status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
- if(ShouldTryUnlockKeybag(query, status_ios)) {
- // The keybag is locked. Attempt to unlock it...
- secitemlog(LOG_WARNING, "SecItemCopyMatching triggering SecurityAgent");
- if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
- CFReleaseNull(result_ios);
- status_ios = SecItemCopyMatching_ios(attrs_ios, &result_ios);
- }
- }
CFRelease(attrs_ios);
}
secitemlog(LOG_NOTICE, "SecItemCopyMatching_ios result: %d", status_ios);
status = errSecParam;
} else {
status = SecItemAdd_ios(attrs_ios, &result_ios);
- if(ShouldTryUnlockKeybag(attributes, status)) {
- // The keybag is locked. Attempt to unlock it...
- secitemlog(LOG_WARNING, "SecItemAdd triggering SecurityAgent");
- if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(3)) {
- CFReleaseNull(result_ios);
- status = SecItemAdd_ios(attrs_ios, &result_ios);
- }
- }
CFRelease(attrs_ios);
}
secitemlog(LOG_NOTICE, "SecItemAdd_ios result: %d", status);
else {
if (SecItemHasSynchronizableUpdate(true, attributesToUpdate)) {
status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
- if(ShouldTryUnlockKeybag(query, status_ios)) {
- // The keybag is locked. Attempt to unlock it...
- secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent");
- if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
- status_ios = SecItemChangeSynchronizability(attrs_ios, attributesToUpdate, false);
- }
- }
} else {
status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
- if(ShouldTryUnlockKeybag(query, status_ios)) {
- // The keybag is locked. Attempt to unlock it...
- secitemlog(LOG_WARNING, "SecItemUpdate triggering SecurityAgent");
- if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
- status_ios = SecItemUpdate_ios(attrs_ios, attributesToUpdate);
- }
- }
}
CFRelease(attrs_ios);
}
SecItemUpdateTokenItems(CFTypeRef tokenID, CFArrayRef tokenItemsAttributes)
{
OSStatus status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes);
- if(ShouldTryUnlockKeybag(NULL, status)) {
- // The keybag is locked. Attempt to unlock it...
- if(errSecSuccess == SecKeychainVerifyKeyStorePassphrase(1)) {
- secitemlog(LOG_WARNING, "SecItemUpdateTokenItems triggering SecurityAgent");
- status = SecItemUpdateTokenItems_ios(tokenID, tokenItemsAttributes);
- }
- }
secitemlog(LOG_NOTICE, "SecItemUpdateTokenItems_ios result: %d", status);
return status;
}