#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>
#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);
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);
}
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);
}
enum SOSMessageType {
SOSManifestInvalidMessageType = 0,
SOSManifestDigestMessageType = 1,
SOSManifestMessageType = 2,
SOSManifestDeltaAndObjectsMessageType = 3,
};
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)));
}
CFDataRef SOSEngineCreateManifestDigestMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
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;
}
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)));
}
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;
}
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)))))));
}
CFDataRef SOSEngineCreateManifestAndObjectsMessage(SOSEngineRef engine, SOSPeerRef peer, CFErrorRef *error) {
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;
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_;
}
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;
}
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
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)) {
if (peerDigest && CFEqual(peerDigest, digest)) {
reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
} else {
SOSManifestRef manifest = SOSDataSourceCopyManifest(engine->dataSource, error);
if (manifest) {
bool ok = SOSPeerSetManifest(peer, manifest, error);
SOSManifestDispose(manifest);
if (ok) {
reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
}
}
}
} else if (peerDigest && CFEqual(peerDigest, digest)) {
reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
} else {
reply = SOSEngineCreateManifestMessage(engine, peer, error);
}
CFRelease(manifestDigest);
}
CFReleaseSafe(peerDigest);
return reply;
}
static CFDataRef SOSEngineCopyManifestReply(SOSEngineRef engine, SOSPeerRef peer,
SOSManifestRef manifest,
CFErrorRef *error) {
CFDataRef reply = NULL;
SOSPeerSetManifest(peer, manifest, error);
CFDataRef peerManifestDigest = SOSPeerCopyManifestDigest(peer, error);
if (peerManifestDigest) {
CFDataRef manifestDigest = SOSDataSourceCopyManifestDigest(engine->dataSource, error);
if (manifestDigest) {
if (CFEqual(peerManifestDigest, manifestDigest)) {
reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
} else {
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;
SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncFailed, 1);
secerror("assertion failure, add failed: %@",
error ? *error : (CFErrorRef)CFSTR("error is null"));
}
CFRelease(ob);
}
});
return result;
}
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);
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)) {
if (!SOSEngineProccesObjects(engine, peer, digest, removals, additions, objects, error)) {
secerror("peer: %@ SOSEngineProccesObjects(): %@", SOSPeerGetID(peer), *error);
}
CFDataRef peerDigest = SOSPeerCopyManifestDigest(peer, error);
if (peerDigest) {
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);
reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
}
} else {
reply = SOSEngineCreateManifestMessage(engine, peer, error);
}
CFReleaseSafe(manifest);
CFRelease(manifestDigest);
}
return reply;
}
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; 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)) {
if (force) {
reply = SOSEngineCreateManifestDigestMessage(engine, peer, error);
} else {
reply = CFDataCreate(kCFAllocatorDefault, NULL, 0);
}
} else {
reply = SOSEngineCreateManifestAndObjectsMessage(engine, peer, error);
}
} else {
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;
}