5 // Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
9 #include <CoreFoundation/CoreFoundation.h>
10 #include <Security/SecCertificate.h>
11 #include <Security/SecItem.h>
12 #include <Security/SecItemPriv.h>
13 #include <Security/SecBase.h>
14 #include <utilities/array_size.h>
15 #include <utilities/SecCFWrappers.h>
18 #include <Security/SecAccessControl.h>
19 #include <Security/SecAccessControlPriv.h>
20 #include <libaks_acl_cf_keys.h>
21 #include <LocalAuthentication/LAPublicDefines.h>
22 #include <LocalAuthentication/LAPrivateDefines.h>
23 #include <securityd/SecItemServer.h>
24 #include <LocalAuthentication/LAPublicDefines.h>
26 #include "secd_regressions.h"
30 #include <coreauthd_spi.h>
31 #include "SecdTestKeychainUtilities.h"
33 #include <MobileKeyBag/MobileKeyBag.h>
36 #if LA_CONTEXT_IMPLEMENTED
37 static keybag_handle_t test_keybag;
38 static const char *passcode1 = "passcode1";
39 static const char *passcode2 = "passcode2";
41 static bool changePasscode(const char *old_passcode, const char *new_passcode)
43 size_t old_passcode_len = 0;
44 size_t new_passcode_len = 0;
47 old_passcode_len = strlen(old_passcode);
50 new_passcode_len = strlen(new_passcode);
52 kern_return_t status = aks_change_secret(test_keybag, old_passcode, (int)old_passcode_len, new_passcode, (int)new_passcode_len, generation_noop, NULL);
67 kAccessabilityItemAttr,
71 extern void LASetErrorCodeBlock(CFErrorRef (^newCreateErrorBlock)(void));
73 static void WithEachString(void(^each)(CFStringRef attr, enum ItemAttrType atype), ...) {
77 while((attr = va_arg(ap, CFStringRef)) != NULL) {
78 enum ItemAttrType atype = va_arg(ap, enum ItemAttrType);
84 static void ItemForEachPKAttr(CFMutableDictionaryRef item, void(^each)(CFStringRef attr, enum ItemAttrType atype)) {
85 CFStringRef iclass = CFDictionaryGetValue(item, kSecClass);
88 } else if (CFEqual(iclass, kSecClassGenericPassword)) {
90 kSecAttrAccessible, kAccessabilityItemAttr,
91 kSecAttrAccessGroup, kAccessGroupItemAttr,
92 kSecAttrAccount, kStringItemAttr,
93 kSecAttrService, kStringItemAttr,
94 kSecAttrSynchronizable, kBoolItemAttr,
96 } else if (CFEqual(iclass, kSecClassInternetPassword)) {
98 kSecAttrAccessible, kAccessabilityItemAttr,
99 kSecAttrAccessGroup, kAccessGroupItemAttr,
100 kSecAttrAccount, kStringItemAttr,
101 kSecAttrSecurityDomain, kStringItemAttr,
102 kSecAttrServer, kStringItemAttr,
103 kSecAttrProtocol, kNumberItemAttr,
104 kSecAttrAuthenticationType, kNumberItemAttr,
105 kSecAttrPort, kNumberItemAttr,
106 kSecAttrPath, kStringItemAttr,
107 kSecAttrSynchronizable, kBoolItemAttr,
109 } else if (CFEqual(iclass, kSecClassCertificate)) {
111 kSecAttrAccessible, kAccessabilityItemAttr,
112 kSecAttrAccessGroup, kAccessGroupItemAttr,
113 kSecAttrCertificateType, kNumberItemAttr,
114 kSecAttrIssuer, kDataItemAttr,
115 kSecAttrSerialNumber, kDataItemAttr,
116 kSecAttrSynchronizable, kBoolItemAttr,
118 } else if (CFEqual(iclass, kSecClassKey)) {
120 kSecAttrAccessible, kAccessabilityItemAttr,
121 kSecAttrAccessGroup, kAccessGroupItemAttr,
122 kSecAttrKeyClass, kStringItemAttr, // kNumberItemAttr on replies
123 kSecAttrApplicationLabel, kDataItemAttr,
124 kSecAttrApplicationTag, kDataItemAttr,
125 kSecAttrKeyType, kNumberItemAttr,
126 kSecAttrKeySizeInBits, kNumberItemAttr,
127 kSecAttrEffectiveKeySize, kNumberItemAttr,
128 kSecAttrStartDate, kDateItemAttr,
129 kSecAttrEndDate, kDateItemAttr,
130 kSecAttrSynchronizable, kBoolItemAttr,
132 } else if (CFEqual(iclass, kSecClassIdentity)) {
134 kSecAttrAccessible, kAccessabilityItemAttr,
135 kSecAttrAccessGroup, kAccessGroupItemAttr,
136 kSecAttrCertificateType, kNumberItemAttr,
137 kSecAttrIssuer, kDataItemAttr,
138 kSecAttrSerialNumber, kDataItemAttr,
139 kSecAttrSynchronizable, kBoolItemAttr,
140 kSecAttrKeyClass, kStringItemAttr, // kNumberItemAttr on replies
141 kSecAttrApplicationLabel, kDataItemAttr,
142 kSecAttrApplicationTag, kDataItemAttr,
143 kSecAttrKeyType, kNumberItemAttr,
144 kSecAttrKeySizeInBits, kNumberItemAttr,
145 kSecAttrEffectiveKeySize, kNumberItemAttr,
146 kSecAttrStartDate, kDateItemAttr,
147 kSecAttrEndDate, kDateItemAttr,
148 kSecAttrSynchronizable, kBoolItemAttr,
153 static void fillItem(CFMutableDictionaryRef item, uint32_t num)
155 ItemForEachPKAttr(item, ^(CFStringRef attr, enum ItemAttrType atype) {
156 CFTypeRef value = NULL;
159 value = (num % 2 == 0 ? kCFBooleanTrue : kCFBooleanFalse);
162 case kNumberItemAttr:
163 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
165 case kStringItemAttr:
167 value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("acl-stress-string-%d"), num);
172 int len = snprintf(buf, sizeof(buf), "acl-stress-data-%d", num);
173 value = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buf, len);
177 value = NULL; // Don't mess with dates on create.
179 case kAccessabilityItemAttr:
181 case kAccessGroupItemAttr:
183 CFStringRef accessGroups[] = {
185 CFSTR("com.apple.security.sos"), // Secd internally uses this
187 value = accessGroups[num % array_size(accessGroups)];
192 CFDictionarySetValue(item, attr, value);
193 CFReleaseSafe(value);
197 #if LA_CONTEXT_IMPLEMENTED
199 static CFErrorRef createCFError(CFStringRef message, CFIndex code)
201 const void* keysPtr[1];
202 const void* messagesPtr[1];
204 keysPtr[0] = kCFErrorLocalizedDescriptionKey;
205 messagesPtr[0] = message;
206 return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, CFSTR(kLAErrorDomain), code, keysPtr, messagesPtr, 1);
210 static void set_app_password(ACMContextRef acmContext)
212 CFDataRef appPwdData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("Application password"), kCFStringEncodingUTF8, 0);
213 ACMCredentialRef acmCredential = NULL;
214 ok_status(ACMCredentialCreate(kACMCredentialTypePassphraseEntered, &acmCredential), "Create ACM credential");
215 ACMPassphrasePurpose purpose = kACMPassphrasePurposeGeneral;
216 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrase, CFDataGetBytePtr(appPwdData), CFDataGetLength(appPwdData)), "Set ACM credential property - passphrase");
217 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrasePurpose, &purpose, sizeof(purpose)), "Set ACM credential property - purpose");
218 ok_status(ACMContextAddCredentialWithScope(acmContext, acmCredential, kACMScopeContext), "aad ACM credential to ACM context");
219 ACMCredentialDelete(acmCredential);
220 CFReleaseSafe(appPwdData);
224 static void item_with_application_password(uint32_t *item_num)
227 CFErrorRef (^okBlock)(void) = ^ {
228 return (CFErrorRef)NULL;
231 CFErrorRef (^authFailedBlock)(void) = ^ {
232 return createCFError(CFSTR(""), kLAErrorAuthenticationFailed);
235 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
236 fillItem(item, (*item_num)++);
238 LASetErrorCodeBlock(okBlock);
239 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlApplicationPassword, NULL);
240 ok(aclRef, "Create SecAccessControlRef");
242 ACMContextRef acmContext = NULL;
243 ok_status(ACMContextCreate(&acmContext), "Create ACM context");
244 set_app_password(acmContext);
246 __block CFDataRef credRefData = NULL;
247 ACMContextGetExternalForm(acmContext, ^(const void *externalForm, size_t dataBufferLength) {
248 credRefData = CFDataCreate(kCFAllocatorDefault, externalForm, dataBufferLength);
251 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
252 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
253 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
254 ok_status(SecItemAdd(item, NULL), "add local - acl with application password");
255 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
256 ok_status(SecItemDelete(item), "delete local - acl with application password");
258 CFReleaseSafe(aclRef);
260 LASetErrorCodeBlock(okBlock);
261 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
262 SecAccessControlSetRequirePassword(aclRef, true);
263 ok(aclRef, "Create SecAccessControlRef");
265 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
266 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
267 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
268 LASetErrorCodeBlock(authFailedBlock);
269 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
270 LASetErrorCodeBlock(okBlock);
271 set_app_password(acmContext);
272 ok_status(SecItemDelete(item), "delete local - acl with application password and user present");
273 CFReleaseSafe(aclRef);
275 LASetErrorCodeBlock(okBlock);
276 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
277 SecAccessControlSetRequirePassword(aclRef, true);
278 SecAccessConstraintRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR(kACMPolicyDeviceOwnerAuthentication), NULL);
279 SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, constraint, NULL);
280 CFRelease(constraint);
281 ok(aclRef, "Create SecAccessControlRef");
283 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
284 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
285 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
286 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
287 LASetErrorCodeBlock(authFailedBlock);
288 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
289 set_app_password(acmContext);
290 is_status(SecItemDelete(item), errSecAuthFailed, "delete local - acl with application password and user present");
293 CFReleaseSafe(aclRef);
295 // Update tests for item with application password:
297 // Prepare query for item without ACL.
298 item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
299 fillItem(item, (*item_num)++);
300 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
302 // Add test item without ACL and check that it can be found.
303 ok_status(SecItemAdd(item, NULL), "add local - no acl");
304 ok_status(SecItemCopyMatching(item, NULL), "find local - no acl");
306 // Update test item by adding ACL with application password flag.
307 CFMutableDictionaryRef update = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
308 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, kSecAccessControlApplicationPassword, NULL);
309 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
310 set_app_password(acmContext);
311 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
312 LASetErrorCodeBlock(okBlock);
313 ok_status(SecItemUpdate(item, update), "update local - acl with application password");
315 LASetErrorCodeBlock(authFailedBlock);
316 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
317 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
318 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password (without ACM context)");
319 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
320 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password (with ACM context)");
322 // Try to update item with ACL with application password with the same password (it will fail because ACM context is not allowd for update attributes).
323 CFDictionarySetValue(update, kSecUseCredentialReference, credRefData);
324 LASetErrorCodeBlock(okBlock);
325 is_status(SecItemUpdate(item, update), errSecNoSuchAttr, "update local - add application password");
327 CFDictionaryRemoveValue(update, kSecUseCredentialReference);
328 LASetErrorCodeBlock(okBlock);
329 ok_status(SecItemUpdate(item, update), "update local - updated with the same application password");
330 LASetErrorCodeBlock(authFailedBlock);
331 ok_status(SecItemCopyMatching(item, NULL), "find local - updated with the same application password"); // LA authFailedBlock is not called.
333 CFReleaseSafe(aclRef);
334 // Update item with ACL without application password.
335 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, 0, NULL);
336 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
338 LASetErrorCodeBlock(okBlock);
339 ok_status(SecItemUpdate(item, update), "update local - remove application password");
341 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
342 LASetErrorCodeBlock(authFailedBlock);
343 ok_status(SecItemCopyMatching(item, NULL), "find local - acl without application password"); // LA authFailedBlock is not called.
345 ok_status(SecItemDelete(item), "delete local - acl without application password");
349 CFReleaseSafe(aclRef);
351 ACMContextDelete(acmContext, true);
352 CFReleaseSafe(credRefData);
356 static void item_with_invalid_acl(uint32_t *item_num)
358 CFErrorRef (^errorParamBlock)(void) = ^ {
359 return createCFError(CFSTR(""), kLAErrorParameter);
362 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
363 fillItem(item, (*item_num)++);
365 SecAccessControlRef invalidAclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
366 ok(invalidAclRef, "Create invalid SecAccessControlRef");
367 ok(SecAccessControlSetProtection(invalidAclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL), "Set protection");
368 CFTypeRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR("invalidPolicy"), NULL);
369 ok(constraint, "Create invalid constraint");
370 ok(SecAccessControlAddConstraintForOperation(invalidAclRef, kAKSKeyOpDecrypt, constraint, NULL), "Add invalid constraint");
371 CFReleaseSafe(constraint);
373 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
374 CFDictionarySetValue(item, kSecAttrAccessControl, invalidAclRef);
376 LASetErrorCodeBlock(errorParamBlock);
377 is_status(SecItemAdd(item, NULL), errSecParam, "do not add local with invalid acl");
378 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after add failed");
380 CFReleaseSafe(invalidAclRef);
384 static void item_with_acl_caused_maxauth(uint32_t *item_num)
386 CFErrorRef (^okBlock)(void) = ^ {
387 return (CFErrorRef)NULL;
390 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
391 fillItem(item, (*item_num)++);
393 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
394 ok(aclRef, "Create SecAccessControlRef");
395 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
396 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanFalse, NULL));
398 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
399 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
401 __security_simulatecrash_enable(false);
403 LASetErrorCodeBlock(okBlock);
404 diag("this will cause an internal assert - on purpose");
405 is_status(SecItemAdd(item, NULL), errSecAuthFailed, "max auth attempts failed");
407 is(__security_simulatecrash_enable(true), 1, "Expecting simcrash max auth threshold passed");
409 CFReleaseSafe(aclRef);
413 static void item_with_akpu(uint32_t *item_num)
415 CFErrorRef (^okBlock)(void) = ^ {
416 return (CFErrorRef)NULL;
419 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassGenericPassword, NULL);
420 fillItem(item, (*item_num)++);
422 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
423 ok(aclRef, "Create SecAccessControlRef");
424 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
425 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanTrue, NULL));
426 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDecrypt, kCFBooleanTrue, NULL));
427 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, kCFBooleanTrue, NULL));
429 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
430 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
432 LASetErrorCodeBlock(okBlock);
433 ok_status(SecItemAdd(item, NULL), "add item with akpu");
434 ok_status(SecItemCopyMatching(item, NULL), "find item with akpu");
435 changePasscode(passcode1, NULL);
436 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
437 is_status(SecItemAdd(item, NULL), errSecNotAvailable, "cannot add item with akpu without passcode");
438 changePasscode(NULL, passcode2);
439 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
440 ok_status(SecItemAdd(item, NULL), "add item with akpu");
442 changePasscode(passcode2, passcode1);
443 CFReleaseSafe(aclRef);
448 static void item_with_skip_auth_ui(uint32_t *item_num)
450 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
451 fillItem(item, (*item_num)++);
453 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlDevicePasscode, NULL);
454 ok(aclRef, "Create SecAccessControlRef");
456 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
457 CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
458 is_status(SecItemAdd(item, NULL), errSecParam, "add local - invalid kSecUseAuthenticationUISkip");
459 is_status(SecItemDelete(item), errSecParam, "delete local - invalid kSecUseAuthenticationUISkip");
461 CFReleaseNull(aclRef);
465 int secd_81_item_acl(int argc, char *const *argv)
467 uint32_t item_num = 1;
468 #if LA_CONTEXT_IMPLEMENTED
469 secd_test_setup_temp_keychain(__FUNCTION__, ^{
470 keybag_state_t state;
471 int passcode_len=(int)strlen(passcode1);
473 ok(kIOReturnSuccess==aks_create_bag(passcode1, passcode_len, kAppleKeyStoreDeviceBag, &test_keybag), "create keybag");
474 ok(kIOReturnSuccess==aks_get_lock_state(test_keybag, &state), "get keybag state");
475 ok(!(state&keybag_state_locked), "keybag unlocked");
476 SecItemServerSetKeychainKeybag(test_keybag);
483 item_with_skip_auth_ui(&item_num);
484 item_with_invalid_acl(&item_num);
485 item_with_application_password(&item_num);
486 item_with_acl_caused_maxauth(&item_num);
487 item_with_akpu(&item_num);
490 item_with_skip_auth_ui(&item_num);
493 #if LA_CONTEXT_IMPLEMENTED
494 SecItemServerResetKeychainKeybag();