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