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);
196 CFDictionarySetValue(item, kSecValueData, (__bridge CFDataRef)[NSData dataWithBytes:"some data" length:9]);
199 #if LA_CONTEXT_IMPLEMENTED
201 static CFErrorRef createCFError(CFStringRef message, CFIndex code)
203 const void* keysPtr[1];
204 const void* messagesPtr[1];
206 keysPtr[0] = kCFErrorLocalizedDescriptionKey;
207 messagesPtr[0] = message;
208 return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, CFSTR(kLAErrorDomain), code, keysPtr, messagesPtr, 1);
212 static void set_app_password(ACMContextRef acmContext)
214 CFDataRef appPwdData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("Application password"), kCFStringEncodingUTF8, 0);
215 ACMCredentialRef acmCredential = NULL;
216 ok_status(ACMCredentialCreate(kACMCredentialTypePassphraseEntered, &acmCredential), "Create ACM credential");
217 ACMPassphrasePurpose purpose = kACMPassphrasePurposeGeneral;
218 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrase, CFDataGetBytePtr(appPwdData), CFDataGetLength(appPwdData)), "Set ACM credential property - passphrase");
219 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrasePurpose, &purpose, sizeof(purpose)), "Set ACM credential property - purpose");
220 ok_status(ACMContextAddCredentialWithScope(acmContext, acmCredential, kACMScopeContext), "aad ACM credential to ACM context");
221 ACMCredentialDelete(acmCredential);
222 CFReleaseSafe(appPwdData);
226 static void item_with_application_password(uint32_t *item_num)
229 CFErrorRef (^okBlock)(void) = ^ {
230 return (CFErrorRef)NULL;
233 CFErrorRef (^authFailedBlock)(void) = ^ {
234 return createCFError(CFSTR(""), kLAErrorAuthenticationFailed);
237 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
238 fillItem(item, (*item_num)++);
240 LASetErrorCodeBlock(okBlock);
241 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlApplicationPassword, NULL);
242 ok(aclRef, "Create SecAccessControlRef");
244 ACMContextRef acmContext = NULL;
245 ok_status(ACMContextCreate(&acmContext), "Create ACM context");
246 set_app_password(acmContext);
248 __block CFDataRef credRefData = NULL;
249 ACMContextGetExternalForm(acmContext, ^(const void *externalForm, size_t dataBufferLength) {
250 credRefData = CFDataCreate(kCFAllocatorDefault, externalForm, dataBufferLength);
253 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
254 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
255 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
256 ok_status(SecItemAdd(item, NULL), "add local - acl with application password");
257 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
258 ok_status(SecItemDelete(item), "delete local - acl with application password");
260 CFReleaseSafe(aclRef);
262 LASetErrorCodeBlock(okBlock);
263 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
264 SecAccessControlSetRequirePassword(aclRef, true);
265 ok(aclRef, "Create SecAccessControlRef");
267 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
268 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
269 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
270 LASetErrorCodeBlock(authFailedBlock);
271 CFDictionarySetValue(item, kSecReturnData, kCFBooleanTrue);
272 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
273 CFDictionaryRemoveValue(item, kSecReturnData);
274 LASetErrorCodeBlock(okBlock);
275 set_app_password(acmContext);
276 ok_status(SecItemDelete(item), "delete local - acl with application password and user present");
277 CFReleaseSafe(aclRef);
279 LASetErrorCodeBlock(okBlock);
280 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
281 SecAccessControlSetRequirePassword(aclRef, true);
282 SecAccessConstraintRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR(kACMPolicyDeviceOwnerAuthentication), NULL);
283 SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, constraint, NULL);
284 CFRelease(constraint);
285 ok(aclRef, "Create SecAccessControlRef");
287 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
288 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
289 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
290 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
291 LASetErrorCodeBlock(authFailedBlock);
292 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
293 set_app_password(acmContext);
294 is_status(SecItemDelete(item), errSecAuthFailed, "delete local - acl with application password and user present");
297 CFReleaseSafe(aclRef);
299 // Update tests for item with application password:
301 // Prepare query for item without ACL.
302 item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
303 fillItem(item, (*item_num)++);
304 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
306 // Add test item without ACL and check that it can be found.
307 ok_status(SecItemAdd(item, NULL), "add local - no acl");
308 ok_status(SecItemCopyMatching(item, NULL), "find local - no acl");
310 // Update test item by adding ACL with application password flag.
311 CFMutableDictionaryRef update = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
312 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, kSecAccessControlApplicationPassword, NULL);
313 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
314 set_app_password(acmContext);
315 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
316 LASetErrorCodeBlock(okBlock);
317 ok_status(SecItemUpdate(item, update), "update local - acl with application password");
319 LASetErrorCodeBlock(authFailedBlock);
320 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
321 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
322 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password (without ACM context)");
323 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
324 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password (with ACM context)");
326 // 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).
327 CFDictionarySetValue(update, kSecUseCredentialReference, credRefData);
328 LASetErrorCodeBlock(okBlock);
329 is_status(SecItemUpdate(item, update), errSecNoSuchAttr, "update local - add application password");
331 CFDictionaryRemoveValue(update, kSecUseCredentialReference);
332 LASetErrorCodeBlock(okBlock);
333 ok_status(SecItemUpdate(item, update), "update local - updated with the same application password");
334 LASetErrorCodeBlock(authFailedBlock);
335 ok_status(SecItemCopyMatching(item, NULL), "find local - updated with the same application password"); // LA authFailedBlock is not called.
337 CFReleaseSafe(aclRef);
338 // Update item with ACL without application password.
339 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, 0, NULL);
340 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
342 LASetErrorCodeBlock(okBlock);
343 ok_status(SecItemUpdate(item, update), "update local - remove application password");
345 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
346 LASetErrorCodeBlock(authFailedBlock);
347 ok_status(SecItemCopyMatching(item, NULL), "find local - acl without application password"); // LA authFailedBlock is not called.
349 ok_status(SecItemDelete(item), "delete local - acl without application password");
353 CFReleaseSafe(aclRef);
355 ACMContextDelete(acmContext, true);
356 CFReleaseSafe(credRefData);
360 static void item_with_invalid_acl(uint32_t *item_num)
362 CFErrorRef (^errorParamBlock)(void) = ^ {
363 return createCFError(CFSTR(""), kLAErrorParameter);
366 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
367 fillItem(item, (*item_num)++);
369 SecAccessControlRef invalidAclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
370 ok(invalidAclRef, "Create invalid SecAccessControlRef");
371 ok(SecAccessControlSetProtection(invalidAclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL), "Set protection");
372 CFTypeRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR("invalidPolicy"), NULL);
373 ok(constraint, "Create invalid constraint");
374 ok(SecAccessControlAddConstraintForOperation(invalidAclRef, kAKSKeyOpDecrypt, constraint, NULL), "Add invalid constraint");
375 CFReleaseSafe(constraint);
377 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
378 CFDictionarySetValue(item, kSecAttrAccessControl, invalidAclRef);
380 LASetErrorCodeBlock(errorParamBlock);
381 is_status(SecItemAdd(item, NULL), errSecParam, "do not add local with invalid acl");
382 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after add failed");
384 CFReleaseSafe(invalidAclRef);
388 static void item_with_acl_caused_maxauth(uint32_t *item_num)
390 CFErrorRef (^okBlock)(void) = ^ {
391 return (CFErrorRef)NULL;
394 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
395 fillItem(item, (*item_num)++);
397 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
398 ok(aclRef, "Create SecAccessControlRef");
399 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
400 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanFalse, NULL));
402 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
403 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
405 __security_simulatecrash_enable(false);
407 LASetErrorCodeBlock(okBlock);
408 diag("this will cause an internal assert - on purpose");
409 is_status(SecItemAdd(item, NULL), errSecAuthFailed, "max auth attempts failed");
411 is(__security_simulatecrash_enable(true), 1, "Expecting simcrash max auth threshold passed");
413 CFReleaseSafe(aclRef);
417 static void item_with_akpu(uint32_t *item_num)
419 CFErrorRef (^okBlock)(void) = ^ {
420 return (CFErrorRef)NULL;
423 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassGenericPassword, NULL);
424 fillItem(item, (*item_num)++);
426 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
427 ok(aclRef, "Create SecAccessControlRef");
428 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
429 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanTrue, NULL));
430 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDecrypt, kCFBooleanTrue, NULL));
431 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, kCFBooleanTrue, NULL));
433 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
434 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
436 LASetErrorCodeBlock(okBlock);
437 ok_status(SecItemAdd(item, NULL), "add item with akpu");
438 ok_status(SecItemCopyMatching(item, NULL), "find item with akpu");
439 changePasscode(passcode1, NULL);
440 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
441 is_status(SecItemAdd(item, NULL), errSecNotAvailable, "cannot add item with akpu without passcode");
442 changePasscode(NULL, passcode2);
443 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
444 ok_status(SecItemAdd(item, NULL), "add item with akpu");
446 changePasscode(passcode2, passcode1);
447 CFReleaseSafe(aclRef);
452 static void item_with_skip_auth_ui(uint32_t *item_num)
454 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
455 fillItem(item, (*item_num)++);
457 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlDevicePasscode, NULL);
458 ok(aclRef, "Create SecAccessControlRef");
460 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
461 CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
462 is_status(SecItemAdd(item, NULL), errSecParam, "add local - invalid kSecUseAuthenticationUISkip");
463 is_status(SecItemDelete(item), errSecParam, "delete local - invalid kSecUseAuthenticationUISkip");
465 CFReleaseNull(aclRef);
469 int secd_81_item_acl(int argc, char *const *argv)
471 uint32_t item_num = 1;
472 #if LA_CONTEXT_IMPLEMENTED
473 secd_test_setup_temp_keychain(__FUNCTION__, ^{
474 keybag_state_t state;
475 int passcode_len=(int)strlen(passcode1);
477 ok(kIOReturnSuccess==aks_create_bag(passcode1, passcode_len, kAppleKeyStoreDeviceBag, &test_keybag), "create keybag");
478 ok(kIOReturnSuccess==aks_get_lock_state(test_keybag, &state), "get keybag state");
479 ok(!(state&keybag_state_locked), "keybag unlocked");
480 SecItemServerSetKeychainKeybag(test_keybag);
487 item_with_skip_auth_ui(&item_num);
488 item_with_invalid_acl(&item_num);
489 item_with_application_password(&item_num);
490 item_with_acl_caused_maxauth(&item_num);
491 item_with_akpu(&item_num);
494 item_with_skip_auth_ui(&item_num);
497 #if LA_CONTEXT_IMPLEMENTED
498 SecItemServerResetKeychainKeybag();