6 #include "SOSAccountPriv.h"
7 #include "SOSCloudKeychainClient.h"
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSViews.h>
13 #include "SOSInternal.h"
18 // MARK: V0 Keybag keychain stuff
20 static bool SecItemUpdateOrAdd(CFDictionaryRef query
, CFDictionaryRef update
, CFErrorRef
*error
)
22 OSStatus saveStatus
= SecItemUpdate(query
, update
);
24 if (errSecItemNotFound
== saveStatus
) {
25 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
26 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
27 CFDictionaryAddValue(add
, key
, value
);
29 saveStatus
= SecItemAdd(add
, NULL
);
33 return SecError(saveStatus
, error
, CFSTR("Error saving %@"), query
);
36 static CFDictionaryRef
SOSCopyV0Attributes() {
37 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
38 kSecClass
, kSecClassGenericPassword
,
39 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
40 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
41 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
42 kSecAttrService
, CFSTR("SecureBackupService"),
43 kSecAttrSynchronizable
, kCFBooleanTrue
,
47 bool SOSDeleteV0Keybag(CFErrorRef
*error
) {
48 CFDictionaryRef attributes
= SOSCopyV0Attributes();
50 OSStatus result
= SecItemDelete(attributes
);
52 CFReleaseNull(attributes
);
54 return SecError(result
!= errSecItemNotFound
? result
: errSecSuccess
, error
, CFSTR("Deleting V0 Keybag failed - %ld"), result
);
57 static bool SOSSaveV0Keybag(CFDataRef v0Keybag
, CFErrorRef
*error
) {
58 CFDictionaryRef attributes
= SOSCopyV0Attributes();
60 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
61 kSecValueData
, v0Keybag
,
65 bool result
= SecItemUpdateOrAdd(attributes
, update
, error
);
66 CFReleaseNull(attributes
);
67 CFReleaseNull(update
);
73 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo
, CFStringRef viewName
) {
74 if (CFEqualSafe(kSOSViewKeychainV0
, viewName
))
77 return SOSPeerInfoHasBackupKey(peerInfo
) && SOSPeerInfoIsViewPermitted(peerInfo
, viewName
);
80 static CFSetRef
SOSAccountCopyBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
) {
81 CFMutableSetRef backupPeers
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
83 SOSCircleRef circle
= SOSAccountGetCircle(account
, NULL
);
85 require_quiet(circle
, exit
);
87 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
88 if (SOSPeerInfoIsViewBackupEnabled(peer
, viewName
))
89 CFSetAddValue(backupPeers
, peer
);
96 static void SOSAccountWithBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
, void (^action
)(CFSetRef peers
)) {
97 CFSetRef backupPeersForView
= SOSAccountCopyBackupPeersForView(account
, viewName
);
99 action(backupPeersForView
);
101 CFReleaseNull(backupPeersForView
);
105 static bool SOSAccountWithBSKBForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
106 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
107 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
109 CFDataRef rkbg
= SOSAccountCopyRecoveryPublic(kCFAllocatorDefault
, account
, error
);
111 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
113 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, peers
, error
);
115 CFMutableDictionaryRef additionalKeys
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
116 CFDictionaryAddValue(additionalKeys
, bskbRkbgPrefix
, rkbg
);
117 bskb
= SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault
, peers
, additionalKeys
, error
);
118 CFReleaseNull(additionalKeys
);
123 require_quiet(bskb
, exit
);
134 CFStringRef
SOSBackupCopyRingNameForView(CFStringRef viewName
) {
135 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewName
);
138 static bool SOSAccountUpdateBackupRing(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
139 SOSRingRef (^modify
)(SOSRingRef existing
, CFErrorRef
*error
)) {
141 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
143 bool result
= SOSAccountUpdateNamedRing(account
, ringName
, error
, ^SOSRingRef(CFStringRef ringName
, CFErrorRef
*error
) {
144 return SOSRingCreate(ringName
, SOSAccountGetMyPeerID(account
), kSOSRingBackup
, error
);
147 CFReleaseNull(ringName
);
152 static CFSetRef
SOSAccountCopyPeerSetForView(SOSAccountRef account
, CFStringRef viewName
) {
153 CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
155 if (account
->trusted_circle
) {
156 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
157 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer
), viewName
)) {
158 CFSetAddValue(result
, peer
);
166 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccountRef account
, CFStringRef viewName
, SOSBackupSliceKeyBagRef keyBag
, CFErrorRef
*error
) {
167 CFMutableSetRef backupViewSet
= CFSetCreateMutableForCFTypes(NULL
);
169 require_quiet(SecAllocationError(backupViewSet
, error
, CFSTR("No backup view set created")), errOut
);
170 CFSetAddValue(backupViewSet
, viewName
);
172 result
= SOSAccountUpdateBackupRing(account
, viewName
, error
, ^SOSRingRef(SOSRingRef existing
, CFErrorRef
*error
) {
173 SOSRingRef newRing
= NULL
;
174 CFSetRef viewPeerSet
= SOSAccountCopyPeerSetForView(account
, viewName
);
175 CFMutableSetRef cleared
= CFSetCreateMutableForCFTypes(NULL
);
177 SOSRingSetPeerIDs(existing
, cleared
);
178 SOSRingAddAll(existing
, viewPeerSet
);
180 require_quiet(SOSRingSetBackupKeyBag(existing
, SOSAccountGetMyFullPeerInfo(account
), backupViewSet
, keyBag
, error
), exit
);
182 newRing
= CFRetainSafe(existing
);
184 CFReleaseNull(viewPeerSet
);
185 CFReleaseNull(cleared
);
191 if (result
&& NULL
!= error
&& NULL
!= *error
) {
192 secerror("Got Success and Error (dropping error): %@", *error
);
193 CFReleaseNull(*error
);
197 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName
, error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space."));
200 CFReleaseNull(backupViewSet
);
204 bool SOSAccountNewBKSBForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
)
206 return SOSAccountWithBSKBForView(account
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
207 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
212 bool SOSAccountIsBackupRingEmpty(SOSAccountRef account
, CFStringRef viewName
) {
213 CFStringRef backupRing
= SOSBackupCopyRingNameForView(viewName
);
214 SOSRingRef ring
= SOSAccountCopyRing(account
, backupRing
, NULL
);
215 CFReleaseNull(backupRing
);
217 if(ring
) peercnt
= SOSRingCountPeers(ring
);
222 bool SOSAccountUpdatePeerInfo(SOSAccountRef account
, CFStringRef updateDescription
, CFErrorRef
*error
, bool (^update
)(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
)) {
223 if (account
->my_identity
== NULL
)
226 bool result
= update(account
->my_identity
, error
);
228 if (result
&& SOSAccountHasCircle(account
, NULL
)) {
229 return SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle_to_change
) {
230 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription
);
231 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
238 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account
, CFStringRef viewname
){
240 CFErrorRef bsError
= NULL
;
241 CFDataRef backupSliceData
= NULL
;
242 SOSRingRef ring
= NULL
;
243 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
245 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account
), viewname
), errOut
);
247 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
248 ring
= SOSAccountCopyRing(account
, ringName
, &bsError
);
249 CFReleaseNull(ringName
);
251 require_quiet(ring
, errOut
);
253 //grab the backup slice from the ring
254 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
255 require_quiet(backupSliceData
, errOut
);
257 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
258 require_quiet(backupSlice
, errOut
);
260 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
261 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
263 SOSPeerInfoRef myPeerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
264 require_quiet(isSOSPeerInfo(myPeerInBSKB
), errOut
);
266 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
267 CFDataRef myPeerInBSKBBK
= SOSPeerInfoCopyBackupKey(myPeerInBSKB
);
268 result
= CFEqualSafe(myBK
, myPeerInBSKBBK
);
270 CFReleaseNull(myPeerInBSKBBK
);
276 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
278 CFReleaseNull(bsError
);
282 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account
, SOSPeerInfoRef testPeer
, CFStringRef viewname
){
284 CFErrorRef bsError
= NULL
;
285 CFDataRef backupSliceData
= NULL
;
286 SOSRingRef ring
= NULL
;
287 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
289 require_quiet(testPeer
, errOut
);
291 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
292 ring
= SOSAccountCopyRing(account
, ringName
, &bsError
);
293 CFReleaseNull(ringName
);
295 require_quiet(ring
, errOut
);
297 //grab the backup slice from the ring
298 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
299 require_quiet(backupSliceData
, errOut
);
301 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
302 require_quiet(backupSlice
, errOut
);
304 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
306 SOSPeerInfoRef peerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, testPeer
);
307 require_quiet(isSOSPeerInfo(peerInBSKB
), errOut
);
309 result
= CFEqualSafe(testPeer
, peerInBSKB
);
315 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
317 CFReleaseNull(bsError
);
322 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account
, SOSRingRef oldRing
, CFErrorRef
*error
){
324 CFSetRef viewNames
= SOSBackupRingGetViews(oldRing
, error
);
325 __block CFStringRef viewName
= NULL
;
326 require_quiet(viewNames
, fail
);
327 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames
), error
, CFSTR("Only support single view backup rings")), fail
);
329 CFSetForEach(viewNames
, ^(const void *value
) {
330 if (isString(value
)) {
331 viewName
= CFRetainSafe((CFStringRef
) value
);
335 result
= SOSAccountNewBKSBForView(account
, viewName
, error
);
338 CFReleaseNull(viewName
);
342 void SOSAccountForEachRingName(SOSAccountRef account
, void (^operation
)(CFStringRef value
)) {
343 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
345 CFSetRef allViews
= SOSViewCopyViewSet(kViewSetAll
); // All non virtual views.
347 CFSetForEach(allViews
, ^(const void *value
) {
348 CFStringRef viewName
= asString(value
, NULL
);
351 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
353 CFReleaseNull(ringName
);
356 CFReleaseNull(allViews
);
357 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
358 operation(kSOSRecoveryRing
);
362 void SOSAccountForEachBackupView(SOSAccountRef account
, void (^operation
)(const void *value
)) {
363 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
366 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
367 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
368 CFSetForEach(myBackupViews
, operation
);
369 CFReleaseNull(myBackupViews
);
374 bool SOSAccountSetBackupPublicKey(SOSAccountTransactionRef aTxn
, CFDataRef backupKey
, CFErrorRef
*error
)
376 SOSAccountRef account
= aTxn
->account
;
378 __block
bool result
= false;
380 CFDataPerformWithHexString(backupKey
, ^(CFStringRef backupKeyString
) {
381 CFDataPerformWithHexString(account
->backup_key
, ^(CFStringRef oldBackupKey
) {
382 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString
, oldBackupKey
);
386 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
388 if (CFEqualSafe(backupKey
, account
->backup_key
))
391 CFRetainAssign(account
->backup_key
, backupKey
);
393 account
->circle_rings_retirements_need_attention
= true;
399 secnotice("backupkey", "SetBackupPublic Failed: %@", error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space"));
404 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account
, CFArrayRef retiree
, CFStringRef viewName
, CFErrorRef
*error
,
405 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
406 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
409 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
410 CFMutableSetRef newPeerList
= CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(peers
), peers
);
411 CFArrayForEach(retiree
, ^(const void *value
) {
412 if (!isSOSPeerInfo(value
)) {
413 secerror("Peer list contains a non-peerInfo element");
415 SOSPeerInfoRef retiringPeer
= (SOSPeerInfoRef
)value
;
416 CFStringRef retiringPeerID
= SOSPeerInfoGetPeerID(retiringPeer
);
418 CFSetForEach(newPeerList
, ^(const void *peerFromAccount
) {
419 CFStringRef peerFromAccountID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)peerFromAccount
);
420 if (peerFromAccountID
&& retiringPeerID
&& CFStringCompare(peerFromAccountID
, retiringPeerID
, 0) == 0){
421 CFSetRemoveValue(newPeerList
, peerFromAccount
);
426 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, newPeerList
, error
);
427 CFReleaseNull(newPeerList
);
430 require_quiet(bskb
, exit
);
441 bool SOSAccountRemoveBackupPublickey(SOSAccountTransactionRef aTxn
, CFErrorRef
*error
)
443 SOSAccountRef account
= aTxn
->account
;
445 __block
bool result
= false;
446 __block CFArrayRef removals
= NULL
;
448 CFReleaseNull(account
->backup_key
);
450 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
451 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
452 return SOSFullPeerInfoUpdateBackupKey(fpi
, NULL
, error
);
455 removals
= CFArrayCreateForCFTypes(kCFAllocatorDefault
,
456 SOSAccountGetMyPeerInfo(account
), NULL
);
458 SOSAccountForEachBackupView(account
, ^(const void *value
) {
459 CFStringRef viewName
= (CFStringRef
)value
;
460 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
461 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
474 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account
, CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
475 __block
bool result
= false;
476 SOSBackupSliceKeyBagRef backup_slice
= NULL
;
478 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
481 result
= SOSSaveV0Keybag(aks_bag
, error
);
482 require_action_quiet(result
, exit
, secnotice("keybag", "failed to set V0 keybag (%@)", *error
));
486 backup_slice
= SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault
, aks_bag
, error
);
488 SOSAccountForEachBackupView(account
, ^(const void *value
) {
489 CFStringRef viewname
= (CFStringRef
) value
;
490 result
&= SOSAccountSetKeybagForViewBackupRing(account
, viewname
, backup_slice
, error
);
495 CFReleaseNull(backup_slice
);
499 static CFMutableArrayRef
SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account
, CFArrayRef peers
, CFSetRef peersInBackup
){
500 CFMutableArrayRef removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
502 CFSetForEach(peersInBackup
, ^(const void *value
) {
503 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
504 CFArrayForEach(peers
, ^(const void *value
) {
505 CFStringRef peerID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)value
);
506 CFStringRef piPeerID
= SOSPeerInfoGetPeerID(peer
);
507 if (peerID
&& piPeerID
&& CFStringCompare(piPeerID
, peerID
, 0) == 0){
508 CFArrayAppendValue(removals
, peer
);
518 bool SOSAccountRemoveBackupPeers(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
*error
){
519 __block
bool result
= true;
521 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
522 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
524 CFSetRef permittedViews
= SOSPeerInfoGetPermittedViews(myPeer
);
525 CFSetForEach(permittedViews
, ^(const void *value
) {
526 CFStringRef viewName
= (CFStringRef
)value
;
527 if(SOSPeerInfoIsViewBackupEnabled(myPeer
, viewName
)){
528 //grab current peers list
529 CFSetRef peersInBackup
= SOSAccountCopyBackupPeersForView(account
, viewName
);
530 //get peer infos that have retired but are still in the backup peer list
531 CFMutableArrayRef removals
= SOSAccountIsRetiredPeerIDInBackupPeerList(account
, peers
, peersInBackup
);
532 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
533 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
543 SOSBackupSliceKeyBagRef
SOSAccountBackupSliceKeyBagForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
* error
){
544 CFDataRef backupSliceData
= NULL
;
545 CFStringRef ringName
= NULL
;
546 SOSRingRef ring
= NULL
;
547 SOSBackupSliceKeyBagRef bskb
= NULL
;
549 ringName
= SOSBackupCopyRingNameForView(viewName
);
551 ring
= SOSAccountCopyRing(account
, ringName
, NULL
);
552 require_action_quiet(ring
, exit
, SOSCreateErrorWithFormat(kSOSErrorNoCircle
, NULL
, error
, NULL
, CFSTR("failed to get ring")));
554 //grab the backup slice from the ring
555 backupSliceData
= SOSRingGetPayload(ring
, error
);
556 require_action_quiet(backupSliceData
, exit
, secnotice("backup", "failed to get backup slice (%@)", *error
));
558 bskb
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, error
);
562 CFReleaseNull(ringName
);
567 bool SOSAccountIsLastBackupPeer(SOSAccountRef account
, CFErrorRef
*error
) {
568 __block
bool retval
= false;
569 SOSPeerInfoRef pi
= SOSAccountGetMyPeerInfo(account
);
570 require_quiet(SOSPeerInfoHasBackupKey(pi
), errOut
);
571 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
572 require_quiet(SOSAccountIsInCircle(account
, error
), errOut
);
573 require_action_quiet(SOSCircleCountValidSyncingPeers(circle
, SOSAccountGetTrustedPublicCredential(account
, error
)) != 1, errOut
, retval
= true);
574 // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
575 SOSAccountForEachBackupView(account
, ^(const void *value
) {
576 CFStringRef viewname
= (CFStringRef
) value
;
577 SOSBackupSliceKeyBagRef keybag
= SOSAccountBackupSliceKeyBagForView(account
, viewname
, error
);
578 require_quiet(keybag
, inner_errOut
);
579 retval
|= ((SOSBSKBCountPeers(keybag
) == 1) && (SOSBSKBPeerIsInKeyBag(keybag
, pi
)));
581 CFReleaseNull(keybag
);