X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sec/SOSCircle/SecureObjectSync/SOSEngine.c diff --git a/sec/SOSCircle/SecureObjectSync/SOSEngine.c b/sec/SOSCircle/SecureObjectSync/SOSEngine.c deleted file mode 100644 index aab5bffb..00000000 --- a/sec/SOSCircle/SecureObjectSync/SOSEngine.c +++ /dev/null @@ -1,932 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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(""); - - 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("")); - - return desc; -}