#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 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)
{
CFRetainSafe(temp);
*error = temp;
}
if (temp) {
return NULL;
}
else
{
return group;
}
}
Boolean SecTransformSetAttribute(SecTransformRef transformRef,
CFStringRef key,
CFTypeRef value,
CFErrorRef *error)
{
Boolean result = false; 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 (NULL == value && CFStringCompare(key, kSecTransformAbortAttributeName, 0) == kCFCompareEqualTo)
{
if (error)
{
*error = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "ABORT requires a parameter.");
}
return result;
}
CFErrorRef temp = transform->ExternalSetAttribute(key, value);
result = (temp == NULL);
if (error)
{
CFRetainSafe(temp);
*error = temp;
}
return result;
}
CFTypeRef SecTransformGetAttribute(SecTransformRef transformRef,
CFStringRef key)
{
if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID())
{
return NULL;
}
Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
if (transform->mIsActive) {
CFErrorRef error = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "Can not get the value of attributes during execution (attempt to fetch %@/%@)", transform->GetName(), key);
CFAutorelease(error);
return error;
}
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
{
CFReleaseNull(localError);
}
return (CFTypeRef)NULL;
}
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);
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;
if (SecGroupTransformGetTypeID() == CFGetTypeID(transformRef))
{
theGroup = (SecGroupTransformRef)transformRef;
}
else
{
myGroup = transform->mGroup;
if (NULL == myGroup)
{
theGroup = SecTransformCreateGroupTransform();
if (NULL == theGroup)
{
if (NULL != errorRef)
{
*errorRef = GetNoMemoryErrorAndRetain();
}
return (CFTypeRef)NULL;
}
releaseTheGroup = true;
#ifdef __clang_analyzer__
SecGroupTransformRef connectResult = NULL;
#else
SecGroupTransformRef connectResult =
SecTransformConnectTransforms(transformRef,
kSecTransformOutputAttributeName,
collectTransform,
kSecTransformInputAttributeName,
theGroup, errorRef);
#endif
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)
{
myGroup = ((GroupTransform*)CoreFoundationHolder::ObjectFromCFType(theGroup))->GetRootGroup();
if (NULL == myGroup)
{
if (NULL != errorRef)
{
*errorRef = GetNoMemoryErrorAndRetain();
}
return (CFTypeRef)NULL;
}
SecTransformRef outputTransform = myGroup->FindLastTransform();
#ifdef __clang_analyzer__
SecGroupTransformRef connectResult = NULL;
#else
SecGroupTransformRef connectResult =
SecTransformConnectTransforms(outputTransform,
kSecTransformOutputAttributeName,
collectTransform,
kSecTransformInputAttributeName,
myGroup->GetCFObject(), errorRef);
if (NULL == connectResult)
{
CFReleaseNull(collectTransform);
if (releaseTheGroup)
{
CFReleaseNull(theGroup);
}
return (CFTypeRef)NULL;
}
#endif // __clang_analyzer__
}
__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)
{
CFRetainSafe(error);
*errorRef = error;
}
if (NULL != myResult)
{
CFReleaseNull(myResult);
myResult = NULL;
}
}
if (NULL != message)
{
myResult = message;
CFRetainSafe(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 (localError != NULL)
{
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)
{
CFArrayRef transforms = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_TRANSFORM_ARRAY);
if (transforms == NULL)
{
*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)
{
*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 = NULL;
for (n = 0; n < numTransforms; ++n)
{
CFDictionaryRef xTransform = (CFDictionaryRef) CFArrayGetValueAtIndex(transforms, n);
CFStringRef xName = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_NAME);
CFStringRef xType = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_TYPE);
aTransform = TransformFactory::MakeTransformWithType(xType, error);
SecTransformSetAttribute(aTransform, kSecTransformTransformName, xName, NULL);
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);
CFDictionaryAddValue(transformHolder, xName, aTransform);
if (CFDictionaryGetCount(transformHolder) <= cnt)
{
if (error)
{
*error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary,
"Out of memory, or damaged input dictonary (duplicate label %@?)", xName);
}
CFSafeRelease(aTransform);
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 {
return (CFStringCompare(name, t->GetName(), 0) == kCFCompareEqualTo) ? transform : NULL;
}
}
CFTypeID SecGroupTransformGetTypeID()
{
return GroupTransform::GetCFTypeID();
}
CFTypeID SecTransformGetTypeID()
{
return GroupTransform::GetCFTypeID();
}