--- /dev/null
+#include "SecTransform.h"
+#include "SecTransformInternal.h"
+
+#include "Transform.h"
+#include "Utilities.h"
+#include "TransformFactory.h"
+#include "GroupTransform.h"
+#include "c++utils.h"
+#include "SecCollectTransform.h"
+
+
+#include <string>
+
+using namespace std;
+
+const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT");
+const CFStringRef kSecTransformOutputAttributeName = CFSTR("OUTPUT");
+const CFStringRef kSecTransformDebugAttributeName = CFSTR("DEBUG");
+const CFStringRef kSecTransformTransformName = CFSTR("NAME");
+//const CFStringRef kSecTransformErrorTransform = CFSTR("TRANSFORM");
+const CFStringRef kSecTransformErrorDomain = CFSTR("com.apple.security.transforms.error");
+const CFStringRef kSecTransformAbortAttributeName = CFSTR("ABORT");
+
+CFErrorRef SecTransformConnectTransformsInternal(SecGroupTransformRef groupRef,
+ SecTransformRef sourceTransformRef,
+ CFStringRef sourceAttributeName,
+ SecTransformRef destinationTransformRef,
+ CFStringRef destinationAttributeName)
+{
+ Transform* destination = (Transform*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef);
+ Transform* source = (Transform*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef);
+
+ GroupTransform* group = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(groupRef);
+ CFErrorRef temp = source->Connect(group, destination, destinationAttributeName, sourceAttributeName);
+ return temp;
+}
+
+
+CFErrorRef SecTransformDisconnectTransforms(SecTransformRef sourceTransformRef, CFStringRef sourceAttributeName,
+ SecTransformRef destinationTransformRef, CFStringRef destinationAttributeName)
+{
+ Transform* destination = (Transform*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef);
+ Transform* source = (Transform*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef);
+ return source->Disconnect(destination, sourceAttributeName, destinationAttributeName);
+}
+
+SecGroupTransformRef SecTransformCreateGroupTransform()
+{
+ return (SecGroupTransformRef) GroupTransform::Make();
+}
+
+SecGroupTransformRef SecTransformConnectTransforms(SecTransformRef sourceTransformRef,
+ CFStringRef sourceAttributeName,
+ SecTransformRef destinationTransformRef,
+ CFStringRef destinationAttributeName,
+ SecGroupTransformRef group,
+ CFErrorRef *error)
+{
+ if (group == NULL)
+ {
+ if (error)
+ {
+ *error = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Group must not be NULL.");
+ }
+
+ return NULL;
+ }
+
+ if (destinationAttributeName == NULL)
+ {
+ destinationAttributeName = kSecTransformInputAttributeName;
+ }
+
+ if (sourceAttributeName == NULL)
+ {
+ sourceAttributeName = kSecTransformOutputAttributeName;
+ }
+
+ GroupTransform* gtr = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(group);
+
+ CFErrorRef temp = SecTransformConnectTransformsInternal(gtr->GetCFObject(),
+ sourceTransformRef, sourceAttributeName,
+ destinationTransformRef, destinationAttributeName);
+
+ if (error)
+ {
+ *error = temp;
+ }
+
+ if (temp) // an error happened?
+ {
+ return NULL;
+ }
+ else
+ {
+ return group;
+ }
+}
+
+
+
+Boolean SecTransformSetAttribute(SecTransformRef transformRef,
+ CFStringRef key,
+ CFTypeRef value,
+ CFErrorRef *error)
+{
+ Boolean result = false; // Guilty until proven
+ Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+
+ if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID() && !transform->getAH(key, false))
+ {
+ if (error)
+ {
+ *error = CreateSecTransformErrorRef(kSecTransformOperationNotSupportedOnGroup, "SecTransformSetAttribute on non-exported attribute: %@ (exported attributes are: %@).", key, transform->GetAllAH());
+ }
+
+ return result;
+ }
+
+ // if the caller is setting the abort attribute, a value must be supplied
+ if (NULL == value && CFStringCompare(key, kSecTransformAbortAttributeName, 0) == kCFCompareEqualTo)
+ {
+ if (error)
+ {
+ // XXX: "a parameter"? It has one: NULL. What it requires is a non-NULL value.
+ *error = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "ABORT requires a parameter.");
+ }
+
+ return result;
+ }
+
+ CFErrorRef temp = transform->ExternalSetAttribute(key, value);
+ result = (temp == NULL);
+ if (error)
+ {
+ *error = temp;
+ }
+ else
+ {
+ if (temp)
+ {
+ CFRelease(temp);
+ }
+ }
+
+ return result;
+}
+
+
+
+CFTypeRef SecTransformGetAttribute(SecTransformRef transformRef,
+ CFStringRef key)
+{
+ // if the transform is a group, we really want to operation on its first object
+ if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID())
+ {
+ return NULL;
+ }
+
+ Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+ if (transform->mIsActive) {
+ return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "Can not get the value of attributes during execution (attempt to fetch %@/%@)", transform->GetName(), key);
+ }
+ return transform->GetAttribute(key);
+}
+
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+static inline GroupTransform* MakeGroupTransformFromTransformRef(SecTransformRef tr)
+{
+ GroupTransform* gt = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(tr);
+ return gt;
+}
+#pragma clang diagnostic pop
+
+static CFTypeRef InternalSecTransformExecute(SecTransformRef transformRef,
+ CFErrorRef* errorRef,
+ dispatch_queue_t deliveryQueue,
+ SecMessageBlock deliveryBlock)
+{
+ if (NULL == transformRef || (deliveryBlock && !deliveryQueue))
+ {
+ CFErrorRef localError = CFErrorCreate(kCFAllocatorDefault, kSecTransformErrorDomain,
+ kSecTransformInvalidArgument, NULL);
+
+ if (NULL != errorRef)
+ {
+ *errorRef = localError;
+ }
+ else
+ {
+ CFRelease(localError);
+ }
+
+ return (CFTypeRef)NULL;
+ }
+
+ // if our transform is a group, connect to its first member instead
+ if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID())
+ {
+ GroupTransform* gtsrc = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+ transformRef = gtsrc->GetAnyMember();
+ }
+
+ Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+ return transform->Execute(deliveryQueue, deliveryBlock, errorRef);
+}
+
+CFTypeRef SecTransformExecute(SecTransformRef transformRef, CFErrorRef* errorRef)
+{
+ if (NULL == transformRef)
+ {
+ if (errorRef)
+ {
+ *errorRef = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "NULL transform can not be executed");
+ }
+ return NULL;
+ }
+
+ Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+
+ // transform->Execute will check this, but by then we have attached a collector which causes all manner of issues.
+ if (transform->mIsActive)
+ {
+ if (errorRef)
+ {
+ *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", transform->GetName());
+ }
+ return NULL;
+ }
+
+ SecTransformRef collectTransform = transforms_assume(SecCreateCollectTransform(errorRef));
+ SecGroupTransformRef theGroup = NULL;
+ Boolean releaseTheGroup = false;
+ GroupTransform* myGroup = NULL;
+ Boolean needConnection = true;
+
+ // Sniff the type of the transformRef to see if it is a group
+ if (SecGroupTransformGetTypeID() == CFGetTypeID(transformRef))
+ {
+ theGroup = (SecGroupTransformRef)transformRef;
+ }
+ else
+ {
+ // Ok TransformRef is a TransformRef so get's it group
+
+ myGroup = transform->mGroup;
+
+ if (NULL == myGroup)
+ {
+ theGroup = SecTransformCreateGroupTransform();
+ if (NULL == theGroup)
+ {
+ if (NULL != errorRef)
+ {
+ *errorRef = GetNoMemoryErrorAndRetain();
+ }
+
+ return (CFTypeRef)NULL;
+
+ }
+
+ releaseTheGroup = true;
+
+ SecGroupTransformRef connectResult =
+ SecTransformConnectTransforms(transformRef,
+ kSecTransformOutputAttributeName,
+ collectTransform,
+ kSecTransformInputAttributeName,
+ theGroup, errorRef);
+
+ if (NULL == connectResult)
+ {
+ return (CFTypeRef)NULL;
+ }
+
+ needConnection = false;
+
+ }
+ else
+ {
+ theGroup = (SecGroupTransformRef)myGroup->GetCFObject();
+ }
+ }
+
+ if (NULL == theGroup || (SecGroupTransformGetTypeID() != CFGetTypeID(theGroup)))
+ {
+ if (NULL != errorRef)
+ {
+ *errorRef = GetNoMemoryErrorAndRetain();
+ }
+
+ return (CFTypeRef)NULL;
+
+ }
+
+
+ if (needConnection)
+ {
+ // Connect the collectTransform to the group
+ myGroup = ((GroupTransform*)CoreFoundationHolder::ObjectFromCFType(theGroup))->GetRootGroup();
+ if (NULL == myGroup)
+ {
+ if (NULL != errorRef)
+ {
+ *errorRef = GetNoMemoryErrorAndRetain();
+ }
+
+ return (CFTypeRef)NULL;
+ }
+
+ SecTransformRef outputTransform = myGroup->FindLastTransform();
+
+ SecGroupTransformRef connectResult =
+ SecTransformConnectTransforms(outputTransform,
+ kSecTransformOutputAttributeName,
+ collectTransform,
+ kSecTransformInputAttributeName,
+ myGroup->GetCFObject(), errorRef);
+
+ if (NULL == connectResult)
+ {
+ CFRelease(collectTransform);
+ if (releaseTheGroup)
+ {
+ CFRelease(theGroup);
+ }
+ return (CFTypeRef)NULL;
+ }
+ }
+
+ __block CFTypeRef myResult = NULL;
+ dispatch_semaphore_t mySem = dispatch_semaphore_create(0L);
+ dispatch_queue_t myQueue = dispatch_queue_create("com.apple.security.sectransfrom.SecTransformExecute", NULL);
+ SecMessageBlock myBlock = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
+ {
+ if (NULL != error)
+ {
+ if (NULL != errorRef)
+ {
+ CFRetain(error);
+ *errorRef = error;
+ }
+
+ if (NULL != myResult)
+ {
+ CFRelease(myResult);
+ myResult = NULL;
+ }
+ }
+
+ if (NULL != message)
+ {
+ myResult = message;
+ CFRetain(myResult);
+ }
+
+ if (isFinal)
+ {
+ dispatch_semaphore_signal(mySem);
+ }
+ };
+
+ SecTransformExecuteAsync(theGroup, myQueue, myBlock);
+ dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER);
+ dispatch_release(mySem);
+ dispatch_release(myQueue);
+
+ if (releaseTheGroup)
+ {
+ CFRelease(theGroup);
+ theGroup = NULL;
+ }
+ CFRelease(collectTransform);
+
+ return myResult;
+}
+
+void SecTransformExecuteAsync(SecTransformRef transformRef,
+ dispatch_queue_t deliveryQueue,
+ SecMessageBlock deliveryBlock)
+{
+ CFErrorRef localError = NULL;
+ InternalSecTransformExecute(transformRef, &localError, deliveryQueue, deliveryBlock);
+
+ // if we got an error (usually a transform startup error), we must deliver it
+ if (localError != NULL)
+ {
+ // The monitor treats a NULL queue as running on it's own queue, which from an appication's point of view is
+ // the same as a default global queue. Chances are there is no monitor at this point, so it is best to just use the
+ // global queue for this.
+ dispatch_queue_t effectiveQueue = deliveryQueue ? deliveryQueue : dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
+ dispatch_async(effectiveQueue, ^{
+ deliveryBlock(NULL, localError, true);
+ });
+ }
+}
+
+CFDictionaryRef SecTransformCopyExternalRepresentation(SecTransformRef transformRef)
+{
+
+ Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+ return tr->Externalize(NULL);
+}
+
+CFStringRef SecTransformDotForDebugging(SecTransformRef transformRef)
+{
+ if (CFGetTypeID(transformRef) == SecGroupTransformGetTypeID()) {
+ GroupTransform* tr = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
+ return tr->DotForDebugging();
+ } else {
+ return CFSTR("Can only dot debug a group");
+ }
+}
+
+SecTransformRef SecTransformCreateFromExternalRepresentation(
+ CFDictionaryRef dictionary,
+ CFErrorRef *error)
+{
+ // The incoming dictionary consists of a list of transforms and
+ // a list of connections. We start by making the individual
+ // transforms and storing them in a dictionary so that we can
+ // efficiently make connections
+
+ CFArrayRef transforms = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_TRANSFORM_ARRAY);
+ if (transforms == NULL)
+ {
+ // The dictionary we got is massively malformed!
+ *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_TRANSFORM_ARRAY);
+ return NULL;
+ }
+
+ CFArrayRef connections = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_CONNECTION_ARRAY);
+ if (connections == NULL)
+ {
+ // The dictionary we got is massively malformed!
+ *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_CONNECTION_ARRAY);
+ return NULL;
+ }
+
+ CFMutableDictionaryRef transformHolder = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFTypeRefHolder _(transformHolder);
+
+ CFIndex numTransforms = CFArrayGetCount(transforms);
+ CFIndex n;
+
+ SecTransformRef aTransform;
+
+ for (n = 0; n < numTransforms; ++n)
+ {
+ // get the basic info we need
+ CFDictionaryRef xTransform = (CFDictionaryRef) CFArrayGetValueAtIndex(transforms, n);
+
+ CFStringRef xName = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_NAME);
+
+ CFStringRef xType = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_TYPE);
+
+ // reconstruct the transform
+ aTransform = TransformFactory::MakeTransformWithType(xType, error);
+ SecTransformSetAttribute(aTransform, kSecTransformTransformName, xName, NULL);
+
+ // restore the transform state
+ Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(aTransform);
+ tr->RestoreState((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_STATE));
+ tr->SetCustomExternalData((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY));
+
+ CFIndex cnt = CFDictionaryGetCount(transformHolder);
+
+ // add the transform to the dictionary
+ CFDictionaryAddValue(transformHolder, xName, aTransform);
+
+ if (CFDictionaryGetCount(transformHolder) <= cnt)
+ {
+ if (error)
+ {
+ *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary,
+ "Out of memory, or damaged input dictonary (duplicate label %@?)", xName);
+ }
+ return NULL;
+ }
+ }
+
+ CFIndex numConnections = CFArrayGetCount(connections);
+ if (numConnections == 0)
+ {
+ return aTransform;
+ }
+
+ SecGroupTransformRef gt = SecTransformCreateGroupTransform();
+
+ for (n = 0; n < numConnections; ++n)
+ {
+ CFDictionaryRef connection = (CFDictionaryRef) CFArrayGetValueAtIndex(connections, n);
+ CFStringRef fromTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_NAME);
+ CFStringRef fromAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE);
+ CFStringRef toTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_NAME);
+ CFStringRef toAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE);
+
+ SecTransformRef fromTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, fromTransformName);
+ if (!fromTransform) {
+ if (error) {
+ *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, fromTransformName);
+ }
+ return NULL;
+ }
+ SecTransformRef toTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, toTransformName);
+ if (!toTransform) {
+ if (error) {
+ *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, toTransformName);
+ }
+ return NULL;
+ }
+
+ aTransform = SecTransformConnectTransforms(fromTransform, fromAttribute, toTransform, toAttribute, gt, error);
+ }
+
+ return gt;
+}
+
+
+
+SecTransformRef SecTransformFindByName(SecTransformRef transform, CFStringRef name)
+{
+ Transform *t = (Transform*)CoreFoundationHolder::ObjectFromCFType(transform);
+ GroupTransform *g = t->GetRootGroup();
+
+ if (g) {
+ return g->FindByName(name);
+ } else {
+ // There is no group, so if transform isn't our guy nobody is.
+ return (CFStringCompare(name, t->GetName(), 0) == kCFCompareEqualTo) ? transform : NULL;
+ }
+}
+
+
+
+CFTypeID SecGroupTransformGetTypeID()
+{
+ return GroupTransform::GetCFTypeID();
+}
+
+CFTypeID SecTransformGetTypeID()
+{
+ // Obviously this is wrong (returns same CFTypeID as SecTransformGetTypeID) Needs to be fixed
+ return GroupTransform::GetCFTypeID();
+}