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 "keychain/securityd/SecItemServer.h"
24 #include <LocalAuthentication/LAPublicDefines.h>
26 #include "secd_regressions.h"
30 #include <coreauthd_spi.h>
31 #include "SecdTestKeychainUtilities.h"
33 #include "OSX/utilities/SecAKSWrappers.h"
35 #if LA_CONTEXT_IMPLEMENTED
36 static keybag_handle_t test_keybag;
37 static const char *passcode1 = "passcode1";
38 static const char *passcode2 = "passcode2";
40 static bool changePasscode(const char *old_passcode, const char *new_passcode)
42 size_t old_passcode_len = 0;
43 size_t new_passcode_len = 0;
46 old_passcode_len = strlen(old_passcode);
49 new_passcode_len = strlen(new_passcode);
51 kern_return_t status = aks_change_secret(test_keybag, old_passcode, (int)old_passcode_len, new_passcode, (int)new_passcode_len, generation_noop, NULL);
66 kAccessabilityItemAttr,
70 extern void LASetErrorCodeBlock(CFErrorRef (^newCreateErrorBlock)(void));
72 static void WithEachString(void(^each)(CFStringRef attr, enum ItemAttrType atype), ...) {
76 while((attr = va_arg(ap, CFStringRef)) != NULL) {
77 enum ItemAttrType atype = va_arg(ap, enum ItemAttrType);
83 static void ItemForEachPKAttr(CFMutableDictionaryRef item, void(^each)(CFStringRef attr, enum ItemAttrType atype)) {
84 CFStringRef iclass = CFDictionaryGetValue(item, kSecClass);
87 } else if (CFEqual(iclass, kSecClassGenericPassword)) {
89 kSecAttrAccessible, kAccessabilityItemAttr,
90 kSecAttrAccessGroup, kAccessGroupItemAttr,
91 kSecAttrAccount, kStringItemAttr,
92 kSecAttrService, kStringItemAttr,
93 kSecAttrSynchronizable, kBoolItemAttr,
95 } else if (CFEqual(iclass, kSecClassInternetPassword)) {
97 kSecAttrAccessible, kAccessabilityItemAttr,
98 kSecAttrAccessGroup, kAccessGroupItemAttr,
99 kSecAttrAccount, kStringItemAttr,
100 kSecAttrSecurityDomain, kStringItemAttr,
101 kSecAttrServer, kStringItemAttr,
102 kSecAttrProtocol, kNumberItemAttr,
103 kSecAttrAuthenticationType, kNumberItemAttr,
104 kSecAttrPort, kNumberItemAttr,
105 kSecAttrPath, kStringItemAttr,
106 kSecAttrSynchronizable, kBoolItemAttr,
108 } else if (CFEqual(iclass, kSecClassCertificate)) {
110 kSecAttrAccessible, kAccessabilityItemAttr,
111 kSecAttrAccessGroup, kAccessGroupItemAttr,
112 kSecAttrCertificateType, kNumberItemAttr,
113 kSecAttrIssuer, kDataItemAttr,
114 kSecAttrSerialNumber, kDataItemAttr,
115 kSecAttrSynchronizable, kBoolItemAttr,
117 } else if (CFEqual(iclass, kSecClassKey)) {
119 kSecAttrAccessible, kAccessabilityItemAttr,
120 kSecAttrAccessGroup, kAccessGroupItemAttr,
121 kSecAttrKeyClass, kStringItemAttr, // kNumberItemAttr on replies
122 kSecAttrApplicationLabel, kDataItemAttr,
123 kSecAttrApplicationTag, kDataItemAttr,
124 kSecAttrKeyType, kNumberItemAttr,
125 kSecAttrKeySizeInBits, kNumberItemAttr,
126 kSecAttrEffectiveKeySize, kNumberItemAttr,
127 kSecAttrStartDate, kDateItemAttr,
128 kSecAttrEndDate, kDateItemAttr,
129 kSecAttrSynchronizable, kBoolItemAttr,
131 } else if (CFEqual(iclass, kSecClassIdentity)) {
133 kSecAttrAccessible, kAccessabilityItemAttr,
134 kSecAttrAccessGroup, kAccessGroupItemAttr,
135 kSecAttrCertificateType, kNumberItemAttr,
136 kSecAttrIssuer, kDataItemAttr,
137 kSecAttrSerialNumber, kDataItemAttr,
138 kSecAttrSynchronizable, kBoolItemAttr,
139 kSecAttrKeyClass, kStringItemAttr, // kNumberItemAttr on replies
140 kSecAttrApplicationLabel, kDataItemAttr,
141 kSecAttrApplicationTag, kDataItemAttr,
142 kSecAttrKeyType, kNumberItemAttr,
143 kSecAttrKeySizeInBits, kNumberItemAttr,
144 kSecAttrEffectiveKeySize, kNumberItemAttr,
145 kSecAttrStartDate, kDateItemAttr,
146 kSecAttrEndDate, kDateItemAttr,
147 kSecAttrSynchronizable, kBoolItemAttr,
152 static void fillItem(CFMutableDictionaryRef item, uint32_t num)
154 ItemForEachPKAttr(item, ^(CFStringRef attr, enum ItemAttrType atype) {
155 CFTypeRef value = NULL;
158 value = (num % 2 == 0 ? kCFBooleanTrue : kCFBooleanFalse);
161 case kNumberItemAttr:
162 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
164 case kStringItemAttr:
166 value = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("acl-stress-string-%d"), num);
171 int len = snprintf(buf, sizeof(buf), "acl-stress-data-%d", num);
172 value = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)buf, len);
176 value = NULL; // Don't mess with dates on create.
178 case kAccessabilityItemAttr:
180 case kAccessGroupItemAttr:
182 CFStringRef accessGroups[] = {
184 CFSTR("com.apple.security.sos"), // Secd internally uses this
186 value = accessGroups[num % array_size(accessGroups)];
191 CFDictionarySetValue(item, attr, value);
192 CFReleaseSafe(value);
195 CFDictionarySetValue(item, kSecValueData, (__bridge CFDataRef)[NSData dataWithBytes:"some data" length:9]);
198 #if LA_CONTEXT_IMPLEMENTED
200 static CFErrorRef createCFError(CFStringRef message, CFIndex code)
202 const void* keysPtr[1];
203 const void* messagesPtr[1];
205 keysPtr[0] = kCFErrorLocalizedDescriptionKey;
206 messagesPtr[0] = message;
207 return CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, CFSTR(kLAErrorDomain), code, keysPtr, messagesPtr, 1);
211 static void set_app_password(ACMContextRef acmContext)
213 CFDataRef appPwdData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("Application password"), kCFStringEncodingUTF8, 0);
214 ACMCredentialRef acmCredential = NULL;
215 ok_status(ACMCredentialCreate(kACMCredentialTypePassphraseEntered, &acmCredential), "Create ACM credential");
216 ACMPassphrasePurpose purpose = kACMPassphrasePurposeGeneral;
217 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrase, CFDataGetBytePtr(appPwdData), CFDataGetLength(appPwdData)), "Set ACM credential property - passphrase");
218 ok_status(ACMCredentialSetProperty(acmCredential, kACMCredentialPropertyPassphrasePurpose, &purpose, sizeof(purpose)), "Set ACM credential property - purpose");
219 ok_status(ACMContextAddCredentialWithScope(acmContext, acmCredential, kACMScopeContext), "aad ACM credential to ACM context");
220 ACMCredentialDelete(acmCredential);
221 CFReleaseSafe(appPwdData);
225 static void item_with_application_password(uint32_t *item_num)
228 CFErrorRef (^okBlock)(void) = ^ {
229 return (CFErrorRef)NULL;
232 CFErrorRef (^authFailedBlock)(void) = ^ {
233 return createCFError(CFSTR(""), kLAErrorAuthenticationFailed);
236 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
237 fillItem(item, (*item_num)++);
239 LASetErrorCodeBlock(okBlock);
240 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlApplicationPassword, NULL);
241 ok(aclRef, "Create SecAccessControlRef");
243 ACMContextRef acmContext = NULL;
244 ok_status(ACMContextCreate(&acmContext), "Create ACM context");
245 set_app_password(acmContext);
247 __block CFDataRef credRefData = NULL;
248 ACMContextGetExternalForm(acmContext, ^(const void *externalForm, size_t dataBufferLength) {
249 credRefData = CFDataCreate(kCFAllocatorDefault, externalForm, dataBufferLength);
252 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
253 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
254 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
255 ok_status(SecItemAdd(item, NULL), "add local - acl with application password");
256 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
257 ok_status(SecItemDelete(item), "delete local - acl with application password");
259 CFReleaseSafe(aclRef);
261 LASetErrorCodeBlock(okBlock);
262 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
263 SecAccessControlSetRequirePassword(aclRef, true);
264 ok(aclRef, "Create SecAccessControlRef");
266 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
267 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
268 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
269 LASetErrorCodeBlock(authFailedBlock);
270 CFDictionarySetValue(item, kSecReturnData, kCFBooleanTrue);
271 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
272 CFDictionaryRemoveValue(item, kSecReturnData);
273 LASetErrorCodeBlock(okBlock);
274 set_app_password(acmContext);
275 ok_status(SecItemDelete(item), "delete local - acl with application password and user present");
276 CFReleaseSafe(aclRef);
278 LASetErrorCodeBlock(okBlock);
279 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlUserPresence, NULL);
280 SecAccessControlSetRequirePassword(aclRef, true);
281 SecAccessConstraintRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR(kACMPolicyDeviceOwnerAuthentication), NULL);
282 SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, constraint, NULL);
283 CFRelease(constraint);
284 ok(aclRef, "Create SecAccessControlRef");
286 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
287 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
288 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
289 ok_status(SecItemAdd(item, NULL), "add local - acl with application password and user present");
290 LASetErrorCodeBlock(authFailedBlock);
291 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password and user present");
292 set_app_password(acmContext);
293 is_status(SecItemDelete(item), errSecAuthFailed, "delete local - acl with application password and user present");
296 CFReleaseSafe(aclRef);
298 // Update tests for item with application password:
300 // Prepare query for item without ACL.
301 item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
302 fillItem(item, (*item_num)++);
303 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
305 // Add test item without ACL and check that it can be found.
306 ok_status(SecItemAdd(item, NULL), "add local - no acl");
307 ok_status(SecItemCopyMatching(item, NULL), "find local - no acl");
309 // Update test item by adding ACL with application password flag.
310 CFMutableDictionaryRef update = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
311 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, kSecAccessControlApplicationPassword, NULL);
312 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
313 set_app_password(acmContext);
314 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
315 LASetErrorCodeBlock(okBlock);
316 ok_status(SecItemUpdate(item, update), "update local - acl with application password");
318 LASetErrorCodeBlock(authFailedBlock);
319 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password");
320 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
321 is_status(SecItemCopyMatching(item, NULL), errSecAuthFailed, "find local - acl with application password (without ACM context)");
322 CFDictionarySetValue(item, kSecUseCredentialReference, credRefData);
323 ok_status(SecItemCopyMatching(item, NULL), "find local - acl with application password (with ACM context)");
325 // 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).
326 CFDictionarySetValue(update, kSecUseCredentialReference, credRefData);
327 LASetErrorCodeBlock(okBlock);
328 is_status(SecItemUpdate(item, update), errSecNoSuchAttr, "update local - add application password");
330 CFDictionaryRemoveValue(update, kSecUseCredentialReference);
331 LASetErrorCodeBlock(okBlock);
332 ok_status(SecItemUpdate(item, update), "update local - updated with the same application password");
333 LASetErrorCodeBlock(authFailedBlock);
334 ok_status(SecItemCopyMatching(item, NULL), "find local - updated with the same application password"); // LA authFailedBlock is not called.
336 CFReleaseSafe(aclRef);
337 // Update item with ACL without application password.
338 aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAlwaysPrivate, 0, NULL);
339 CFDictionarySetValue(update, kSecAttrAccessControl, aclRef);
341 LASetErrorCodeBlock(okBlock);
342 ok_status(SecItemUpdate(item, update), "update local - remove application password");
344 CFDictionaryRemoveValue(item, kSecUseCredentialReference);
345 LASetErrorCodeBlock(authFailedBlock);
346 ok_status(SecItemCopyMatching(item, NULL), "find local - acl without application password"); // LA authFailedBlock is not called.
348 ok_status(SecItemDelete(item), "delete local - acl without application password");
352 CFReleaseSafe(aclRef);
354 ACMContextDelete(acmContext, true);
355 CFReleaseSafe(credRefData);
359 static void item_with_invalid_acl(uint32_t *item_num)
361 CFErrorRef (^errorParamBlock)(void) = ^ {
362 return createCFError(CFSTR(""), kLAErrorParameter);
365 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
366 fillItem(item, (*item_num)++);
368 SecAccessControlRef invalidAclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
369 ok(invalidAclRef, "Create invalid SecAccessControlRef");
370 ok(SecAccessControlSetProtection(invalidAclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL), "Set protection");
371 CFTypeRef constraint = SecAccessConstraintCreatePolicy(kCFAllocatorDefault, CFSTR("invalidPolicy"), NULL);
372 ok(constraint, "Create invalid constraint");
373 ok(SecAccessControlAddConstraintForOperation(invalidAclRef, kAKSKeyOpDecrypt, constraint, NULL), "Add invalid constraint");
374 CFReleaseSafe(constraint);
376 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
377 CFDictionarySetValue(item, kSecAttrAccessControl, invalidAclRef);
379 LASetErrorCodeBlock(errorParamBlock);
380 is_status(SecItemAdd(item, NULL), errSecParam, "do not add local with invalid acl");
381 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find after add failed");
383 CFReleaseSafe(invalidAclRef);
387 static void item_with_acl_caused_maxauth(uint32_t *item_num)
389 CFErrorRef (^okBlock)(void) = ^ {
390 return (CFErrorRef)NULL;
393 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
394 fillItem(item, (*item_num)++);
396 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
397 ok(aclRef, "Create SecAccessControlRef");
398 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
399 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanFalse, NULL));
401 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
402 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
404 __security_simulatecrash_enable(false);
406 LASetErrorCodeBlock(okBlock);
407 diag("this will cause an internal assert - on purpose");
408 is_status(SecItemAdd(item, NULL), errSecAuthFailed, "max auth attempts failed");
410 is(__security_simulatecrash_enable(true), 1, "Expecting simcrash max auth threshold passed");
412 CFReleaseSafe(aclRef);
416 static void item_with_akpu(uint32_t *item_num)
418 CFErrorRef (^okBlock)(void) = ^ {
419 return (CFErrorRef)NULL;
422 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassGenericPassword, NULL);
423 fillItem(item, (*item_num)++);
425 SecAccessControlRef aclRef = SecAccessControlCreate(kCFAllocatorDefault, NULL);
426 ok(aclRef, "Create SecAccessControlRef");
427 ok(SecAccessControlSetProtection(aclRef, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, NULL));
428 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpEncrpyt, kCFBooleanTrue, NULL));
429 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDecrypt, kCFBooleanTrue, NULL));
430 ok(SecAccessControlAddConstraintForOperation(aclRef, kAKSKeyOpDelete, kCFBooleanTrue, NULL));
432 CFDictionarySetValue(item, kSecAttrSynchronizable, kCFBooleanFalse);
433 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
435 LASetErrorCodeBlock(okBlock);
436 ok_status(SecItemAdd(item, NULL), "add item with akpu");
437 ok_status(SecItemCopyMatching(item, NULL), "find item with akpu");
438 changePasscode(passcode1, NULL);
439 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
440 is_status(SecItemAdd(item, NULL), errSecNotAvailable, "cannot add item with akpu without passcode");
441 changePasscode(NULL, passcode2);
442 is_status(SecItemCopyMatching(item, NULL), errSecItemNotFound, "do not find item with akpu");
443 ok_status(SecItemAdd(item, NULL), "add item with akpu");
445 changePasscode(passcode2, passcode1);
446 CFReleaseSafe(aclRef);
451 static void item_with_skip_auth_ui(uint32_t *item_num)
453 CFMutableDictionaryRef item = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kSecClass, kSecClassInternetPassword, NULL);
454 fillItem(item, (*item_num)++);
456 SecAccessControlRef aclRef = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, kSecAccessControlDevicePasscode, NULL);
457 ok(aclRef, "Create SecAccessControlRef");
459 CFDictionarySetValue(item, kSecAttrAccessControl, aclRef);
460 CFDictionarySetValue(item, kSecUseAuthenticationUI, kSecUseAuthenticationUISkip);
461 is_status(SecItemAdd(item, NULL), errSecParam, "add local - invalid kSecUseAuthenticationUISkip");
462 is_status(SecItemDelete(item), errSecParam, "delete local - invalid kSecUseAuthenticationUISkip");
464 CFReleaseNull(aclRef);
468 int secd_81_item_acl(int argc, char *const *argv)
470 uint32_t item_num = 1;
471 #if LA_CONTEXT_IMPLEMENTED
472 secd_test_setup_temp_keychain(__FUNCTION__, ^{
473 keybag_state_t state;
474 int passcode_len=(int)strlen(passcode1);
476 ok(kAKSReturnSuccess==aks_create_bag(passcode1, passcode_len, kAppleKeyStoreDeviceBag, &test_keybag), "create keybag");
477 ok(kAKSReturnSuccess==aks_get_lock_state(test_keybag, &state), "get keybag state");
478 ok(!(state&keybag_state_locked), "keybag unlocked");
479 SecItemServerSetKeychainKeybag(test_keybag);
486 item_with_skip_auth_ui(&item_num);
487 item_with_invalid_acl(&item_num);
488 item_with_application_password(&item_num);
489 item_with_acl_caused_maxauth(&item_num);
490 item_with_akpu(&item_num);
493 item_with_skip_auth_ui(&item_num);
496 #if LA_CONTEXT_IMPLEMENTED
497 SecItemServerResetKeychainKeybag();