2 * Created by Michael Brouwer on 7/17/12.
3 * Copyright 2012 Apple Inc. All Rights Reserved.
7 * SOSEngine.c - Implementation of a secure object syncing engine
10 #include <SecureObjectSync/SOSEngine.h>
11 #include <SecureObjectSync/SOSPeer.h>
12 #include <SecureObjectSync/SOSPeerInfo.h>
13 #include <corecrypto/ccder.h>
16 #include <utilities/SecCFError.h>
17 #include <utilities/SecCFRelease.h>
18 #include <utilities/SecCFWrappers.h>
19 #include <utilities/der_plist.h>
20 #include <utilities/der_plist_internal.h>
21 #include <utilities/debugging.h>
22 #include <utilities/iCloudKeychainTrace.h>
23 #include <AssertMacros.h>
24 #include <CoreFoundation/CoreFoundation.h>
25 #include <SecItemServer.h>
26 #include <SecItemPriv.h>
28 /* DataSource helper macros and functions. */
30 // TODO: Change to create with DER.
31 #define SOSObjectCreateWithPropertyList(dataSource, plist, error) (dataSource->createWithPropertyList(dataSource, plist, error))
33 #define SOSObjectCopyPropertyList(dataSource, object, error) (dataSource->copyPropertyList(object, error))
34 #define SOSObjectCopyDigest(dataSource, object, error) (dataSource->copyDigest(object, error))
35 #define SOSObjectCopyPrimaryKey(dataSource, object, error) (dataSource->copyPrimaryKey(object, error))
36 #define SOSObjectCopyMergedObject(dataSource, object1, object2, error) (dataSource->copyMergedObject(object1, object2, error))
38 #define kSOSMaxObjectPerMessage (500)
40 static CFArrayRef
SOSDataSourceCopyObjectArray(SOSDataSourceRef data_source
, SOSManifestRef manifest
, CFErrorRef
*error
) {
41 CFMutableArrayRef objects
= CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks
);
43 // Delta sync by only sending a max of kSOSMaxObjectPerMessage objects at a time.
44 SOSManifestRef toSend
= NULL
;
45 if (SOSManifestGetCount(manifest
) > kSOSMaxObjectPerMessage
) {
46 toSend
= SOSManifestCreateWithBytes(SOSManifestGetBytePtr(manifest
), kSOSMaxObjectPerMessage
* SOSDigestSize
, error
);
52 if (!data_source
->foreach_object(data_source
, toSend
, error
, ^bool (SOSObjectRef object
, CFErrorRef
*localError
) {
53 CFDictionaryRef plist
= SOSObjectCopyPropertyList(data_source
, object
, localError
);
55 CFArrayAppendValue(objects
, plist
);
60 CFReleaseNull(objects
);
66 static CFDataRef
SOSDataSourceCopyManifestDigest(SOSDataSourceRef ds
, CFErrorRef
*error
) {
67 CFMutableDataRef manifestDigest
= CFDataCreateMutable(0, SOSDigestSize
);
68 CFDataSetLength(manifestDigest
, SOSDigestSize
);
69 if (!ds
->get_manifest_digest(ds
, CFDataGetMutableBytePtr(manifestDigest
), error
))
70 CFReleaseNull(manifestDigest
);
72 return manifestDigest
;
75 static SOSManifestRef
SOSDataSourceCopyManifest(SOSDataSourceRef ds
, CFErrorRef
*error
) {
76 return ds
->copy_manifest(ds
, error
);
79 static void SOSDataSourceRelease(SOSDataSourceRef ds
) {
84 /* SOSEngine implementation. */
86 static CFStringRef sErrorDomain
= CFSTR("com.apple.security.sos.engine.error");
88 static bool SOSEngineCreateError(CFIndex errorCode
, CFStringRef descriptionString
, CFErrorRef previousError
, CFErrorRef
*newError
) {
89 SecCFCreateError(errorCode
, descriptionString
, sErrorDomain
, previousError
, newError
);
93 struct __OpaqueSOSEngine
{
94 SOSDataSourceRef dataSource
;
97 SOSEngineRef
SOSEngineCreate(SOSDataSourceRef dataSource
, CFErrorRef
*error
) {
98 SOSEngineRef engine
= calloc(1, sizeof(struct __OpaqueSOSEngine
));
99 engine
->dataSource
= dataSource
;
104 void SOSEngineDispose(SOSEngineRef engine
) {
105 SOSDataSourceRelease(engine
->dataSource
);
110 enum SOSMessageType
{
111 SOSManifestInvalidMessageType
= 0,
112 SOSManifestDigestMessageType
= 1,
113 SOSManifestMessageType
= 2,
114 SOSManifestDeltaAndObjectsMessageType
= 3,
117 /* H(): SHA1 hash function.
118 M: Manifest of peer p
122 SOSPeerMessage := SEQUENCE {
123 messageType INTEGER (manifestDigest, manifest, manifestDeltaAndObjects)
124 version INTEGER OPTIONAL default v0
125 content ANY defined by messageType
128 ManifestDigest := OCTECT STRING (length 20)
129 Manifest := OCTECT STRING (length 20 * number of entries)
147 Array := SEQUENCE of Value
148 Dictionary := SET of KVPair
151 [0] conflict OCTECT STRING OPTIONAL
152 [1] change OCTECT STRING OPTIONAL
156 ManifestDeltaAndObjects := SEQUENCE {
157 manfestDigest ManifestDigest
160 addedObjects SEQUENCE of Object
163 manifestDigest content = OCTECT STRING
164 manifest content := OCTECT STRING
165 manifestDeltaAndObjects := SEQUENCE {
166 manfestDigest ManifestDigest
172 /* ManifestDigest message */
173 static size_t der_sizeof_manifest_digest_message(void) {
174 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
,
175 (ccder_sizeof_uint64(SOSManifestDigestMessageType
) +
176 ccder_sizeof_raw_octet_string(SOSDigestSize
)));
179 static uint8_t *der_encode_manifest_digest_message(const uint8_t digest
[SOSDigestSize
], const uint8_t *der
, uint8_t *der_end
) {
180 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
181 ccder_encode_uint64(SOSManifestDigestMessageType
, der
,
182 ccder_encode_raw_octet_string(SOSDigestSize
, digest
, der
, der_end
)));
185 /* This message is sent to each peer that joins a circle and can also be sent
186 as a form of ACK to confirm that the local peer is in sync with the peer
187 this is beig sent to. */
188 CFDataRef
SOSEngineCreateManifestDigestMessage(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
189 /* TODO: avoid copying the digest here by inlining der_encode_manifest_digest_message(). */
191 uint8_t digest
[SOSDigestSize
];
192 if (!engine
->dataSource
->get_manifest_digest(engine
->dataSource
, &digest
[0], error
)) {
196 size_t der_size
= der_sizeof_manifest_digest_message();
197 CFMutableDataRef message
= CFDataCreateMutable(NULL
, der_size
);
198 if (message
== NULL
) {
201 CFDataSetLength(message
, der_size
);
202 uint8_t *der_end
= CFDataGetMutableBytePtr(message
);
203 const uint8_t *der
= der_end
;
206 der_end
= der_encode_manifest_digest_message(digest
, der
, der_end
);
207 assert(der
== der_end
);
213 /* Manifest message */
214 static size_t der_sizeof_manifest_message(SOSManifestRef manifest
) {
215 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
,
216 (ccder_sizeof_uint64(SOSManifestMessageType
) +
217 ccder_sizeof_raw_octet_string(SOSManifestGetSize(manifest
))));
220 static uint8_t *der_encode_manifest_message(SOSManifestRef manifest
, const uint8_t *der
, uint8_t *der_end
) {
221 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
222 ccder_encode_uint64(SOSManifestMessageType
, der
,
223 ccder_encode_raw_octet_string(SOSManifestGetSize(manifest
),
224 SOSManifestGetBytePtr(manifest
), der
, der_end
)));
227 /* This message is sent in response to a manifestDigest if our manifestDigest
228 differs from that of the received manifestDigest, or in response to a
229 manifestAndObjects message if the manifestDigest in the received message
230 doesn't match our own manifestDigest. */
231 CFDataRef
SOSEngineCreateManifestMessage(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
232 SOSManifestRef manifest
= SOSDataSourceCopyManifest(engine
->dataSource
, error
);
236 size_t der_size
= der_sizeof_manifest_message(manifest
);
237 CFMutableDataRef message
= CFDataCreateMutable(NULL
, der_size
);
238 CFDataSetLength(message
, der_size
);
239 uint8_t *der_end
= CFDataGetMutableBytePtr(message
);
240 const uint8_t *der
= der_end
;
243 der_end
= der_encode_manifest_message(manifest
, der
, der_end
);
244 assert(der
== der_end
);
250 /* ManifestDeltaAndObjects message */
251 static size_t der_sizeof_manifest_and_objects_message(SOSManifestRef removals
, SOSManifestRef additions
, CFArrayRef objects
, CFErrorRef
*error
) {
252 size_t objects_size
= der_sizeof_plist(objects
, error
);
253 if (objects_size
== 0)
256 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
,
257 (ccder_sizeof_uint64(SOSManifestDeltaAndObjectsMessageType
) +
258 ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
,
259 (ccder_sizeof_raw_octet_string(SOSDigestSize
) +
260 ccder_sizeof_raw_octet_string(SOSManifestGetSize(removals
)) +
261 ccder_sizeof_raw_octet_string(SOSManifestGetSize(additions
)) +
265 static uint8_t *der_encode_manifest_and_objects_message(CFDataRef digest
, SOSManifestRef removals
, SOSManifestRef additions
, CFArrayRef objects
, CFErrorRef
*error
, const uint8_t *der
, uint8_t *der_end
) {
266 assert(CFDataGetLength(digest
) == SOSDigestSize
);
267 return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
268 ccder_encode_uint64(SOSManifestDeltaAndObjectsMessageType
, der
,
269 ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
270 ccder_encode_raw_octet_string(SOSDigestSize
, CFDataGetBytePtr(digest
), der
,
271 ccder_encode_raw_octet_string(SOSManifestGetSize(removals
), SOSManifestGetBytePtr(removals
), der
,
272 ccder_encode_raw_octet_string(SOSManifestGetSize(additions
), SOSManifestGetBytePtr(additions
), der
,
273 der_encode_plist(objects
, error
, der
, der_end
)))))));
276 /* This message is sent in response to a local change that needs to be
277 propagated to our peers or in response to a manifest or manifestDigest
278 message from a peer that is not in sync with us yet. */
279 CFDataRef
SOSEngineCreateManifestAndObjectsMessage(SOSEngineRef engine
, SOSPeerRef peer
, CFErrorRef
*error
) {
281 peer has a manifest that corresponds to peers real manifest.
282 we send everything in our datasource that's not in peers manifest already to peer.
284 CFMutableDataRef message
= NULL
;
285 SOSManifestRef manifest
, peerManifest
, additions
, removals
;
288 manifest
= SOSDataSourceCopyManifest(engine
->dataSource
, error
);
292 peerManifest
= SOSPeerCopyManifest(peer
, error
);
296 if (!SOSManifestDiff(manifest
, peerManifest
, &additions
, &removals
, error
))
299 CFErrorRef localError
= NULL
;
300 CFArrayRef objects
= SOSDataSourceCopyObjectArray(engine
->dataSource
, additions
, &localError
);
302 if(SecErrorGetOSStatus(localError
)==errSecDecode
) {
303 secnotice("engine", "Corrupted item found: %@", localError
);
304 CFReleaseNull(manifest
);
305 CFReleaseNull(additions
);
306 CFReleaseNull(removals
);
307 CFReleaseNull(peerManifest
);
308 CFReleaseNull(localError
);
311 if(error
&& *error
==NULL
)
314 CFReleaseNull(localError
);
318 size_t der_size
= der_sizeof_manifest_and_objects_message(removals
, additions
, objects
, error
);
322 /* TODO: avoid copying the digest here by inlining der_encode_manifest_and_objects_message(). */
323 CFDataRef peerDigest
= SOSPeerCopyManifestDigest(peer
, error
);
327 message
= CFDataCreateMutable(NULL
, der_size
);
328 CFDataSetLength(message
, der_size
);
329 uint8_t *der_end
= CFDataGetMutableBytePtr(message
);
330 const uint8_t *der
= der_end
;
333 der_end
= der_encode_manifest_and_objects_message(peerDigest
, removals
, additions
, objects
, error
, der
, der_end
);
334 assert(der
== der_end
);
335 if (der_end
== NULL
) {
336 CFReleaseNull(message
);
340 /* Record the peers new manifest assuming that peer will accept all the
341 changes we are about to send them. */
342 SOSPeerSetManifest(peer
, manifest
, error
);
345 CFRelease(peerDigest
);
349 SOSManifestDispose(removals
);
350 SOSManifestDispose(additions
);
352 SOSManifestDispose(peerManifest
);
354 SOSManifestDispose(manifest
);
360 static const uint8_t *der_decode_msg_type(enum SOSMessageType
*msg_type
,
362 const uint8_t *der_end
,
364 const uint8_t *body_end
;
365 der
= ccder_decode_sequence_tl(&body_end
, der
, der_end
);
369 if (body_end
!= der_end
) {
370 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Trailing garbage at end of message"), NULL
, error
);
375 der
= ccder_decode_uint64(&msgType
, der
, der_end
);
376 if (msgType
< 1 || msgType
> SOSManifestDeltaAndObjectsMessageType
) {
377 SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError
, sErrorDomain
,
379 CFSTR("Bad message type: %llu"), msgType
);
382 *msg_type
= (enum SOSMessageType
)msgType
;
386 static const uint8_t *
387 der_decode_manifest_digest(CFDataRef
*digest
, CFErrorRef
*error
,
388 const uint8_t *der
, const uint8_t *der_end
) {
389 require_quiet(der
, errOut
);
391 der
= ccder_decode_tl(CCDER_OCTET_STRING
, &len
, der
, der_end
);
392 require_action_quiet(der
, errOut
, SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Failed to find string"), NULL
, error
));
393 require_action_quiet(len
== SOSDigestSize
, errOut
, SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Invalid digest size"), NULL
, error
));
395 *digest
= CFDataCreate(0, der
, len
);
396 require_action_quiet(*digest
, errOut
, SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Failed to create digest"), NULL
, error
));
399 require_action_quiet(der
, errOut
, CFReleaseNull(*digest
); SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Failed to find string"), NULL
, error
));
407 static const uint8_t *
408 der_decode_manifest(SOSManifestRef
*manifest
, CFErrorRef
*error
,
409 const uint8_t *der
, const uint8_t *der_end
) {
413 der
= ccder_decode_tl(CCDER_OCTET_STRING
, &len
, der
, der_end
);
415 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Failed to decode manifest"), NULL
, error
);
418 if (len
% SOSDigestSize
!= 0) {
419 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("manifest not a multiple of digest size"), NULL
, error
);
422 *manifest
= SOSManifestCreateWithBytes(der
, len
, error
);
432 static const uint8_t *
433 der_decode_manifest_digest_message(CFDataRef
*digest
, CFErrorRef
*error
,
434 const uint8_t *der
, const uint8_t *der_end
) {
435 der
= der_decode_manifest_digest(digest
, error
, der
, der_end
);
436 if (der
&& der
!= der_end
) {
437 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Trailing garbage after digest"), NULL
, error
);
438 CFReleaseNull(*digest
);
444 static const uint8_t *
445 der_decode_manifest_message(SOSManifestRef
*manifest
, CFErrorRef
*error
,
446 const uint8_t *der
, const uint8_t *der_end
) {
447 der
= der_decode_manifest(manifest
, error
, der
, der_end
);
448 if (der
&& der
!= der_end
) {
449 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Trailing garbage after manifest"), NULL
, error
);
450 SOSManifestDispose(*manifest
);
457 static const uint8_t *
458 der_decode_manifest_and_objects_message(CFDataRef
*peerManifestDigest
,
459 SOSManifestRef
*removals
,
460 SOSManifestRef
*additions
,
462 CFErrorRef
*error
, const uint8_t *der
,
463 const uint8_t *der_end
) {
464 const uint8_t *body_end
;
465 der
= ccder_decode_sequence_tl(&body_end
, der
, der_end
);
467 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Failed to decode top level sequence"), NULL
, error
);
471 if (body_end
!= der_end
) {
472 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Trailing garbage at end of message"), NULL
, error
);
476 der
= der_decode_manifest_digest(peerManifestDigest
, error
, der
, der_end
);
479 der
= der_decode_manifest(removals
, error
, der
, der_end
);
482 der
= der_decode_manifest(additions
, error
, der
, der_end
);
486 CFPropertyListRef pl
;
487 der
= der_decode_plist(0, 0, &pl
, error
, der
, der_end
);
491 if (der
!= der_end
) {
492 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("Trailing garbage at end of message body"), NULL
, error
);
496 // TODO Check that objects is in fact an array. */
497 if (CFArrayGetTypeID() != CFGetTypeID(pl
)) {
498 SOSEngineCreateError(kSOSEngineInvalidMessageError
, CFSTR("objects is not an array"), NULL
, error
);
508 CFRelease(additions
);
512 CFRelease(peerManifestDigest
);
519 enum SOSMessageType
SOSMessageGetType(CFDataRef message
) {
520 const uint8_t *der
= CFDataGetBytePtr(message
);
521 const uint8_t *der_end
= der
+ CFDataGetLength(message
);
522 enum SOSMessageType msg_type
;
523 der_decode_msg_type(&msg_type
, der
, der_end
, NULL
);
525 return SOSManifestInvalidMessageType
;
532 /* H(): SHA1 hash function.
533 M: Manifest of peer p
535 static CFDataRef
SOSEngineCopyManifestDigestReply(SOSEngineRef engine
,
539 CFDataRef reply
= NULL
;
540 CFDataRef peerDigest
= SOSPeerCopyManifestDigest(peer
, NULL
);
541 CFDataRef manifestDigest
= SOSDataSourceCopyManifestDigest(engine
->dataSource
, error
);
542 if (manifestDigest
) {
543 if (CFEqual(manifestDigest
, digest
)) {
544 /* Our dataSources manifest and that of the peer are equal, we are in sync. */
545 if (peerDigest
&& CFEqual(peerDigest
, digest
)) {
546 /* The last known digest we had for peer already matched the digest peer
547 sent us, so this message is redundant, consider it an ack of our last
549 reply
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
551 /* Our peer just sent us a manifest digest that matches our own, but the digest
552 we have for the peer (if any) doesn't match that. Peer must have the same
553 manifest we do, so record that. */
554 SOSManifestRef manifest
= SOSDataSourceCopyManifest(engine
->dataSource
, error
);
556 bool ok
= SOSPeerSetManifest(peer
, manifest
, error
);
557 SOSManifestDispose(manifest
);
559 /* Since we got lucky and happen to have the same digest as our peer, we
560 send back an ack to ensure our peer ends up knowning our manifest as well. */
561 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
565 } else if (peerDigest
&& CFEqual(peerDigest
, digest
)) {
566 /* We know peer's current manifest is correct (the computed digest
567 matches the passed in one) but peer and our dataSource
568 are not in sync. Send the deltas to peer. */
569 reply
= SOSEngineCreateManifestAndObjectsMessage(engine
, peer
, error
);
571 /* Our peer has no digest yet, or the manifestDigest peer just sent
572 us doesn't match the digest of the manifest we think peer has.
573 We need to get peer to tell us their manifest, to do so we sent
574 it ours and hope it responds with deltas. */
575 reply
= SOSEngineCreateManifestMessage(engine
, peer
, error
);
577 CFRelease(manifestDigest
);
579 CFReleaseSafe(peerDigest
);
583 /* M: Manifest of peer p
585 static CFDataRef
SOSEngineCopyManifestReply(SOSEngineRef engine
, SOSPeerRef peer
,
586 SOSManifestRef manifest
,
588 CFDataRef reply
= NULL
;
589 // Peer just told us what his manifest was. Let's roll with it.
590 SOSPeerSetManifest(peer
, manifest
, error
);
591 CFDataRef peerManifestDigest
= SOSPeerCopyManifestDigest(peer
, error
);
592 if (peerManifestDigest
) {
593 CFDataRef manifestDigest
= SOSDataSourceCopyManifestDigest(engine
->dataSource
, error
);
594 if (manifestDigest
) {
595 if (CFEqual(peerManifestDigest
, manifestDigest
)) {
596 /* We're in sync, optionally send peer an ack. */
597 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
599 /* Send peer the objects it is missing from our manifest. */
600 reply
= SOSEngineCreateManifestAndObjectsMessage(engine
, peer
, error
);
602 CFRelease(manifestDigest
);
604 CFRelease(peerManifestDigest
);
609 static bool SOSEngineProccesObjects(SOSEngineRef engine
,
612 SOSManifestRef removals
,
613 SOSManifestRef additions
,
616 __block
bool result
= true;
617 CFArrayForEach(objects
, ^(const void *value
) {
618 SOSObjectRef ob
= SOSObjectCreateWithPropertyList(engine
->dataSource
, value
, error
);
620 SOSMergeResult mr
= engine
->dataSource
->add(engine
->dataSource
, ob
, error
);
623 // assertion failure, duplicate object added during transaction, that wasn't explicitly listed in removal list.
624 // treat as conflict?
625 // oa = ds->lookup(pkb);
626 // ds->choose_between(oa, ob)
627 // TODO: This is needed is we want to allow conflicts with other circles.
628 SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed
, 1);
629 secerror("assertion failure, add failed: %@",
630 error
? *error
: (CFErrorRef
)CFSTR("error is null"));
638 /* H(): SHA1 hash function.
639 L: Manifest of local peer.
640 M: Manifest of peer p.
641 M-L: Manifest of entries in M but not in L
642 L-M: Manifest of entries in L but not in M
643 O(M): Objects in manifest M
644 MSG: H(L) || L-M || M-L || O(M-L) */
645 static CFDataRef
SOSEngineCopyManifestAndObjectsReply(SOSEngineRef engine
,
648 SOSManifestRef removals
,
649 SOSManifestRef additions
,
652 CFDataRef reply
= NULL
;
653 CFMutableDataRef manifestDigest
= (CFMutableDataRef
)SOSDataSourceCopyManifestDigest(engine
->dataSource
, error
);
654 if (manifestDigest
) {
655 SOSManifestRef manifest
= SOSDataSourceCopyManifest(engine
->dataSource
, error
);
657 /* Always proccess the objects after we snapshot our manifest. */
658 if (!SOSEngineProccesObjects(engine
, peer
, digest
, removals
, additions
, objects
, error
)) {
659 secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer
), *error
);
662 if (CFEqual(manifestDigest
, digest
)) {
663 SOSManifestRef peerManifest
= NULL
;
665 peerManifest
= SOSManifestCreateWithPatch(manifest
, removals
, additions
, error
);
668 if (SOSPeerSetManifest(peer
, peerManifest
, error
)) {
669 /* Now proccess the objects. */
670 if (!SOSEngineProccesObjects(engine
, peer
, digest
, removals
, additions
, objects
, error
)) {
671 secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer
), *error
);
674 CFDataRef peerDigest
= SOSPeerCopyManifestDigest(peer
, error
);
676 /* Depending on whether after proccess objects we still have objects that need to be sent back to peer we respond with our digestManifest or with a manifestAndObjectsMessage. */
677 if (engine
->dataSource
->get_manifest_digest(engine
->dataSource
, CFDataGetMutableBytePtr(manifestDigest
), error
)) {
678 if (CFEqual(manifestDigest
, peerDigest
)) {
679 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
681 reply
= SOSEngineCreateManifestAndObjectsMessage(engine
, peer
, error
);
684 CFRelease(peerDigest
);
687 CFRelease(peerManifest
);
689 secerror("Received peer: %@ sent bad message: %@", SOSPeerGetID(peer
), *error
);
690 /* We failed to compute peer's digest, let's tell him ours again and hope for a retransmission. */
691 /* TODO: Perhaps this should be sent by the top level whenever an error occurs during parsing. */
692 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
695 /* ds->manifestDigest != msg->manigestDigest => We received deltas
696 against a manifest we don't have respond with our current
697 manifest to get back in sync. */
698 reply
= SOSEngineCreateManifestMessage(engine
, peer
, error
);
700 CFReleaseSafe(manifest
);
701 CFRelease(manifestDigest
);
706 /* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
707 bool SOSEngineHandleMessage(SOSEngineRef engine
, SOSPeerRef peer
,
708 CFDataRef message
, CFErrorRef
*error
) {
709 CFDataRef reply
= NULL
;
710 SOSManifestRef oldPeerManifest
= SOSPeerCopyManifest(peer
, NULL
);
711 const uint8_t *der
= CFDataGetBytePtr(message
);
712 const uint8_t *der_end
= der
+ CFDataGetLength(message
);
713 enum SOSMessageType msgType
;
715 der
= der_decode_msg_type(&msgType
, der
, der_end
, error
);
716 if (der
) switch (msgType
) {
717 case SOSManifestDigestMessageType
:
719 CFDataRef digest
= NULL
; // Make the static analyzer happy by NULL and Release safe
720 der
= der_decode_manifest_digest_message(&digest
, error
, der
, der_end
);
722 reply
= SOSEngineCopyManifestDigestReply(engine
, peer
, digest
, error
);
724 CFReleaseSafe(digest
);
727 case SOSManifestMessageType
:
729 SOSManifestRef manifest
;
730 der
= der_decode_manifest_message(&manifest
, error
, der
, der_end
);
732 reply
= SOSEngineCopyManifestReply(engine
, peer
, manifest
, error
);
733 SOSManifestDispose(manifest
);
737 case SOSManifestDeltaAndObjectsMessageType
:
739 CFDataRef peerManifestDigest
;
740 SOSManifestRef removals
;
741 SOSManifestRef additions
;
743 der
= der_decode_manifest_and_objects_message(&peerManifestDigest
, &removals
, &additions
, &objects
, error
, der
, der_end
);
745 reply
= SOSEngineCopyManifestAndObjectsReply(engine
, peer
, peerManifestDigest
, removals
, additions
, objects
, error
);
746 CFRelease(peerManifestDigest
);
747 SOSManifestDispose(removals
);
748 SOSManifestDispose(additions
);
754 SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError
, sErrorDomain
,
755 NULL
, error
, NULL
, CFSTR("Invalid message type %d"), msgType
);
760 if (reply
&& CFDataGetLength(reply
)) {
761 ok
= SOSPeerSendMessage(peer
, reply
, error
);
763 SOSPeerSetManifest(peer
, oldPeerManifest
, NULL
);
765 secnotice("engine", "%@", SOSPeerGetID(peer
));
766 CFReleaseSafe(oldPeerManifest
);
767 CFReleaseSafe(reply
);
771 bool SOSEngineSyncWithPeer(SOSEngineRef engine
, SOSPeerRef peer
, bool force
,
773 CFDataRef reply
= NULL
;
774 SOSManifestRef oldPeerManifest
= SOSPeerCopyManifest(peer
, NULL
);
776 require_quiet(SOSPeerCanSendMessage(peer
), exit
);
777 CFDataRef peerDigest
= SOSPeerCopyManifestDigest(peer
, NULL
);
778 CFMutableDataRef manifestDigest
= CFDataCreateMutable(0, SOSDigestSize
);
779 CFDataSetLength(manifestDigest
, SOSDigestSize
);
780 if (engine
->dataSource
->get_manifest_digest(engine
->dataSource
, CFDataGetMutableBytePtr(manifestDigest
), error
)) {
782 if (CFEqual(peerDigest
, manifestDigest
)) {
783 /* We are in sync with peer already. */
785 /* If we are at the end of the OTR handshake, we have to send
786 something to our peer no matter what to break the symmmetry. */
787 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
789 reply
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
792 /* We have have a digest for peer's manifest and it doesn't
793 match our current digest, so send deltas to peer. */
794 reply
= SOSEngineCreateManifestAndObjectsMessage(engine
, peer
, error
);
797 /* We have no digest for peer yet, send our manifest digest to peer,
798 it should respond with it's manifest so we can sync. */
799 reply
= SOSEngineCreateManifestDigestMessage(engine
, peer
, error
);
802 CFRelease(manifestDigest
);
803 CFReleaseSafe(peerDigest
);
806 if (ok
&& CFDataGetLength(reply
)) {
807 ok
= SOSPeerSendMessage(peer
, reply
, error
);
809 SOSPeerSetManifest(peer
, oldPeerManifest
, NULL
);
813 secnotice("engine", "%@", SOSPeerGetID(peer
));
814 CFReleaseSafe(oldPeerManifest
);
815 CFReleaseSafe(reply
);
820 static void appendObject(CFMutableStringRef desc
, CFDictionaryRef object
) {
821 __block
bool needComma
= false;
822 CFDictionaryForEach(object
, ^(const void *key
, const void *value
) {
824 CFStringAppend(desc
, CFSTR(","));
828 CFStringAppend(desc
, key
);
829 CFStringAppend(desc
, CFSTR("="));
830 if (CFEqual(CFSTR("data"), key
)) {
831 CFStringAppend(desc
, CFSTR("<?>"));
832 } else if (isData(value
)) {
833 CFStringAppendHexData(desc
, value
);
835 CFStringAppendFormat(desc
, 0, CFSTR("%@"), value
);
841 static void appendObjects(CFMutableStringRef desc
, CFArrayRef objects
) {
842 __block
bool needComma
= false;
843 CFArrayForEach(objects
, ^(const void *value
) {
845 CFStringAppend(desc
, CFSTR(","));
849 SecItemServerAppendItemDescription(desc
, value
);
853 CFStringRef
SOSMessageCopyDescription(CFDataRef message
) {
855 return CFSTR("<NULL>");
857 CFMutableStringRef desc
= CFStringCreateMutable(0, 0);
858 const uint8_t *der
= CFDataGetBytePtr(message
);
859 const uint8_t *der_end
= der
+ CFDataGetLength(message
);
860 enum SOSMessageType msgType
;
862 CFStringAppend(desc
, CFSTR("<Msg"));
863 der
= der_decode_msg_type(&msgType
, der
, der_end
, 0);
864 if (der
) switch (msgType
) {
865 case SOSManifestDigestMessageType
:
867 CFStringAppend(desc
, CFSTR("ManifestDigest digest: "));
868 CFDataRef digest
= NULL
;
869 der
= der_decode_manifest_digest_message(&digest
, 0, der
, der_end
);
871 CFStringAppendHexData(desc
, digest
);
873 CFReleaseNull(digest
);
877 case SOSManifestMessageType
:
879 CFStringAppend(desc
, CFSTR("Manifest"));
881 SOSManifestRef manifest
;
882 der
= der_decode_manifest_message(&manifest
, 0, der
, der_end
);
884 CFStringRef mfdesc
= SOSManifestCopyDescription(manifest
);
886 CFStringAppendFormat(desc
, 0, CFSTR(" manifest: %@"), mfdesc
);
889 SOSManifestDispose(manifest
);
893 case SOSManifestDeltaAndObjectsMessageType
:
895 CFStringAppend(desc
, CFSTR("ManifestDeltaAndObjects digest:"));
897 CFDataRef peerManifestDigest
;
898 SOSManifestRef removals
;
899 SOSManifestRef additions
;
901 der
= der_decode_manifest_and_objects_message(&peerManifestDigest
, &removals
, &additions
, &objects
, 0, der
, der_end
);
903 CFStringAppendHexData(desc
, peerManifestDigest
);
904 CFStringRef remdesc
= SOSManifestCopyDescription(removals
);
906 CFStringAppendFormat(desc
, 0, CFSTR(" removals: %@"), remdesc
);
909 CFStringRef adddesc
= SOSManifestCopyDescription(additions
);
911 CFStringAppendFormat(desc
, 0, CFSTR(" additions: %@"), adddesc
);
914 CFStringAppendFormat(desc
, 0, CFSTR(" objects: "));
915 appendObjects(desc
, objects
);
917 CFRelease(peerManifestDigest
);
918 SOSManifestDispose(removals
);
919 SOSManifestDispose(additions
);
925 CFStringAppendFormat(desc
, 0, CFSTR("InvalidType: %d"), msgType
);
929 CFStringAppend(desc
, CFSTR(">"));