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"
16 // MARK: V0 Keybag keychain stuff
18 static bool SecItemUpdateOrAdd(CFDictionaryRef query
, CFDictionaryRef update
, CFErrorRef
*error
)
20 OSStatus saveStatus
= SecItemUpdate(query
, update
);
22 if (errSecItemNotFound
== saveStatus
) {
23 CFMutableDictionaryRef add
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, query
);
24 CFDictionaryForEach(update
, ^(const void *key
, const void *value
) {
25 CFDictionaryAddValue(add
, key
, value
);
27 saveStatus
= SecItemAdd(add
, NULL
);
31 return SecError(saveStatus
, error
, CFSTR("Error saving %@"), query
);
34 static CFDictionaryRef
SOSCopyV0Attributes() {
35 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
36 kSecClass
, kSecClassGenericPassword
,
37 kSecAttrAccessGroup
, CFSTR("com.apple.sbd"),
38 kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
,
39 kSecAttrAccount
, CFSTR("SecureBackupPublicKeybag"),
40 kSecAttrService
, CFSTR("SecureBackupService"),
41 kSecAttrSynchronizable
, kCFBooleanTrue
,
45 bool SOSDeleteV0Keybag(CFErrorRef
*error
) {
46 CFDictionaryRef attributes
= SOSCopyV0Attributes();
48 OSStatus result
= SecItemDelete(attributes
);
50 CFReleaseNull(attributes
);
52 return SecError(result
!= errSecItemNotFound
? result
: errSecSuccess
, error
, CFSTR("Deleting V0 Keybag failed - %ld"), result
);
55 static bool SOSSaveV0Keybag(CFDataRef v0Keybag
, CFErrorRef
*error
) {
56 CFDictionaryRef attributes
= SOSCopyV0Attributes();
58 CFDictionaryRef update
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
59 kSecValueData
, v0Keybag
,
63 bool result
= SecItemUpdateOrAdd(attributes
, update
, error
);
64 CFReleaseNull(attributes
);
65 CFReleaseNull(update
);
71 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo
, CFStringRef viewName
) {
72 if (CFEqualSafe(kSOSViewKeychainV0
, viewName
))
75 return SOSPeerInfoHasBackupKey(peerInfo
) && SOSPeerInfoIsViewPermitted(peerInfo
, viewName
);
78 static CFSetRef
SOSAccountCopyBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
) {
79 CFMutableSetRef backupPeers
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
81 SOSCircleRef circle
= SOSAccountGetCircle(account
, NULL
);
83 require_quiet(circle
, exit
);
85 SOSCircleForEachValidPeer(circle
, account
->user_public
, ^(SOSPeerInfoRef peer
) {
86 if (SOSPeerInfoIsViewBackupEnabled(peer
, viewName
))
87 CFSetAddValue(backupPeers
, peer
);
94 static void SOSAccountWithBackupPeersForView(SOSAccountRef account
, CFStringRef viewName
, void (^action
)(CFSetRef peers
)) {
95 CFSetRef backupPeersForView
= SOSAccountCopyBackupPeersForView(account
, viewName
);
97 action(backupPeersForView
);
99 CFReleaseNull(backupPeersForView
);
102 static bool SOSAccountWithBSKBForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
103 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
104 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
107 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
108 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, peers
, error
);
111 require_quiet(bskb
, exit
);
122 CFStringRef
SOSBackupCopyRingNameForView(CFStringRef viewName
) {
123 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@-tomb"), viewName
);
126 static bool SOSAccountUpdateNamedRing(SOSAccountRef account
, CFStringRef ringName
, CFErrorRef
*error
,
127 SOSRingRef (^create
)(CFStringRef ringName
, CFErrorRef
*error
),
128 SOSRingRef (^copyModified
)(SOSRingRef existing
, CFErrorRef
*error
)) {
130 SOSRingRef found
= SOSAccountCopyRing(account
, ringName
, error
);
131 SOSRingRef newRing
= NULL
;
133 found
= create(ringName
, error
);
135 require_quiet(found
, errOut
);
136 newRing
= copyModified(found
, error
);
137 CFReleaseNull(found
);
139 require_quiet(newRing
, errOut
);
141 result
= SOSAccountHandleUpdateRing(account
, newRing
, true, error
);
144 CFReleaseNull(found
);
145 CFReleaseNull(newRing
);
149 static bool SOSAccountUpdateBackupRing(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
,
150 SOSRingRef (^modify
)(SOSRingRef existing
, CFErrorRef
*error
)) {
152 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
154 bool result
= SOSAccountUpdateNamedRing(account
, ringName
, error
, ^SOSRingRef(CFStringRef ringName
, CFErrorRef
*error
) {
155 return SOSRingCreate(ringName
, SOSAccountGetMyPeerID(account
), kSOSRingBackup
, error
);
158 CFReleaseNull(ringName
);
163 static CFSetRef
SOSAccountCopyPeerSetForView(SOSAccountRef account
, CFStringRef viewName
) {
164 CFMutableSetRef result
= CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault
);
166 if (account
->trusted_circle
) {
167 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
168 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer
), viewName
)) {
169 CFSetAddValue(result
, peer
);
177 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccountRef account
, CFStringRef viewName
, SOSBackupSliceKeyBagRef keyBag
, CFErrorRef
*error
) {
178 CFMutableSetRef backupViewSet
= CFSetCreateMutableForCFTypes(NULL
);
180 require_quiet(SecAllocationError(backupViewSet
, error
, CFSTR("No backup view set created")), errOut
);
181 CFSetAddValue(backupViewSet
, viewName
);
183 result
= SOSAccountUpdateBackupRing(account
, viewName
, error
, ^SOSRingRef(SOSRingRef existing
, CFErrorRef
*error
) {
184 SOSRingRef newRing
= NULL
;
185 CFSetRef viewPeerSet
= SOSAccountCopyPeerSetForView(account
, viewName
);
186 CFMutableSetRef cleared
= CFSetCreateMutableForCFTypes(NULL
);
188 SOSRingSetPeerIDs(existing
, cleared
);
189 SOSRingAddAll(existing
, viewPeerSet
);
191 require_quiet(SOSRingSetBackupKeyBag(existing
, SOSAccountGetMyFullPeerInfo(account
), backupViewSet
, keyBag
, error
), exit
);
193 newRing
= CFRetainSafe(existing
);
195 CFReleaseNull(viewPeerSet
);
196 CFReleaseNull(cleared
);
202 if (result
&& NULL
!= error
&& NULL
!= *error
) {
203 secerror("Got Success and Error (dropping error): %@", *error
);
204 CFReleaseNull(*error
);
208 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName
, error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space."));
211 CFReleaseNull(backupViewSet
);
215 bool SOSAccountNewBKSBForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
*error
)
217 return SOSAccountWithBSKBForView(account
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
218 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
223 bool SOSAccountIsBackupRingEmpty(SOSAccountRef account
, CFStringRef viewName
) {
224 CFStringRef backupRing
= SOSBackupCopyRingNameForView(viewName
);
225 SOSRingRef ring
= SOSAccountCopyRing(account
, backupRing
, NULL
);
226 CFReleaseNull(backupRing
);
228 if(ring
) peercnt
= SOSRingCountPeers(ring
);
233 bool SOSAccountUpdatePeerInfo(SOSAccountRef account
, CFStringRef updateDescription
, CFErrorRef
*error
, bool (^update
)(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
)) {
234 if (account
->my_identity
== NULL
)
237 bool result
= update(account
->my_identity
, error
);
239 if (result
&& SOSAccountHasCircle(account
, NULL
)) {
240 return SOSAccountModifyCircle(account
, error
, ^(SOSCircleRef circle_to_change
) {
241 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription
);
242 return SOSCircleUpdatePeerInfo(circle_to_change
, SOSAccountGetMyPeerInfo(account
));
249 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account
, CFStringRef viewname
){
251 CFErrorRef bsError
= NULL
;
252 CFDataRef backupSliceData
= NULL
;
253 SOSRingRef ring
= NULL
;
254 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
256 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account
), viewname
), errOut
);
258 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
259 ring
= SOSAccountCopyRing(account
, ringName
, &bsError
);
260 CFReleaseNull(ringName
);
262 require_quiet(ring
, errOut
);
264 //grab the backup slice from the ring
265 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
266 require_quiet(backupSliceData
, errOut
);
268 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
269 require_quiet(backupSlice
, errOut
);
271 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
272 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
274 SOSPeerInfoRef myPeerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, myPeer
);
275 require_quiet(isSOSPeerInfo(myPeerInBSKB
), errOut
);
277 CFDataRef myBK
= SOSPeerInfoCopyBackupKey(myPeer
);
278 CFDataRef myPeerInBSKBBK
= SOSPeerInfoCopyBackupKey(myPeerInBSKB
);
279 result
= CFEqualSafe(myBK
, myPeerInBSKBBK
);
281 CFReleaseNull(myPeerInBSKBBK
);
287 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
289 CFReleaseNull(bsError
);
293 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account
, SOSPeerInfoRef testPeer
, CFStringRef viewname
){
295 CFErrorRef bsError
= NULL
;
296 CFDataRef backupSliceData
= NULL
;
297 SOSRingRef ring
= NULL
;
298 SOSBackupSliceKeyBagRef backupSlice
= NULL
;
300 require_quiet(testPeer
, errOut
);
302 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewname
);
303 ring
= SOSAccountCopyRing(account
, ringName
, &bsError
);
304 CFReleaseNull(ringName
);
306 require_quiet(ring
, errOut
);
308 //grab the backup slice from the ring
309 backupSliceData
= SOSRingGetPayload(ring
, &bsError
);
310 require_quiet(backupSliceData
, errOut
);
312 backupSlice
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, &bsError
);
313 require_quiet(backupSlice
, errOut
);
315 CFSetRef peers
= SOSBSKBGetPeers(backupSlice
);
317 SOSPeerInfoRef peerInBSKB
= (SOSPeerInfoRef
) CFSetGetValue(peers
, testPeer
);
318 require_quiet(isSOSPeerInfo(peerInBSKB
), errOut
);
320 result
= CFEqualSafe(testPeer
, peerInBSKB
);
326 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData
, backupSlice
, bsError
);
328 CFReleaseNull(bsError
);
333 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account
, SOSRingRef oldRing
, CFErrorRef
*error
){
335 CFSetRef viewNames
= SOSBackupRingGetViews(oldRing
, error
);
336 __block CFStringRef viewName
= NULL
;
337 require_quiet(viewNames
, fail
);
338 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames
), error
, CFSTR("Only support single view backup rings")), fail
);
340 CFSetForEach(viewNames
, ^(const void *value
) {
341 if (isString(value
)) {
342 viewName
= CFRetainSafe((CFStringRef
) value
);
346 result
= SOSAccountNewBKSBForView(account
, viewName
, error
);
349 CFReleaseNull(viewName
);
353 void SOSAccountForEachBackupRingName(SOSAccountRef account
, void (^operation
)(CFStringRef value
)) {
354 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
356 CFSetRef allViews
= SOSViewCopyViewSet(kViewSetAll
); // All non virtual views.
358 CFSetForEach(allViews
, ^(const void *value
) {
359 CFStringRef viewName
= asString(value
, NULL
);
362 CFStringRef ringName
= SOSBackupCopyRingNameForView(viewName
);
364 CFReleaseNull(ringName
);
368 CFReleaseNull(allViews
);
372 void SOSAccountForEachBackupView(SOSAccountRef account
, void (^operation
)(const void *value
)) {
373 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
376 CFMutableSetRef myBackupViews
= CFSetCreateMutableCopy(kCFAllocatorDefault
, 0, SOSPeerInfoGetPermittedViews(myPeer
));
377 CFSetRemoveValue(myBackupViews
, kSOSViewKeychainV0
);
378 CFSetForEach(myBackupViews
, operation
);
379 CFReleaseNull(myBackupViews
);
383 bool SOSAccountSetBackupPublicKey(SOSAccountTransactionRef aTxn
, CFDataRef backupKey
, CFErrorRef
*error
)
385 SOSAccountRef account
= aTxn
->account
;
387 __block
bool result
= false;
389 CFDataPerformWithHexString(backupKey
, ^(CFStringRef backupKeyString
) {
390 CFDataPerformWithHexString(account
->backup_key
, ^(CFStringRef oldBackupKey
) {
391 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString
, oldBackupKey
);
395 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
397 if (CFEqualSafe(backupKey
, account
->backup_key
))
400 CFRetainAssign(account
->backup_key
, backupKey
);
402 account
->circle_rings_retirements_need_attention
= true;
408 secnotice("backupkey", "SetBackupPublic Failed: %@", error
? (CFTypeRef
) *error
: (CFTypeRef
) CFSTR("No error space"));
413 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account
, CFArrayRef retiree
, CFStringRef viewName
, CFErrorRef
*error
,
414 bool (^action
)(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
)) {
415 __block SOSBackupSliceKeyBagRef bskb
= NULL
;
418 SOSAccountWithBackupPeersForView(account
, viewName
, ^(CFSetRef peers
) {
419 CFMutableSetRef newPeerList
= CFSetCreateMutableCopy(kCFAllocatorDefault
, CFSetGetCount(peers
), peers
);
420 CFArrayForEach(retiree
, ^(const void *value
) {
421 if (!isSOSPeerInfo(value
)) {
422 secerror("Peer list contains a non-peerInfo element");
424 SOSPeerInfoRef retiringPeer
= (SOSPeerInfoRef
)value
;
425 CFStringRef retiringPeerID
= SOSPeerInfoGetPeerID(retiringPeer
);
427 CFSetForEach(newPeerList
, ^(const void *peerFromAccount
) {
428 CFStringRef peerFromAccountID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)peerFromAccount
);
429 if (peerFromAccountID
&& retiringPeerID
&& CFStringCompare(peerFromAccountID
, retiringPeerID
, 0) == 0){
430 CFSetRemoveValue(newPeerList
, peerFromAccount
);
435 bskb
= SOSBackupSliceKeyBagCreate(kCFAllocatorDefault
, newPeerList
, error
);
436 CFReleaseNull(newPeerList
);
439 require_quiet(bskb
, exit
);
450 bool SOSAccountRemoveBackupPublickey(SOSAccountTransactionRef aTxn
, CFErrorRef
*error
)
452 SOSAccountRef account
= aTxn
->account
;
454 __block
bool result
= false;
455 __block CFArrayRef removals
= NULL
;
457 CFReleaseNull(account
->backup_key
);
459 require_quiet(SOSAccountUpdatePeerInfo(account
, CFSTR("Backup public key"), error
,
460 ^bool(SOSFullPeerInfoRef fpi
, CFErrorRef
*error
) {
461 return SOSFullPeerInfoUpdateBackupKey(fpi
, NULL
, error
);
464 removals
= CFArrayCreateForCFTypes(kCFAllocatorDefault
,
465 SOSAccountGetMyPeerInfo(account
), NULL
);
467 SOSAccountForEachBackupView(account
, ^(const void *value
) {
468 CFStringRef viewName
= (CFStringRef
)value
;
469 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
470 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
483 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account
, CFDataRef aks_bag
, bool setupV0Only
, CFErrorRef
*error
){
484 __block
bool result
= false;
485 SOSBackupSliceKeyBagRef backup_slice
= NULL
;
487 require_quiet(SOSAccountIsInCircle(account
, error
), exit
);
490 result
= SOSSaveV0Keybag(aks_bag
, error
);
491 require_action_quiet(result
, exit
, secnotice("keybag", "failed to set V0 keybag (%@)", *error
));
495 backup_slice
= SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault
, aks_bag
, error
);
497 SOSAccountForEachBackupView(account
, ^(const void *value
) {
498 CFStringRef viewname
= (CFStringRef
) value
;
499 result
&= SOSAccountSetKeybagForViewBackupRing(account
, viewname
, backup_slice
, error
);
504 CFReleaseNull(backup_slice
);
508 static CFMutableArrayRef
SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account
, CFArrayRef peers
, CFSetRef peersInBackup
){
509 CFMutableArrayRef removals
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
511 CFSetForEach(peersInBackup
, ^(const void *value
) {
512 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
513 CFArrayForEach(peers
, ^(const void *value
) {
514 CFStringRef peerID
= SOSPeerInfoGetPeerID((SOSPeerInfoRef
)value
);
515 CFStringRef piPeerID
= SOSPeerInfoGetPeerID(peer
);
516 if (peerID
&& piPeerID
&& CFStringCompare(piPeerID
, peerID
, 0) == 0){
517 CFArrayAppendValue(removals
, peer
);
527 bool SOSAccountRemoveBackupPeers(SOSAccountRef account
, CFArrayRef peers
, CFErrorRef
*error
){
528 __block
bool result
= true;
530 SOSFullPeerInfoRef fpi
= SOSAccountGetMyFullPeerInfo(account
);
531 SOSPeerInfoRef myPeer
= SOSFullPeerInfoGetPeerInfo(fpi
);
533 CFSetRef permittedViews
= SOSPeerInfoGetPermittedViews(myPeer
);
534 CFSetForEach(permittedViews
, ^(const void *value
) {
535 CFStringRef viewName
= (CFStringRef
)value
;
536 if(SOSPeerInfoIsViewBackupEnabled(myPeer
, viewName
)){
537 //grab current peers list
538 CFSetRef peersInBackup
= SOSAccountCopyBackupPeersForView(account
, viewName
);
539 //get peer infos that have retired but are still in the backup peer list
540 CFMutableArrayRef removals
= SOSAccountIsRetiredPeerIDInBackupPeerList(account
, peers
, peersInBackup
);
541 result
= SOSAccountWithBSKBAndPeerInfosForView(account
, removals
, viewName
, error
, ^(SOSBackupSliceKeyBagRef bskb
, CFErrorRef
*error
) {
542 bool result
= SOSAccountSetKeybagForViewBackupRing(account
, viewName
, bskb
, error
);
552 SOSBackupSliceKeyBagRef
SOSAccountBackupSliceKeyBagForView(SOSAccountRef account
, CFStringRef viewName
, CFErrorRef
* error
){
553 CFDataRef backupSliceData
= NULL
;
554 CFStringRef ringName
= NULL
;
555 SOSRingRef ring
= NULL
;
556 SOSBackupSliceKeyBagRef bskb
= NULL
;
558 ringName
= SOSBackupCopyRingNameForView(viewName
);
560 ring
= SOSAccountCopyRing(account
, ringName
, NULL
);
561 require_action_quiet(ring
, exit
, SOSCreateErrorWithFormat(kSOSErrorNoCircle
, NULL
, error
, NULL
, CFSTR("failed to get ring")));
563 //grab the backup slice from the ring
564 backupSliceData
= SOSRingGetPayload(ring
, error
);
565 require_action_quiet(backupSliceData
, exit
, secnotice("backup", "failed to get backup slice (%@)", *error
));
567 bskb
= SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault
, backupSliceData
, error
);
571 CFReleaseNull(ringName
);
576 bool SOSAccountIsLastBackupPeer(SOSAccountRef account
, CFErrorRef
*error
) {
577 __block
bool retval
= false;
578 SOSPeerInfoRef pi
= SOSAccountGetMyPeerInfo(account
);
579 require_quiet(SOSPeerInfoHasBackupKey(pi
), errOut
);
580 SOSCircleRef circle
= SOSAccountGetCircle(account
, error
);
581 require_quiet(SOSAccountIsInCircle(account
, error
), errOut
);
582 require_action_quiet(SOSCircleCountValidSyncingPeers(circle
, SOSAccountGetTrustedPublicCredential(account
, error
)) != 1, errOut
, retval
= true);
583 // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
584 SOSAccountForEachBackupView(account
, ^(const void *value
) {
585 CFStringRef viewname
= (CFStringRef
) value
;
586 SOSBackupSliceKeyBagRef keybag
= SOSAccountBackupSliceKeyBagForView(account
, viewname
, error
);
587 require_quiet(keybag
, inner_errOut
);
588 retval
|= ((SOSBSKBCountPeers(keybag
) == 1) && (SOSBSKBPeerIsInKeyBag(keybag
, pi
)));
590 CFReleaseNull(keybag
);