]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSAccountBackup.c
Security-57336.1.9.tar.gz
[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 // MARK: V0 Keybag keychain stuff
17 //
18 static bool SecItemUpdateOrAdd(CFDictionaryRef query, CFDictionaryRef update, CFErrorRef *error)
19 {
20 OSStatus saveStatus = SecItemUpdate(query, update);
21
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);
26 });
27 saveStatus = SecItemAdd(add, NULL);
28 CFReleaseNull(add);
29 }
30
31 return SecError(saveStatus, error, CFSTR("Error saving %@"), query);
32 }
33
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,
42 NULL);
43 }
44
45 static bool SOSDeleteV0Keybag(CFErrorRef *error) {
46 CFDictionaryRef attributes = SOSCopyV0Attributes();
47
48 OSStatus result = SecItemDelete(attributes);
49
50 CFReleaseNull(attributes);
51
52 return SecError(result != errSecItemNotFound ? result : errSecSuccess, error, CFSTR("Deleting V0 Keybag failed - %ld"), result);
53 }
54
55 static bool SOSSaveV0Keybag(CFDataRef v0Keybag, CFErrorRef *error) {
56 CFDictionaryRef attributes = SOSCopyV0Attributes();
57
58 CFDictionaryRef update = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
59 kSecValueData, v0Keybag,
60 NULL);
61
62
63 bool result = SecItemUpdateOrAdd(attributes, update, error);
64 CFReleaseNull(attributes);
65 CFReleaseNull(update);
66
67 return result;
68 }
69
70
71 static bool SOSPeerInfoIsViewBackupEnabled(SOSPeerInfoRef peerInfo, CFStringRef viewName) {
72 if (CFEqualSafe(kSOSViewKeychainV0, viewName))
73 return false;
74
75 return SOSPeerInfoHasBackupKey(peerInfo) && SOSPeerInfoIsViewPermitted(peerInfo, viewName);
76 }
77
78 static CFSetRef SOSAccountCopyBackupPeersForView(SOSAccountRef account, CFStringRef viewName) {
79 CFMutableSetRef backupPeers = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
80
81 SOSCircleRef circle = SOSAccountGetCircle(account, NULL);
82
83 require_quiet(circle, exit);
84
85 SOSCircleForEachActiveValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
86 if (SOSPeerInfoIsViewBackupEnabled(peer, viewName))
87 CFSetAddValue(backupPeers, peer);
88 });
89
90 exit:
91 return backupPeers;
92 }
93
94 static void SOSAccountWithBackupPeersForView(SOSAccountRef account, CFStringRef viewName, void (^action)(CFSetRef peers)) {
95 CFSetRef backupPeersForView = SOSAccountCopyBackupPeersForView(account, viewName);
96
97 action(backupPeersForView);
98
99 CFReleaseNull(backupPeersForView);
100 }
101
102 static bool SOSAccountWithBSKBForView(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error,
103 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
104 __block SOSBackupSliceKeyBagRef bskb = NULL;
105 bool result = false;
106
107 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
108 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, peers, error);
109 });
110
111 require_quiet(bskb, exit);
112
113 action(bskb, error);
114
115 result = true;
116
117 exit:
118 CFReleaseNull(bskb);
119 return result;
120 }
121
122 CFStringRef SOSBackupCopyRingNameForView(CFStringRef viewName) {
123 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@-tomb"), viewName);
124 }
125
126 static bool SOSAccountUpdateNamedRing(SOSAccountRef account, CFStringRef ringName, CFErrorRef *error,
127 SOSRingRef (^create)(CFStringRef ringName, CFErrorRef *error),
128 SOSRingRef (^copyModified)(SOSRingRef existing, CFErrorRef *error)) {
129 bool result = false;
130 SOSRingRef newRing = NULL;
131 SOSRingRef found = (SOSRingRef) CFDictionaryGetValue(account->trusted_rings, ringName);
132 if (isSOSRing(found)) {
133 found = SOSRingCopyRing(found, error);
134 } else {
135 if (found) {
136 secerror("Non ring in ring table: %@, purging!", found);
137 CFDictionaryRemoveValue(account->trusted_rings, ringName);
138 }
139 found = create(ringName, error);
140 }
141
142 require_quiet(found, exit);
143 newRing = copyModified(found, error);
144 CFReleaseNull(found);
145
146 require_quiet(newRing, exit);
147
148 result = SOSAccountHandleUpdateRing(account, newRing, true, error);
149
150 exit:
151 CFReleaseNull(found);
152 CFReleaseNull(newRing);
153 return result;
154 }
155
156 static bool SOSAccountUpdateBackupRing(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error,
157 SOSRingRef (^modify)(SOSRingRef existing, CFErrorRef *error)) {
158
159 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
160
161 bool result = SOSAccountUpdateNamedRing(account, ringName, error, ^SOSRingRef(CFStringRef ringName, CFErrorRef *error) {
162 return SOSRingCreate(ringName, SOSAccountGetMyPeerID(account), kSOSRingBackup, error);
163 }, modify);
164
165 CFReleaseNull(ringName);
166
167 return result;
168 }
169
170 static CFSetRef SOSAccountCopyPeerSetForView(SOSAccountRef account, CFStringRef viewName) {
171 CFMutableSetRef result = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
172
173 if (account->trusted_circle) {
174 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
175 if (CFSetContainsValue(SOSPeerInfoGetPermittedViews(peer), viewName)) {
176 CFSetAddValue(result, peer);
177 }
178 });
179 }
180
181 return result;
182 }
183
184 static bool SOSAccountSetKeybagForViewBackupRing(SOSAccountRef account, CFStringRef viewName, SOSBackupSliceKeyBagRef keyBag, CFErrorRef *error) {
185 CFMutableSetRef backupViewSet = CFSetCreateMutableForCFTypes(NULL);
186 bool result = false;
187 require_quiet(SecAllocationError(backupViewSet, error, CFSTR("No backup view set created")), errOut);
188 CFSetAddValue(backupViewSet, viewName);
189
190 result = SOSAccountUpdateBackupRing(account, viewName, error, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
191 SOSRingRef newRing = NULL;
192 CFSetRef viewPeerSet = SOSAccountCopyPeerSetForView(account, viewName);
193 CFMutableSetRef cleared = CFSetCreateMutableForCFTypes(NULL);
194
195 SOSRingSetPeerIDs(existing, cleared);
196 SOSRingAddAll(existing, viewPeerSet);
197
198 require_quiet(SOSRingSetBackupKeyBag(existing, SOSAccountGetMyFullPeerInfo(account), backupViewSet, keyBag, error), exit);
199
200 newRing = CFRetainSafe(existing);
201 exit:
202 CFReleaseNull(viewPeerSet);
203 CFReleaseNull(cleared);
204 return newRing;
205 });
206
207 errOut:
208
209 if (result && *error) {
210 secerror("Got Success and Error (dropping error): %@", *error);
211 CFReleaseNull(*error);
212 }
213
214 if (!result) {
215 secnotice("backupring", "Got error setting keybag for backup view '%@': %@", viewName, error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space."));
216 }
217
218 CFReleaseNull(backupViewSet);
219 return result;
220 }
221
222 bool SOSAccountStartNewBackup(SOSAccountRef account, CFStringRef viewName, CFErrorRef *error)
223 {
224 return SOSAccountWithBSKBForView(account, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
225 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
226 return result;
227 });
228 }
229
230 static bool SOSAccountUpdatePeerInfo(SOSAccountRef account, CFStringRef updateDescription, CFErrorRef *error, bool (^update)(SOSFullPeerInfoRef fpi, CFErrorRef *error)) {
231 if (account->my_identity == NULL)
232 return true;
233
234 bool result = update(account->my_identity, error);
235
236 if (result && SOSAccountHasCircle(account, NULL)) {
237 return SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle_to_change) {
238 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for %@", updateDescription);
239 return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
240 });
241 }
242
243 return result;
244 }
245
246 bool SOSAccountIsMyPeerInBackupAndCurrentInView(SOSAccountRef account, CFStringRef viewname){
247 bool result = false;
248 CFErrorRef bsError = NULL;
249 CFDataRef backupSliceData = NULL;
250 SOSBackupSliceKeyBagRef backupSlice = NULL;
251
252 require_quiet(SOSPeerInfoIsViewBackupEnabled(SOSAccountGetMyPeerInfo(account), viewname), exit);
253
254 CFMutableDictionaryRef trusted_rings = SOSAccountGetRings(account, &bsError);
255 require_quiet(trusted_rings, exit);
256
257 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
258 SOSRingRef ring = (SOSRingRef)CFDictionaryGetValue(trusted_rings, ringName);
259 CFReleaseNull(ringName);
260
261 require_quiet(ring, exit);
262
263 //grab the backup slice from the ring
264 backupSliceData = SOSRingGetPayload(ring, &bsError);
265 require_quiet(backupSliceData, exit);
266
267 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
268 require_quiet(backupSlice, exit);
269
270 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
271 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
272
273 SOSPeerInfoRef myPeerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, myPeer);
274 require_quiet(isSOSPeerInfo(myPeerInBSKB), exit);
275
276 CFDataRef myBK = SOSPeerInfoCopyBackupKey(myPeer);
277 CFDataRef myPeerInBSKBBK = SOSPeerInfoCopyBackupKey(myPeerInBSKB);
278 result = CFEqualSafe(myBK, myPeerInBSKBBK);
279 CFReleaseNull(myBK);
280 CFReleaseNull(myPeerInBSKB);
281
282 exit:
283 if (bsError) {
284 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
285 }
286 CFReleaseNull(bsError);
287 return result;
288 }
289
290 bool SOSAccountIsPeerInBackupAndCurrentInView(SOSAccountRef account, SOSPeerInfoRef testPeer, CFStringRef viewname){
291 bool result = false;
292 CFErrorRef bsError = NULL;
293 CFDataRef backupSliceData = NULL;
294 SOSBackupSliceKeyBagRef backupSlice = NULL;
295
296 require_quiet(testPeer, exit);
297
298 CFMutableDictionaryRef trusted_rings = SOSAccountGetRings(account, &bsError);
299 require_quiet(trusted_rings, exit);
300
301 CFStringRef ringName = SOSBackupCopyRingNameForView(viewname);
302 SOSRingRef ring = (SOSRingRef)CFDictionaryGetValue(trusted_rings, ringName);
303 CFReleaseNull(ringName);
304
305 require_quiet(ring, exit);
306
307 //grab the backup slice from the ring
308 backupSliceData = SOSRingGetPayload(ring, &bsError);
309 require_quiet(backupSliceData, exit);
310
311 backupSlice = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, backupSliceData, &bsError);
312 require_quiet(backupSlice, exit);
313
314 CFSetRef peers = SOSBSKBGetPeers(backupSlice);
315
316 SOSPeerInfoRef peerInBSKB = (SOSPeerInfoRef) CFSetGetValue(peers, testPeer);
317 require_quiet(isSOSPeerInfo(peerInBSKB), exit);
318
319 result = CFEqualSafe(testPeer, peerInBSKB);
320
321 exit:
322 if (bsError) {
323 secnotice("backup", "Failed to find BKSB: %@, %@ (%@)", backupSliceData, backupSlice, bsError);
324 }
325 CFReleaseNull(bsError);
326 return result;
327
328 }
329
330 bool SOSAccountUpdateOurPeerInBackup(SOSAccountRef account, SOSRingRef oldRing, CFErrorRef *error){
331 bool result = false;
332 CFSetRef viewNames = SOSBackupRingGetViews(oldRing, error);
333 __block CFStringRef viewName = NULL;
334 require_quiet(viewNames, fail);
335 require_quiet(SecRequirementError(1 == CFSetGetCount(viewNames), error, CFSTR("Only support single view backup rings")), fail);
336
337 CFSetForEach(viewNames, ^(const void *value) {
338 if (isString(value)) {
339 viewName = CFRetainSafe((CFStringRef) value);
340 }
341 });
342
343 result = SOSAccountStartNewBackup(account, viewName, error);
344
345 fail:
346 CFReleaseNull(viewName);
347 return result;
348 }
349
350 void SOSAccountForEachBackupRingName(SOSAccountRef account, void (^operation)(CFStringRef value)) {
351 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
352 if (myPeer) {
353 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
354
355 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
356
357 CFSetForEach(myBackupViews, ^(const void *value) {
358 CFStringRef viewName = asString(value, NULL);
359
360 if (viewName) {
361 CFStringRef ringName = SOSBackupCopyRingNameForView(viewName);
362 operation(ringName);
363 CFReleaseNull(ringName);
364 }
365 });
366
367 CFReleaseNull(myBackupViews);
368 }
369 }
370
371 static
372 void SOSAccountForEachBackupView(SOSAccountRef account, void (^operation)(const void *value)) {
373 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(account);
374
375 if (myPeer) {
376 CFMutableSetRef myBackupViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, SOSPeerInfoGetPermittedViews(myPeer));
377
378 CFSetRemoveValue(myBackupViews, kSOSViewKeychainV0);
379
380 CFSetForEach(myBackupViews, operation);
381
382 CFReleaseNull(myBackupViews);
383 }
384 }
385
386 bool SOSAccountSetBackupPublicKey(SOSAccountRef account, CFDataRef backupKey, CFErrorRef *error)
387 {
388 __block bool result = false;
389
390 #if !ENABLE_V2_BACKUP
391 // We should fill in an error, but exit does it for us.
392 goto exit;
393 #endif
394
395 require_quiet(SOSAccountIsInCircle(account, error), exit);
396
397 if (CFEqualSafe(backupKey, account->backup_key))
398 return true;
399
400 require_quiet(SOSBSKBIsGoodBackupPublic(backupKey, error), exit);
401 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
402 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
403 return SOSFullPeerInfoUpdateBackupKey(fpi, backupKey, error);
404 }), exit);
405
406 CFRetainAssign(account->backup_key, backupKey);
407
408 CFErrorRef localError = NULL;
409 if (!SOSDeleteV0Keybag(&localError)) {
410 secerror("Failed to delete v0 keybag: %@", localError);
411 }
412 CFReleaseNull(localError);
413
414 result = true;
415
416 SOSAccountForEachBackupView(account, ^(const void *value) {
417 CFStringRef viewName = (CFStringRef)value;
418 result &= SOSAccountStartNewBackup(account, viewName, error);
419 });
420
421 exit:
422 if (!result) {
423 secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) *error : (CFTypeRef) CFSTR("No error space provided"));
424 }
425 return result;
426 }
427
428 static bool SOSAccountWithBSKBAndPeerInfosForView(SOSAccountRef account, CFArrayRef retiree, CFStringRef viewName, CFErrorRef *error,
429 bool (^action)(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error)) {
430 __block SOSBackupSliceKeyBagRef bskb = NULL;
431 bool result = false;
432
433 SOSAccountWithBackupPeersForView(account, viewName, ^(CFSetRef peers) {
434 CFMutableSetRef newPeerList = CFSetCreateMutableCopy(kCFAllocatorDefault, CFSetGetCount(peers), peers);
435 CFArrayForEach(retiree, ^(const void *value) {
436 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
437 CFSetRemoveValue(newPeerList, peer);
438 });
439 bskb = SOSBackupSliceKeyBagCreate(kCFAllocatorDefault, newPeerList, error);
440 CFReleaseNull(newPeerList);
441 });
442
443 require_quiet(bskb, exit);
444
445 action(bskb, error);
446
447 result = true;
448
449 exit:
450 CFReleaseNull(bskb);
451 return result;
452 }
453
454 bool SOSAccountRemoveBackupPublickey(SOSAccountRef account, CFErrorRef *error)
455 {
456 __block bool result = false;
457 __block CFMutableArrayRef removals = NULL;
458
459 require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), error,
460 ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
461 return SOSFullPeerInfoUpdateBackupKey(fpi, NULL, error);
462 }), exit);
463
464 CFReleaseNull(account->backup_key);
465
466 removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
467 CFArrayAppendValue(removals, SOSAccountGetMyPeerInfo(account));
468
469 SOSAccountForEachBackupView(account, ^(const void *value) {
470 CFStringRef viewName = (CFStringRef)value;
471 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
472 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
473 return result;
474 });
475 });
476
477
478 result = true;
479
480 exit:
481 return result;
482
483 }
484
485 bool SOSAccountSetBSKBagForAllSlices(SOSAccountRef account, CFDataRef aks_bag, bool includeV0, CFErrorRef *error){
486 __block bool result = false;
487 SOSBackupSliceKeyBagRef backup_slice = NULL;
488
489 require_quiet(SOSAccountIsInCircle(account, error), exit);
490
491 if (includeV0) {
492 result = SOSSaveV0Keybag(aks_bag, error);
493 require_action_quiet(result, exit, secnotice("keybag", "failed to set V0 keybag (%@)", *error));
494 }
495
496 result = true;
497
498
499 #if !ENABLE_V2_BACKUP
500 // We always succeed when no V2 backup.
501 goto exit;
502 #endif
503
504 backup_slice = SOSBackupSliceKeyBagCreateDirect(kCFAllocatorDefault, aks_bag, error);
505
506 SOSAccountForEachBackupView(account, ^(const void *value) {
507 CFStringRef viewname = (CFStringRef) value;
508 result &= SOSAccountSetKeybagForViewBackupRing(account, viewname, backup_slice, error);
509 });
510
511 exit:
512 CFReleaseNull(backup_slice);
513 return result;
514 }
515
516 static CFMutableArrayRef SOSAccountIsRetiredPeerIDInBackupPeerList(SOSAccountRef account, CFArrayRef peerIDs, CFSetRef peersInBackup){
517 CFMutableArrayRef removals = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
518
519 CFSetForEach(peersInBackup, ^(const void *value) {
520 SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
521 CFArrayForEach(peerIDs, ^(const void *value) {
522 CFStringRef peerID = (CFStringRef)value;
523 CFStringRef piPeerID = SOSPeerInfoGetPeerID(peer);
524 if (piPeerID && CFStringCompare(piPeerID, peerID, 0) == 0){
525 CFArrayAppendValue(removals, peer);
526 }
527 });
528
529 });
530
531 return removals;
532
533 }
534
535 bool SOSAccountRemoveBackupPeers(SOSAccountRef account, CFArrayRef peerIDs, CFErrorRef *error){
536 __block bool result = true;
537
538 SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
539 SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
540
541 CFSetRef permittedViews = SOSPeerInfoGetPermittedViews(myPeer);
542 CFSetForEach(permittedViews, ^(const void *value) {
543 CFStringRef viewName = (CFStringRef)value;
544 if(SOSPeerInfoIsViewBackupEnabled(myPeer, viewName)){
545 //grab current peers list
546 CFSetRef peersInBackup = SOSAccountCopyBackupPeersForView(account, viewName);
547 //get peer infos that have retired but are still in the backup peer list
548 CFMutableArrayRef removals = SOSAccountIsRetiredPeerIDInBackupPeerList(account, peerIDs, peersInBackup);
549 result = SOSAccountWithBSKBAndPeerInfosForView(account, removals, viewName, error, ^(SOSBackupSliceKeyBagRef bskb, CFErrorRef *error) {
550 bool result = SOSAccountSetKeybagForViewBackupRing(account, viewName, bskb, error);
551 return result;
552 });
553 }
554 });
555
556 return result;
557
558 }
559