X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_transform/lib/SecTransform.cpp diff --git a/Security/libsecurity_transform/lib/SecTransform.cpp b/Security/libsecurity_transform/lib/SecTransform.cpp new file mode 100644 index 00000000..958d0974 --- /dev/null +++ b/Security/libsecurity_transform/lib/SecTransform.cpp @@ -0,0 +1,547 @@ +#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 + +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(); +}