]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountBackup.m
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccountBackup.m
1 //
2 // SOSAccountCircles.c
3 // sec
4 //
5
6 #include "SOSAccount.h"
7 #include "SOSCloudKeychainClient.h"
8
9 #include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoCollections.h>
11 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
12 #include <Security/SecureObjectSync/SOSViews.h>
13 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
14 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h>
15
16 #include "SOSInternal.h"
17
18
19
20 //
21 // MARK: V0 Keybag keychain stuff
22 //
23 static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
24 {
25 OSStatus saveStatus = SecItemUpdate(query, update);
26
27 if (errSecItemNotFound == saveStatus) {
28 CFMutableDictionaryRef add = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, query);
29 CFDictionaryForEach(update, ^(const void *key, const void *value) {
30 CFDictionaryAddValue(add, key, value);
31 });
32 saveStatus = SecItemAdd(add, NULL);
33 CFReleaseNull(add);
34 }
35
36 return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
37 }
38
39 static CFDictionaryRef SOSCopyV0Attributes() {
40 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
41 kSecClass, kSecClassGenericPassword,
42 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
43 kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked,
44 kSecAttrAccount, CFSTR("SecureBackupPublicKeybag"),
45 kSecAttrService, CFSTR("SecureBackupService"),
46 kSecAttrSynchronizable, kCFBooleanTrue,
47 NULL);
48 }
49
50 bool SOSDeleteV0Keybag(CFErrorRef *error) {
51 CFDictionaryRef attributes = SOSCopyV0Attributes();
52
53 OSStatus result = SecItemDelete(attributes);
54
55 CFReleaseNull(attributes);
56
57 return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %d"), (int)result);
58 }
59
60 static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
61 CFDictionaryRef attributes = SOSCopyV0Attributes();
62
63 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
64 kSecValueData, v0Keybag,
65 NULL);
66
67
68 bool result = SecItemUpdateOrAdd(attributes, update, error);
69 CFReleaseNull(attributes);
70 CFReleaseNull(update);
71
72 return result;
73 }
74
75
76 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
77 if (CFEqualSafe(kSOSViewKeychainV0, viewName))
78 return false;
79
80 return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
81 }
82
83 static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccount* account, CFStringRef viewName) {
84 CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
85
86 SOSCircleRef circle = [account.trust getCircle:NULL];
87
88 require_quiet(circle, exit);
89
90 SOSCircleForEachValidPeer(circle, account.accountKey, ^(SOSPeerInfoRef peer) {
91 if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
92 CFSetAddValue(backupPeers, peer);
93 });
94
95 exit:
96 return backupPeers;
97 }
98
99 static void SOSAccountWithBackupPeersForView(SOSAccount* account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
100 CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
101
102 action(backupPeersForView);
103
104 CFReleaseNull(backupPeersForView);
105 }
106
107
108 static bool SOSAccountWithBSKBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
109 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
110 __block SOSBackupSliceKeyBagRef bskb = NULL;
111 bool result = false;
112 CFDataRef rkbg = SOSAccountCopyRecoveryPublic(kCFAllocatorDefault, account, NULL);
113
114 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
115 if(! rkbg) {
116 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
117 } else {
118 CFMutableDictionaryRef additionalKeys = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
119 CFDictionaryAddValue(additionalKeys, bskbRkbgPrefix, rkbg);
120 bskb = SOSBackupSliceKeyBagCreateWithAdditionalKeys(kCFAllocatorDefault, peers, additionalKeys, error);
121 CFReleaseNull(additionalKeys);
122 }
123 });
124 CFReleaseNull(rkbg);
125
126 require_quiet(bskb, exit);
127
128 action(bskb, error);
129
130 result = true;
131
132 exit:
133 CFReleaseNull(bskb);
134 return result;
135 }
136
137 CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
138 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
139 }
140
141 static bool SOSAccountUpdateBackupRing(SOSAccount* account, CFStringRef viewName, CFErrorRef *error,
142 SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
143
144 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
145
146 bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
147 return SOSRingCreate(ringName, (__bridge CFStringRef) account.peerID, kSOSRingBackup, error);
148 }, modify);
149
150 CFReleaseNull(ringName);
151
152 return result;
153 }
154
155
156
157 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccount* account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
158 CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
159 bool result = false;
160
161 if(!SecAllocationError(backupViewSet, error, CFSTR("No backup view set created"))){
162 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
163
164 return result;
165 }
166 CFSetAddValue(backupViewSet, viewName);
167
168 result = SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
169 SOSRingRef newRing = NULL;
170 CFSetRef viewPeerSet = [account.trust copyPeerSetForView:viewName];
171 CFMutableSetRef cleared = CFSetCreateMutableForCFTypes(NULL);
172
173 SOSRingSetPeerIDs(existing, cleared);
174 SOSRingAddAll(existing, viewPeerSet);
175
176 require_quiet(SOSRingSetBackupKeyBag(existing, account.fullPeerInfo, backupViewSet, keyBag, error), exit);
177
178 newRing = CFRetainSafe(existing);
179 exit:
180 CFReleaseNull(viewPeerSet);
181 CFReleaseNull(cleared);
182 return newRing;
183 });
184
185 if (result && NULL != error && NULL != *error) {
186 secerror("Got Success and Error (dropping error): %@", *error);
187 CFReleaseNull(*error);
188 }
189
190 if (!result) {
191 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
192 }
193
194 CFReleaseNull(backupViewSet);
195 return result;
196 }
197
198 bool SOSAccountNewBKSBForView(SOSAccount* account, CFStringRef viewName, CFErrorRef *error)
199 {
200 return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
201 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
202 return result;
203 });
204 }
205
206 bool SOSAccountIsBackupRingEmpty(SOSAccount* account, CFStringRef viewName) {
207 CFStringRef backupRing = SOSBackupCopyRingNameForView(viewName);
208 SOSRingRef ring = [account.trust copyRing:backupRing err:NULL];
209 CFReleaseNull(backupRing);
210 int peercnt = 0;
211 if(ring) peercnt = SOSRingCountPeers(ring);
212 CFReleaseNull(ring);
213 return peercnt == 0;
214 }
215
216 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccount* account, CFStringRef viewname){
217 bool result = false;
218 CFErrorRef bsError = NULL;
219 CFDataRef backupSliceData = NULL;
220 SOSRingRef ring = NULL;
221 SOSBackupSliceKeyBagRef backupSlice = NULL;
222
223 require_quiet(SOSPeerInfoIsViewBackupEnabled(account.peerInfo, viewname), errOut);
224
225 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
226 ring = [account.trust copyRing:ringName err:&bsError];
227 CFReleaseNull(ringName);
228
229 require_quiet(ring, errOut);
230
231 //grab the backup slice from the ring
232 backupSliceData = SOSRingGetPayload(ring, &bsError);
233 require_quiet(backupSliceData, errOut);
234
235 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
236 require_quiet(backupSlice, errOut);
237
238 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
239 SOSPeerInfoRef myPeer = account.peerInfo;
240
241 SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
242 require_quiet(isSOSPeerInfo(myPeerInBSKB), errOut);
243
244 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
245 CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
246 result = CFEqualSafe(myBK, myPeerInBSKBBK);
247 CFReleaseNull(myBK);
248 CFReleaseNull(myPeerInBSKBBK);
249
250 errOut:
251 CFReleaseNull(ring);
252 CFReleaseNull(backupSlice);
253
254 if (bsError) {
255 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
256 }
257 CFReleaseNull(bsError);
258 return result;
259 }
260
261 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccount* account, SOSPeerInfoRef testPeer, CFStringRef viewname){
262 bool result = false;
263 CFErrorRef bsError = NULL;
264 CFDataRef backupSliceData = NULL;
265 SOSRingRef ring = NULL;
266 SOSBackupSliceKeyBagRef backupSlice = NULL;
267
268 require_quiet(testPeer, errOut);
269
270 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
271
272 ring = [account.trust copyRing:ringName err:&bsError];
273 CFReleaseNull(ringName);
274
275 require_quiet(ring, errOut);
276
277 //grab the backup slice from the ring
278 backupSliceData = SOSRingGetPayload(ring, &bsError);
279 require_quiet(backupSliceData, errOut);
280
281 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
282 require_quiet(backupSlice, errOut);
283
284 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
285
286 SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
287 require_quiet(isSOSPeerInfo(peerInBSKB), errOut);
288
289 result = CFEqualSafe(testPeer, peerInBSKB);
290
291 errOut:
292 CFReleaseNull(ring);
293 CFReleaseNull(backupSlice);
294
295 if (bsError) {
296 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
297 }
298 CFReleaseNull(bsError);
299 return result;
300
301 }
302
303 bool SOSAccountUpdateOurPeerInBackup(SOSAccount* account, SOSRingRef oldRing, CFErrorRef *error){
304 bool result = false;
305 CFSetRef viewNames = SOSBackupRingGetViews(oldRing, error);
306 __block CFStringRef viewName = NULL;
307 require_quiet(viewNames, fail);
308 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames), error, CFSTR("Only support single view backup rings")), fail);
309
310 CFSetForEach(viewNames, ^(const void *value) {
311 if (isString(value)) {
312 viewName = CFRetainSafe((CFStringRef) value);
313 }
314 });
315
316 result = SOSAccountNewBKSBForView(account, viewName, error);
317
318 fail:
319 CFReleaseNull(viewName);
320 return result;
321 }
322
323 void SOSAccountForEachBackupRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
324 SOSPeerInfoRef myPeer = account.peerInfo;
325 if (myPeer) {
326 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
327
328 CFSetForEach(allViews, ^(const void *value) {
329 CFStringRef viewName = asString(value, NULL);
330
331 if (viewName) {
332 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
333 operation(ringName);
334 CFReleaseNull(ringName);
335 }
336 });
337 CFReleaseNull(allViews);
338 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
339 operation(kSOSRecoveryRing);
340 }
341 }
342
343
344 void SOSAccountForEachRingName(SOSAccount* account, void (^operation)(CFStringRef value)) {
345 SOSPeerInfoRef myPeer = account.peerInfo;
346 if (myPeer) {
347 CFSetRef allViews = SOSViewCopyViewSet(kViewSetAll); // All non virtual views.
348
349 CFSetForEach(allViews, ^(const void *value) {
350 CFStringRef viewName = asString(value, NULL);
351
352 if (viewName) {
353 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
354 operation(ringName);
355 CFReleaseNull(ringName);
356 }
357 });
358 CFReleaseNull(allViews);
359 // Only one "ring" now (other than backup rings) when there's more this will need to be modified.
360 operation(kSOSRecoveryRing);
361 }
362 }
363
364 void SOSAccountForEachBackupView(SOSAccount* account, void (^operation)(const void *value)) {
365 SOSPeerInfoRef myPeer = account.peerInfo;
366
367 if (myPeer) {
368 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
369 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
370 CFSetForEach(myBackupViews, operation);
371 CFReleaseNull(myBackupViews);
372 }
373 }
374
375
376 bool SOSAccountSetBackupPublicKey(SOSAccountTransaction* aTxn, CFDataRef cfBackupKey, CFErrorRef *error)
377 {
378 SOSAccount* account = aTxn.account;
379 NSData* backupKey = [[NSData alloc]initWithData:(__bridge NSData * _Nonnull)(cfBackupKey)];
380 __block bool result = false;
381
382 if(![account isInCircle:error]) {
383 return result;
384 }
385
386 CFDataPerformWithHexString((__bridge CFDataRef)(backupKey), ^(CFStringRef backupKeyString) {
387 CFDataPerformWithHexString((__bridge CFDataRef)((account.backup_key)), ^(CFStringRef oldBackupKey) {
388 secnotice("backup", "SetBackupPublic: %@ from %@", backupKeyString, oldBackupKey);
389 });
390 });
391
392 if ([backupKey isEqual:account.backup_key])
393 return true;
394
395 account.backup_key = [[NSData alloc] initWithData:backupKey];
396
397 account.circle_rings_retirements_need_attention = true;
398
399 result = true;
400
401 if (!result) {
402 secnotice("backupkey", "SetBackupPublic Failed: %@", error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space"));
403 }
404 return result;
405 }
406
407 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccount* account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
408 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
409 __block SOSBackupSliceKeyBagRef bskb = NULL;
410 bool result = false;
411
412 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
413 CFMutableSetRef newPeerList = CFSetCreateMutableCopy(kCFAllocatorDefault, CFSetGetCount(peers), peers);
414 CFArrayForEach(retiree, ^(const void *value) {
415 if (!isSOSPeerInfo(value)) {
416 secerror("Peer list contains a non-peerInfo element");
417 } else {
418 SOSPeerInfoRef retiringPeer = (SOSPeerInfoRef)value;
419 CFStringRef retiringPeerID = SOSPeerInfoGetPeerID(retiringPeer);
420
421 CFSetForEach(newPeerList, ^(const void *peerFromAccount) {
422 CFStringRef peerFromAccountID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)peerFromAccount);
423 if (peerFromAccountID && retiringPeerID && CFStringCompare(peerFromAccountID, retiringPeerID, 0) == 0){
424 CFSetRemoveValue(newPeerList, peerFromAccount);
425 }
426 });
427 }
428 });
429 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
430 CFReleaseNull(newPeerList);
431 });
432
433 require_quiet(bskb, exit);
434
435 action(bskb, error);
436
437 result = true;
438
439 exit:
440 CFReleaseNull(bskb);
441 return result;
442 }
443
444 bool SOSAccountRemoveBackupPublickey(SOSAccountTransaction* aTxn, CFErrorRef *error)
445 {
446 SOSAccount* account = aTxn.account;
447
448 __block bool result = false;
449 __block CFArrayRef removals = NULL;
450
451 account.backup_key = nil;
452
453 if(!SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
454 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
455 return SOSFullPeerInfoUpdateBackupKey(fpi, NULL, error);
456 })){
457 return result;
458 }
459
460 removals = CFArrayCreateForCFTypes(kCFAllocatorDefault,
461 account.peerInfo, NULL);
462
463 SOSAccountForEachBackupView(account, ^(const void *value) {
464 CFStringRef viewName = (CFStringRef)value;
465 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
466 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
467 return result;
468 });
469 });
470
471
472 result = true;
473
474 return result;
475
476 }
477
478 bool SOSAccountSetBSKBagForAllSlices(SOSAccount* account, CFDataRef aks_bag, bool setupV0Only, CFErrorRef *error){
479 __block bool result = false;
480 SOSBackupSliceKeyBagRef backup_slice = NULL;
481
482 if(![account isInCircle:error]) {
483 return result;
484 }
485
486 if (setupV0Only) {
487 result = SOSSaveV0Keybag(aks_bag, error);
488 require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
489 } else {
490 result = true;
491
492 backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
493
494 SOSAccountForEachBackupView(account, ^(const void *value) {
495 CFStringRef viewname = (CFStringRef) value;
496 result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
497 });
498 }
499
500 exit:
501 CFReleaseNull(backup_slice);
502 return result;
503 }
504
505 static CF_RETURNS_RETAINED CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccount* account, CFArrayRef peers, CFSetRef peersInBackup){
506 CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
507
508 CFSetForEach(peersInBackup, ^(const void *value) {
509 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
510 CFArrayForEach(peers, ^(const void *value) {
511 CFStringRef peerID = SOSPeerInfoGetPeerID((SOSPeerInfoRef)value);
512 CFStringRef piPeerID = SOSPeerInfoGetPeerID(peer);
513 if (peerID && piPeerID && CFStringCompare(piPeerID, peerID, 0) == 0){
514 CFArrayAppendValue(removals, peer);
515 }
516 });
517
518 });
519
520 return removals;
521
522 }
523
524 bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peers, CFErrorRef *error){
525 __block bool result = true;
526
527 SOSFullPeerInfoRef fpi = account.fullPeerInfo;
528 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
529
530 CFSetRef permittedViews = SOSPeerInfoGetPermittedViews(myPeer);
531 CFSetForEach(permittedViews, ^(const void *value) {
532 CFStringRef viewName = (CFStringRef)value;
533 if(SOSPeerInfoIsViewBackupEnabled(myPeer, viewName)){
534 //grab current peers list
535 CFSetRef peersInBackup = SOSAccountCopyBackupPeersForView(account, viewName);
536 //get peer infos that have retired but are still in the backup peer list
537 CFMutableArrayRef removals = SOSAccountIsRetiredPeerIDInBackupPeerList(account, peers, peersInBackup);
538 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
539 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
540 return result;
541 });
542 CFReleaseNull(removals);
543 CFReleaseNull(peersInBackup);
544 }
545 });
546
547 return result;
548
549 }
550
551 SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount* account, CFStringRef viewName, CFErrorRef* error){
552 CFDataRef backupSliceData = NULL;
553 CFStringRef ringName = NULL;
554 SOSRingRef ring = NULL;
555 SOSBackupSliceKeyBagRef bskb = NULL;
556
557 ringName = SOSBackupCopyRingNameForView(viewName);
558 ring = [account.trust copyRing:ringName err:NULL];
559 require_action_quiet(ring, exit, SOSCreateErrorWithFormat(kSOSErrorNoCircle, NULL, error, NULL, CFSTR("failed to get ring")));
560
561 //grab the backup slice from the ring
562 backupSliceData = SOSRingGetPayload(ring, error);
563 require_action_quiet(backupSliceData, exit, secnotice("backup", "failed to get backup slice (%@)", *error));
564
565 bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, error);
566
567 exit:
568 CFReleaseNull(ring);
569 CFReleaseNull(ringName);
570
571 return bskb;
572 }
573
574 bool SOSAccountIsLastBackupPeer(SOSAccount* account, CFErrorRef *error) {
575 __block bool retval = false;
576 SOSPeerInfoRef pi = account.peerInfo;
577
578 if(![account isInCircle:error]) {
579 return retval;
580 }
581
582 if(!SOSPeerInfoHasBackupKey(pi))
583 return retval;
584
585 SOSCircleRef circle = [account.trust getCircle:error];
586
587 if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
588 retval = true;
589 return retval;
590 }
591 // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
592 SOSAccountForEachBackupView(account, ^(const void *value) {
593 CFStringRef viewname = (CFStringRef) value;
594 SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
595 require_quiet(keybag, inner_errOut);
596 retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
597 inner_errOut:
598 CFReleaseNull(keybag);
599 });
600
601 return retval;
602 }