]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSPeer.m
Security-58286.41.2.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / 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 <Security/SecureObjectSync/SOSPeer.h>
29 #include <Security/SecureObjectSync/SOSPeerRateLimiter.h>
30 #include <Security/SecureObjectSync/SOSDigestVector.h>
31 #include <Security/SecureObjectSync/SOSInternal.h>
32 #include <Security/SecureObjectSync/SOSTransport.h>
33 #include <Security/SecureObjectSync/SOSEngine.h>
34 #include <Security/SecureObjectSync/SOSViews.h>
35 #include <Security/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 <Security/SecureObjectSync/SOSBackupEvent.h>
45 #include <Security/SecItemBackup.h>
46
47 #include <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 <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 }
653
654 bool SOSPeerDidConnect(SOSPeerRef peer) {
655 SOSPeerSetMustSendMessage(peer, true);
656 SOSPeerSetProposedManifest(peer, SOSPeerGetConfirmedManifest(peer));
657 // TODO: Return false if nothing changed.
658 return true;
659 }
660
661 // MARK: accessors
662
663 CFIndex SOSPeerGetVersion(SOSPeerRef peer) {
664 return peer->version;
665 }
666
667 void SOSPeerSetRateLimiter(SOSPeerRef peer, CFTypeRef limiter)
668 {
669 peer->limiter = CFRetainSafe(limiter);
670 }
671
672 CFTypeRef SOSPeerGetRateLimiter(SOSPeerRef peer)
673 {
674 return (peer->limiter ? peer->limiter : NULL);
675 }
676
677 CFStringRef SOSPeerGetID(SOSPeerRef peer) {
678 return peer->peer_id;
679 }
680
681 CFSetRef SOSPeerGetViewNameSet(SOSPeerRef peer) {
682 return peer->views;
683 }
684
685 void SOSPeerSetViewNameSet(SOSPeerRef peer, CFSetRef views) {
686 if (peer->views && !CFSetIsSubset(views, peer->views)) {
687 SOSPeerSetHasBeenInSync(peer, false);
688 }
689
690 CFRetainAssign(peer->views, views);
691 }
692
693 CFDataRef SOSPeerGetKeyBag(SOSPeerRef peer) {
694 return peer->_keyBag;
695 }
696
697 static bool SOSPeerUnlinkBackupJournal(SOSPeerRef peer, CFErrorRef *error) {
698 __block bool ok = true;
699 ok &= SOSPeerDoWithJournalPath(peer, error, ^(const char *journalName) {
700 secnotice("backup", "%@ unlinking journal file %s", peer, journalName);
701 ok &= SecCheckErrno(unlink(journalName), error, CFSTR("unlink %s"), journalName);
702 });
703 return ok;
704 }
705
706 static bool SOSPeerWriteReset(SOSPeerRef peer, CFErrorRef *error) {
707 __block bool ok = true;
708 __block CFErrorRef localError = NULL;
709 ok &= SOSPeerTruncateJournal(peer, &localError, ^(FILE *journalFile) {
710 ok = SOSBackupEventWriteReset(journalFile, peer->_keyBag, &localError);
711 if (ok && !peer->_keyBag)
712 ok = SOSBackupEventWriteCompleteMarker(journalFile, 999, &localError);
713 });
714
715 if (!ok) {
716 secwarning("%@ failed to write reset to backup journal: %@", peer->peer_id, localError);
717 CFErrorPropagate(localError, error);
718 } else {
719 secnotice("backup-peer", "%@ Wrote reset.", peer->peer_id);
720 }
721
722 // Forget we ever wrote anything to the journal.
723 SOSPeerSetConfirmedManifest(peer, NULL);
724 SOSPeerSetProposedManifest(peer, NULL);
725
726 SOSPeerSetMustSendMessage(peer, !ok);
727 return ok;
728 }
729
730 void SOSPeerKeyBagDidChange(SOSPeerRef peer) {
731 // If !keyBag unlink the file, instead of writing a reset.
732 // CloudServices does not want to hear about empty keybags
733 SOSPeerSetSendObjects(peer, false);
734 if (!peer->_keyBag) {
735 SOSPeerUnlinkBackupJournal(peer, NULL);
736 } else {
737 // Attempt to write a reset (ignoring failures since it will
738 // be pended stickily if it fails).
739 SOSPeerWriteReset(peer, NULL);
740 SOSCCRequestSyncWithBackupPeer(SOSPeerGetID(peer));
741 }
742 }
743
744 void SOSPeerSetKeyBag(SOSPeerRef peer, CFDataRef keyBag) {
745 if (CFEqualSafe(keyBag, peer->_keyBag)) return;
746 bool hadKeybag = peer->_keyBag;
747 if (!keyBag) {
748 secwarning("%@ keybag for backup unset", SOSPeerGetID(peer));
749 } else {
750 secnotice("backup", "%@ backup bag: %@", SOSPeerGetID(peer), keyBag);
751 }
752 CFRetainAssign(peer->_keyBag, keyBag);
753 // Don't call SOSPeerKeybagDidChange for the inital edge from NULL -> having a keybag.
754 if (hadKeybag) {
755 SOSPeerKeyBagDidChange(peer);
756 }
757 }
758
759 bool SOSPeerWritePendingReset(SOSPeerRef peer, CFErrorRef *error) {
760 return !SOSPeerMustSendMessage(peer) || SOSPeerWriteReset(peer, error);
761 }
762
763 uint64_t SOSPeerNextSequenceNumber(SOSPeerRef peer) {
764 return ++peer->sequenceNumber;
765 }
766
767 uint64_t SOSPeerGetMessageVersion(SOSPeerRef peer) {
768 return SOSPeerGetVersion(peer);
769 }
770
771 bool SOSPeerMustSendMessage(SOSPeerRef peer) {
772 return peer->mustSendMessage;
773 }
774
775 void SOSPeerSetMustSendMessage(SOSPeerRef peer, bool sendMessage) {
776 peer->mustSendMessage = sendMessage;
777 }
778
779 bool SOSPeerSendObjects(SOSPeerRef peer) {
780 return peer->sendObjects;
781 }
782
783 void SOSPeerSetSendObjects(SOSPeerRef peer, bool sendObjects) {
784 peer->sendObjects = sendObjects;
785 }
786
787 bool SOSPeerHasBeenInSync(SOSPeerRef peer) {
788 return peer->hasBeenInSync;
789 }
790
791 void SOSPeerSetHasBeenInSync(SOSPeerRef peer, bool hasBeenInSync) {
792 peer->hasBeenInSync = hasBeenInSync;
793 }
794
795 // MARK: Manifests
796
797 SOSManifestRef SOSPeerGetProposedManifest(SOSPeerRef peer) {
798 if (peer->proposedManifests && CFArrayGetCount(peer->proposedManifests))
799 return (SOSManifestRef)CFArrayGetValueAtIndex(peer->proposedManifests, 0);
800 return NULL;
801 }
802
803 SOSManifestRef SOSPeerGetConfirmedManifest(SOSPeerRef peer) {
804 return peer->confirmedManifest;
805 }
806
807 void SOSPeerSetConfirmedManifest(SOSPeerRef peer, SOSManifestRef confirmed) {
808 CFRetainAssign(peer->confirmedManifest, confirmed);
809
810 // TODO: Clear only expired pending and local manifests from the array - this clears them all
811 // To do so we'd have to track the messageIds we sent to our peer and when we proposed a particular manifest.
812 // Then we simply remove the entries from messages older than the one we are confirming now
813 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerProposedManifestKey));
814 //CFArrayRemoveAllValues(SOSPeerGetDigestsWithKey(peer, kSOSPeerLocalManifestKey));
815 }
816
817 void SOSPeerAddProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
818 SOSManifestArrayAppendManifest(&peer->proposedManifests, proposed);
819 }
820
821 void SOSPeerSetProposedManifest(SOSPeerRef peer, SOSManifestRef proposed) {
822 SOSManifestArraySetManifest(&peer->proposedManifests, proposed);
823 }
824
825 void SOSPeerAddLocalManifest(SOSPeerRef peer, SOSManifestRef local) {
826 SOSManifestArrayAppendManifest(&peer->localManifests, local);
827 }
828
829 SOSManifestRef SOSPeerGetPendingObjects(SOSPeerRef peer) {
830 return peer->pendingObjects;
831 }
832
833 void SOSPeerSetPendingObjects(SOSPeerRef peer, SOSManifestRef pendingObjects) {
834 CFRetainAssign(peer->pendingObjects, pendingObjects);
835 }
836
837 SOSManifestRef SOSPeerGetUnwantedManifest(SOSPeerRef peer) {
838 return peer->unwantedManifest;
839 }
840
841 void SOSPeerSetUnwantedManifest(SOSPeerRef peer, SOSManifestRef unwantedManifest) {
842 CFRetainAssign(peer->unwantedManifest, unwantedManifest);
843 }
844 bool SOSPeerTimerForPeerExist(SOSPeerRef peer){
845 dispatch_source_t timer = SOSPeerGetOTRTimer(peer);
846 return timer ? true : false;
847 }
848 void SOSPeerSetOTRTimer(SOSPeerRef peer, dispatch_source_t timer){
849 NSMutableDictionary* timers = (__bridge NSMutableDictionary*)peer->otrTimers;
850 if(!timers)
851 timers = [[NSMutableDictionary alloc]init];
852
853 [timers setObject:timer forKey:(__bridge NSString*)SOSPeerGetID(peer)];
854 peer->otrTimers = (CFMutableDictionaryRef)CFBridgingRetain(timers);
855 }
856
857 dispatch_source_t SOSPeerGetOTRTimer(SOSPeerRef peer){
858 return (dispatch_source_t)CFDictionaryGetValue(peer->otrTimers, SOSPeerGetID(peer));
859 }
860
861 void SOSPeerRemoveOTRTimerEntry(SOSPeerRef peer){
862 CFDictionaryRemoveValue(peer->otrTimers, SOSPeerGetID(peer));
863 }
864
865 SOSManifestRef SOSPeerCopyManifestForDigest(SOSPeerRef peer, CFDataRef digest) {
866 if (!digest) return NULL;
867 SOSManifestRef manifest;
868 if (peer->proposedManifests) CFArrayForEachC(peer->proposedManifests, manifest) {
869 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
870 return CFRetainSafe(manifest);
871 }
872 if (peer->localManifests) CFArrayForEachC(peer->localManifests, manifest) {
873 if (CFEqual(digest, SOSManifestGetDigest(manifest, NULL)))
874 return CFRetainSafe(manifest);
875 }
876 if (peer->confirmedManifest && CFEqual(digest, SOSManifestGetDigest(peer->confirmedManifest, NULL)))
877 return CFRetainSafe(peer->confirmedManifest);
878
879 return NULL;
880 }
881
882 static void SOSMarkManifestInUse(struct SOSDigestVector *mdInUse, SOSManifestRef manifest) {
883 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
884 if (digest)
885 SOSDigestVectorAppend(mdInUse, CFDataGetBytePtr(digest));
886 }
887
888 static void SOSMarkManifestsInUse(struct SOSDigestVector *mdInUse, CFArrayRef manifests) {
889 if (!isArray(manifests)) return;
890 SOSManifestRef manifest = NULL;
891 CFArrayForEachC(manifests, manifest) {
892 SOSMarkManifestInUse(mdInUse, manifest);
893 }
894 }
895
896 // Add all digests we are using to mdInUse
897 void SOSPeerMarkDigestsInUse(SOSPeerRef peer, struct SOSDigestVector *mdInUse) {
898 SOSMarkManifestInUse(mdInUse, peer->pendingObjects);
899 SOSMarkManifestInUse(mdInUse, peer->unwantedManifest);
900 SOSMarkManifestInUse(mdInUse, peer->confirmedManifest);
901 SOSMarkManifestsInUse(mdInUse, peer->localManifests);
902 SOSMarkManifestsInUse(mdInUse, peer->proposedManifests);
903 }
904
905 static void SOSAddManifestInUse(CFMutableDictionaryRef mfc, SOSManifestRef manifest) {
906 CFDataRef digest = SOSManifestGetDigest(manifest, NULL);
907 CFDataRef data = SOSManifestGetData(manifest);
908 if (digest && data)
909 CFDictionarySetValue(mfc, digest, data);
910 }
911
912 static void SOSAddManifestsInUse(CFMutableDictionaryRef mfc, CFArrayRef manifests) {
913 if (!isArray(manifests)) return;
914 SOSManifestRef manifest = NULL;
915 CFArrayForEachC(manifests, manifest) {
916 SOSAddManifestInUse(mfc, manifest);
917 }
918 }
919
920 void SOSPeerAddManifestsInUse(SOSPeerRef peer, CFMutableDictionaryRef mfc) {
921 SOSAddManifestInUse(mfc, peer->pendingObjects);
922 SOSAddManifestInUse(mfc, peer->unwantedManifest);
923 SOSAddManifestInUse(mfc, peer->confirmedManifest);
924 SOSAddManifestsInUse(mfc, peer->localManifests);
925 SOSAddManifestsInUse(mfc, peer->proposedManifests);
926
927 }
928
929 // absentFromRemote
930 // AbsentLocally
931 // additionsFromRemote
932 // original intent was that digests only got added to pendingObjects. We only know for sure if it is something added locally via api call
933
934
935 bool SOSPeerDidReceiveRemovalsAndAdditions(SOSPeerRef peer, SOSManifestRef absentFromRemote, SOSManifestRef additionsFromRemote, SOSManifestRef unwantedFromRemote,
936 SOSManifestRef local, CFErrorRef *error) {
937 // We assume that incoming manifests are all sorted, and absentFromRemote is disjoint from additionsFromRemote
938 bool ok = true;
939 SOSManifestRef remoteMissing = NULL, sharedRemovals = NULL, sharedAdditions = NULL;
940
941 // TODO: Simplify -- a lot.
942 ok = ok && (remoteMissing = SOSManifestCreateIntersection(absentFromRemote, local, error)); // remoteMissing = absentFromRemote <Intersected> local
943 ok = ok && (sharedRemovals = SOSManifestCreateComplement(remoteMissing, absentFromRemote, error)); // sharedRemovals = absentFromRemote - remoteMissing
944 ok = ok && (sharedAdditions = SOSManifestCreateIntersection(additionsFromRemote, local, error)); // sharedAdditions = additionsFromRemote <Intersected> local
945 //ok = ok && (remoteAdditions = SOSManifestCreateComplement(sharedAdditions, additionsFromRemote, error)); // remoteAdditions = additionsFromRemote - sharedAdditions
946
947 // remoteMissing are things we have that remote has asked for => add to pendingObjects
948 // sharedRemovals are things we don't have that remote has asked for => remove from pendingDeletes
949 // sharedAdditions are things we have that remote has too => remove from pendingObjects
950 // remoteAdditions are things that remote said they have that we don't and we should probably ask for => add to pendingDeletes?
951 // unwantedFromRemote are things we received from remote for which we already have a newer object according to the conflict resolver.
952 secnotice("peer", "%@ RM:%@ SR:%@ SA:%@ UR:%@", peer, remoteMissing, sharedRemovals, sharedAdditions, unwantedFromRemote);
953
954 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, sharedAdditions, remoteMissing, error);
955 SOSManifestRef unwantedManifest = SOSManifestCreateWithPatch(peer->unwantedManifest, sharedRemovals, unwantedFromRemote, error);
956 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest); // PO = PO - sharedAdditions + remoteMissing
957 CFAssignRetained(peer->unwantedManifest, unwantedManifest); // U = U - sharedRemovals + unwantedFromRemote
958
959 CFReleaseSafe(remoteMissing);
960 CFReleaseSafe(sharedRemovals);
961 CFReleaseSafe(sharedAdditions);
962
963 secnotice("peer", "%@ C:%@ U:%@ O:%@", peer, SOSPeerGetConfirmedManifest(peer), SOSPeerGetUnwantedManifest(peer), SOSPeerGetPendingObjects(peer));
964
965 return ok;
966 }
967
968 // Called for a normal syncing peer. Only updates pendingObjects currently.
969 bool SOSPeerDataSourceWillCommit(SOSPeerRef peer, SOSDataSourceTransactionSource source, SOSManifestRef removals, SOSManifestRef additions, CFErrorRef *error) {
970 bool isAPITransaction = source == kSOSDataSourceAPITransaction;
971 bool isCKKSTransaction = source == kSOSDataSourceCKKSTransaction;
972 SOSManifestRef unconfirmedAdditions = NULL;
973 if ((isAPITransaction || isCKKSTransaction) && SOSManifestGetCount(additions)) {
974 // Remove confirmed from additions, leaving us with additions to the local db that the remote peer doesn't have yet
975 unconfirmedAdditions = SOSManifestCreateComplement(SOSPeerGetConfirmedManifest(peer), additions, error);
976 }
977
978 if (SOSManifestGetCount(removals) || SOSManifestGetCount(unconfirmedAdditions)) {
979 SOSManifestRef pendingObjectsManifest = SOSManifestCreateWithPatch(peer->pendingObjects, removals, unconfirmedAdditions, error);
980
981 //#if DEBUG
982 // TODO: Only do this if debugScope "peer", notice is enabled.
983 // if (!SecIsScopeActive(kSecLevelNotice, "peer"))
984 {
985 // pended == UA unless the db is renotifying of an addition for something we already have
986 SOSManifestRef unpended = NULL, pended = NULL;
987 SOSManifestDiff(peer->pendingObjects, pendingObjectsManifest, &unpended, &pended, error);
988 secinfo("peer", "%@: willCommit R:%@ A:%@ UA:%@ %s O%s%@%s%@",
989 SOSPeerGetID(peer), removals, additions, unconfirmedAdditions,
990 (isAPITransaction ? "api": isCKKSTransaction ? "ckks" : "sos"),
991 (SOSManifestGetCount(unpended) ? "-" : ""),
992 (SOSManifestGetCount(unpended) ? (CFStringRef)unpended : CFSTR("")),
993 (SOSManifestGetCount(pended) ? "+" : SOSManifestGetCount(unpended) ? "" : "="),
994 (SOSManifestGetCount(pended) ? (CFStringRef)pended : CFSTR("")));
995 CFReleaseSafe(unpended);
996 CFReleaseSafe(pended);
997 }
998 //#endif /* DEBUG */
999 CFAssignRetained(peer->pendingObjects, pendingObjectsManifest);
1000 }
1001 CFReleaseSafe(unconfirmedAdditions);
1002
1003 return true;
1004 }
1005
1006 bool SOSPeerWriteAddEvent(FILE *journalFile, keybag_handle_t kbhandle, SOSDataSourceRef dataSource, SOSObjectRef object, CFErrorRef *error) {
1007 CFDictionaryRef backup_item = NULL;
1008 bool ok = ((backup_item = SOSObjectCopyBackup(dataSource, object, kbhandle, error))
1009 && SOSBackupEventWriteAdd(journalFile, backup_item, error));
1010 CFReleaseSafe(backup_item);
1011 return ok;
1012 }
1013
1014 // Called for a backup peer, should stream objects in changes right away
1015 bool SOSPeerDataSourceWillChange(SOSPeerRef peer, SOSDataSourceRef dataSource, SOSDataSourceTransactionSource source, CFArrayRef changes, CFErrorRef *error) {
1016 __block bool ok = true;
1017 ok &= SOSPeerWritePendingReset(peer, error) && SOSPeerAppendToJournal(peer, error, ^(FILE *journalFile, keybag_handle_t kbhandle) {
1018 struct SOSDigestVector dvdel = SOSDigestVectorInit;
1019 struct SOSDigestVector dvadd = SOSDigestVectorInit;
1020 SOSChangeRef change;
1021 CFArrayForEachC(changes, change) {
1022 bool isDelete = false;
1023 CFErrorRef localError = NULL;
1024 CFDataRef digest = NULL;
1025 SOSObjectRef object = NULL;
1026 bool ok = digest = SOSChangeCopyDigest(dataSource, change, &isDelete, &object, &localError);
1027 if (isDelete) {
1028 ok &= SOSBackupEventWriteDelete(journalFile, digest, &localError);
1029 SOSDigestVectorAppend(&dvdel, CFDataGetBytePtr(digest));
1030 } else {
1031 ok &= SOSPeerWriteAddEvent(journalFile, kbhandle, dataSource, object, &localError);
1032 SOSDigestVectorAppend(&dvadd, CFDataGetBytePtr(digest));
1033 }
1034 if (!ok) {
1035 secerror("bad change %@: %@", change, localError);
1036 }
1037 CFReleaseSafe(digest);
1038 CFReleaseSafe(localError);
1039 };
1040
1041 if (ok) {
1042 // Update our proposed manifest since we just wrote stuff
1043 struct SOSDigestVector dvresult = SOSDigestVectorInit;
1044 SOSDigestVectorSort(&dvdel);
1045 SOSDigestVectorSort(&dvadd);
1046 if ((ok = SOSDigestVectorPatchSorted(SOSManifestGetDigestVector(SOSPeerGetProposedManifest(peer)), &dvdel,
1047 &dvadd, &dvresult, error))) {
1048 SOSManifestRef proposed;
1049 ok = proposed = SOSManifestCreateWithDigestVector(&dvresult, error);
1050 SOSPeerSetProposedManifest(peer, proposed);
1051 CFReleaseSafe(proposed);
1052 }
1053 SOSDigestVectorFree(&dvresult);
1054 }
1055 SOSDigestVectorFree(&dvdel);
1056 SOSDigestVectorFree(&dvadd);
1057
1058 // Only Write marker if we are actually in sync now (local == propopsed).
1059 if (SOSPeerSendObjects(peer))
1060 SOSBackupEventWriteCompleteMarker(journalFile, 799, error);
1061 });
1062
1063 if (!ok) {
1064 // We were unable to stream everything out neatly
1065 SOSCCRequestSyncWithBackupPeer(SOSPeerGetID(peer));
1066 }
1067 return ok;
1068 }
1069