]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountBackup.c
f27550f4be7f5cea68e52272891a284cde3ed111
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountBackup.c
1 //
2 // SOSAccountCircles.c
3 // sec
4 //
5
6 #include "SOSAccountPriv.h"
7 #include "SOSCloudKeychainClient.h"
8
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSViews.h>
12
13 #include "SOSInternal.h"
14
15
16
17 //
18 // MARK: V0 Keybag keychain stuff
19 //
20 static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
21 {
22 OSStatus saveStatus = SecItemUpdate(query, update);
23
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);
28 });
29 saveStatus = SecItemAdd(add, NULL);
30 CFReleaseNull(add);
31 }
32
33 return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
34 }
35
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,
44 NULL);
45 }
46
47 bool SOSDeleteV0Keybag(CFErrorRef *error) {
48 CFDictionaryRef attributes = SOSCopyV0Attributes();
49
50 OSStatus result = SecItemDelete(attributes);
51
52 CFReleaseNull(attributes);
53
54 return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %ld"), result);
55 }
56
57 static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
58 CFDictionaryRef attributes = SOSCopyV0Attributes();
59
60 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
61 kSecValueData, v0Keybag,
62 NULL);
63
64
65 bool result = SecItemUpdateOrAdd(attributes, update, error);
66 CFReleaseNull(attributes);
67 CFReleaseNull(update);
68
69 return result;
70 }
71
72
73 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
74 if (CFEqualSafe(kSOSViewKeychainV0, viewName))
75 return false;
76
77 return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
78 }
79
80 static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccountRef account, CFStringRef viewName) {
81 CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
82
83 SOSCircleRef circle = SOSAccountGetCircle(account, NULL);
84
85 require_quiet(circle, exit);
86
87 SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
88 if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
89 CFSetAddValue(backupPeers, peer);
90 });
91
92 exit:
93 return backupPeers;
94 }
95
96 static void SOSAccountWithBackupPeersForView(SOSAccountRef account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
97 CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
98
99 action(backupPeersForView);
100
101 CFReleaseNull(backupPeersForView);
102 }
103
104
105 static bool SOSAccountWithBSKBForView(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error,
106 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
107 __block SOSBackupSliceKeyBagRef bskb = NULL;
108 bool result = false;
109 CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, error);
110
111 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
112 if(! rkbg) {
113 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
114 } else {
115 CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
116 CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, rkbg);
117 bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, peers, additionalKeys, error);
118 CFReleaseNull(additionalKeys);
119 }
120 });
121 CFReleaseNull(rkbg);
122
123 require_quiet(bskb, exit);
124
125 action(bskb, error);
126
127 result = true;
128
129 exit:
130 CFReleaseNull(bskb);
131 return result;
132 }
133
134 CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
135 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
136 }
137
138 static bool SOSAccountUpdateBackupRing(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error,
139 SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
140
141 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
142
143 bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
144 return SOSRingCreate(ringName, SOSAccountGetMyPeerID(account), kSOSRingBackup, error);
145 }, modify);
146
147 CFReleaseNull(ringName);
148
149 return result;
150 }
151
152 static CFSetRef SOSAccountCopyPeerSetForView(SOSAccountRef account, CFStringRef viewName) {
153 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
154
155 if (account->trusted_circle) {
156 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
157 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer), viewName)) {
158 CFSetAddValue(result, peer);
159 }
160 });
161 }
162
163 return result;
164 }
165
166 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccountRef account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
167 CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
168 bool result = false;
169 require_quiet(SecAllocationError(backupViewSet, error, CFSTR("No backup view set created")), errOut);
170 CFSetAddValue(backupViewSet, viewName);
171
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);
176
177 SOSRingSetPeerIDs(existing, cleared);
178 SOSRingAddAll(existing, viewPeerSet);
179
180 require_quiet(SOSRingSetBackupKeyBag(existing, SOSAccountGetMyFullPeerInfo(account), backupViewSet, keyBag, error), exit);
181
182 newRing = CFRetainSafe(existing);
183 exit:
184 CFReleaseNull(viewPeerSet);
185 CFReleaseNull(cleared);
186 return newRing;
187 });
188
189 errOut:
190
191 if (result && NULL != error && NULL != *error) {
192 secerror("Got Success and Error (dropping error): %@", *error);
193 CFReleaseNull(*error);
194 }
195
196 if (!result) {
197 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
198 }
199
200 CFReleaseNull(backupViewSet);
201 return result;
202 }
203
204 bool SOSAccountNewBKSBForView(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error)
205 {
206 return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
207 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
208 return result;
209 });
210 }
211
212 bool SOSAccountIsBackupRingEmpty(SOSAccountRef account, CFStringRef viewName) {
213 CFStringRef backupRing = SOSBackupCopyRingNameForView(viewName);
214 SOSRingRef ring = SOSAccountCopyRing(account, backupRing, NULL);
215 CFReleaseNull(backupRing);
216 int peercnt = 0;
217 if(ring) peercnt = SOSRingCountPeers(ring);
218 CFReleaseNull(ring);
219 return peercnt == 0;
220 }
221
222 bool SOSAccountUpdatePeerInfo(SOSAccountRef account, CFStringRef updateDescription, CFErrorRef *error, bool (^update)(SOSFullPeerInfoRef fpi, CFErrorRef *error)) {
223 if (account->my_identity == NULL)
224 return true;
225
226 bool result = update(account->my_identity, error);
227
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));
232 });
233 }
234
235 return result;
236 }
237
238 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account, CFStringRef viewname){
239 bool result = false;
240 CFErrorRef bsError = NULL;
241 CFDataRef backupSliceData = NULL;
242 SOSRingRef ring = NULL;
243 SOSBackupSliceKeyBagRef backupSlice = NULL;
244
245 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account), viewname), errOut);
246
247 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
248 ring = SOSAccountCopyRing(account, ringName, &bsError);
249 CFReleaseNull(ringName);
250
251 require_quiet(ring, errOut);
252
253 //grab the backup slice from the ring
254 backupSliceData = SOSRingGetPayload(ring, &bsError);
255 require_quiet(backupSliceData, errOut);
256
257 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
258 require_quiet(backupSlice, errOut);
259
260 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
261 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
262
263 SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
264 require_quiet(isSOSPeerInfo(myPeerInBSKB), errOut);
265
266 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
267 CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
268 result = CFEqualSafe(myBK, myPeerInBSKBBK);
269 CFReleaseNull(myBK);
270 CFReleaseNull(myPeerInBSKBBK);
271
272 errOut:
273 CFReleaseNull(ring);
274
275 if (bsError) {
276 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
277 }
278 CFReleaseNull(bsError);
279 return result;
280 }
281
282 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account, SOSPeerInfoRef testPeer, CFStringRef viewname){
283 bool result = false;
284 CFErrorRef bsError = NULL;
285 CFDataRef backupSliceData = NULL;
286 SOSRingRef ring = NULL;
287 SOSBackupSliceKeyBagRef backupSlice = NULL;
288
289 require_quiet(testPeer, errOut);
290
291 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
292 ring = SOSAccountCopyRing(account, ringName, &bsError);
293 CFReleaseNull(ringName);
294
295 require_quiet(ring, errOut);
296
297 //grab the backup slice from the ring
298 backupSliceData = SOSRingGetPayload(ring, &bsError);
299 require_quiet(backupSliceData, errOut);
300
301 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
302 require_quiet(backupSlice, errOut);
303
304 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
305
306 SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
307 require_quiet(isSOSPeerInfo(peerInBSKB), errOut);
308
309 result = CFEqualSafe(testPeer, peerInBSKB);
310
311 errOut:
312 CFReleaseNull(ring);
313
314 if (bsError) {
315 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
316 }
317 CFReleaseNull(bsError);
318 return result;
319
320 }
321
322 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account, SOSRingRef oldRing, CFErrorRef *error){
323 bool result = false;
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);
328
329 CFSetForEach(viewNames, ^(const void *value) {
330 if (isString(value)) {
331 viewName = CFRetainSafe((CFStringRef) value);
332 }
333 });
334
335 result = SOSAccountNewBKSBForView(account, viewName, error);
336
337 fail:
338 CFReleaseNull(viewName);
339 return result;
340 }
341
342 void SOSAccountForEachRingName(SOSAccountRef account, void (^operation)(CFStringRef value)) {
343 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
344 if (myPeer) {
345 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
346
347 CFSetForEach(allViews, ^(const void *value) {
348 CFStringRef viewName = asString(value, NULL);
349
350 if (viewName) {
351 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
352 operation(ringName);
353 CFReleaseNull(ringName);
354 }
355 });
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);
359 }
360 }
361
362 void SOSAccountForEachBackupView(SOSAccountRef account, void (^operation)(const void *value)) {
363 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
364
365 if (myPeer) {
366 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
367 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
368 CFSetForEach(myBackupViews, operation);
369 CFReleaseNull(myBackupViews);
370 }
371 }
372
373
374 bool SOSAccountSetBackupPublicKey(SOSAccountTransactionRef aTxn, CFDataRef backupKey, CFErrorRef *error)
375 {
376 SOSAccountRef account = aTxn->account;
377
378 __block bool result = false;
379
380 CFDataPerformWithHexString(backupKey, ^(CFStringRef backupKeyString) {
381 CFDataPerformWithHexString(account->backup_key, ^(CFStringRef oldBackupKey) {
382 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString, oldBackupKey);
383 });
384 });
385
386 require_quiet(SOSAccountIsInCircle(account, error), exit);
387
388 if (CFEqualSafe(backupKey, account->backup_key))
389 return true;
390
391 CFRetainAssign(account->backup_key, backupKey);
392
393 account->circle_rings_retirements_need_attention = true;
394
395 result = true;
396
397 exit:
398 if (!result) {
399 secnotice("backupkey", "SetBackupPublic Failed: %@", error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space"));
400 }
401 return result;
402 }
403
404 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
405 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
406 __block SOSBackupSliceKeyBagRef bskb = NULL;
407 bool result = false;
408
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");
414 } else {
415 SOSPeerInfoRef retiringPeer = (SOSPeerInfoRef)value;
416 CFStringRef retiringPeerID = SOSPeerInfoGetPeerID(retiringPeer);
417
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);
422 }
423 });
424 }
425 });
426 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
427 CFReleaseNull(newPeerList);
428 });
429
430 require_quiet(bskb, exit);
431
432 action(bskb, error);
433
434 result = true;
435
436 exit:
437 CFReleaseNull(bskb);
438 return result;
439 }
440
441 bool SOSAccountRemoveBackupPublickey(SOSAccountTransactionRef aTxn, CFErrorRef *error)
442 {
443 SOSAccountRef account = aTxn->account;
444
445 __block bool result = false;
446 __block CFArrayRef removals = NULL;
447
448 CFReleaseNull(account->backup_key);
449
450 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
451 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
452 return SOSFullPeerInfoUpdateBackupKey(fpi, NULL, error);
453 }), exit);
454
455 removals = CFArrayCreateForCFTypes(kCFAllocatorDefault,
456 SOSAccountGetMyPeerInfo(account), NULL);
457
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);
462 return result;
463 });
464 });
465
466
467 result = true;
468
469 exit:
470 return result;
471
472 }
473
474 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account, CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
475 __block bool result = false;
476 SOSBackupSliceKeyBagRef backup_slice = NULL;
477
478 require_quiet(SOSAccountIsInCircle(account, error), exit);
479
480 if (setupV0Only) {
481 result = SOSSaveV0Keybag(aks_bag, error);
482 require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
483 } else {
484 result = true;
485
486 backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
487
488 SOSAccountForEachBackupView(account, ^(const void *value) {
489 CFStringRef viewname = (CFStringRef) value;
490 result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
491 });
492 }
493
494 exit:
495 CFReleaseNull(backup_slice);
496 return result;
497 }
498
499 static CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account, CFArrayRef peers, CFSetRef peersInBackup){
500 CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
501
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);
509 }
510 });
511
512 });
513
514 return removals;
515
516 }
517
518 bool SOSAccountRemoveBackupPeers(SOSAccountRef account, CFArrayRef peers, CFErrorRef *error){
519 __block bool result = true;
520
521 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
522 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
523
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);
534 return result;
535 });
536 }
537 });
538
539 return result;
540
541 }
542
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;
548
549 ringName = SOSBackupCopyRingNameForView(viewName);
550
551 ring = SOSAccountCopyRing(account, ringName, NULL);
552 require_action_quiet(ring, exit, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("failed to get ring")));
553
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));
557
558 bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, error);
559
560 exit:
561 CFReleaseNull(ring);
562 CFReleaseNull(ringName);
563
564 return bskb;
565 }
566
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)));
580 inner_errOut:
581 CFReleaseNull(keybag);
582 });
583
584 errOut:
585 return retval;
586 }