+++ /dev/null
-/*
- * Created by Michael Brouwer on 7/17/12.
- * Copyright 2012 Apple Inc. All Rights Reserved.
- */
-
-/*
- * SOSEngine.c - Implementation of a secure object syncing engine
- */
-
-#include <SecureObjectSync/SOSEngine.h>
-#include <SecureObjectSync/SOSPeer.h>
-#include <SecureObjectSync/SOSPeerInfo.h>
-#include <corecrypto/ccder.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <utilities/SecCFError.h>
-#include <utilities/SecCFRelease.h>
-#include <utilities/SecCFWrappers.h>
-#include <utilities/der_plist.h>
-#include <utilities/der_plist_internal.h>
-#include <utilities/debugging.h>
-#include <utilities/iCloudKeychainTrace.h>
-#include <AssertMacros.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <SecItemServer.h>
-#include <SecItemPriv.h>
-
-/* DataSource helper macros and functions. */
-
-// TODO: Change to create with DER.
-#define SOSObjectCreateWithPropertyList(dataSource, plist, error) (dataSource->createWithPropertyList(dataSource, plist, error))
-
-#define SOSObjectCopyPropertyList(dataSource, object, error) (dataSource->copyPropertyList(object, error))
-#define SOSObjectCopyDigest(dataSource, object, error) (dataSource->copyDigest(object, error))
-#define SOSObjectCopyPrimaryKey(dataSource, object, error) (dataSource->copyPrimaryKey(object, error))
-#define SOSObjectCopyMergedObject(dataSource, object1, object2, error) (dataSource->copyMergedObject(object1, object2, error))
-
-#define kSOSMaxObjectPerMessage (500)
-
-static CFArrayRef SOSDataSourceCopyObjectArray(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error) {
- CFMutableArrayRef objects = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
-
- // Delta sync by only sending a max of kSOSMaxObjectPerMessage objects at a time.
- SOSManifestRef toSend = NULL;
- if (SOSManifestGetCount(manifest) > kSOSMaxObjectPerMessage) {
- toSend = SOSManifestCreateWithBytes(SOSManifestGetBytePtr(manifest), kSOSMaxObjectPerMessage * SOSDigestSize, error);
- } else {
- toSend = manifest;
- CFRetain(toSend);
- }
-
- if (!data_source->foreach_object(data_source, toSend, error, ^bool (SOSObjectRef object, CFErrorRef *localError) {
- CFDictionaryRef plist = SOSObjectCopyPropertyList(data_source, object, localError);
- if (plist) {
- CFArrayAppendValue(objects, plist);
- CFRelease(plist);
- }
- return plist;
- })) {
- CFReleaseNull(objects);
- }
- CFRetainSafe(toSend);
- return objects;
-}
-
-static CFDataRef SOSDataSourceCopyManifestDigest(SOSDataSourceRef ds, CFErrorRef *error) {
- CFMutableDataRef manifestDigest = CFDataCreateMutable(0, SOSDigestSize);
- CFDataSetLength(manifestDigest, SOSDigestSize);
- if (!ds->get_manifest_digest(ds, CFDataGetMutableBytePtr(manifestDigest), error))
- CFReleaseNull(manifestDigest);
-
- return manifestDigest;
-}
-
-static SOSManifestRef SOSDataSourceCopyManifest(SOSDataSourceRef ds, CFErrorRef *error) {
- return ds->copy_manifest(ds, error);
-}
-
-static void SOSDataSourceRelease(SOSDataSourceRef ds) {
- ds->release(ds);
-}
-
-
-/* SOSEngine implementation. */
-
-static CFStringRef sErrorDomain = CFSTR("com.apple.security.sos.engine.error");
-
-static bool SOSEngineCreateError(CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
- SecCFCreateError(errorCode, descriptionString, sErrorDomain, previousError, newError);
- return true;
-}
-
-struct __OpaqueSOSEngine {
- SOSDataSourceRef dataSource;
-};
-
-SOSEngineRef SOSEngineCreate(SOSDataSourceRef dataSource, CFErrorRef *error) {
- SOSEngineRef engine = calloc(1, sizeof(struct __OpaqueSOSEngine));
- engine->dataSource = dataSource;
-
- return engine;
-}
-
-void SOSEngineDispose(SOSEngineRef engine) {
- SOSDataSourceRelease(engine->dataSource);
- free(engine);
-}
-
-/* SOSEngine. */
-enum SOSMessageType {
- SOSManifestInvalidMessageType = 0,
- SOSManifestDigestMessageType = 1,
- SOSManifestMessageType = 2,
- SOSManifestDeltaAndObjectsMessageType = 3,
-};
-
-/* H(): SHA1 hash function.
- M: Manifest of peer p
- MSG: H(M).
-
-
- SOSPeerMessage := SEQUENCE {
- messageType INTEGER (manifestDigest, manifest, manifestDeltaAndObjects)
- version INTEGER OPTIONAL default v0
- content ANY defined by messageType
- }
-
- ManifestDigest := OCTECT STRING (length 20)
- Manifest := OCTECT STRING (length 20 * number of entries)
-
-
- Value := CHOICE {
- bool Boolean
- number INTEGER
- string UTF8String
- data OCTECT STRING
- date GENERAL TIME
- dictionary Object
- array Array
- }
-
- KVPair := SEQUENCE {
- key UTF8String
- value Value
- }
-
- Array := SEQUENCE of Value
- Dictionary := SET of KVPair
-
- Object := SEQUENCE {
- [0] conflict OCTECT STRING OPTIONAL
- [1] change OCTECT STRING OPTIONAL
- object Dictionary
-
-
- ManifestDeltaAndObjects := SEQUENCE {
- manfestDigest ManifestDigest
- removals Manifest
- additions Manifest
- addedObjects SEQUENCE of Object
- }
-
- manifestDigest content = OCTECT STRING
- manifest content := OCTECT STRING
- manifestDeltaAndObjects := SEQUENCE {
- manfestDigest ManifestDigest
- }
-
- */
-
-
-/* ManifestDigest message */
-static size_t der_sizeof_manifest_digest_message(void) {
- return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
- (ccder_sizeof_uint64(SOSManifestDigestMessageType) +
- ccder_sizeof_raw_octet_string(SOSDigestSize)));
-}
-
-static uint8_t *der_encode_manifest_digest_message(const uint8_t digest[SOSDigestSize], const uint8_t *der, uint8_t *der_end) {
- return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
- ccder_encode_uint64(SOSManifestDigestMessageType, der,
- ccder_encode_raw_octet_string(SOSDigestSize, digest, der, der_end)));
-}
-
-/* This message is sent to each peer that joins a circle and can also be sent
- as a form of ACK to confirm that the local peer is in sync with the peer
- this is beig sent to. */
-CFDataRef SOSEngineCreateManifestDigestMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
- /* TODO: avoid copying the digest here by inlining der_encode_manifest_digest_message(). */
-
- uint8_t digest[SOSDigestSize];
- if (!engine->dataSource->get_manifest_digest(engine->dataSource, &digest[0], error)) {
- return NULL;
- }
-
- size_t der_size = der_sizeof_manifest_digest_message();
- CFMutableDataRef message = CFDataCreateMutable(NULL, der_size);
- if (message == NULL) {
- return NULL;
- }
- CFDataSetLength(message, der_size);
- uint8_t *der_end = CFDataGetMutableBytePtr(message);
- const uint8_t *der = der_end;
- der_end += der_size;
-
- der_end = der_encode_manifest_digest_message(digest, der, der_end);
- assert(der == der_end);
-
- return message;
-}
-
-
-/* Manifest message */
-static size_t der_sizeof_manifest_message(SOSManifestRef manifest) {
- return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
- (ccder_sizeof_uint64(SOSManifestMessageType) +
- ccder_sizeof_raw_octet_string(SOSManifestGetSize(manifest))));
-}
-
-static uint8_t *der_encode_manifest_message(SOSManifestRef manifest, const uint8_t *der, uint8_t *der_end) {
- return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
- ccder_encode_uint64(SOSManifestMessageType, der,
- ccder_encode_raw_octet_string(SOSManifestGetSize(manifest),
- SOSManifestGetBytePtr(manifest), der, der_end)));
-}
-
-/* This message is sent in response to a manifestDigest if our manifestDigest
- differs from that of the received manifestDigest, or in response to a
- manifestAndObjects message if the manifestDigest in the received message
- doesn't match our own manifestDigest. */
-CFDataRef SOSEngineCreateManifestMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
- SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
- if (!manifest)
- return NULL;
-
- size_t der_size = der_sizeof_manifest_message(manifest);
- CFMutableDataRef message = CFDataCreateMutable(NULL, der_size);
- CFDataSetLength(message, der_size);
- uint8_t *der_end = CFDataGetMutableBytePtr(message);
- const uint8_t *der = der_end;
- der_end += der_size;
-
- der_end = der_encode_manifest_message(manifest, der, der_end);
- assert(der == der_end);
-
- return message;
-}
-
-
-/* ManifestDeltaAndObjects message */
-static size_t der_sizeof_manifest_and_objects_message(SOSManifestRef removals, SOSManifestRef additions, CFArrayRef objects, CFErrorRef *error) {
- size_t objects_size = der_sizeof_plist(objects, error);
- if (objects_size == 0)
- return objects_size;
-
- return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
- (ccder_sizeof_uint64(SOSManifestDeltaAndObjectsMessageType) +
- ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
- (ccder_sizeof_raw_octet_string(SOSDigestSize) +
- ccder_sizeof_raw_octet_string(SOSManifestGetSize(removals)) +
- ccder_sizeof_raw_octet_string(SOSManifestGetSize(additions)) +
- objects_size))));
-}
-
-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) {
- assert(CFDataGetLength(digest) == SOSDigestSize);
- return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
- ccder_encode_uint64(SOSManifestDeltaAndObjectsMessageType, der,
- ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
- ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(digest), der,
- ccder_encode_raw_octet_string(SOSManifestGetSize(removals), SOSManifestGetBytePtr(removals), der,
- ccder_encode_raw_octet_string(SOSManifestGetSize(additions), SOSManifestGetBytePtr(additions), der,
- der_encode_plist(objects, error, der, der_end)))))));
-}
-
-/* This message is sent in response to a local change that needs to be
- propagated to our peers or in response to a manifest or manifestDigest
- message from a peer that is not in sync with us yet. */
-CFDataRef SOSEngineCreateManifestAndObjectsMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
- /* Assumptions:
- peer has a manifest that corresponds to peers real manifest.
- we send everything in our datasource that's not in peers manifest already to peer.
- */
- CFMutableDataRef message = NULL;
- SOSManifestRef manifest, peerManifest, additions, removals;
-
-retry:
- manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
- if (!manifest)
- goto errOut4;
-
- peerManifest = SOSPeerCopyManifest(peer, error);
- if (!peerManifest)
- goto errOut3;
-
- if (!SOSManifestDiff(manifest, peerManifest, &additions, &removals, error))
- goto errOut2;
-
- CFErrorRef localError = NULL;
- CFArrayRef objects = SOSDataSourceCopyObjectArray(engine->dataSource, additions, &localError);
- if (!objects) {
- if(SecErrorGetOSStatus(localError)==errSecDecode) {
- secnotice("engine", "Corrupted item found: %@", localError);
- CFReleaseNull(manifest);
- CFReleaseNull(additions);
- CFReleaseNull(removals);
- CFReleaseNull(peerManifest);
- CFReleaseNull(localError);
- goto retry;
- }
- if(error && *error==NULL)
- *error=localError;
- else
- CFReleaseNull(localError);
- goto errOut1;
- }
-
- size_t der_size = der_sizeof_manifest_and_objects_message(removals, additions, objects, error);
- if (der_size == 0)
- goto errOut0;
-
- /* TODO: avoid copying the digest here by inlining der_encode_manifest_and_objects_message(). */
- CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, error);
- if (!peerDigest)
- goto errOut0;
-
- message = CFDataCreateMutable(NULL, der_size);
- CFDataSetLength(message, der_size);
- uint8_t *der_end = CFDataGetMutableBytePtr(message);
- const uint8_t *der = der_end;
- der_end += der_size;
-
- der_end = der_encode_manifest_and_objects_message(peerDigest, removals, additions, objects, error, der, der_end);
- assert(der == der_end);
- if (der_end == NULL) {
- CFReleaseNull(message);
- goto errOut_;
- }
-
- /* Record the peers new manifest assuming that peer will accept all the
- changes we are about to send them. */
- SOSPeerSetManifest(peer, manifest, error);
-
-errOut_:
- CFRelease(peerDigest);
-errOut0:
- CFRelease(objects);
-errOut1:
- SOSManifestDispose(removals);
- SOSManifestDispose(additions);
-errOut2:
- SOSManifestDispose(peerManifest);
-errOut3:
- SOSManifestDispose(manifest);
-errOut4:
-
- return message;
-}
-
-static const uint8_t *der_decode_msg_type(enum SOSMessageType *msg_type,
- const uint8_t *der,
- const uint8_t *der_end,
- CFErrorRef *error) {
- const uint8_t *body_end;
- der = ccder_decode_sequence_tl(&body_end, der, der_end);
- if (!der)
- return NULL;
-
- if (body_end != der_end) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message"), NULL, error);
- return NULL;
- }
-
- uint64_t msgType;
- der = ccder_decode_uint64(&msgType, der, der_end);
- if (msgType < 1 || msgType > SOSManifestDeltaAndObjectsMessageType) {
- SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError, sErrorDomain,
- NULL, error, NULL,
- CFSTR("Bad message type: %llu"), msgType);
- return NULL;
- }
- *msg_type = (enum SOSMessageType)msgType;
- return der;
-}
-
-static const uint8_t *
-der_decode_manifest_digest(CFDataRef *digest, CFErrorRef *error,
- const uint8_t *der, const uint8_t *der_end) {
- require_quiet(der, errOut);
- size_t len;
- der = ccder_decode_tl(CCDER_OCTET_STRING, &len, der, der_end);
- require_action_quiet(der, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to find string"), NULL, error));
- require_action_quiet(len == SOSDigestSize, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Invalid digest size"), NULL, error));
-
- *digest = CFDataCreate(0, der, len);
- require_action_quiet(*digest, errOut, SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to create digest"), NULL, error));
-
- der += len;
- require_action_quiet(der, errOut, CFReleaseNull(*digest); SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to find string"), NULL, error));
-
- return der;
-
-errOut:
- return NULL;
-}
-
-static const uint8_t *
-der_decode_manifest(SOSManifestRef *manifest, CFErrorRef *error,
- const uint8_t *der, const uint8_t *der_end) {
- if (!der)
- goto errOut;
- size_t len;
- der = ccder_decode_tl(CCDER_OCTET_STRING, &len, der, der_end);
- if (!der) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to decode manifest"), NULL, error);
- goto errOut;
- }
- if (len % SOSDigestSize != 0) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("manifest not a multiple of digest size"), NULL, error);
- goto errOut;
- }
- *manifest = SOSManifestCreateWithBytes(der, len, error);
- if (!*manifest)
- goto errOut;
-
- return der += len;
-
-errOut:
- return NULL;
-}
-
-static const uint8_t *
-der_decode_manifest_digest_message(CFDataRef *digest, CFErrorRef *error,
- const uint8_t *der, const uint8_t *der_end) {
- der = der_decode_manifest_digest(digest, error, der, der_end);
- if (der && der != der_end) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage after digest"), NULL, error);
- CFReleaseNull(*digest);
- der = NULL;
- }
- return der;
-}
-
-static const uint8_t *
-der_decode_manifest_message(SOSManifestRef *manifest, CFErrorRef *error,
- const uint8_t *der, const uint8_t *der_end) {
- der = der_decode_manifest(manifest, error, der, der_end);
- if (der && der != der_end) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage after manifest"), NULL, error);
- SOSManifestDispose(*manifest);
- *manifest = NULL;
- der = NULL;
- }
- return der;
-}
-
-static const uint8_t *
-der_decode_manifest_and_objects_message(CFDataRef *peerManifestDigest,
- SOSManifestRef *removals,
- SOSManifestRef *additions,
- CFArrayRef *objects,
- CFErrorRef *error, const uint8_t *der,
- const uint8_t *der_end) {
- const uint8_t *body_end;
- der = ccder_decode_sequence_tl(&body_end, der, der_end);
- if (!der) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Failed to decode top level sequence"), NULL, error);
- goto errOut;
- }
-
- if (body_end != der_end) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message"), NULL, error);
- goto errOut;
- }
-
- der = der_decode_manifest_digest(peerManifestDigest, error, der, der_end);
- if (!der)
- goto errOut;
- der = der_decode_manifest(removals, error, der, der_end);
- if (!der)
- goto errOut1;
- der = der_decode_manifest(additions, error, der, der_end);
- if (!der)
- goto errOut2;
-
- CFPropertyListRef pl;
- der = der_decode_plist(0, 0, &pl, error, der, der_end);
- if (!der)
- goto errOut3;
-
- if (der != der_end) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("Trailing garbage at end of message body"), NULL, error);
- goto errOut4;
- }
-
- // TODO Check that objects is in fact an array. */
- if (CFArrayGetTypeID() != CFGetTypeID(pl)) {
- SOSEngineCreateError(kSOSEngineInvalidMessageError, CFSTR("objects is not an array"), NULL, error);
- goto errOut4;
- }
- *objects = pl;
-
- return der;
-
-errOut4:
- CFRelease(pl);
-errOut3:
- CFRelease(additions);
-errOut2:
- CFRelease(removals);
-errOut1:
- CFRelease(peerManifestDigest);
-errOut:
- return NULL;
-}
-
-
-#if 0
-enum SOSMessageType SOSMessageGetType(CFDataRef message) {
- const uint8_t *der = CFDataGetBytePtr(message);
- const uint8_t *der_end = der + CFDataGetLength(message);
- enum SOSMessageType msg_type;
- der_decode_msg_type(&msg_type, der, der_end, NULL);
- if (!der) {
- return SOSManifestInvalidMessageType;
- }
-
- return msg_type;
-}
-#endif
-
-/* H(): SHA1 hash function.
- M: Manifest of peer p
- MSG: H(M) */
-static CFDataRef SOSEngineCopyManifestDigestReply(SOSEngineRef engine,
- SOSPeerRef peer,
- CFDataRef digest,
- CFErrorRef *error) {
- CFDataRef reply = NULL;
- CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, NULL);
- CFDataRef manifestDigest = SOSDataSourceCopyManifestDigest(engine->dataSource, error);
- if (manifestDigest) {
- if (CFEqual(manifestDigest, digest)) {
- /* Our dataSources manifest and that of the peer are equal, we are in sync. */
- if (peerDigest && CFEqual(peerDigest, digest)) {
- /* The last known digest we had for peer already matched the digest peer
- sent us, so this message is redundant, consider it an ack of our last
- message to peer. */
- reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
- } else {
- /* Our peer just sent us a manifest digest that matches our own, but the digest
- we have for the peer (if any) doesn't match that. Peer must have the same
- manifest we do, so record that. */
- SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
- if (manifest) {
- bool ok = SOSPeerSetManifest(peer, manifest, error);
- SOSManifestDispose(manifest);
- if (ok) {
- /* Since we got lucky and happen to have the same digest as our peer, we
- send back an ack to ensure our peer ends up knowning our manifest as well. */
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- }
- }
- }
- } else if (peerDigest && CFEqual(peerDigest, digest)) {
- /* We know peer's current manifest is correct (the computed digest
- matches the passed in one) but peer and our dataSource
- are not in sync. Send the deltas to peer. */
- reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
- } else {
- /* Our peer has no digest yet, or the manifestDigest peer just sent
- us doesn't match the digest of the manifest we think peer has.
- We need to get peer to tell us their manifest, to do so we sent
- it ours and hope it responds with deltas. */
- reply = SOSEngineCreateManifestMessage(engine, peer, error);
- }
- CFRelease(manifestDigest);
- }
- CFReleaseSafe(peerDigest);
- return reply;
-}
-
-/* M: Manifest of peer p
- MSG: M */
-static CFDataRef SOSEngineCopyManifestReply(SOSEngineRef engine, SOSPeerRef peer,
- SOSManifestRef manifest,
- CFErrorRef *error) {
- CFDataRef reply = NULL;
- // Peer just told us what his manifest was. Let's roll with it.
- SOSPeerSetManifest(peer, manifest, error);
- CFDataRef peerManifestDigest = SOSPeerCopyManifestDigest(peer, error);
- if (peerManifestDigest) {
- CFDataRef manifestDigest = SOSDataSourceCopyManifestDigest(engine->dataSource, error);
- if (manifestDigest) {
- if (CFEqual(peerManifestDigest, manifestDigest)) {
- /* We're in sync, optionally send peer an ack. */
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- } else {
- /* Send peer the objects it is missing from our manifest. */
- reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
- }
- CFRelease(manifestDigest);
- }
- CFRelease(peerManifestDigest);
- }
- return reply;
-}
-
-static bool SOSEngineProccesObjects(SOSEngineRef engine,
- SOSPeerRef peer,
- CFDataRef digest,
- SOSManifestRef removals,
- SOSManifestRef additions,
- CFArrayRef objects,
- CFErrorRef *error) {
- __block bool result = true;
- CFArrayForEach(objects, ^(const void *value) {
- SOSObjectRef ob = SOSObjectCreateWithPropertyList(engine->dataSource, value, error);
- if (ob) {
- SOSMergeResult mr = engine->dataSource->add(engine->dataSource, ob, error);
- if (!mr) {
- result = false;
- // assertion failure, duplicate object added during transaction, that wasn't explicitly listed in removal list.
- // treat as conflict?
- // oa = ds->lookup(pkb);
- // ds->choose_between(oa, ob)
- // TODO: This is needed is we want to allow conflicts with other circles.
- SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
- secerror("assertion failure, add failed: %@",
- error ? *error : (CFErrorRef)CFSTR("error is null"));
- }
- CFRelease(ob);
- }
- });
- return result;
-}
-
-/* H(): SHA1 hash function.
- L: Manifest of local peer.
- M: Manifest of peer p.
- M-L: Manifest of entries in M but not in L
- L-M: Manifest of entries in L but not in M
- O(M): Objects in manifest M
- MSG: H(L) || L-M || M-L || O(M-L) */
-static CFDataRef SOSEngineCopyManifestAndObjectsReply(SOSEngineRef engine,
- SOSPeerRef peer,
- CFDataRef digest,
- SOSManifestRef removals,
- SOSManifestRef additions,
- CFArrayRef objects,
- CFErrorRef *error) {
- CFDataRef reply = NULL;
- CFMutableDataRef manifestDigest = (CFMutableDataRef)SOSDataSourceCopyManifestDigest(engine->dataSource, error);
- if (manifestDigest) {
- SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
-
- /* Always proccess the objects after we snapshot our manifest. */
- if (!SOSEngineProccesObjects(engine, peer, digest, removals, additions, objects, error)) {
- secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer), *error);
- }
-
- if (CFEqual(manifestDigest, digest)) {
- SOSManifestRef peerManifest = NULL;
- if (manifest) {
- peerManifest = SOSManifestCreateWithPatch(manifest, removals, additions, error);
- }
- if (peerManifest) {
- if (SOSPeerSetManifest(peer, peerManifest, error)) {
- /* Now proccess the objects. */
- if (!SOSEngineProccesObjects(engine, peer, digest, removals, additions, objects, error)) {
- secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer), *error);
- }
-
- CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, error);
- if (peerDigest) {
- /* 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. */
- if (engine->dataSource->get_manifest_digest(engine->dataSource, CFDataGetMutableBytePtr(manifestDigest), error)) {
- if (CFEqual(manifestDigest, peerDigest)) {
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- } else {
- reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
- }
- }
- CFRelease(peerDigest);
- }
- }
- CFRelease(peerManifest);
- } else {
- secerror("Received peer: %@ sent bad message: %@", SOSPeerGetID(peer), *error);
- /* We failed to compute peer's digest, let's tell him ours again and hope for a retransmission. */
- /* TODO: Perhaps this should be sent by the top level whenever an error occurs during parsing. */
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- }
- } else {
- /* ds->manifestDigest != msg->manigestDigest => We received deltas
- against a manifest we don't have respond with our current
- manifest to get back in sync. */
- reply = SOSEngineCreateManifestMessage(engine, peer, error);
- }
- CFReleaseSafe(manifest);
- CFRelease(manifestDigest);
- }
- return reply;
-}
-
-/* Handle incoming message from peer p. Return false if there was an error, true otherwise. */
-bool SOSEngineHandleMessage(SOSEngineRef engine, SOSPeerRef peer,
- CFDataRef message, CFErrorRef *error) {
- CFDataRef reply = NULL;
- SOSManifestRef oldPeerManifest = SOSPeerCopyManifest(peer, NULL);
- const uint8_t *der = CFDataGetBytePtr(message);
- const uint8_t *der_end = der + CFDataGetLength(message);
- enum SOSMessageType msgType;
-
- der = der_decode_msg_type(&msgType, der, der_end, error);
- if (der) switch (msgType) {
- case SOSManifestDigestMessageType:
- {
- CFDataRef digest = NULL; // Make the static analyzer happy by NULL and Release safe
- der = der_decode_manifest_digest_message(&digest, error, der, der_end);
- if (der) {
- reply = SOSEngineCopyManifestDigestReply(engine, peer, digest, error);
- }
- CFReleaseSafe(digest);
- break;
- }
- case SOSManifestMessageType:
- {
- SOSManifestRef manifest;
- der = der_decode_manifest_message(&manifest, error, der, der_end);
- if (der) {
- reply = SOSEngineCopyManifestReply(engine, peer, manifest, error);
- SOSManifestDispose(manifest);
- }
- break;
- }
- case SOSManifestDeltaAndObjectsMessageType:
- {
- CFDataRef peerManifestDigest;
- SOSManifestRef removals;
- SOSManifestRef additions;
- CFArrayRef objects;
- der = der_decode_manifest_and_objects_message(&peerManifestDigest, &removals, &additions, &objects, error, der, der_end);
- if (der) {
- reply = SOSEngineCopyManifestAndObjectsReply(engine, peer, peerManifestDigest, removals, additions, objects, error);
- CFRelease(peerManifestDigest);
- SOSManifestDispose(removals);
- SOSManifestDispose(additions);
- CFRelease(objects);
- }
- break;
- }
- default:
- SecCFCreateErrorWithFormat(kSOSEngineInvalidMessageError, sErrorDomain,
- NULL, error, NULL, CFSTR("Invalid message type %d"), msgType);
- break;
- }
-
- bool ok = reply;
- if (reply && CFDataGetLength(reply)) {
- ok = SOSPeerSendMessage(peer, reply, error);
- if (!ok)
- SOSPeerSetManifest(peer, oldPeerManifest, NULL);
- }
- secnotice("engine", "%@", SOSPeerGetID(peer));
- CFReleaseSafe(oldPeerManifest);
- CFReleaseSafe(reply);
- return ok;
-}
-
-bool SOSEngineSyncWithPeer(SOSEngineRef engine, SOSPeerRef peer, bool force,
- CFErrorRef *error) {
- CFDataRef reply = NULL;
- SOSManifestRef oldPeerManifest = SOSPeerCopyManifest(peer, NULL);
- bool ok = true;
- require_quiet(SOSPeerCanSendMessage(peer), exit);
- CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, NULL);
- CFMutableDataRef manifestDigest = CFDataCreateMutable(0, SOSDigestSize);
- CFDataSetLength(manifestDigest, SOSDigestSize);
- if (engine->dataSource->get_manifest_digest(engine->dataSource, CFDataGetMutableBytePtr(manifestDigest), error)) {
- if (peerDigest) {
- if (CFEqual(peerDigest, manifestDigest)) {
- /* We are in sync with peer already. */
- if (force) {
- /* If we are at the end of the OTR handshake, we have to send
- something to our peer no matter what to break the symmmetry. */
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- } else {
- reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
- }
- } else {
- /* We have have a digest for peer's manifest and it doesn't
- match our current digest, so send deltas to peer. */
- reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
- }
- } else {
- /* We have no digest for peer yet, send our manifest digest to peer,
- it should respond with it's manifest so we can sync. */
- reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
- }
- }
- CFRelease(manifestDigest);
- CFReleaseSafe(peerDigest);
-
- ok = ok && reply;
- if (ok && CFDataGetLength(reply)) {
- ok = SOSPeerSendMessage(peer, reply, error);
- if (!ok)
- SOSPeerSetManifest(peer, oldPeerManifest, NULL);
- }
-
-exit:
- secnotice("engine", "%@", SOSPeerGetID(peer));
- CFReleaseSafe(oldPeerManifest);
- CFReleaseSafe(reply);
- return ok;
-}
-
-#if 0
-static void appendObject(CFMutableStringRef desc, CFDictionaryRef object) {
- __block bool needComma = false;
- CFDictionaryForEach(object, ^(const void *key, const void *value) {
- if (needComma)
- CFStringAppend(desc, CFSTR(","));
- else
- needComma = true;
-
- CFStringAppend(desc, key);
- CFStringAppend(desc, CFSTR("="));
- if (CFEqual(CFSTR("data"), key)) {
- CFStringAppend(desc, CFSTR("<?>"));
- } else if (isData(value)) {
- CFStringAppendHexData(desc, value);
- } else {
- CFStringAppendFormat(desc, 0, CFSTR("%@"), value);
- }
- });
-}
-#endif
-
-static void appendObjects(CFMutableStringRef desc, CFArrayRef objects) {
- __block bool needComma = false;
- CFArrayForEach(objects, ^(const void *value) {
- if (needComma)
- CFStringAppend(desc, CFSTR(","));
- else
- needComma = true;
-
- SecItemServerAppendItemDescription(desc, value);
- });
-}
-
-CFStringRef SOSMessageCopyDescription(CFDataRef message) {
- if (!message)
- return CFSTR("<NULL>");
-
- CFMutableStringRef desc = CFStringCreateMutable(0, 0);
- const uint8_t *der = CFDataGetBytePtr(message);
- const uint8_t *der_end = der + CFDataGetLength(message);
- enum SOSMessageType msgType;
-
- CFStringAppend(desc, CFSTR("<Msg"));
- der = der_decode_msg_type(&msgType, der, der_end, 0);
- if (der) switch (msgType) {
- case SOSManifestDigestMessageType:
- {
- CFStringAppend(desc, CFSTR("ManifestDigest digest: "));
- CFDataRef digest = NULL;
- der = der_decode_manifest_digest_message(&digest, 0, der, der_end);
- if (der) {
- CFStringAppendHexData(desc, digest);
- }
- CFReleaseNull(digest);
-
- break;
- }
- case SOSManifestMessageType:
- {
- CFStringAppend(desc, CFSTR("Manifest"));
-
- SOSManifestRef manifest;
- der = der_decode_manifest_message(&manifest, 0, der, der_end);
- if (der) {
- CFStringRef mfdesc = SOSManifestCopyDescription(manifest);
- if (mfdesc) {
- CFStringAppendFormat(desc, 0, CFSTR(" manifest: %@"), mfdesc);
- CFRelease(mfdesc);
- }
- SOSManifestDispose(manifest);
- }
- break;
- }
- case SOSManifestDeltaAndObjectsMessageType:
- {
- CFStringAppend(desc, CFSTR("ManifestDeltaAndObjects digest:"));
-
- CFDataRef peerManifestDigest;
- SOSManifestRef removals;
- SOSManifestRef additions;
- CFArrayRef objects;
- der = der_decode_manifest_and_objects_message(&peerManifestDigest, &removals, &additions, &objects, 0, der, der_end);
- if (der) {
- CFStringAppendHexData(desc, peerManifestDigest);
- CFStringRef remdesc = SOSManifestCopyDescription(removals);
- if (remdesc) {
- CFStringAppendFormat(desc, 0, CFSTR(" removals: %@"), remdesc);
- CFRelease(remdesc);
- }
- CFStringRef adddesc = SOSManifestCopyDescription(additions);
- if (adddesc) {
- CFStringAppendFormat(desc, 0, CFSTR(" additions: %@"), adddesc);
- CFRelease(adddesc);
- }
- CFStringAppendFormat(desc, 0, CFSTR(" objects: "));
- appendObjects(desc, objects);
-
- CFRelease(peerManifestDigest);
- SOSManifestDispose(removals);
- SOSManifestDispose(additions);
- CFRelease(objects);
- }
- break;
- }
- default:
- CFStringAppendFormat(desc, 0, CFSTR("InvalidType: %d"), msgType);
- break;
- }
-
- CFStringAppend(desc, CFSTR(">"));
-
- return desc;
-}