]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_transform/lib/SecTransform.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / 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 (file)
index 0000000..958d097
--- /dev/null
@@ -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 <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();
+}