]> git.saurik.com Git - apple/security.git/blob - keychain/SecureObjectSync/SOSPeer.m
Security-59306.61.1.tar.gz
[apple/security.git] / keychain / SecureObjectSync / SOSPeer.m
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 * SOSPeer.c - Implementation of a secure object syncing peer
27 */
28 #include "keychain/SecureObjectSync/SOSPeer.h"
29 #import "keychain/SecureObjectSync/SOSPeerRateLimiter.h"
30 #include "keychain/SecureObjectSync/SOSDigestVector.h"
31 #include "keychain/SecureObjectSync/SOSInternal.h"
32 #include "keychain/SecureObjectSync/SOSTransport.h"
33 #include "keychain/SecureObjectSync/SOSEngine.h"
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #import "keychain/SecureObjectSync/SOSChangeTracker.h"
36 #include <utilities/SecCFError.h>
37 #include <utilities/SecCFRelease.h>
38 #include <utilities/SecCFWrappers.h>
39 #include <utilities/SecDb.h>
40 #include <utilities/SecFileLocations.h>
41 #include <utilities/SecIOFormat.h>
42 #include <utilities/array_size.h>
43 #include <utilities/debugging.h>
44 #include "keychain/SecureObjectSync/SOSBackupEvent.h"
45 #include <Security/SecItemBackup.h>
46
47 #include "keychain/securityd/SOSCloudCircleServer.h"
48
49 #include <CoreFoundation/CoreFoundation.h>
50
51 #include <stdlib.h>
52
53 #include <AssertMacros.h>
54
55 // Backup Peer Support
56 #include "keychain/securityd/SecKeybagSupport.h"
57 #include <notify.h>
58
59
60 //
61 // MARK: - SOSPeerPersistence code
62 //
63 static CFStringRef kSOSPeerSequenceNumberKey = CFSTR("sequence-number");
64
65 CFStringRef kSOSPeerDataLabel = CFSTR("iCloud Peer Data Meta-data");
66
67 //
68 // MARK: SOSPeerState (dictionary keys)
69 //
70
71 // PeerState dictionary keys
72 static CFStringRef kSOSPeerSendObjectsKey = CFSTR("send-objects"); // bool
73 static CFStringRef kSOSPeerMustSendMessageKey = CFSTR("must-send"); // bool
74 static CFStringRef kSOSPeerHasBeenInSyncKey = CFSTR("has-been-in-sync"); // bool
75 static CFStringRef kSOSPeerPendingObjectsKey = CFSTR("pending-objects"); // digest
76 static CFStringRef kSOSPeerUnwantedManifestKey = CFSTR("unwanted-manifest"); // digest
77 static CFStringRef kSOSPeerConfirmedManifestKey = CFSTR("confirmed-manifest"); //digest
78 static CFStringRef kSOSPeerProposedManifestKey = CFSTR("pending-manifest"); // array of digests
79 static CFStringRef kSOSPeerLocalManifestKey = CFSTR("local-manifest"); // array of digests
80 static CFStringRef kSOSPeerVersionKey = CFSTR("vers"); // int
81
82 //
83 // SOSPeerMeta keys that can also be used in peerstate...
84 //
85 static CFStringRef kSOSPeerPeerIDKey = CFSTR("peer-id"); // string
86 static CFStringRef kSOSPeerViewsKey = CFSTR("views"); // set (or array) of string
87 static CFStringRef kSOSPeerKeyBagKey = CFSTR("keybag"); // data
88
89 /*
90 Theory of syncing for both incoming and outgoing messages
91
92 A peerstate consists of:
93 (T, U, C, P, L, D->M, M->D, H->O, O->{(Op,Or)|Oa,(Op,Or)|(Om,Oa),(Op,Or)|Om,Oi|Oi|Ob|Oa,Ob|(Om,Oa),Ob|(Ol,Ou)|Oa,(Ol,Ou)|(Om,Oa)(Ol,Ou)})
94 T: to be sent (pendingObjects) manifest
95 U: unwanted objects manifest
96 C: confirmed manifest
97 P: proposed manifest
98 D->M? digest or manifest to optional manifest function
99 M->D manifest to digest of manifest function
100 H->O? hash (manifest entry) to optional object function (the datasource)
101 O->{ Mapping from incoming O objects to one of:
102 (Op,Or): Op = Peers object, Or is replaced local object.
103 Oa,(Op,Or): Oa = appeared local object, Or = Oa, see above for (Op, Or)
104 (Om,Oa),(Op,Or): Om missing local object, was apparently Oa instead see above for (Oa, Op, Or)
105 Om,Oi: Om missing local object, inserted Oi (nothing replaced), but Om still disapeared from manifest
106 Oi Oi inserted object from peer (nothing replaced)
107 Ob: Ob both remote and local object are identical, nothing changed
108 Oa,Ob: Oa = appeared local object equal to Oa = Ob. Equivalent to single Oi
109 (Om,Oa),Ob: Om missing local object, must be O->H->Ob, Oa found in place, Ob != Oa, Oa magically appeared unrelated to O->H->Ob
110 (Ol,Ou): Ol local object wins from peers Ou older object => append Ou to U
111 Oa,(Ol,Ou): Oa appeared as a local object and Oa = Ol above
112 (Om,Oa),(Ol,Ou): Om removed and Oa replaced Om and Oa = Ol as above
113 }
114 A message consists of
115 (B,M,E,O)
116 B: base digest
117 M: missing manifest
118 E: extra manifest
119 O: objects
120
121 To send a message we simply compute:
122 B: D->M->C
123 M: C \ L
124 E: L \ C # subsetted if not all of O is sent
125 O: H->O?->O # as size permits
126 and change P to (C \ M:) union E: union O->H->O:
127
128 To receive a message we compute
129 Op,Oi,Ob,Oo and Om,Oa,Ol
130 C: (D->M->B \ M) union E union O->H->O
131 A = E \ (O->H->O)
132 T: T \ (Om union Or union A union Oa,Ob.last union (Om,Oa),Ob.last ) union (M intersect L)
133 */
134
135
136 //
137 // MARK: SOSPeerMeta
138 //
139
140 static SOSPeerMetaRef SOSPeerMetaCreate(CFStringRef peerID) {
141 return CFRetain(peerID);
142 }
143
144 static SOSPeerMetaRef SOSPeerMetaCreateWithViews(CFStringRef peerID, CFSetRef views) {
145 const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey };
146 const void *values[] = { peerID, views };
147 return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
148 }
149
150 static SOSPeerMetaRef SOSPeerMetaCreateWithViewsAndKeyBag(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
151 const void *keys[] = { kSOSPeerPeerIDKey, kSOSPeerViewsKey, kSOSPeerKeyBagKey };
152 const void *values[] = { peerID, views, keybag };
153 return CFDictionaryCreate(kCFAllocatorDefault, keys, values, array_size(keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
154 }
155
156 SOSPeerMetaRef SOSPeerMetaCreateWithComponents(CFStringRef peerID, CFSetRef views, CFDataRef keybag) {
157 if (!isString(peerID))
158 return NULL;
159 if (views) {
160 if (keybag)
161 return SOSPeerMetaCreateWithViewsAndKeyBag(peerID, views, keybag);
162 else
163 return SOSPeerMetaCreateWithViews(peerID, views);
164 } else
165 return SOSPeerMetaCreate(peerID);
166 }
167
168 SOSPeerMetaRef SOSPeerMetaCreateWithState(CFStringRef peerID, CFDictionaryRef state) {
169 return SOSPeerMetaCreateWithComponents(peerID, CFDictionaryGetValue(state, kSOSPeerViewsKey), CFDictionaryGetValue(state, kSOSPeerKeyBagKey));
170 }
171
172 CFStringRef SOSPeerMetaGetComponents(SOSPeerMetaRef peerMeta, CFSetRef *views, CFDataRef *keybag, CFErrorRef *error) {
173 if (isDictionary(peerMeta)) {
174 CFDictionaryRef meta = (CFDictionaryRef)peerMeta;
175 CFStringRef peerID = asString(CFDictionaryGetValue(meta, kSOSPeerPeerIDKey), error);
176 CFSetRef vns = asSet(CFDictionaryGetValue(meta, kSOSPeerViewsKey), error);
177 if (vns && asDataOptional(CFDictionaryGetValue(meta, kSOSPeerKeyBagKey), keybag, error)) {
178 if (views)
179 *views = vns;
180 return peerID;
181 }
182 return NULL;
183 } else {
184 if (views) {
185 // Hack so tests can pass simple peerIDs
186 *views = SOSViewsGetV0ViewSet();
187 }
188 return asString(peerMeta, error);
189 }
190 }
191
192 CFTypeRef SOSPeerOrStateSetViewsKeyBagAndCreateCopy(CFTypeRef peerOrState, CFSetRef views, CFDataRef keyBag) {
193 assert(views);
194 if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
195 // Inflated peer, update its views and move on
196 SOSPeerRef peer = (SOSPeerRef)peerOrState;
197 SOSPeerSetViewNameSet(peer, views);
198 SOSPeerSetKeyBag(peer, keyBag);
199 return CFRetainSafe(peer);
200 } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
201 CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
202 // Deserialized peer, just updated the serialized state with the new views
203 CFDictionarySetValue(state, kSOSPeerViewsKey, views);
204 if (keyBag)
205 CFDictionarySetValue(state, kSOSPeerKeyBagKey, keyBag);
206 else
207 CFDictionaryRemoveValue(state, kSOSPeerKeyBagKey);
208 return state;
209 } else {
210 // New peer, just create a state object.
211 if (keyBag)
212 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, kSOSPeerKeyBagKey, keyBag, NULL);
213 else
214 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSOSPeerViewsKey, views, NULL);
215 }
216 }
217
218 CFTypeRef SOSPeerOrStateSetViewsAndCopyState(CFTypeRef peerOrState, CFSetRef views) {
219 assert(views);
220
221 if (peerOrState && CFGetTypeID(peerOrState) == SOSPeerGetTypeID()) {
222 // Inflated peer, update its views and deflate it
223 SOSPeerRef peer = (SOSPeerRef)peerOrState;
224 SOSPeerSetViewNameSet(peer, views);
225 return SOSPeerCopyState(peer, NULL);
226 } else if (peerOrState && CFGetTypeID(peerOrState) == CFDictionaryGetTypeID()) {
227 // We have a deflated peer. Update its views and keep it deflated
228 CFSetRef oldViews = (CFSetRef) CFDictionaryGetValue(peerOrState, kSOSPeerViewsKey);
229 CFMutableDictionaryRef state = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, peerOrState);
230 CFDictionarySetValue(state, kSOSPeerViewsKey, views);
231 if (oldViews && !CFSetIsSubset(views, oldViews)) {
232 CFDictionarySetValue(state, kSOSPeerHasBeenInSyncKey, kCFBooleanFalse);
233 }
234 return state;
235 } else {
236 return NULL;
237 }
238 }
239
240 bool SOSPeerMapEntryIsBackup(const void *mapEntry) {
241 if (!mapEntry) return false;
242 if (CFGetTypeID(mapEntry) == SOSPeerGetTypeID()) {
243 return SOSPeerGetKeyBag((SOSPeerRef)mapEntry);
244 } else {
245 return CFDictionaryContainsKey(mapEntry, kSOSPeerKeyBagKey);
246 }
247 }
248
249 //
250 // MARK: - SOSManifest
251 //
252
253 enum {
254 kSOSPeerMaxManifestWindowDepth = 4
255 };
256
257 static CFStringRef SOSManifestCreateOptionalDescriptionWithLabel(SOSManifestRef manifest, CFStringRef label) {
258 if (!manifest) return CFSTR(" - ");
259 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@%@"), label, manifest);
260 }
261
262 static CFStringRef SOSManifestArrayCreateOptionalDescriptionWithLabel(CFArrayRef manifests, CFStringRef label) {
263 CFIndex count = manifests ? CFArrayGetCount(manifests) : 0;
264 if (count == 0) return CFSTR(" - ");
265 SOSManifestRef manifest = (SOSManifestRef)CFArrayGetValueAtIndex(manifests, 0);
266 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(" %@[%" PRIdCFIndex "]%@"), label, count, manifest);
267 }
268
269 static void SOSManifestArraySetManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
270 if (manifest) {
271 if (*manifests)
272 CFArrayRemoveAllValues(*manifests);
273 else
274 *manifests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
275 CFArrayAppendValue(*manifests, manifest);
276 } else {
277 CFReleaseNull(*manifests);
278 }
279 }
280
281 static void SOSManifestMutableArrayAppendManifest(CFMutableArrayRef manifests, SOSManifestRef manifest) {
282 if (manifest) {
283 CFIndex count = CFArrayGetCount(manifests);
284 CFIndex ixOfManifest = CFArrayGetFirstIndexOfValue(manifests, CFRangeMake(0, count), manifest);
285 if (ixOfManifest != 0) {
286 // If the manifest isn't at the front of the array move it there.
287 // If it's not in the array, remove enough entires from the end to
288 // make room to put it in the front.
289 if (ixOfManifest != kCFNotFound) {
290 CFArrayRemoveValueAtIndex(manifests, ixOfManifest);
291 } else {
292 while (count >= kSOSPeerMaxManifestWindowDepth)
293 CFArrayRemoveValueAtIndex(manifests, --count);
294 }
295
296 CFArrayInsertValueAtIndex(manifests, 0, manifest);
297 }
298 } else {
299 // pending == NULL => nothing clear history
300 CFArrayRemoveAllValues(manifests);
301 }
302 }
303
304 static void SOSManifestArrayAppendManifest(CFMutableArrayRef *manifests, SOSManifestRef manifest) {
305 if (*manifests)
306 SOSManifestMutableArrayAppendManifest(*manifests, manifest);
307 else
308 SOSManifestArraySetManifest(manifests, manifest);
309 }
310
311 //
312 // MARK: - SOSPeer
313 //
314
315 struct __OpaqueSOSPeer {
316 CFRuntimeBase _base;
317
318 CFStringRef peer_id;
319 CFSetRef views;
320 CFIndex version;
321 uint64_t sequenceNumber;
322 bool mustSendMessage;
323 bool sendObjects;
324
325 bool hasBeenInSync;
326
327 SOSManifestRef pendingObjects;
328 SOSManifestRef unwantedManifest;
329 SOSManifestRef confirmedManifest;
330 CFMutableArrayRef proposedManifests;
331 CFMutableArrayRef localManifests;
332
333 CFTypeRef limiter;
334
335 CFMutableDictionaryRef otrTimers;
336
337 // Only backup peers have these:
338 CFDataRef _keyBag;
339 FILE *journalFile;
340 };
341
342 CFGiblisWithCompareFor(SOSPeer)
343
344 static CFStringRef SOSPeerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
345 SOSPeerRef peer = (SOSPeerRef)cf;
346 if(peer){
347 CFStringRef po = SOSManifestCreateOptionalDescriptionWithLabel(peer->pendingObjects, CFSTR("O"));
348 CFStringRef uo = SOSManifestCreateOptionalDescriptionWithLabel(peer->unwantedManifest, CFSTR("U"));
349 CFStringRef co = SOSManifestCreateOptionalDescriptionWithLabel(peer->confirmedManifest, CFSTR("C"));
350 CFStringRef pe = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->proposedManifests, CFSTR("P"));
351 CFStringRef lo = SOSManifestArrayCreateOptionalDescriptionWithLabel(peer->localManifests, CFSTR("L"));
352 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<%@ %s%s%s%@%@%@%@%@>"),
353 SOSPeerGetID(peer),
354 SOSPeerMustSendMessage(peer) ? "F" : "f",
355 SOSPeerSendObjects(peer) ? "S" : "s",
356 SOSPeerHasBeenInSync(peer) ? "K" : "k",
357 po, uo, co, pe, lo);
358 CFReleaseSafe(lo);
359 CFReleaseSafe(pe);
360 CFReleaseSafe(co);
361 CFReleaseSafe(uo);
362 CFReleaseSafe(po);
363
364 return desc;
365 }
366 else
367 return CFSTR("NULL");
368 }
369
370 static Boolean SOSPeerCompare(CFTypeRef cfA, CFTypeRef cfB)
371 {
372 SOSPeerRef peerA = (SOSPeerRef)cfA, peerB = (SOSPeerRef)cfB;
373 // Use mainly to see if peerB is actually this device (peerA)
374 return CFStringCompare(SOSPeerGetID(peerA), SOSPeerGetID(peerB), 0) == kCFCompareEqualTo;
375 }
376
377
378 static bool SOSPeerGetPersistedBoolean(CFDictionaryRef persisted, CFStringRef key) {
379 CFBooleanRef boolean = CFDictionaryGetValue(persisted, key);
380 return boolean && CFBooleanGetValue(boolean);
381 }
382
383 static CFDataRef SOSPeerGetPersistedData(CFDictionaryRef persisted, CFStringRef key) {
384 return asData(CFDictionaryGetValue(persisted, key), NULL);
385 }
386
387 static int64_t SOSPeerGetPersistedInt64(CFDictionaryRef persisted, CFStringRef key) {
388 int64_t integer = 0;
389 CFNumberRef number = CFDictionaryGetValue(persisted, key);
390 if (number) {
391 CFNumberGetValue(number, kCFNumberSInt64Type, &integer);
392 }
393 return integer;
394 }
395
396 static void SOSPeerGetOptionalPersistedCFIndex(CFDictionaryRef persisted, CFStringRef key, CFIndex *value) {
397 CFNumberRef number = CFDictionaryGetValue(persisted, key);
398 if (number) {
399 CFNumberGetValue(number, kCFNumberCFIndexType, value);
400 }
401 }
402
403 static CFSetRef SOSPeerGetPersistedViewNameSet(SOSPeerRef peer, CFDictionaryRef persisted, CFStringRef key) {
404 CFSetRef vns = CFDictionaryGetValue(persisted, key);
405 if (!vns) {
406 // Engine state in db contained a v0 peer, thus it must be in the V0ViewSet.
407 vns = SOSViewsGetV0ViewSet();
408 secnotice("peer", "%@ had no views, inferring: %@", peer->peer_id, vns);
409 }
410 return vns;
411 }
412
413 //
414 // MARK: Backup Peers
415 //
416
417 void SOSBackupPeerPostNotification(const char *reason) {
418 // Let sbd know when a notable event occurs
419 // - Disk full
420 // - Backup bag change
421 secnotice("backup", "posting notification to CloudServices: %s", reason?reason:"");
422 notify_post(kSecItemBackupNotification);
423 }
424
425 static bool SOSPeerDoWithJournalPath(SOSPeerRef peer, CFErrorRef *error, void(^with)(const char *journalPath)) {
426 // TODO: Probably switch to using CFURL to construct the path.
427 bool ok = true;
428 char strBuffer[PATH_MAX + 1];
429 size_t userTempLen = confstr(_CS_DARWIN_USER_TEMP_DIR, strBuffer, sizeof(strBuffer));
430 if (userTempLen == 0) {
431 ok = SecCheckErrno(-1, error, CFSTR("confstr on _CS_DARWIN_USER_TEMP_DIR returned an error."));
432 } else {
433 CFStringRef journalName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s/SOSBackup-%@"), strBuffer, SOSPeerGetID(peer));
434 CFStringPerformWithCString(journalName, with);
435 CFReleaseSafe(journalName);
436 }
437 return ok;
438 }
439
440 static FILE *fopen_journal(const char *journalPath, const char *mode, CFErrorRef *error) {
441 FILE *file = fopen(journalPath, mode);
442 SecCheckErrno(!file, error, CFSTR("fopen %s,%s"), journalPath, mode);
443 return file;
444 }
445
446 #include <sys/stat.h>
447
448 #if !defined(NDEBUG)
449 static off_t getFileSize(int fd) {
450 struct stat sb;
451 fstat(fd, &sb);
452 return sb.st_size;
453 }
454 #endif
455
456 int SOSPeerHandoffFD(SOSPeerRef peer, CFErrorRef *error) {
457 __block int fd = -1;
458 SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
459 fd = open(journalName, O_RDONLY | O_CLOEXEC);
460 if (SecCheckErrno(fd < 0, error, CFSTR("open %s"), journalName)) {
461 if (!SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName)) {
462 close(fd);
463 fd = -1;
464 } else {
465 secdebug("backup", "Handing off file %s with fd %d of size %llu", journalName, fd, getFileSize(fd));
466 }
467 } else {
468 secdebug("backup", "Handing off file %s failed, %@", journalName, error?*error:NULL);
469 }
470 });
471 return fd;
472 }
473
474 static CFDataRef SOSPeerCopyAKSKeyBag(SOSPeerRef peer, CFErrorRef *error) {
475 if (CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb)) {
476 return CFRetainSafe(peer->_keyBag);
477 } else {
478 CFDataRef aksKeybag = NULL;
479 SOSBackupSliceKeyBagRef backupSliceKeyBag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, peer->_keyBag, error);
480 if (backupSliceKeyBag) {
481 aksKeybag = SOSBSKBCopyAKSBag(backupSliceKeyBag, error);
482 CFRelease(backupSliceKeyBag);
483 }
484 return aksKeybag;
485 }
486 }
487
488 bool SOSPeerAppendToJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile, keybag_handle_t kbhandle)) {
489 __block bool ok = true;
490 // We only need a keybag if we are writing ADDs. Since we don't know at this layer
491 // what operations we may be doing, open keybag if we have one, otherwise don't
492 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
493 FILE *file = fopen_journal(fname, "a", error);
494 if (file) {
495 keybag_handle_t kbhandle = bad_keybag_handle;
496 CFDataRef keybag = SOSPeerCopyAKSKeyBag(peer, error);
497 ok = keybag;
498 if (ok && (ok = ks_open_keybag(keybag, NULL, &kbhandle, error))) {
499 with(file, kbhandle);
500 if (kbhandle != bad_keybag_handle)
501 ok &= ks_close_keybag(kbhandle, error);
502 }
503 CFReleaseSafe(keybag);
504 fclose(file);
505 }
506 });
507 return ok;
508 }
509
510 static bool SOSPeerTruncateJournal(SOSPeerRef peer, CFErrorRef *error, void(^with)(FILE *journalFile)) {
511 __block bool ok = true;
512 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *fname) {
513 FILE *file = fopen_journal(fname, "w", error);
514 if (file) {
515 with(file);
516 fclose(file);
517 }
518 });
519 return ok;
520 }
521
522 bool SOSPeerSetState(SOSPeerRef p, SOSEngineRef engine, CFDictionaryRef state, CFErrorRef *error) {
523 bool ok = true;
524 if (state) {
525 SOSPeerGetOptionalPersistedCFIndex(state, kSOSPeerVersionKey, &p->version);
526
527 p->sequenceNumber = SOSPeerGetPersistedInt64(state, kSOSPeerSequenceNumberKey);
528 p->mustSendMessage = SOSPeerGetPersistedBoolean(state, kSOSPeerMustSendMessageKey);
529 p->sendObjects = SOSPeerGetPersistedBoolean(state, kSOSPeerSendObjectsKey);
530 p->hasBeenInSync = SOSPeerGetPersistedBoolean(state, kSOSPeerHasBeenInSyncKey);
531 CFRetainAssign(p->views, SOSPeerGetPersistedViewNameSet(p, state, kSOSPeerViewsKey));
532 SOSPeerSetKeyBag(p, SOSPeerGetPersistedData(state, kSOSPeerKeyBagKey));
533 CFAssignRetained(p->pendingObjects, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerPendingObjectsKey));
534 CFAssignRetained(p->unwantedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerUnwantedManifestKey));
535 CFAssignRetained(p->confirmedManifest, SOSEngineCopyPersistedManifest(engine, state, kSOSPeerConfirmedManifestKey));
536 CFAssignRetained(p->proposedManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerProposedManifestKey, error));
537 ok &= p->proposedManifests != NULL;
538 CFAssignRetained(p->localManifests, SOSEngineCopyPersistedManifestArray(engine, state, kSOSPeerLocalManifestKey, error));
539 ok &= p->localManifests != NULL;
540 }
541 return ok;
542 }
543
544 static SOSPeerRef SOSPeerCreate_Internal(SOSEngineRef engine, CFDictionaryRef state, CFStringRef theirPeerID, CFIndex version, CFErrorRef *error) {
545 SOSPeerRef p = CFTypeAllocate(SOSPeer, struct __OpaqueSOSPeer, kCFAllocatorDefault);
546 p->peer_id = CFRetainSafe(theirPeerID);
547 p->version = version;
548 p->otrTimers = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
549
550 CFDictionaryRef empty = NULL;
551 if (!state) {
552 empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
553 state = empty;
554 }
555 if (!SOSPeerSetState(p, engine, state, error)) {
556 CFReleaseNull(p);
557 }
558 CFReleaseNull(empty);
559 return p;
560 }
561
562 static void SOSPeerPersistBool(CFMutableDictionaryRef persist, CFStringRef key, bool value) {
563 CFDictionarySetValue(persist, key, value ? kCFBooleanTrue : kCFBooleanFalse);
564 }
565
566 static void SOSPeerPersistInt64(CFMutableDictionaryRef persist, CFStringRef key, int64_t value) {
567 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value);
568 CFDictionarySetValue(persist, key, number);
569 CFReleaseSafe(number);
570 }
571
572 static void SOSPeerPersistCFIndex(CFMutableDictionaryRef persist, CFStringRef key, CFIndex value) {
573 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &value);
574 CFDictionarySetValue(persist, key, number);
575 CFReleaseSafe(number);
576 }
577
578 static bool SOSPeerPersistOptionalManifest(CFMutableDictionaryRef persist, CFStringRef key, SOSManifestRef manifest, CFErrorRef *error) {
579 if (!manifest)
580 return true;
581 CFDataRef digest = SOSManifestGetDigest(manifest, error);
582 bool ok = digest;
583 if (ok)
584 CFDictionarySetValue(persist, key, digest);
585 return ok;
586 }
587
588 static bool SSOSPeerPersistManifestArray(CFMutableDictionaryRef persist, CFStringRef key, CFArrayRef manifests, CFErrorRef *error) {
589 CFMutableArrayRef digests = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
590 SOSManifestRef manifest;
591 if (manifests) CFArrayForEachC(manifests, manifest) {
592 CFDataRef digest = SOSManifestGetDigest(manifest, error);
593 if (!digest)
594 CFReleaseNull(digests);
595 if (digests) {
596 CFArrayAppendValue(digests, digest);
597 }
598 }
599 if (digests) {
600 CFDictionarySetValue(persist, key, digests);
601 CFRelease(digests);
602 }
603 return digests;
604 }
605
606 static void SOSPeerPersistOptionalValue(CFMutableDictionaryRef persist, CFStringRef key, CFTypeRef value) {
607 if (value)
608 CFDictionarySetValue(persist, key, value);
609 }
610
611 CFDictionaryRef SOSPeerCopyState(SOSPeerRef peer, CFErrorRef *error) {
612 CFMutableDictionaryRef state = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
613 SOSPeerPersistInt64(state, kSOSPeerSequenceNumberKey, peer->sequenceNumber);
614 if (peer->version) {
615 SOSPeerPersistCFIndex(state, kSOSPeerVersionKey, peer->version);
616 }
617
618 SOSPeerPersistBool(state, kSOSPeerMustSendMessageKey, peer->mustSendMessage);
619 SOSPeerPersistBool(state, kSOSPeerSendObjectsKey, peer->sendObjects);
620 SOSPeerPersistBool(state, kSOSPeerHasBeenInSyncKey, peer->hasBeenInSync);
621 SOSPeerPersistOptionalValue(state, kSOSPeerViewsKey, peer->views);
622
623 CFDataRef keybag = SOSPeerGetKeyBag(peer);
624 if (keybag && !CFEqual(peer->peer_id, kSOSViewKeychainV0_tomb))
625 SOSPeerPersistOptionalValue(state, kSOSPeerKeyBagKey, keybag);
626
627 if (!SOSPeerPersistOptionalManifest(state, kSOSPeerPendingObjectsKey, peer->pendingObjects, error)
628 || !SOSPeerPersistOptionalManifest(state, kSOSPeerUnwantedManifestKey, peer->unwantedManifest, error)
629 || !SOSPeerPersistOptionalManifest(state, kSOSPeerConfirmedManifestKey, peer->confirmedManifest, error)
630 || !SSOSPeerPersistManifestArray(state, kSOSPeerProposedManifestKey, peer->proposedManifests, error)
631 || !SSOSPeerPersistManifestArray(state, kSOSPeerLocalManifestKey, peer->localManifests, error)) {
632 CFReleaseNull(state);
633 }
634 return state;
635 }
636
637 SOSPeerRef SOSPeerCreateWithState(SOSEngineRef engine, CFStringRef peer_id, CFDictionaryRef state, CFErrorRef *error) {
638 return SOSPeerCreate_Internal(engine, state, peer_id, 0, error);
639 }
640
641 static void SOSPeerDestroy(CFTypeRef cf) {
642 SOSPeerRef peer = (SOSPeerRef)cf;
643 CFReleaseNull(peer->peer_id);
644 CFReleaseNull(peer->views);
645 CFReleaseNull(peer->pendingObjects);
646 CFReleaseNull(peer->unwantedManifest);
647 CFReleaseNull(peer->confirmedManifest);
648 CFReleaseNull(peer->proposedManifests);
649 CFReleaseNull(peer->localManifests);
650 CFReleaseNull(peer->otrTimers);
651 CFReleaseNull(peer->limiter);
652 CFReleaseNull(peer->_keyBag);
653 }
654
655 bool SOSPeerDidConnect(SOSPeerRef peer) {
656 SOSPeerSetMustSendMessage(peer, true);
657 SOSPeerSetProposedManifest(peer, SOSPeerGetConfirmedManifest(peer));
658 // TODO: Return false if nothing changed.
659 return true;
660 }
661
662 // MARK: accessors
663
664 CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
665 return peer->version;
666 }
667
668 void SOSPeerSetRateLimiter(SOSPeerRef peer, CFTypeRef limiter)
669 {
670 peer->limiter = CFRetainSafe(limiter);
671 }
672
673 CFTypeRef SOSPeerGetRateLimiter(SOSPeerRef peer)
674 {
675 return (peer->limiter ? peer->limiter : NULL);
676 }
677
678 CFStringRef SOSPeerGetID(SOSPeerRef peer) {
679 return peer->peer_id;
680 }
681
682 CFSetRef SOSPeerGetViewNameSet(SOSPeerRef peer) {
683 return peer->views;
684 }
685
686 void SOSPeerSetViewNameSet(SOSPeerRef peer, CFSetRef views) {
687 if (peer->views && !CFSetIsSubset(views, peer->views)) {
688 SOSPeerSetHasBeenInSync(peer, false);
689 }
690
691 CFRetainAssign(peer->views, views);
692 }
693
694 CFDataRef SOSPeerGetKeyBag(SOSPeerRef peer) {
695 return peer->_keyBag;
696 }
697
698 static bool SOSPeerUnlinkBackupJournal(SOSPeerRef peer, CFErrorRef *error) {
699 __block bool ok = true;
700 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
701 secnotice("backup", "%@ unlinking journal file %s", peer, journalName);
702 ok &= SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName);
703 });
704 return ok;
705 }
706
707 static bool SOSPeerWriteReset(SOSPeerRef peer, CFErrorRef *error) {
708 __block bool ok = true;
709 __block CFErrorRef localError = NULL;
710 ok &= SOSPeerTruncateJournal(peer, &localError, ^(FILE *journalFile) {
711 ok = SOSBackupEventWriteReset(journalFile, peer->_keyBag, &localError);
712 if (ok && !peer->_keyBag)
713 ok = SOSBackupEventWriteCompleteMarker(journalFile, 999, &localError);
714 });
715
716 if (!ok) {
717 secwarning("%@ failed to write reset to backup journal: %@", peer->peer_id, localError);
718 CFErrorPropagate(localError, error);
719 } else {
720 secnotice("backup-peer", "%@ Wrote reset.", peer->peer_id);
721 }
722
723 // Forget we ever wrote anything to the journal.
724 SOSPeerSetConfirmedManifest(peer, NULL);
725 SOSPeerSetProposedManifest(peer, NULL);
726
727 SOSPeerSetMustSendMessage(peer, !ok);
728 return ok;
729 }
730
731 void SOSPeerKeyBagDidChange(SOSPeerRef peer) {
732 // If !keyBag unlink the file, instead of writing a reset.
733 // CloudServices does not want to hear about empty keybags
734 SOSPeerSetSendObjects(peer, false);
735 if (!peer->_keyBag) {
736 SOSPeerUnlinkBackupJournal(peer, NULL);
737 } else {
738 // Attempt to write a reset (ignoring failures since it will
739 // be pended stickily if it fails).
740 SOSPeerWriteReset(peer, NULL);
741 SOSCCAccountTriggerSyncWithBackupPeer_server(SOSPeerGetID(peer));
742 }
743 }
744
745 void SOSPeerSetKeyBag(SOSPeerRef peer, CFDataRef keyBag) {
746 if (CFEqualSafe(keyBag, peer->_keyBag)) return;
747 bool hadKeybag = peer->_keyBag;
748 if (!keyBag) {
749 secwarning("%@ keybag for backup unset", SOSPeerGetID(peer));
750 } else {
751 secnotice("backup", "%@ backup bag: %@", SOSPeerGetID(peer), keyBag);
752 }
753 CFRetainAssign(peer->_keyBag, keyBag);
754 // Don't call SOSPeerKeybagDidChange for the inital edge from NULL -> having a keybag.
755 if (hadKeybag) {
756 SOSPeerKeyBagDidChange(peer);
757 }
758 }
759
760 bool SOSPeerWritePendingReset(SOSPeerRef peer, CFErrorRef *error) {
761 return !SOSPeerMustSendMessage(peer) || SOSPeerWriteReset(peer, error);
762 }
763
764 uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer) {
765 return ++peer->sequenceNumber;
766 }
767
768 uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer) {
769 return SOSPeerGetVersion(peer);
770 }
771
772 bool SOSPeerMustSendMessage(SOSPeerRef peer) {
773 return peer->mustSendMessage;
774 }
775
776 void SOSPeerSetMustSendMessage(SOSPeerRef peer, bool sendMessage) {
777 peer->mustSendMessage = sendMessage;
778 }
779
780 bool SOSPeerSendObjects(SOSPeerRef peer) {
781 return peer->sendObjects;
782 }
783
784 void SOSPeerSetSendObjects(SOSPeerRef peer, bool sendObjects) {
785 peer->sendObjects = sendObjects;
786 }
787
788 bool SOSPeerHasBeenInSync(SOSPeerRef peer) {
789 return peer->hasBeenInSync;
790 }
791
792 void SOSPeerSetHasBeenInSync(SOSPeerRef peer, bool hasBeenInSync) {
793 peer->hasBeenInSync = hasBeenInSync;
794 }
795
796 // MARK: Manifests
797
798 SOSManifestRef SOSPeerGetProposedManifest(SOSPeerRef peer) {
799 if (peer->proposedManifests && CFArrayGetCount(peer->proposedManifests))
800 return (SOSManifestRef)CFArrayGetValueAtIndex(peer->proposedManifests, 0);
801 return NULL;
802 }
803
804 SOSManifestRef SOSPeerGetConfirmedManifest(SOSPeerRef peer) {
805 return peer->confirmedManifest;
806 }
807
808 void SOSPeerSetConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed) {
809 CFRetainAssign(peer->confirmedManifest, confirmed);
810
811 // TODO: Clear only expired pending and local manifests from the array - this clears them all
812 // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
813 // Then we simply remove the entries from messages older than the one we are confirming now
814 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
815 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
816 }
817
818 void SOSPeerAddProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
819 SOSManifestArrayAppendManifest(&peer->proposedManifests, proposed);
820 }
821
822 void SOSPeerSetProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
823 SOSManifestArraySetManifest(&peer->proposedManifests, proposed);
824 }
825
826 void SOSPeerAddLocalManifest(SOSPeerRef peer, SOSManifestRef local) {
827 SOSManifestArrayAppendManifest(&peer->localManifests, local);
828 }
829
830 SOSManifestRef SOSPeerGetPendingObjects(SOSPeerRef peer) {
831 return peer->pendingObjects;
832 }
833
834 void SOSPeerSetPendingObjects(SOSPeerRef peer, SOSManifestRef pendingObjects) {
835 CFRetainAssign(peer->pendingObjects, pendingObjects);
836 }
837
838 SOSManifestRef SOSPeerGetUnwantedManifest(SOSPeerRef peer) {
839 return peer->unwantedManifest;
840 }
841
842 void SOSPeerSetUnwantedManifest(SOSPeerRef peer, SOSManifestRef unwantedManifest) {
843 CFRetainAssign(peer->unwantedManifest, unwantedManifest);
844 }
845 bool SOSPeerTimerForPeerExist(SOSPeerRef peer){
846 dispatch_source_t timer = SOSPeerGetOTRTimer(peer);
847 return timer ? true : false;
848 }
849 void SOSPeerSetOTRTimer(SOSPeerRef peer, dispatch_source_t timer){
850 NSMutableDictionary* timers = (NSMutableDictionary*)CFBridgingRelease(peer->otrTimers);
851 if(!timers)
852 timers = [[NSMutableDictionary alloc]init];
853
854 [timers setObject:timer forKey:(__bridge NSString*)SOSPeerGetID(peer)];
855 peer->otrTimers = (CFMutableDictionaryRef)CFBridgingRetain(timers);
856 }
857
858 dispatch_source_t SOSPeerGetOTRTimer(SOSPeerRef peer){
859 return (dispatch_source_t)CFDictionaryGetValue(peer->otrTimers, SOSPeerGetID(peer));
860 }
861
862 void SOSPeerRemoveOTRTimerEntry(SOSPeerRef peer){
863 CFDictionaryRemoveValue(peer->otrTimers, SOSPeerGetID(peer));
864 }
865
866 SOSManifestRef SOSPeerCopyManifestForDigest(SOSPeerRef peer, CFDataRef digest) {
867 if (!digest) return NULL;
868 SOSManifestRef manifest;
869 if (peer->proposedManifests) CFArrayForEachC(peer->proposedManifests, manifest) {
870 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
871 return CFRetainSafe(manifest);
872 }
873 if (peer->localManifests) CFArrayForEachC(peer->localManifests, manifest) {
874 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
875 return CFRetainSafe(manifest);
876 }
877 if (peer->confirmedManifest && CFEqual(digest, SOSManifestGetDigest(peer->confirmedManifest, NULL)))
878 return CFRetainSafe(peer->confirmedManifest);
879
880 return NULL;
881 }
882
883 static void SOSMarkManifestInUse(struct SOSDigestVector *mdInUse, SOSManifestRef manifest) {
884 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
885 if (digest)
886 SOSDigestVectorAppend(mdInUse, CFDataGetBytePtr(digest));
887 }
888
889 static void SOSMarkManifestsInUse(struct SOSDigestVector *mdInUse, CFArrayRef manifests) {
890 if (!isArray(manifests)) return;
891 SOSManifestRef manifest = NULL;
892 CFArrayForEachC(manifests, manifest) {
893 SOSMarkManifestInUse(mdInUse, manifest);
894 }
895 }
896
897 // Add all digests we are using to mdInUse
898 void SOSPeerMarkDigestsInUse(SOSPeerRef peer, struct SOSDigestVector *mdInUse) {
899 SOSMarkManifestInUse(mdInUse, peer->pendingObjects);
900 SOSMarkManifestInUse(mdInUse, peer->unwantedManifest);
901 SOSMarkManifestInUse(mdInUse, peer->confirmedManifest);
902 SOSMarkManifestsInUse(mdInUse, peer->localManifests);
903 SOSMarkManifestsInUse(mdInUse, peer->proposedManifests);
904 }
905
906 static void SOSAddManifestInUse(CFMutableDictionaryRef mfc, SOSManifestRef manifest) {
907 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
908 CFDataRef data = SOSManifestGetData(manifest);
909 if (digest && data)
910 CFDictionarySetValue(mfc, digest, data);
911 }
912
913 static void SOSAddManifestsInUse(CFMutableDictionaryRef mfc, CFArrayRef manifests) {
914 if (!isArray(manifests)) return;
915 SOSManifestRef manifest = NULL;
916 CFArrayForEachC(manifests, manifest) {
917 SOSAddManifestInUse(mfc, manifest);
918 }
919 }
920
921 void SOSPeerAddManifestsInUse(SOSPeerRef peer, CFMutableDictionaryRef mfc) {
922 SOSAddManifestInUse(mfc, peer->pendingObjects);
923 SOSAddManifestInUse(mfc, peer->unwantedManifest);
924 SOSAddManifestInUse(mfc, peer->confirmedManifest);
925 SOSAddManifestsInUse(mfc, peer->localManifests);
926 SOSAddManifestsInUse(mfc, peer->proposedManifests);
927
928 }
929
930 // absentFromRemote
931 // AbsentLocally
932 // additionsFromRemote
933 // original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
934
935
936 bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer, SOSManifestRef absentFromRemote, SOSManifestRef additionsFromRemote, SOSManifestRef unwantedFromRemote,
937 SOSManifestRef local, CFErrorRef *error) {
938 // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
939 bool ok = true;
940 SOSManifestRef remoteMissing = NULL, sharedRemovals = NULL, sharedAdditions = NULL;
941
942 // TODO: Simplify -- a lot.
943 ok = ok && (remoteMissing = SOSManifestCreateIntersection(absentFromRemote, local, error)); // remoteMissing = absentFromRemote <Intersected> local
944 ok = ok && (sharedRemovals = SOSManifestCreateComplement(remoteMissing, absentFromRemote, error)); // sharedRemovals = absentFromRemote - remoteMissing
945 ok = ok && (sharedAdditions = SOSManifestCreateIntersection(additionsFromRemote, local, error)); // sharedAdditions = additionsFromRemote <Intersected> local
946 //ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
947
948 // remoteMissing are things we have that remote has asked for => add to pendingObjects
949 // sharedRemovals are things we don't have that remote has asked for => remove from pendingDeletes
950 // sharedAdditions are things we have that remote has too => remove from pendingObjects
951 // remoteAdditions are things that remote said they have that we don't and we should probably ask for => add to pendingDeletes?
952 // unwantedFromRemote are things we received from remote for which we already have a newer object according to the conflict resolver.
953 secnotice("peer", "%@ RM:%@ SR:%@ SA:%@ UR:%@", peer, remoteMissing, sharedRemovals, sharedAdditions, unwantedFromRemote);
954
955 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, sharedAdditions, remoteMissing, error);
956 SOSManifestRef unwantedManifest = SOSManifestCreateWithPatch(peer->unwantedManifest, sharedRemovals, unwantedFromRemote, error);
957 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest); // PO = PO - sharedAdditions + remoteMissing
958 CFAssignRetained(peer->unwantedManifest, unwantedManifest); // U = U - sharedRemovals + unwantedFromRemote
959
960 CFReleaseSafe(remoteMissing);
961 CFReleaseSafe(sharedRemovals);
962 CFReleaseSafe(sharedAdditions);
963
964 secnotice("peer", "%@ C:%@ U:%@ O:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetUnwantedManifest(peer), SOSPeerGetPendingObjects(peer));
965
966 return ok;
967 }
968
969 // Called for a normal syncing peer. Only updates pendingObjects currently.
970 bool SOSPeerDataSourceWillCommit(SOSPeerRef peer, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
971 bool isAPITransaction = source == kSOSDataSourceAPITransaction;
972 bool isCKKSTransaction = source == kSOSDataSourceCKKSTransaction;
973 SOSManifestRef unconfirmedAdditions = NULL;
974 if ((isAPITransaction || isCKKSTransaction) && SOSManifestGetCount(additions)) {
975 // Remove confirmed from additions, leaving us with additions to the local db that the remote peer doesn't have yet
976 unconfirmedAdditions = SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer), additions, error);
977 }
978
979 if (SOSManifestGetCount(removals) || SOSManifestGetCount(unconfirmedAdditions)) {
980 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, removals, unconfirmedAdditions, error);
981
982 //#if DEBUG
983 // TODO: Only do this if debugScope "peer", notice is enabled.
984 // if (!SecIsScopeActive(kSecLevelNotice, "peer"))
985 {
986 // pended == UA unless the db is renotifying of an addition for something we already have
987 SOSManifestRef unpended = NULL, pended = NULL;
988 SOSManifestDiff(peer->pendingObjects, pendingObjectsManifest, &unpended, &pended, error);
989 secinfo("peer", "%@: willCommit R:%@ A:%@ UA:%@ %s O%s%@%s%@",
990 SOSPeerGetID(peer), removals, additions, unconfirmedAdditions,
991 (isAPITransaction ? "api": isCKKSTransaction ? "ckks" : "sos"),
992 (SOSManifestGetCount(unpended) ? "-" : ""),
993 (SOSManifestGetCount(unpended) ? (CFStringRef)unpended : CFSTR("")),
994 (SOSManifestGetCount(pended) ? "+" : SOSManifestGetCount(unpended) ? "" : "="),
995 (SOSManifestGetCount(pended) ? (CFStringRef)pended : CFSTR("")));
996 CFReleaseSafe(unpended);
997 CFReleaseSafe(pended);
998 }
999 //#endif /* DEBUG */
1000 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest);
1001 }
1002 CFReleaseSafe(unconfirmedAdditions);
1003
1004 return true;
1005 }
1006
1007 bool SOSPeerWriteAddEvent(FILE *journalFile, keybag_handle_t kbhandle, SOSDataSourceRef dataSource, SOSObjectRef object, CFErrorRef *error) {
1008 CFDictionaryRef backup_item = NULL;
1009 bool ok = ((backup_item = SOSObjectCopyBackup(dataSource, object, kbhandle, error))
1010 && SOSBackupEventWriteAdd(journalFile, backup_item, error));
1011 CFReleaseSafe(backup_item);
1012 return ok;
1013 }
1014
1015 // Called for a backup peer, should stream objects in changes right away
1016 bool SOSPeerDataSourceWillChange(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
1017 __block bool ok = true;
1018 ok &= SOSPeerWritePendingReset(peer, error) && SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
1019 struct SOSDigestVector dvdel = SOSDigestVectorInit;
1020 struct SOSDigestVector dvadd = SOSDigestVectorInit;
1021 SOSChangeRef change;
1022 CFArrayForEachC(changes, change) {
1023 bool isDelete = false;
1024 CFErrorRef localError = NULL;
1025 CFDataRef digest = NULL;
1026 SOSObjectRef object = NULL;
1027 bool ok = digest = SOSChangeCopyDigest(dataSource, change, &isDelete, &object, &localError);
1028 if (isDelete) {
1029 ok &= SOSBackupEventWriteDelete(journalFile, digest, &localError);
1030 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest));
1031 } else {
1032 ok &= SOSPeerWriteAddEvent(journalFile, kbhandle, dataSource, object, &localError);
1033 SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
1034 }
1035 if (!ok) {
1036 secerror("bad change %@: %@", change, localError);
1037 }
1038 CFReleaseSafe(digest);
1039 CFReleaseSafe(localError);
1040 };
1041
1042 if (ok) {
1043 // Update our proposed manifest since we just wrote stuff
1044 struct SOSDigestVector dvresult = SOSDigestVectorInit;
1045 SOSDigestVectorSort(&dvdel);
1046 SOSDigestVectorSort(&dvadd);
1047 if ((ok = SOSDigestVectorPatchSorted(SOSManifestGetDigestVector(SOSPeerGetProposedManifest(peer)), &dvdel,
1048 &dvadd, &dvresult, error))) {
1049 SOSManifestRef proposed;
1050 ok = proposed = SOSManifestCreateWithDigestVector(&dvresult, error);
1051 SOSPeerSetProposedManifest(peer, proposed);
1052 CFReleaseSafe(proposed);
1053 }
1054 SOSDigestVectorFree(&dvresult);
1055 }
1056 SOSDigestVectorFree(&dvdel);
1057 SOSDigestVectorFree(&dvadd);
1058
1059 // Only Write marker if we are actually in sync now (local == propopsed).
1060 if (SOSPeerSendObjects(peer))
1061 SOSBackupEventWriteCompleteMarker(journalFile, 799, error);
1062 });
1063
1064 if (!ok) {
1065 // We were unable to stream everything out neatly
1066 NSArray<NSString *> *backupPeerList = @[ (__bridge NSString *)SOSPeerGetID(peer) ];
1067 SOSCCRequestSyncWithBackupPeerList((__bridge CFArrayRef)backupPeerList);
1068 }
1069 return ok;
1070 }
1071