2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecDbKeychainItem.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecDbKeychainItem.h>
32 #include <securityd/SecItemSchema.h>
33 #include <CommonCrypto/CommonCryptor.h>
34 #include <CommonCrypto/CommonCryptorSPI.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecItemInternal.h>
38 #include <Security/SecRandom.h>
39 #include <Security/SecAccessControl.h>
40 #include <Security/SecAccessControlPriv.h>
41 #include <utilities/der_plist.h>
44 #include <LocalAuthentication/LAPublicDefines.h>
45 #include <LocalAuthentication/LAPrivateDefines.h>
46 #include <coreauthd_spi.h>
47 #include <libaks_acl_cf_keys.h>
48 #include <securityd/spi.h>
49 #endif /* USE_KEYSTORE */
51 pthread_key_t CURRENT_CONNECTION_KEY
;
53 // From SecItemServer, should be a acl-check block
54 bool itemInAccessGroup(CFDictionaryRef item
, CFArrayRef accessGroups
);
56 static keyclass_t
kc_parse_keyclass(CFTypeRef value
, CFErrorRef
*error
);
57 static CFTypeRef
kc_encode_keyclass(keyclass_t keyclass
);
58 static CFDataRef
kc_copy_protection_data(SecAccessControlRef access_control
);
59 static CFTypeRef
kc_copy_protection_from_data(CFDataRef data
);
60 static CFMutableDictionaryRef
s3dl_item_v2_decode(CFDataRef plain
, CFErrorRef
*error
);
61 static CFMutableDictionaryRef
s3dl_item_v3_decode(CFDataRef plain
, CFErrorRef
*error
);
63 static CFDataRef
kc_copy_constraints_data(SecAccessControlRef access_control
, CFDictionaryRef auth_attributes
);
64 static void kc_dict_from_auth_data(const SecDbClass
*class, const uint8_t *der
, const uint8_t *der_end
, CFMutableDictionaryRef
*authenticated_attributes
, CFMutableDictionaryRef
*acl
);
65 static CFDataRef
kc_copy_access_groups_data(CFArrayRef access_groups
, CFErrorRef
*error
);
68 /* Given plainText create and return a CFDataRef containing:
69 BULK_KEY = RandomKey()
70 version || keyclass|ACL || KeyStore_WRAP(keyclass, BULK_KEY) ||
71 AES(BULK_KEY, NULL_IV, plainText || padding)
73 bool ks_encrypt_data(keybag_handle_t keybag
, SecAccessControlRef access_control
, CFTypeRef
*cred_handle
,
74 CFDictionaryRef attributes
, CFDictionaryRef authenticated_attributes
, CFDataRef
*pBlob
, CFErrorRef
*error
) {
75 CFMutableDataRef blob
= NULL
;
76 CFDataRef ac_data
= NULL
;
80 /* Precalculate output blob length. */
81 const uint32_t bulkKeySize
= 32; /* Use 256 bit AES key for bulkKey. */
82 const uint32_t maxKeyWrapOverHead
= 8 + 32;
83 uint8_t bulkKey
[bulkKeySize
];
84 CFMutableDataRef bulkKeyWrapped
= CFDataCreateMutable(NULL
, 0);
85 CFDataSetLength(bulkKeyWrapped
, bulkKeySize
+ maxKeyWrapOverHead
);
86 uint32_t key_wrapped_size
;
89 CFDataRef access_control_data
= NULL
;
90 CFDataRef constraint_data
= NULL
;
91 CFDataRef acm_context
= NULL
;
94 /* If access_control specifies only protection and no ACL, use legacy blob format version 3,
95 which has better support for sync/backup. Otherwise, force new format v4. */
96 const uint32_t version
= SecAccessControlGetConstraints(access_control
) ? 4 : 3;
97 CFDataRef plainText
= NULL
;
99 CFMutableDictionaryRef attributes_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
100 if (authenticated_attributes
) {
101 CFDictionaryForEach(authenticated_attributes
, ^(const void *key
, const void *value
) {
102 CFDictionaryAddValue(attributes_dict
, key
, value
);
106 if (attributes_dict
) {
107 // Drop the accc attribute for non v4 items during encode.
108 CFDictionaryRemoveValue(attributes_dict
, kSecAttrAccessControl
);
109 plainText
= kc_plist_copy_der(attributes_dict
, error
);
110 CFRelease(attributes_dict
);
115 plainText
= kc_plist_copy_der(attributes
, error
);
118 CFMutableDictionaryRef attributes_dict
= CFDictionaryCreateMutableCopy(NULL
, 0, attributes
);
119 if (authenticated_attributes
) {
120 CFDictionaryForEach(authenticated_attributes
, ^(const void *key
, const void *value
) {
121 CFDictionaryAddValue(attributes_dict
, key
, value
);
125 if (attributes_dict
) {
126 plainText
= kc_plist_copy_der(attributes_dict
, error
);
127 CFRelease(attributes_dict
);
132 if (!plainText
|| CFGetTypeID(plainText
) != CFDataGetTypeID()
133 || access_control
== 0) {
134 ok
= SecError(errSecParam
, error
, CFSTR("ks_encrypt_data: invalid plain text"));
138 size_t ptLen
= CFDataGetLength(plainText
);
139 size_t ctLen
= ptLen
;
141 keyclass_t actual_class
;
143 if (SecRandomCopyBytes(kSecRandomDefault
, bulkKeySize
, bulkKey
)) {
144 ok
= SecError(errSecAllocate
, error
, CFSTR("ks_encrypt_data: SecRandomCopyBytes failed"));
148 /* Extract keyclass from access control. */
149 keyclass_t keyclass
= kc_parse_keyclass(SecAccessControlGetProtection(access_control
), error
);
155 if (*cred_handle
== NULL
|| isData(*cred_handle
)) {
156 CFTypeRef auth_ref
= VRCreateNewReferenceWithACMContext(*cred_handle
, error
);
161 CFReleaseSafe(*cred_handle
);
162 *cred_handle
= auth_ref
;
165 access_control_data
= SecAccessControlCopyData(access_control
);
166 CFErrorRef authError
= NULL
;
167 ok
= VRValidateACL(*cred_handle
, access_control_data
, &authError
);
169 ok
= SecCFCreateError(errSecParam
, kSecErrorDomain
, CFSTR("Invalid ACL"), authError
, error
);
170 CFReleaseSafe(authError
);
174 constraint_data
= kc_copy_constraints_data(access_control
, authenticated_attributes
);
175 require_quiet(ok
= ks_crypt_acl(kSecKsWrap
, keybag
,
176 keyclass
, bulkKeySize
, bulkKey
, bulkKeyWrapped
, constraint_data
, NULL
, NULL
, error
), out
);
180 /* Encrypt bulkKey. */
181 require_quiet(ok
= ks_crypt(kSecKsWrap
, keybag
,
182 keyclass
, bulkKeySize
, bulkKey
,
183 &actual_class
, bulkKeyWrapped
,
189 key_wrapped_size
= (uint32_t)CFDataGetLength(bulkKeyWrapped
);
191 size_t blobLen
= sizeof(version
);
192 uint32_t prot_length
;
195 blobLen
+= sizeof(actual_class
);
197 require_quiet(ac_data
= kc_copy_protection_data(access_control
), out
);
198 prot_length
= (uint32_t)CFDataGetLength(ac_data
);
199 blobLen
+= sizeof(prot_length
) + prot_length
;
202 blobLen
+= sizeof(key_wrapped_size
) + key_wrapped_size
+ ctLen
+ tagLen
;
203 require_quiet(blob
= CFDataCreateMutable(NULL
, blobLen
), out
);
204 CFDataSetLength(blob
, blobLen
);
205 cursor
= CFDataGetMutableBytePtr(blob
);
207 *((uint32_t *)cursor
) = version
;
208 cursor
+= sizeof(version
);
210 //secerror("class: %d actual class: %d", keyclass, actual_class);
212 *((keyclass_t
*)cursor
) = actual_class
;
213 cursor
+= sizeof(keyclass
);
215 *((uint32_t *)cursor
) = prot_length
;
216 cursor
+= sizeof(prot_length
);
218 CFDataGetBytes(ac_data
, CFRangeMake(0, prot_length
), cursor
);
219 cursor
+= prot_length
;
222 *((uint32_t *)cursor
) = key_wrapped_size
;
223 cursor
+= sizeof(key_wrapped_size
);
225 memcpy(cursor
, CFDataGetBytePtr(bulkKeyWrapped
), key_wrapped_size
);
226 cursor
+= key_wrapped_size
;
228 /* Encrypt the plainText with the bulkKey. */
229 CCCryptorStatus ccerr
= CCCryptorGCM(kCCEncrypt
, kCCAlgorithmAES128
,
230 bulkKey
, bulkKeySize
,
232 NULL
, 0, /* auth data */
233 CFDataGetBytePtr(plainText
), ptLen
,
235 cursor
+ ctLen
, &tagLen
);
237 ok
= SecError(errSecInternal
, error
, CFSTR("ks_encrypt_data: CCCryptorGCM failed: %d"), ccerr
);
241 ok
= SecError(errSecInternal
, error
, CFSTR("ks_encrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen
);
246 memset(bulkKey
, 0, sizeof(bulkKey
));
247 CFReleaseSafe(ac_data
);
248 CFReleaseSafe(bulkKeyWrapped
);
249 CFReleaseSafe(plainText
);
257 CFReleaseSafe(access_control_data
);
258 CFReleaseSafe(constraint_data
);
259 CFReleaseSafe(acm_context
);
264 /* Given cipherText containing:
265 version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) ||
266 AES(BULK_KEY, NULL_IV, plainText || padding)
267 return the plainText. */
268 bool ks_decrypt_data(keybag_handle_t keybag
, enum SecKsCryptoOp cryptoOp
, SecAccessControlRef
*paccess_control
, CFTypeRef
*cred_handle
,
269 CFDataRef blob
, const SecDbClass
*db_class
, CFArrayRef caller_access_groups
,
270 CFMutableDictionaryRef
*attributes_p
, uint32_t *version_p
, CFErrorRef
*error
) {
271 const uint32_t v0KeyWrapOverHead
= 8;
272 CFMutableDataRef bulkKey
= CFDataCreateMutable(0, 32); /* Use 256 bit AES key for bulkKey. */
273 CFDataSetLength(bulkKey
, 32); /* Use 256 bit AES key for bulkKey. */
275 SecAccessControlRef access_control
= NULL
;
277 CFMutableDataRef plainText
= NULL
;
278 CFMutableDictionaryRef attributes
= NULL
;
279 uint32_t version
= 0;
280 CFDataRef access_control_data
= NULL
;
283 CFDataRef acm_context
= NULL
;
284 CFMutableDictionaryRef authenticated_attributes
= NULL
;
285 CFDataRef caller_access_groups_data
= NULL
;
290 check((keybag
>= 0) || (keybag
== session_keybag_handle
));
295 ok
= SecError(errSecParam
, error
, CFSTR("ks_decrypt_data: invalid blob"));
299 size_t blobLen
= CFDataGetLength(blob
);
300 const uint8_t *cursor
= CFDataGetBytePtr(blob
);
302 uint32_t wrapped_key_size
;
304 /* Check for underflow, ensuring we have at least one full AES block left. */
305 if (blobLen
< sizeof(version
) + sizeof(keyclass
) +
306 CFDataGetLength(bulkKey
) + v0KeyWrapOverHead
+ 16) {
307 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: Check for underflow"));
311 version
= *((uint32_t *)cursor
);
312 cursor
+= sizeof(version
);
314 size_t minimum_blob_len
= sizeof(version
) + 16;
315 size_t ctLen
= blobLen
- sizeof(version
);
318 /* Deserialize SecAccessControl object from the blob. */
319 uint32_t prot_length
= *((uint32_t *)cursor
);
320 cursor
+= sizeof(prot_length
);
322 CFDataRef protection_data
= CFDataCreate(kCFAllocatorDefault
, cursor
, prot_length
);
323 CFTypeRef protection
= kc_copy_protection_from_data(protection_data
);
324 CFRelease(protection_data
);
326 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid ACL"));
330 access_control
= SecAccessControlCreate(NULL
, NULL
);
331 require_quiet(access_control
, out
);
332 ok
= SecAccessControlSetProtection(access_control
, protection
, NULL
);
333 CFRelease(protection
);
335 SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid ACL"));
340 cursor
+= prot_length
;
342 minimum_blob_len
+= sizeof(prot_length
) + prot_length
;
343 ctLen
-= sizeof(prot_length
) + prot_length
;
345 /* Get numeric value of keyclass from the access_control. */
346 keyclass
= kc_parse_keyclass(SecAccessControlGetProtection(access_control
), error
);
348 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid ACL"));
352 keyclass
= *((keyclass_t
*)cursor
);
353 //secerror("class: %d keyclass: %d", keyclass, keyclass & key_class_last);
355 CFTypeRef protection
= kc_encode_keyclass(keyclass
& key_class_last
); // mask out generation
357 CFTypeRef protection
= kc_encode_keyclass(keyclass
);
360 access_control
= SecAccessControlCreateWithFlags(kCFAllocatorDefault
, protection
, 0, error
);
362 if (!access_control
) {
363 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid keyclass detected"));
366 cursor
+= sizeof(keyclass
);
368 minimum_blob_len
+= sizeof(keyclass
);
369 ctLen
-= sizeof(keyclass
);
375 wrapped_key_size
= (uint32_t)CFDataGetLength(bulkKey
) + v0KeyWrapOverHead
;
380 /* v2 and v3 have the same crypto, just different dictionary encodings. */
381 /* Difference between v3 and v4 is already handled above, so treat v3 as v4. */
384 minimum_blob_len
-= 16; // Remove PKCS7 padding block requirement
385 ctLen
-= tagLen
; // Remove tagLen from ctLen
388 wrapped_key_size
= *((uint32_t *)cursor
);
389 cursor
+= sizeof(wrapped_key_size
);
390 minimum_blob_len
+= sizeof(wrapped_key_size
);
391 ctLen
-= sizeof(wrapped_key_size
);
394 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid version %d"), version
);
398 /* Validate key wrap length against total length */
399 require(blobLen
- minimum_blob_len
- tagLen
>= wrapped_key_size
, out
);
400 ctLen
-= wrapped_key_size
;
401 if (version
< 2 && (ctLen
& 0xF) != 0) {
402 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid version"));
408 /* Verify before we try to unwrap the key. */
409 if (*cred_handle
== NULL
|| isData(*cred_handle
)) {
410 CFTypeRef auth_ref
= VRCreateNewReferenceWithACMContext(*cred_handle
, error
);
415 CFReleaseSafe(*cred_handle
);
416 *cred_handle
= auth_ref
;
419 CFMutableDictionaryRef acl
= NULL
;
420 kc_dict_from_auth_data(db_class
, cursor
, cursor
+ wrapped_key_size
, &authenticated_attributes
, &acl
);
421 SecAccessControlSetConstraints(access_control
, acl
);
423 access_control_data
= SecAccessControlCopyData(access_control
);
425 static CFDictionaryRef hints
= NULL
;
426 static dispatch_once_t onceToken
;
427 dispatch_once(&onceToken
, ^{
428 CFNumberRef noninteractiveKey
= CFNumberCreateWithCFIndex(kCFAllocatorDefault
, kLAOptionNotInteractive
);
429 hints
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, noninteractiveKey
, kCFBooleanTrue
, NULL
);
430 CFRelease(noninteractiveKey
);
433 CFErrorRef authError
= NULL
;
434 ok
= VREvaluateACL(*cred_handle
, access_control_data
, (cryptoOp
== kSecKsDelete
)?kAKSKeyOpDelete
:kAKSKeyOpDecrypt
, hints
, &authError
);
436 if (CFEqual(CFErrorGetDomain(authError
), CFSTR(kLAErrorDomain
)) &&
437 CFErrorGetCode(authError
) == kLAErrorNotInteractive
) {
438 /* UI is needed, but this is not really an error, just leave with no output data. */
441 ok
= SecCFCreateError(errSecAuthFailed
, kSecErrorDomain
, CFSTR("CoreAuthentication failed"), authError
, error
);
443 CFReleaseSafe(authError
);
447 acm_context
= VRCopyACMContext(*cred_handle
, error
);
449 if (caller_access_groups
) {
450 caller_access_groups_data
= kc_copy_access_groups_data(caller_access_groups
, error
);
451 require_quiet(ok
= (caller_access_groups_data
!= NULL
), out
);
453 require_quiet(ok
= ks_crypt_acl(cryptoOp
, keybag
,
454 keyclass
, wrapped_key_size
, cursor
, bulkKey
, NULL
, acm_context
, caller_access_groups_data
, error
), out
);
455 if (cryptoOp
== kSecKsDelete
) {
456 attributes
= CFRetainSafe(authenticated_attributes
);
462 /* Now unwrap the bulk key using a key in the keybag. */
463 require_quiet(ok
= ks_crypt(kSecKsUnwrap
, keybag
,
464 keyclass
, wrapped_key_size
, cursor
, NULL
, bulkKey
, error
), out
);
469 cursor
+= wrapped_key_size
;
471 plainText
= CFDataCreateMutable(NULL
, ctLen
);
473 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: failed to allocate data for plain text"));
476 CFDataSetLength(plainText
, ctLen
);
478 /* Decrypt the cipherText with the bulkKey. */
479 CCCryptorStatus ccerr
;
482 ccerr
= CCCryptorGCM(kCCDecrypt
, kCCAlgorithmAES128
,
483 CFDataGetBytePtr(bulkKey
), CFDataGetLength(bulkKey
),
485 NULL
, 0, /* auth data */
487 CFDataGetMutableBytePtr(plainText
),
490 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
491 identifies uuid unwrap failures? */
492 /* errSecInteractionNotAllowed; */
493 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM failed: %d"), ccerr
);
497 ok
= SecError(errSecInternal
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen
);
501 if (memcmp(tag
, cursor
, tagLen
)) {
502 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM computed tag not same as tag in blob"));
507 ccerr
= CCCrypt(kCCDecrypt
, kCCAlgorithmAES128
, kCCOptionPKCS7Padding
,
508 CFDataGetBytePtr(bulkKey
), CFDataGetLength(bulkKey
), NULL
, cursor
, ctLen
,
509 CFDataGetMutableBytePtr(plainText
), ctLen
, &ptLen
);
511 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
512 identifies uuid unwrap failures? */
513 /* errSecInteractionNotAllowed; */
514 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCrypt failed: %d"), ccerr
);
517 CFDataSetLength(plainText
, ptLen
);
521 attributes
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
522 CFDictionaryAddValue(attributes
, CFSTR("v_Data"), plainText
);
523 } else if (version
< 3) {
524 attributes
= s3dl_item_v2_decode(plainText
, error
);
526 attributes
= s3dl_item_v3_decode(plainText
, error
);
529 if (version
>= 4 && authenticated_attributes
!= NULL
) {
530 CFDictionaryForEach(authenticated_attributes
, ^(const void *key
, const void *value
) {
531 CFDictionaryAddValue(attributes
, key
, value
);
538 secerror("decode v%d failed: %@ [item: %@]", version
, error
? *error
: NULL
, plainText
);
542 memset(CFDataGetMutableBytePtr(bulkKey
), 0, CFDataGetLength(bulkKey
));
543 CFReleaseSafe(bulkKey
);
544 CFReleaseSafe(plainText
);
546 // Always copy access control data (if present), because if we fail it may indicate why.
548 *paccess_control
= access_control
;
550 CFReleaseSafe(access_control
);
554 *attributes_p
= CFRetainSafe(attributes
);
556 *version_p
= version
;
558 CFReleaseSafe(attributes
);
559 CFReleaseSafe(access_control_data
);
561 CFReleaseSafe(acm_context
);
562 CFReleaseSafe(authenticated_attributes
);
563 CFReleaseSafe(caller_access_groups_data
);
568 // TODO: Move to utilities - CFPropertyListCopyDERData()
569 CFDataRef
kc_plist_copy_der(CFPropertyListRef plist
, CFErrorRef
*error
) {
570 size_t len
= der_sizeof_plist(plist
, error
);
571 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
572 CFDataSetLength(encoded
, len
);
573 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
574 const uint8_t *der
= der_end
;
576 der_end
= der_encode_plist(plist
, error
, der
, der_end
);
578 CFReleaseNull(encoded
);
580 assert(!der_end
|| der_end
== der
);
585 static CFDataRef
kc_copy_digest(const struct ccdigest_info
*di
, size_t len
,
586 const void *data
, CFErrorRef
*error
) {
587 CFMutableDataRef digest
= CFDataCreateMutable(0, di
->output_size
);
588 CFDataSetLength(digest
, di
->output_size
);
589 ccdigest(di
, len
, data
, CFDataGetMutableBytePtr(digest
));
593 CFDataRef
kc_copy_sha1(size_t len
, const void *data
, CFErrorRef
*error
) {
594 return kc_copy_digest(ccsha1_di(), len
, data
, error
);
597 CFDataRef
kc_copy_plist_sha1(CFPropertyListRef plist
, CFErrorRef
*error
) {
598 CFDataRef der
= kc_plist_copy_der(plist
, error
);
599 CFDataRef digest
= NULL
;
601 digest
= kc_copy_sha1(CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
607 static keyclass_t
kc_parse_keyclass(CFTypeRef value
, CFErrorRef
*error
) {
608 if (!isString(value
)) {
609 SecError(errSecParam
, error
, CFSTR("accessible attribute %@ not a string"), value
);
610 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlocked
)) {
612 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlock
)) {
614 } else if (CFEqual(value
, kSecAttrAccessibleAlways
)) {
616 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)) {
617 return key_class_aku
;
618 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)) {
619 return key_class_cku
;
620 } else if (CFEqual(value
, kSecAttrAccessibleAlwaysThisDeviceOnly
)) {
621 return key_class_dku
;
622 } else if (CFEqual(value
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
623 return key_class_akpu
;
625 SecError(errSecParam
, error
, CFSTR("accessible attribute %@ unknown"), value
);
630 static CFTypeRef
kc_encode_keyclass(keyclass_t keyclass
) {
633 return kSecAttrAccessibleWhenUnlocked
;
635 return kSecAttrAccessibleAfterFirstUnlock
;
637 return kSecAttrAccessibleAlways
;
639 return kSecAttrAccessibleWhenUnlockedThisDeviceOnly
;
641 return kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
;
643 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
645 return kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
;
652 static void kc_dict_from_auth_data(const SecDbClass
*class, const uint8_t *der
, const uint8_t *der_end
, CFMutableDictionaryRef
*authenticated_attributes
, CFMutableDictionaryRef
*acl
)
654 CFPropertyListRef aks_data
= NULL
;
655 der
= der_decode_plist(NULL
, kCFPropertyListImmutable
, &aks_data
, NULL
, der
, der_end
);
657 CFDictionaryRef authenticated_data
= CFDictionaryGetValue(aks_data
, kAKSKeyAuthData
);
658 if (authenticated_data
) {
659 *acl
= CFDictionaryCreateMutableCopy(NULL
, 0, authenticated_data
);
660 SecDbForEachAttrWithMask(class, attr_desc
, kSecDbInAuthenticatedDataFlag
) {
661 CFDictionaryRemoveValue(*acl
, attr_desc
->name
);
662 CFTypeRef value
= CFDictionaryGetValue(authenticated_data
, attr_desc
->name
);
664 if (!*authenticated_attributes
)
665 *authenticated_attributes
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
667 CFDictionaryAddValue(*authenticated_attributes
, attr_desc
->name
, value
);
673 CFReleaseSafe(aks_data
);
676 static CFDataRef
kc_copy_constraints_data(SecAccessControlRef access_control
, CFDictionaryRef auth_attributes
) {
677 CFDictionaryRef constraints
= SecAccessControlGetConstraints(access_control
);
678 CFMutableDictionaryRef auth_data
= CFDictionaryCreateMutableCopy(NULL
, 0, constraints
);
679 if (auth_attributes
) {
680 CFDictionaryForEach(auth_attributes
, ^(const void *key
, const void *value
) {
681 CFDictionaryAddValue(auth_data
, key
, value
);
685 CFDataRef encoded
= kc_plist_copy_der(auth_data
, NULL
);
686 CFReleaseSafe(auth_data
);
690 static CFDataRef
kc_copy_access_groups_data(CFArrayRef access_groups
, CFErrorRef
*error
)
692 size_t ag_size
= der_sizeof_plist(access_groups
, error
);
693 CFMutableDataRef result
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
694 CFDataSetLength(result
, ag_size
);
695 if (!der_encode_plist(access_groups
, error
, CFDataGetMutableBytePtr(result
), CFDataGetMutableBytePtr(result
) + ag_size
)) {
703 #endif /* USE_KEYSTORE */
705 static CFDataRef
kc_copy_protection_data(SecAccessControlRef access_control
)
707 CFTypeRef protection
= SecAccessControlGetProtection(access_control
);
708 size_t protection_size
= der_sizeof_plist(protection
, NULL
);
709 CFMutableDataRef result
= CFDataCreateMutable(NULL
, 0);
710 CFDataSetLength(result
, protection_size
);
711 if (!der_encode_plist(protection
, NULL
, CFDataGetMutableBytePtr(result
), CFDataGetMutableBytePtr(result
) + protection_size
)) {
719 static CFTypeRef
kc_copy_protection_from_data(CFDataRef data
)
721 CFTypeRef result
= NULL
;
722 der_decode_plist(NULL
, kCFPropertyListImmutable
, &result
, NULL
, CFDataGetBytePtr(data
), CFDataGetBytePtr(data
) + CFDataGetLength(data
));
726 /* Return a (mutable) dictionary if plist is a dictionary, return NULL and set error otherwise. Does nothing if plist is already NULL. */
727 static CF_RETURNS_RETAINED
728 CFMutableDictionaryRef
dictionaryFromPlist(CFPropertyListRef plist CF_CONSUMED
, CFErrorRef
*error
) {
729 if (plist
&& !isDictionary(plist
)) {
730 CFStringRef typeName
= CFCopyTypeIDDescription(CFGetTypeID((CFTypeRef
)plist
));
731 SecError(errSecDecode
, error
, CFSTR("plist is a %@, expecting a dictionary"), typeName
);
732 CFReleaseSafe(typeName
);
733 CFReleaseNull(plist
);
735 return (CFMutableDictionaryRef
)plist
;
738 static CF_RETURNS_RETAINED
739 CFMutableDictionaryRef
s3dl_item_v2_decode(CFDataRef plain
, CFErrorRef
*error
) {
740 CFPropertyListRef item
;
741 item
= CFPropertyListCreateWithData(0, plain
, kCFPropertyListMutableContainers
, NULL
, error
);
742 return dictionaryFromPlist(item
, error
);
745 static CF_RETURNS_RETAINED
746 CFMutableDictionaryRef
s3dl_item_v3_decode(CFDataRef plain
, CFErrorRef
*error
) {
747 CFPropertyListRef item
= NULL
;
748 const uint8_t *der
= CFDataGetBytePtr(plain
);
749 const uint8_t *der_end
= der
+ CFDataGetLength(plain
);
750 der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &item
, error
, der
, der_end
);
751 if (der
&& der
!= der_end
) {
752 SecCFCreateError(errSecDecode
, kSecErrorDomain
, CFSTR("trailing garbage at end of decrypted item"), NULL
, error
);
755 return dictionaryFromPlist(item
, error
);
758 bool s3dl_item_from_data(CFDataRef edata
, Query
*q
, CFArrayRef accessGroups
,
759 CFMutableDictionaryRef
*item
, SecAccessControlRef
*access_control
, CFErrorRef
*error
) {
760 SecAccessControlRef ac
= NULL
;
761 CFDataRef ac_data
= NULL
;
764 /* Decrypt and decode the item and check the decoded attributes against the query. */
765 uint32_t version
= 0;
766 require_quiet((ok
= ks_decrypt_data(q
->q_keybag
, q
->q_crypto_op
, &ac
, &q
->q_use_cred_handle
, edata
, q
->q_class
, q
->q_caller_access_groups
, item
, &version
, error
)), out
);
771 ac_data
= SecAccessControlCopyData(ac
);
773 /* Item cannot be decrypted, because interactive authentication is needed. */
774 if (!q
->q_required_access_controls
) {
775 ok
= SecError(errSecInteractionNotAllowed
, error
, CFSTR("item would need ui for decrypting"));
778 CFArrayAppendValue(q
->q_required_access_controls
, ac_data
);
783 if (*item
&& !itemInAccessGroup(*item
, accessGroups
)) {
784 secerror("items accessGroup %@ not in %@",
785 CFDictionaryGetValue(*item
, kSecAttrAccessGroup
),
787 ok
= SecError(errSecDecode
, error
, CFSTR("items accessGroup %@ not in %@"),
788 CFDictionaryGetValue(*item
, kSecAttrAccessGroup
),
790 CFReleaseNull(*item
);
793 /* AccessControl attribute does not exist in the db, so synthesize it. */
795 CFDictionarySetValue(*item
, kSecAttrAccessControl
, ac_data
);
798 /* TODO: Validate access_control attribute. */
802 *access_control
= CFRetainSafe(ac
);
804 CFReleaseSafe(ac_data
);
808 /* Infer accessibility and access group for pre-v2 (iOS4.x and earlier) items
809 being imported from a backup. */
810 static bool SecDbItemImportMigrate(SecDbItemRef item
, CFErrorRef
*error
) {
812 CFStringRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
813 CFStringRef accessible
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
);
815 if (!isString(agrp
) || !isString(accessible
))
817 if (SecDbItemGetClass(item
) == &genp_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
818 CFStringRef svce
= SecDbItemGetCachedValueWithName(item
, kSecAttrService
);
819 if (!isString(svce
)) return ok
;
820 if (CFEqual(agrp
, CFSTR("apple"))) {
821 if (CFEqual(svce
, CFSTR("AirPort"))) {
822 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, error
);
823 } else if (CFEqual(svce
, CFSTR("com.apple.airplay.password"))) {
824 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
825 } else if (CFEqual(svce
, CFSTR("YouTube"))) {
826 ok
= (SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
) &&
827 SecDbItemSetValueWithName(item
, kSecAttrAccessGroup
, CFSTR("com.apple.youtube.credentials"), error
));
829 CFStringRef desc
= SecDbItemGetCachedValueWithName(item
, kSecAttrDescription
);
830 if (!isString(desc
)) return ok
;
831 if (CFEqual(desc
, CFSTR("IPSec Shared Secret")) || CFEqual(desc
, CFSTR("PPP Password"))) {
832 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, error
);
836 } else if (SecDbItemGetClass(item
) == &inet_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
837 if (CFEqual(agrp
, CFSTR("PrintKitAccessGroup"))) {
838 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
839 } else if (CFEqual(agrp
, CFSTR("apple"))) {
840 CFTypeRef ptcl
= SecDbItemGetCachedValueWithName(item
, kSecAttrProtocol
);
841 bool is_proxy
= false;
842 if (isNumber(ptcl
)) {
844 CFNumberGetValue(ptcl
, kCFNumberSInt32Type
, &iptcl
);
845 is_proxy
= (iptcl
== FOUR_CHAR_CODE('htpx') ||
846 iptcl
== FOUR_CHAR_CODE('htsx') ||
847 iptcl
== FOUR_CHAR_CODE('ftpx') ||
848 iptcl
== FOUR_CHAR_CODE('rtsx') ||
849 iptcl
== FOUR_CHAR_CODE('xpth') ||
850 iptcl
== FOUR_CHAR_CODE('xsth') ||
851 iptcl
== FOUR_CHAR_CODE('xptf') ||
852 iptcl
== FOUR_CHAR_CODE('xstr'));
853 } else if (isString(ptcl
)) {
854 is_proxy
= (CFEqual(ptcl
, kSecAttrProtocolHTTPProxy
) ||
855 CFEqual(ptcl
, kSecAttrProtocolHTTPSProxy
) ||
856 CFEqual(ptcl
, kSecAttrProtocolRTSPProxy
) ||
857 CFEqual(ptcl
, kSecAttrProtocolFTPProxy
));
860 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
866 bool SecDbItemDecrypt(SecDbItemRef item
, CFDataRef edata
, CFDataRef
*neededAuth
, CFErrorRef
*error
) {
868 CFMutableDictionaryRef dict
= NULL
;
869 SecAccessControlRef access_control
= NULL
;
870 uint32_t version
= 0;
872 if (!ks_decrypt_data(SecDbItemGetKeybag(item
), item
->cryptoOp
, &access_control
, &item
->credHandle
, edata
, item
->class, item
->callerAccessGroups
, &dict
, &version
, error
)) {
873 // Copy access control data, which might indicate why decryption failed.
875 *neededAuth
= SecAccessControlCopyData(access_control
);
882 *neededAuth
= SecAccessControlCopyData(access_control
);
884 require_quiet(ok
= SecError(errSecInteractionNotAllowed
, error
, CFSTR("auth needed, but caller does not provide it")), out
);
889 /* Old V4 style keychain backup being imported. */
890 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), CFDictionaryGetValue(dict
, CFSTR("v_Data")), error
) &&
891 SecDbItemImportMigrate(item
, error
);
893 ok
= dict
&& SecDbItemSetValues(item
, dict
, error
);
897 SecAccessControlRef my_access_control
= SecDbItemCopyAccessControl(item
, error
);
898 if (!my_access_control
) {
903 /* Make sure that the protection of ACL in the dictionary (read from DB) matched what we got
904 back from decoding the data blob. */
905 if (!CFEqual(SecAccessControlGetProtection(my_access_control
), SecAccessControlGetProtection(access_control
))) {
906 ok
= SecError(errSecDecode
, error
, CFSTR("ACL protection doesn't match the one in blob (%@ : %@)"),
907 SecAccessControlGetProtection(my_access_control
),
908 SecAccessControlGetProtection(access_control
));
910 CFRelease(my_access_control
);
912 // Update real protection used for decrypting in the item.
913 ok
= ok
&& SecDbItemSetAccessControl(item
, access_control
, error
);
917 CFReleaseSafe(access_control
);
921 /* Automagically make a item syncable, based on various attributes. */
922 bool SecDbItemInferSyncable(SecDbItemRef item
, CFErrorRef
*error
)
924 CFStringRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
929 if (CFEqual(agrp
, CFSTR("com.apple.cfnetwork")) && SecDbItemGetClass(item
) == &inet_class
) {
930 CFTypeRef srvr
= SecDbItemGetCachedValueWithName(item
, kSecAttrServer
);
931 CFTypeRef ptcl
= SecDbItemGetCachedValueWithName(item
, kSecAttrProtocol
);
932 CFTypeRef atyp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAuthenticationType
);
934 if (isString(srvr
) && isString(ptcl
) && isString(atyp
)) {
935 /* This looks like a Mobile Safari Password, make syncable */
936 secnotice("item", "Make this item syncable: %@", item
);
937 return SecDbItemSetSyncable(item
, true, error
);
944 /* This create a SecDbItem from the item dictionnary that are exported for backups.
945 Item are stored in the backup as a dictionary containing two keys:
946 - v_Data: the encrypted data blob
947 - v_PersistentRef: a persistent Ref.
948 src_keybag is normally the backup keybag.
949 dst_keybag is normally the device keybag.
951 SecDbItemRef
SecDbItemCreateWithBackupDictionary(CFAllocatorRef allocator
, const SecDbClass
*dbclass
, CFDictionaryRef dict
, keybag_handle_t src_keybag
, keybag_handle_t dst_keybag
, CFErrorRef
*error
)
953 CFDataRef edata
= CFDictionaryGetValue(dict
, CFSTR("v_Data"));
954 SecDbItemRef item
= NULL
;
957 item
= SecDbItemCreateWithEncryptedData(kCFAllocatorDefault
, dbclass
, edata
, src_keybag
, error
);
959 if (!SecDbItemSetKeybag(item
, dst_keybag
, error
))
962 SecError(errSecDecode
, error
, CFSTR("No v_Data in backup dictionary %@"), dict
);
968 bool SecDbItemExtractRowIdFromBackupDictionary(SecDbItemRef item
, CFDictionaryRef dict
, CFErrorRef
*error
) {
969 CFDataRef ref
= CFDictionaryGetValue(dict
, CFSTR("v_PersistentRef"));
971 return SecError(errSecDecode
, error
, CFSTR("No v_PersistentRef in backup dictionary %@"), dict
);
973 CFStringRef className
;
975 if (!_SecItemParsePersistentRef(ref
, &className
, &rowid
))
976 return SecError(errSecDecode
, error
, CFSTR("v_PersistentRef %@ failed to decode"), ref
);
978 if (!CFEqual(SecDbItemGetClass(item
)->name
, className
))
979 return SecError(errSecDecode
, error
, CFSTR("v_PersistentRef has unexpected class %@"), className
);
981 return SecDbItemSetRowId(item
, rowid
, error
);