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  *  SecKeybagSupport.c - CoreFoundation-based constants and functions for 
  26     access to Security items (certificates, keys, identities, and 
  30 #include <securityd/SecKeybagSupport.h> 
  32 #include <securityd/SecItemServer.h> 
  35 #include <IOKit/IOKitLib.h> 
  37 #include <libaks_acl_cf_keys.h> 
  38 #include <utilities/der_plist.h> 
  39 #include <corecrypto/ccder.h> 
  41 #if TARGET_OS_EMBEDDED 
  42 #include <MobileKeyBag/MobileKeyBag.h> 
  44 #endif /* USE_KEYSTORE */ 
  46 #include <CommonCrypto/CommonCryptor.h> 
  47 #include <CommonCrypto/CommonCryptorSPI.h> 
  50 /* g_keychain_handle is the keybag handle used for encrypting item in the keychain. 
  51  For testing purposes, it can be set to something other than the default, with SecItemServerSetKeychainKeybag */ 
  53 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED 
  54 keybag_handle_t g_keychain_keybag 
= session_keybag_handle
; 
  56 keybag_handle_t g_keychain_keybag 
= device_keybag_handle
; 
  58 #else /* !USE_KEYSTORE */ 
  59 keybag_handle_t g_keychain_keybag 
= 0; /* 0 == device_keybag_handle, constant dictated by AKS */ 
  60 #endif /* USE_KEYSTORE */ 
  62 const CFStringRef kSecKSKeyData1 
= CFSTR("d1"); 
  63 const CFStringRef kSecKSKeyData2 
= CFSTR("d2"); 
  65 void SecItemServerSetKeychainKeybag(int32_t keybag
) 
  67     g_keychain_keybag
=keybag
; 
  70 void SecItemServerResetKeychainKeybag(void) 
  73 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED 
  74     g_keychain_keybag 
= session_keybag_handle
; 
  76     g_keychain_keybag 
= device_keybag_handle
; 
  78 #else /* !USE_KEYSTORE */ 
  79     g_keychain_keybag 
= 0; /* 0 == device_keybag_handle, constant dictated by AKS */ 
  80 #endif /* USE_KEYSTORE */ 
  85 static bool hwaes_key_available(void) 
  87     keybag_handle_t handle 
= bad_keybag_handle
; 
  88     keybag_handle_t special_handle 
= bad_keybag_handle
; 
  90     special_handle 
= session_keybag_handle
; 
  91 #elif TARGET_OS_EMBEDDED 
  92     special_handle 
= device_keybag_handle
; 
  94 #error "supported keybag target" 
  97     kern_return_t kr 
= aks_get_system(special_handle
, &handle
); 
  98     if (kr 
!= kIOReturnSuccess
) { 
  99 #if TARGET_OS_EMBEDDED 
 100         /* TODO: Remove this once the kext runs the daemon on demand if 
 101          there is no system keybag. */ 
 102         int kb_state 
= MKBGetDeviceLockState(NULL
); 
 103         asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "AppleKeyStore lock state: %d", kb_state
); 
 109 #else /* !USE_KEYSTORE */ 
 111 static bool hwaes_key_available(void) 
 116 #endif /* USE_KEYSTORE */ 
 118 /* Wrap takes a 128 - 256 bit key as input and returns output of 
 120  In bytes this means that a 
 121  16 byte (128 bit) key returns a 24 byte wrapped key 
 122  24 byte (192 bit) key returns a 32 byte wrapped key 
 123  32 byte (256 bit) key returns a 40 byte wrapped key  */ 
 124 bool ks_crypt(CFTypeRef operation
, keybag_handle_t keybag
, 
 125               keyclass_t keyclass
, uint32_t textLength
, const uint8_t *source
, keyclass_t 
*actual_class
, CFMutableDataRef dest
, CFErrorRef 
*error
) { 
 127     kern_return_t kernResult 
= kIOReturnBadArgument
; 
 129     int dest_len 
= (int)CFDataGetLength(dest
); 
 130     if (CFEqual(operation
, kAKSKeyOpEncrypt
)) { 
 131         kernResult 
= aks_wrap_key(source
, textLength
, keyclass
, keybag
, CFDataGetMutableBytePtr(dest
), &dest_len
, actual_class
); 
 132     } else if (CFEqual(operation
, kAKSKeyOpDecrypt
) || CFEqual(operation
, kAKSKeyOpDelete
)) { 
 133         kernResult 
= aks_unwrap_key(source
, textLength
, keyclass
, keybag
, CFDataGetMutableBytePtr(dest
), &dest_len
); 
 136     if (kernResult 
!= KERN_SUCCESS
) { 
 137         if ((kernResult 
== kIOReturnNotPermitted
) || (kernResult 
== kIOReturnNotPrivileged
)) { 
 138             const char *substatus 
= ""; 
 139             if (keyclass 
== key_class_ck 
|| keyclass 
== key_class_cku
) 
 140                 substatus 
= " (hiberation ?)"; 
 141             /* Access to item attempted while keychain is locked. */ 
 142             return SecError(errSecInteractionNotAllowed
, error
, CFSTR("ks_crypt: %x failed to '%@' item (class %"PRId32
", bag: %"PRId32
") Access to item attempted while keychain is locked%s."), 
 143                             kernResult
, operation
, keyclass
, keybag
, substatus
); 
 144         } else if (kernResult 
== kIOReturnError
) { 
 145             /* Item can't be decrypted on this device, ever, so drop the item. */ 
 146             return SecError(errSecDecode
, error
, CFSTR("ks_crypt: %x failed to '%@' item (class %"PRId32
", bag: %"PRId32
") Item can't be decrypted on this device, ever, so drop the item."), 
 147                             kernResult
, operation
, keyclass
, keybag
); 
 149             return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: %x failed to '%@' item (class %"PRId32
", bag: %"PRId32
")"), 
 150                             kernResult
, operation
, keyclass
, keybag
); 
 154         CFDataSetLength(dest
, dest_len
);     
 156 #else /* !USE_KEYSTORE */ 
 157     uint32_t dest_len 
= (uint32_t)CFDataGetLength(dest
); 
 158     if (CFEqual(operation
, kAKSKeyOpEncrypt
)) { 
 159         /* The no encryption case. */ 
 160         if (dest_len 
>= textLength 
+ 8) { 
 161             memcpy(CFDataGetMutableBytePtr(dest
), source
, textLength
); 
 162             memset(CFDataGetMutableBytePtr(dest
) + textLength
, 8, 8); 
 163             CFDataSetLength(dest
, textLength 
+ 8); 
 164             *actual_class 
= keyclass
; 
 166             return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: failed to wrap item (class %"PRId32
")"), keyclass
); 
 167     } else if (CFEqual(operation
, kAKSKeyOpDecrypt
) || CFEqual(operation
, kAKSKeyOpDelete
)) { 
 168         if (dest_len 
+ 8 >= textLength
) { 
 169             memcpy(CFDataGetMutableBytePtr(dest
), source
, textLength 
- 8); 
 170             CFDataSetLength(dest
, textLength 
- 8); 
 172             return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: failed to unwrap item (class %"PRId32
")"), keyclass
); 
 175 #endif /* USE_KEYSTORE */ 
 179 static bool ks_access_control_needed_error(CFErrorRef 
*error
, CFDataRef access_control_data
, CFTypeRef operation
) { 
 183     if (*error 
&& CFErrorGetCode(*error
) != errSecAuthNeeded
) { 
 184         // If we already had an error there, just leave it, no access_control specific error is needed here. 
 188     // Create new error instance which adds new access control data appended to existing 
 189     CFMutableDictionaryRef user_info
; 
 191         CFDictionaryRef old_user_info 
= CFErrorCopyUserInfo(*error
); 
 192         user_info 
= CFDictionaryCreateMutableCopy(NULL
, 0, old_user_info
); 
 193         CFRelease(old_user_info
); 
 194         CFReleaseNull(*error
); 
 196         user_info 
= CFDictionaryCreateMutableForCFTypes(NULL
); 
 199     if (access_control_data
) { 
 200         CFNumberRef key 
= CFNumberCreateWithCFIndex(NULL
, errSecAuthNeeded
); 
 201         CFMutableArrayRef acls
; 
 202         CFArrayRef old_acls 
= CFDictionaryGetValue(user_info
, key
); 
 204             acls 
= CFArrayCreateMutableCopy(NULL
, 0, old_acls
); 
 206             acls 
= CFArrayCreateMutableForCFTypes(NULL
); 
 208         CFArrayRef pair 
= CFArrayCreateForCFTypes(NULL
, access_control_data
, operation
, NULL
); 
 209         CFArrayAppendValue(acls
, pair
); 
 212         CFDictionarySetValue(user_info
, key
, acls
); 
 216         *error 
= CFErrorCreate(NULL
, kSecErrorDomain
, errSecAuthNeeded
, user_info
); 
 219         *error 
= CFErrorCreate(NULL
, kSecErrorDomain
, errSecAuthNeeded
, NULL
); 
 221     CFReleaseSafe(user_info
); 
 225 static bool merge_der_in_to_data(const void *ed_blob
, size_t ed_blob_len
, const void *key_blob
, size_t key_blob_len
, CFMutableDataRef mergedData
) 
 228     CFDataRef ed_data 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, ed_blob
, ed_blob_len
, kCFAllocatorNull
); 
 229     CFDataRef key_data 
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, key_blob
, key_blob_len
, kCFAllocatorNull
); 
 231     if (ed_data 
&& key_data
) { 
 232         CFDictionaryRef result_dict 
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kSecKSKeyData1
, ed_data
, kSecKSKeyData2
, key_data
, NULL
); 
 234         CFDataSetLength(mergedData
, 0); 
 235         CFDataRef der_data 
= CFPropertyListCreateDERData(kCFAllocatorDefault
, result_dict
, NULL
); 
 237             CFDataAppend(mergedData
, der_data
); 
 239             ok 
= CFDataGetLength(mergedData
) > 0; 
 241         CFRelease(result_dict
); 
 244     CFReleaseSafe(ed_data
); 
 245     CFReleaseSafe(key_data
); 
 249 bool ks_separate_data_and_key(CFDictionaryRef blob_dict
, CFDataRef 
*ed_data
, CFDataRef 
*key_data
) 
 252     CFDataRef tmp_ed_data 
= CFDictionaryGetValue(blob_dict
, kSecKSKeyData1
); 
 253     CFDataRef tmp_key_data 
= CFDictionaryGetValue(blob_dict
, kSecKSKeyData2
); 
 255     if (tmp_ed_data 
&& tmp_key_data 
&& 
 256         CFDataGetTypeID() == CFGetTypeID(tmp_ed_data
) && 
 257         CFDataGetTypeID() == CFGetTypeID(tmp_key_data
)) { 
 258         *ed_data 
= CFRetain(tmp_ed_data
); 
 259         *key_data 
= CFRetain(tmp_key_data
); 
 266 static bool create_cferror_from_aks(int aks_return
, CFTypeRef operation
, keybag_handle_t keybag
, keyclass_t keyclass
, CFDataRef access_control_data
, CFDataRef acm_context_data
, CFErrorRef 
*error
) 
 268     const char *operation_string 
= ""; 
 269     if (CFEqual(operation
, kAKSKeyOpDecrypt
)) { 
 270         operation_string 
= "decrypt"; 
 271     } else if (CFEqual(operation
, kAKSKeyOpEncrypt
)) { 
 272         operation_string 
= "encrypt"; 
 273     } if (CFEqual(operation
, kAKSKeyOpDelete
)) { 
 274         operation_string 
= "delete"; 
 277     if (aks_return 
== kAKSReturnNoPermission
) { 
 278         /* Keychain is locked. */ 
 279         SecError(errSecInteractionNotAllowed
, error
, CFSTR("aks_ref_key: %x failed to '%s' item (class %"PRId32
", bag: %"PRId32
") Access to item attempted while keychain is locked."), 
 280                            aks_return
, operation_string
, keyclass
, keybag
); 
 281     } else if (aks_return 
== kAKSReturnPolicyError 
|| aks_return 
== kAKSReturnBadPassword
) { 
 282         if (aks_return 
== kAKSReturnBadPassword
) { 
 283             ACMContextRef acm_context_ref 
= ACMContextCreateWithExternalForm(CFDataGetBytePtr(acm_context_data
), CFDataGetLength(acm_context_data
)); 
 284             if (acm_context_ref
) { 
 285                 ACMContextRemovePassphraseCredentialsByPurposeAndScope(acm_context_ref
, kACMPassphrasePurposeGeneral
, kACMScopeContext
); 
 286                 ACMContextDelete(acm_context_ref
, false); 
 290         /* Item needed authentication. */ 
 291         ks_access_control_needed_error(error
, access_control_data
, operation
); 
 292     } else if (aks_return 
== kAKSReturnError 
|| aks_return 
== kAKSReturnPolicyInvalid
) { 
 293         /* Item can't be decrypted on this device, ever, so drop the item. */ 
 294         SecError(errSecDecode
, error
, CFSTR("aks_ref_key: %x failed to '%s' item (class %"PRId32
", bag: %"PRId32
") Item can't be decrypted on this device, ever, so drop the item."), 
 295                           aks_return
, operation_string
, keyclass
, keybag
); 
 297         SecError(errSecNotAvailable
, error
, CFSTR("aks_ref_key: %x failed to '%s' item (class %"PRId32
", bag: %"PRId32
")"), 
 298                           aks_return
, operation_string
, keyclass
, keybag
); 
 304 bool ks_encrypt_acl(keybag_handle_t keybag
, keyclass_t keyclass
, uint32_t textLength
, const uint8_t *source
, 
 305                     CFMutableDataRef dest
, CFDataRef auth_data
, CFDataRef acm_context
, 
 306                     SecAccessControlRef access_control
, CFErrorRef 
*error
) { 
 307     void *params 
= NULL
, *der 
= NULL
; 
 308     size_t params_len 
= 0, der_len 
= 0; 
 309     CFDataRef access_control_data 
= SecAccessControlCopyData(access_control
); 
 310     int aks_return 
= kAKSReturnSuccess
; 
 311     aks_ref_key_t key_handle 
= NULL
; 
 313     /* Verify that we have credential handle, otherwise generate proper error containing ACL and operation requested. */ 
 315     if (!acm_context 
|| !SecAccessControlIsBound(access_control
)) { 
 316         require_quiet(ok 
= ks_access_control_needed_error(error
, access_control_data
, SecAccessControlIsBound(access_control
) ? kAKSKeyOpEncrypt 
: CFSTR("")), out
); 
 319     aks_operation_optional_params(0, 0, CFDataGetBytePtr(auth_data
), CFDataGetLength(auth_data
), CFDataGetBytePtr(acm_context
), (int)CFDataGetLength(acm_context
), ¶ms
, ¶ms_len
); 
 320     require_noerr_action_quiet(aks_return 
= aks_ref_key_create(keybag
, keyclass
, key_type_sym
, params
, params_len
, &key_handle
), out
, 
 321                                create_cferror_from_aks(aks_return
, kAKSKeyOpEncrypt
, keybag
, keyclass
, access_control_data
, acm_context
, error
)); 
 322     require_noerr_action_quiet(aks_return 
= aks_ref_key_encrypt(key_handle
, params
, params_len
, source
, textLength
, &der
, &der_len
), out
, 
 323                                create_cferror_from_aks(aks_return
, kAKSKeyOpEncrypt
, keybag
, keyclass
, access_control_data
, acm_context
, error
)); 
 325     const void *key_blob 
= aks_ref_key_get_blob(key_handle
, &key_blob_len
); 
 326     require_action_quiet(key_blob
, out
, SecError(errSecDecode
, error
, CFSTR("ks_crypt_acl: %x failed to '%s' item (class %"PRId32
", bag: %"PRId32
") Item can't be encrypted due to invalid key data, so drop the item."), 
 327                                            aks_return
, "encrypt", keyclass
, keybag
)); 
 329     require_action_quiet(merge_der_in_to_data(der
, der_len
, key_blob
, key_blob_len
, dest
), out
, 
 330                    SecError(errSecDecode
, error
, CFSTR("ks_crypt_acl: %x failed to '%s' item (class %"PRId32
", bag: %"PRId32
") Item can't be encrypted due to merge failed, so drop the item."), 
 331                             aks_return
, "encrypt", keyclass
, keybag
)); 
 337         aks_ref_key_free(&key_handle
); 
 342     CFReleaseSafe(access_control_data
); 
 346 bool ks_decrypt_acl(aks_ref_key_t ref_key
, CFDataRef encrypted_data
, CFMutableDataRef dest
, 
 347                   CFDataRef acm_context
, CFDataRef caller_access_groups
, 
 348                   SecAccessControlRef access_control
, CFErrorRef 
*error
) { 
 349     void *params 
= NULL
, *der 
= NULL
; 
 350     const uint8_t *access_groups 
= caller_access_groups
?CFDataGetBytePtr(caller_access_groups
):NULL
; 
 351     size_t params_len 
= 0, der_len 
= 0, access_groups_len 
= caller_access_groups
?CFDataGetLength(caller_access_groups
):0; 
 352     CFDataRef access_control_data 
= SecAccessControlCopyData(access_control
); 
 353     int aks_return 
= kAKSReturnSuccess
; 
 355     /* Verify that we have credential handle, otherwise generate proper error containing ACL and operation requested. */ 
 358         require_quiet(ok 
= ks_access_control_needed_error(error
, NULL
, NULL
), out
); 
 361     aks_operation_optional_params(access_groups
, access_groups_len
, 0, 0, CFDataGetBytePtr(acm_context
), (int)CFDataGetLength(acm_context
), ¶ms
, ¶ms_len
); 
 362     require_noerr_action_quiet(aks_return 
= aks_ref_key_decrypt(ref_key
, params
, params_len
, CFDataGetBytePtr(encrypted_data
), CFDataGetLength(encrypted_data
), &der
, &der_len
), out
, 
 363                                create_cferror_from_aks(aks_return
, kAKSKeyOpDecrypt
, 0, 0, access_control_data
, acm_context
, error
)); 
 364     require_action_quiet(der
, out
, SecError(errSecDecode
, error
, CFSTR("ks_crypt_acl: %x failed to '%s' item, Item can't be decrypted due to invalid der data, so drop the item."), 
 365                                                   aks_return
, "decrypt")); 
 367     CFPropertyListRef decoded_data 
= NULL
; 
 368     der_decode_plist(kCFAllocatorDefault
, kCFPropertyListImmutable
, &decoded_data
, NULL
, der
, der 
+ der_len
); 
 369     require_action_quiet(decoded_data
, out
, SecError(errSecDecode
, error
, CFSTR("ks_crypt_acl: %x failed to '%s' item, Item can't be decrypted due to failed decode der, so drop the item."), 
 370                                                            aks_return
, "decrypt")); 
 371     if (CFGetTypeID(decoded_data
) == CFDataGetTypeID()) { 
 372         CFDataSetLength(dest
, 0); 
 373         CFDataAppend(dest
, decoded_data
); 
 374         CFRelease(decoded_data
); 
 377         CFRelease(decoded_data
); 
 378         require_action_quiet(false, out
, SecError(errSecDecode
, error
, CFSTR("ks_crypt_acl: %x failed to '%s' item, Item can't be decrypted due to wrong data, so drop the item."), 
 379                                                         aks_return
, "decrypt")); 
 389     CFReleaseSafe(access_control_data
); 
 393 bool ks_delete_acl(aks_ref_key_t ref_key
, CFDataRef encrypted_data
,  
 394                   CFDataRef acm_context
, CFDataRef caller_access_groups
, 
 395                   SecAccessControlRef access_control
, CFErrorRef 
*error
) { 
 397     CFDataRef access_control_data 
= NULL
; 
 398     int aks_return 
= kAKSReturnSuccess
; 
 401     nrequire_action_quiet(CFEqual(SecAccessControlGetConstraint(access_control
, kAKSKeyOpDelete
), kCFBooleanTrue
), out
, ok 
= true); 
 403     /* Verify that we have credential handle, otherwise generate proper error containing ACL and operation requested. */ 
 405         require_quiet(ok 
= ks_access_control_needed_error(error
, NULL
, NULL
), out
); 
 408     access_control_data 
= SecAccessControlCopyData(access_control
); 
 409     const uint8_t *access_groups 
= caller_access_groups
?CFDataGetBytePtr(caller_access_groups
):NULL
; 
 410     size_t params_len 
= 0, access_groups_len 
= caller_access_groups
?CFDataGetLength(caller_access_groups
):0; 
 411     aks_operation_optional_params(access_groups
, access_groups_len
, 0, 0, CFDataGetBytePtr(acm_context
), (int)CFDataGetLength(acm_context
), ¶ms
, ¶ms_len
); 
 412     require_noerr_action_quiet(aks_return 
= aks_ref_key_delete(ref_key
, params
, params_len
), out
, 
 413                                create_cferror_from_aks(aks_return
, kAKSKeyOpDelete
, 0, 0, access_control_data
, acm_context
, error
)); 
 420     CFReleaseSafe(access_control_data
); 
 424 const void* ks_ref_key_get_external_data(keybag_handle_t keybag
, CFDataRef key_data
, aks_ref_key_t 
*ref_key
, size_t *external_data_len
, CFErrorRef 
*error
) { 
 425     const void* result 
= NULL
; 
 426     int aks_return 
= kAKSReturnSuccess
; 
 427     require_noerr_action_quiet(aks_ref_key_create_with_blob(keybag
, CFDataGetBytePtr(key_data
), CFDataGetLength(key_data
), ref_key
), out
, 
 428                                SecError(errSecNotAvailable
, error
, CFSTR("aks_ref_key: %x failed to '%s' item (bag: %"PRId32
")"), aks_return
, "create ref key with blob", keybag
)); 
 429     result 
= aks_ref_key_get_external_data(*ref_key
, external_data_len
); 
 436 bool use_hwaes(void) { 
 437     static bool use_hwaes
; 
 438     static dispatch_once_t check_once
; 
 439     dispatch_once(&check_once
, ^{ 
 440         use_hwaes 
= hwaes_key_available(); 
 442             asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "using hwaes key"); 
 444             asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "unable to access hwaes key"); 
 450 bool ks_open_keybag(CFDataRef keybag
, CFDataRef password
, keybag_handle_t 
*handle
, CFErrorRef 
*error
) { 
 452     kern_return_t kernResult
; 
 453     if (!asData(keybag
, error
)) return false; 
 454     kernResult 
= aks_load_bag(CFDataGetBytePtr(keybag
), (int)CFDataGetLength(keybag
), handle
); 
 456         return SecKernError(kernResult
, error
, CFSTR("aks_load_bag failed: %@"), keybag
); 
 459         kernResult 
= aks_unlock_bag(*handle
, CFDataGetBytePtr(password
), (int)CFDataGetLength(password
)); 
 461             aks_unload_bag(*handle
); 
 462             return SecKernError(kernResult
, error
, CFSTR("aks_unlock_bag failed")); 
 466 #else /* !USE_KEYSTORE */ 
 467     *handle 
= KEYBAG_NONE
; 
 469 #endif /* USE_KEYSTORE */ 
 472 bool ks_close_keybag(keybag_handle_t keybag
, CFErrorRef 
*error
) { 
 474         IOReturn kernResult 
= aks_unload_bag(keybag
); 
 476         return SecKernError(kernResult
, error
, CFSTR("aks_unload_bag failed")); 
 478 #endif /* USE_KEYSTORE */