]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_transform/lib/SecCustomTransform.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_transform / lib / SecCustomTransform.cpp
diff --git a/Security/libsecurity_transform/lib/SecCustomTransform.cpp b/Security/libsecurity_transform/lib/SecCustomTransform.cpp
new file mode 100644 (file)
index 0000000..e8a243e
--- /dev/null
@@ -0,0 +1,1142 @@
+/*
+ * Copyright (c) 2010-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include "SecCustomTransform.h"
+#include "SecTransformValidator.h"
+
+#include "TransformFactory.h"
+#include <CoreFoundation/CoreFoundation.h>
+#include <Block.h>
+#include <syslog.h>
+#include "Utilities.h"
+#include "misc.h"
+
+static const CFStringRef kSecCustom = CFSTR("CustomTransform");
+const CFStringRef kSecTransformPreviousErrorKey = CFSTR("PreviousError");
+const CFStringRef kSecTransformAbortOriginatorKey = CFSTR("Originating Transform");
+const CFStringRef kSecTransformActionCanExecute = CFSTR("CanExecute");
+const CFStringRef kSecCustomTransformWhatIsRequired = CFSTR("WhatIsRequired");
+const CFStringRef kSecCustomTransformAttributesToExternalize = CFSTR("AttributesToExternalize");
+const CFStringRef kSecTransformActionStartingExecution = CFSTR("ExecuteStarting");
+const CFStringRef kSecTransformActionProcessData = CFSTR("TransformProcessData");
+const CFStringRef kSecTransformActionAttributeNotification = CFSTR("GenericAttributeSetNotification");
+const CFStringRef kSecTransformActionFinalize = CFSTR("Finalize");
+const CFStringRef kSecTransformActionExternalizeExtraData = CFSTR("ExternalizeExtraData");
+const CFStringRef kSecTransformActionInternalizeExtraData = CFSTR("InternalizeExtraData");
+const CFStringRef kSecTransformActionAttributeValidation = CFSTR("AttributeValidation");
+
+/*!
+       @function               SecTransformOverrideTransformAction
+       
+       @abstract               Used to override the default behavior of a custom transform. 
+       
+       @param action   This should be either kSecTransformActionCanExecute, 
+                                       kSecTransformActionStartingExecution, or 
+                                       kSecTransformActionFinalize which signifies the behavior
+                                       that is being overridden.                                                                       
+                                       
+       @param newAction        
+                                       A SecTransformAttributeActionBlock block that implements the 
+                                       override behavior.  Please see the 
+                                       SecTransformActionBlock discussion for more 
+                                       information.
+                               
+       @result                 A CFErrorRef if an error occurred, NULL otherwise.
+                                       
+       @discussion             An action may be overridden more then once, the most
+                                       recent override will be used.Please see the example in 
+                                       the documentation for the SecTransformActionBlock 
+                                       block.
+                                       
+*/
+typedef CFTypeRef (^SecTransformOverrideTransformAction)(CFStringRef action, 
+                                                               SecTransformActionBlock newAction);
+                                                               
+/*!
+       @function               SecTransformOverrideDataAction
+       
+       @abstract               Changes the default attribute handling for a 
+                                       specified attribute.
+                                       
+       @param action   This should be either kSecTransformActionProcessData, 
+                                       kSecTransformActionExternalizeExtraData which signifies  
+                                       what behavior is being overridden.
+                                                                                                                       
+       @param newAction         
+                                       A SecTransformDataBlock block that implements the 
+                                       override behavior. Please see the 
+                                       SecTransformDataBlock discussion for more 
+                                       information.
+
+       @result                 A CFErrorRef if an error occurred. NULL otherwise.
+                                       
+       @discussion             An action may be overridden more then once, the most
+                                   recent override will be used. Please see the example 
+                                       in the documentation for the SecTransformAttributeActionBlock 
+                                       block.
+                               
+*/
+typedef CFTypeRef (^SecTransformOverrideDataAction)(CFStringRef action, 
+                                                               SecTransformDataBlock newAction);
+                                                               
+/*!
+       @function               SecTransformOverrideAttributeAction
+       
+       @abstract               Changes the default attribute handling for a 
+                                       specified attribute.
+                                       
+       @param action   This should be either SecTransformSetAttributeAction, 
+                                       kSecTransformActionAttributeValidation which signifies  
+                                       what behavior is being overridden.
+       
+       @param attribute        
+                                       The attribute whose attribute default attribute handling is 
+                                       being overridden.  Passing NULL will override all attributes
+                                       that have not been specifically overridden.
+                                                                               
+       @param newAction         
+                                       A SecTransformAttributeActionBlock block 
+                                       that implements the  override behavior. 
+                                       
+                                       If the action parameter is SecTransformSetAttributeAction
+                                       then this block is called whenever a set is called on the
+                                       attribute that this block was registered for or in the case
+                                       of a NULL attribute name any attribute that has not been specifically
+                                       overridden.  The block may transmogrify the data as needed.  It may
+                                       also send the data to any other attribue by calling 
+                                       SecTransformCustomSetAttribute.  The value returned from the block 
+                                       will be the new value for the attribute.
+                                       
+                                       If the action parameter is kSecTransformActionAttributeValidation then
+                                       this block is called to validate the new value for the
+                                       attribute that this block was registered for or in the case
+                                       of a NULL attribute name any attribute that has not been specifically
+                                       overridden.  The block should test if the new value is acceptable
+                                       and return NULL if it is valid a CFErrorRef otherwise.
+                                       
+       @result                 A CFErrorRef if an error occurred. NULL otherwise.
+                                       
+       @discussion             An action may be overridden more then once, the most
+                                   recent override will be used. Please see the example 
+                                       in the documentation for the 
+                                       SecTransformAttributeActionBlock block.
+                               
+*/
+typedef CFTypeRef (^SecTransformOverrideAttributeAction)( 
+                                                               CFStringRef action,
+                                                               SecTransformStringOrAttributeRef attribute, 
+                                                               SecTransformAttributeActionBlock newAction);
+
+                                                               
+/*!
+       @function               SecTransformGetAttributeBlock
+       
+       @abstract               Retrieves the value of the attribute metadata of the 
+                                       type specified.
+                                       
+       @param attribute        
+                                       The attribute from which to retrieve the metadata from.
+                                       
+       @param type             The type of the metadata to be fetched.
+                                               
+       @result                 The value of the metadata that was retrieved or a CFErrorRef
+                                       if an error occurred
+       
+       @result                 The value of the metadata that was retrieved.
+       
+
+*/
+typedef CFTypeRef (^SecTransformGetAttributeBlock)(
+                                                                               SecTransformStringOrAttributeRef attribute, 
+                                                                               SecTransformMetaAttributeType type);
+                                                                               
+/*!
+       @function               SecTransformSetAttributeBlock
+       
+       @abstract               This sets the value of the metadata of an attribute.  
+               
+       @param attribute        
+                                       The attribute whose value is sent
+                                       
+       @param type             The metadata type that specifies what metadata value
+                                       is set.
+                                       
+       @param value    The value of the metadata to be sent.
+                                               
+       @result                 A CFErrorRef is an error occurred, NULL otherwise.
+       
+       @discussion             The attribute parameter specifies which attribute will 
+                                       have its data set. The type parameter specifies which of 
+                                       the metadata items is set. The value parameter is the 
+                                       new metadata value.
+                                                       
+*/
+typedef CFErrorRef (^SecTransformSetAttributeBlock)(
+       SecTransformStringOrAttributeRef attribute, 
+       SecTransformMetaAttributeType type, 
+       CFTypeRef value);
+       
+
+/*!
+       @function               SecTransformPushBackAttributeBlock
+
+       @abstract               Allows for putting a single value back for a 
+                                       specific attribute.  This will stop the flow of
+                                       data into the specified attribute until an
+                                       attribute is changed.
+
+       @param attribute        
+                                       The attribute that has its data pushed back.
+
+       @param value    The value being pushed back.
+
+
+       @result                 A CFErrorRef is an error occurred, NULL otherwise.
+                                       Note: pushing back a second value will abort the
+                                       transform, not return an error from this call.
+
+       @discussion             A particular custom transform may need multple
+                                       values to be set before it can do the processing
+                                       that the custom transform is designed to do. For
+                                       example, it may need a key and a salt value.  The
+                                       salt value maybe supplied by another transform while
+                                       the key transform may have been set explicitly.  When
+                                       data is presented to this custom transform the salt
+                                       value may not have been sent from the upstream transform.
+                                       The custom transform can then push back the input data
+                                       which causes the transform to stall.  When any 
+                                       attribute on the custom transform is changed, such as 
+                                       the upstream transform delivers the salt value, then 
+                                       the data that was pushed back is re-delivered
+
+*/
+typedef CFErrorRef (^SecTransformPushBackAttributeBlock)(
+                                                                               SecTransformStringOrAttributeRef attribute, 
+                                                                               CFTypeRef value);
+
+/*!
+       @const kSecTransformCreateBlockParametersVersion
+                       The current version number of the SecTransformCreateBlockParameters
+                       struct
+       
+*/
+enum 
+{
+       kSecTransformCreateBlockParametersVersion = 1
+};
+
+extern "C" {
+       Boolean SecExternalSourceSetValue(SecTransformRef xst, CFTypeRef value, CFErrorRef *error);
+}
+
+/*!
+       @struct OpaqueSecTransformImplementation
+       
+       @field version
+                       The version number of this structure
+                       
+       @field overrideTransform
+                       A SecTransformOverrideTransformAction block.  See
+                       the headerdoc for this block for additional information.
+                       
+       @field overrideAttribute
+                       A SecTransformOverrideAttributeAction block. See
+                       the headerdoc for this block for additional information.
+
+       @field get
+                       A SecTransformGetAttributeBlock block. See
+                       the headerdoc for this block for additional information.
+                       
+       @field send
+               A SecTransformSetAttributeBlock block.  See
+               the headerdoc for this block for additional information.
+               
+       @field pushback
+               A SecTransformPushBackAttributeBlock block. See
+               the headerdoc for this block for additional information.
+*/
+struct OpaqueSecTransformImplementation
+{
+       CFIndex version;        // Set to kSecTransformCreateBlockParametersVersion
+       
+       // The following two blocks allow for overriding 'standard' 
+       // transform behavior
+       SecTransformOverrideTransformAction overrideTransform;
+       SecTransformOverrideDataAction overrideData;
+       SecTransformOverrideAttributeAction overrideAttribute;
+       
+       // The following methods allow for dealing with the transform mechanism
+       // They are called synchronously
+       SecTransformGetAttributeBlock get;
+       SecTransformSetAttributeBlock send;
+       SecTransformPushBackAttributeBlock pushback;
+};
+
+                                                                               
+class CustomTransformFactory : public TransformFactory 
+{
+protected:
+       SecTransformCreateFP  createFuncPtr;
+public:
+       CustomTransformFactory(CFStringRef name, SecTransformCreateFP createFP, CFErrorRef *error);
+    virtual ~CustomTransformFactory() {};
+       virtual CFTypeRef Make();
+};
+
+
+static SecTransformActionBlock default_can_run = ^{ return (CFTypeRef)NULL; };
+static SecTransformActionBlock default_execute_starting = default_can_run;
+static SecTransformActionBlock default_finalize = default_execute_starting;
+static SecTransformActionBlock default_externalize_data = default_finalize;
+
+static SecTransformDataBlock default_process_data = ^(CFTypeRef value) { return value; };
+//static SecTransformDataBlock default_validate = ^(CFTypeRef value) { return (CFTypeRef)NULL; };
+static SecTransformAttributeActionBlock default_generic_attribute_set_notification = 
+       ^(SecTransformAttributeRef ah, CFTypeRef value) { return value; };
+
+static SecTransformAttributeActionBlock default_generic_attribute_validation = 
+^(SecTransformAttributeRef ah, CFTypeRef value) 
+{
+        return (CFTypeRef)NULL;
+};
+
+static SecTransformDataBlock default_internalize_data = 
+                       ^(CFTypeRef value) 
+{ 
+       return (CFTypeRef)NULL; 
+};
+
+class CustomTransform : public Transform 
+{
+protected:
+       SecTransformCreateFP                                    createFuncPtr;
+       SecTransformInstanceBlock                       instanceBlock;    
+                       
+       SecTransformActionBlock                         can_run;
+       SecTransformActionBlock                         execute_starting;
+       SecTransformActionBlock                         finalize;
+       SecTransformAttributeActionBlock        generic_attribute_set_notification;
+       SecTransformAttributeActionBlock        generic_attribute_validation;
+       SecTransformDataBlock                           process_data;
+       SecTransformActionBlock                         externalize_data;
+       SecTransformDataBlock                           internalize_data;
+               
+       SecTransformRef tr;     
+       
+       SecTransformAttributeRef                        input_ah;
+       SecTransformAttributeRef                        output_ah;
+       
+       OpaqueSecTransformImplementation parameters;
+       
+       void SetCanExecute(SecTransformActionBlock CanExecuteBlock)
+       {
+               Block_release(can_run);
+               if (CanExecuteBlock)
+               {
+                       can_run = Block_copy(CanExecuteBlock);
+                       
+               }
+               else
+               {
+                       can_run  = Block_copy(default_can_run);
+               }
+       }
+       
+       void SetExecuteStarting(SecTransformActionBlock executeStartingBlock)
+       {
+               Block_release(execute_starting);
+               if (executeStartingBlock)
+               {
+                       execute_starting = Block_copy(executeStartingBlock);
+                       
+               }
+               else
+               {
+                       execute_starting = Block_copy(default_execute_starting);
+               }
+       }
+       
+       void SetFinalize(SecTransformActionBlock finalizeBlock)
+       {
+               Block_release(finalize);
+               if (finalizeBlock)
+               {
+                       finalize = Block_copy(finalizeBlock);
+                       
+               }
+               else
+               {
+                       finalize = Block_copy(default_finalize);
+               }
+       }
+       
+       void SetExternalizeExtraData(SecTransformActionBlock externalizeBlock)
+       {
+               Block_release(externalizeBlock);
+               if (externalizeBlock)
+               {
+                       externalize_data = Block_copy(externalizeBlock);
+                       
+               }
+               else
+               {
+                       externalize_data = Block_copy(default_externalize_data);
+               }
+       }
+       
+       void SetProcessData(SecTransformDataBlock processDataBlock)
+       {
+               Block_release(process_data);
+               if (processDataBlock)
+               {
+                       process_data = Block_copy(processDataBlock);
+                       
+               }
+               else
+               {
+                       process_data = Block_copy(default_process_data);
+               }
+       }
+       
+       void SetInternalizeExtraData(SecTransformDataBlock InternalizeExtraDataBlock)
+       {
+               Block_release(internalize_data);
+               if (InternalizeExtraDataBlock)
+               {
+                       internalize_data = Block_copy(InternalizeExtraDataBlock);
+                       
+               }
+               else
+               {
+                       internalize_data = Block_copy(default_internalize_data);
+               }
+       }
+       
+       
+               
+       void SetNotficationBlock(SecTransformStringOrAttributeRef attribute, 
+                                                       SecTransformAttributeActionBlock notificationBlock)
+       {
+               SecTransformAttributeActionBlock blockToSet = 
+                       Block_copy((notificationBlock) ? notificationBlock : 
+                                  default_generic_attribute_set_notification);
+               
+               if (attribute)
+               {
+                       transform_attribute *ta = getTA(attribute, true);
+                       
+                       if (ta->attribute_changed_block) 
+                       {
+                               Block_release(ta->attribute_changed_block);
+                       }
+                       
+                       ta->attribute_changed_block = blockToSet;
+               }
+               else            
+               {       
+                       
+                       if (generic_attribute_set_notification) 
+                       {
+                               Block_release(generic_attribute_set_notification);
+                       }
+                       
+                       generic_attribute_set_notification = blockToSet;                        
+               }
+       }
+
+       void SetVerifyBlock(SecTransformStringOrAttributeRef attribute, 
+                                                       SecTransformAttributeActionBlock verifyBlock)
+       {
+               SecTransformAttributeActionBlock blockToSet = 
+                       Block_copy((verifyBlock) ? verifyBlock : 
+                                  generic_attribute_validation);
+               
+               if (attribute)
+               {
+                       transform_attribute *ta = getTA(attribute, true);
+                       
+                       if (ta->attribute_validate_block) 
+                       {
+                               Block_release(ta->attribute_validate_block);
+                       }
+                       
+                       ta->attribute_validate_block = blockToSet;
+               }
+               else            
+               {       
+                       if (generic_attribute_validation) 
+                       {
+                               Block_release(generic_attribute_validation);
+                       }
+                       
+                       generic_attribute_validation = blockToSet;                      
+               }
+       }
+               
+               
+
+public:
+       CustomTransform(CFStringRef name, SecTransformCreateFP createFP);
+       virtual ~CustomTransform();
+       
+       void Create();
+       
+       CFTypeRef rebind_data_action(CFStringRef action, 
+               SecTransformDataBlock new_action);
+               
+       CFTypeRef rebind_transform_action(CFStringRef action, SecTransformActionBlock new_action);
+       
+       CFTypeRef rebind_attribute_action(CFStringRef action,
+                                                       SecTransformStringOrAttributeRef attribute, 
+                                                       SecTransformAttributeActionBlock new_action);
+       
+       SecTransformRef get_ref() { return tr; }
+       
+       virtual void AttributeChanged(CFStringRef name, CFTypeRef value);
+       virtual void AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value);
+       virtual CFErrorRef TransformStartingExecution();
+       virtual CFDictionaryRef GetCustomExternalData();
+       virtual void SetCustomExternalData(CFDictionaryRef customData);
+       
+       friend Boolean SecExternalSourceSetValue(SecTransformRef xst, CFTypeRef value, CFErrorRef *error);
+};
+
+
+
+#pragma mark CustomTransformFactory
+
+CustomTransformFactory::CustomTransformFactory(CFStringRef uniqueName, SecTransformCreateFP createFP, CFErrorRef* error) : 
+       TransformFactory(uniqueName, false, kSecCustom), 
+       createFuncPtr(createFP) 
+{
+       TransformFactory *existing = FindTransformFactoryByType(uniqueName);
+       if (existing) 
+       {
+               if (error) 
+               {
+                       *error = CreateSecTransformErrorRef(kSecTransformErrorNameAlreadyRegistered, 
+                               "Custom transform type %s already exists.", uniqueName);
+               }
+        return;
+       }
+       
+    
+    if (CFStringGetCharacterAtIndex(uniqueName, 0) == '_')
+    {
+        if (error)
+        {
+            *error = CreateSecTransformErrorRef(kSecTransformInvalidArgument, 
+                                                "Invalid transform type name %s -- type names must not start with an _", uniqueName);
+        }
+        return;
+    }
+    
+    static CFCharacterSetRef invalidTypeCharactors = NULL;
+    static dispatch_once_t setupInvalidTypeCharactors;
+    dispatch_once(&setupInvalidTypeCharactors, ^{
+        invalidTypeCharactors = CFCharacterSetCreateWithCharactersInString(NULL, CFSTR("/:"));
+    });
+    CFRange has_bad;
+    if (CFStringFindCharacterFromSet(uniqueName, invalidTypeCharactors, CFRangeMake(0, CFStringGetLength(uniqueName)), 0, &has_bad)) {
+        if (error)
+        {
+            *error = CreateSecTransformErrorRef(kSecTransformInvalidArgument, 
+                                            "Invalid character '%c' in transform type name %s", CFStringGetCharacterAtIndex(uniqueName, has_bad.location), uniqueName);
+        }
+        return;
+    }
+    RegisterTransform(this, kSecCustom);
+}
+
+CFTypeRef CustomTransformFactory::Make() 
+{
+       CustomTransform *ct = new CustomTransform(this->GetTypename(), createFuncPtr);
+       ct->Create();
+       return ct->get_ref();
+}
+
+#pragma mark MISC
+
+extern "C" {
+       SecTransformAttributeActionBlock SecTransformCreateValidatorForCFtype(CFTypeID expected_type, Boolean null_allowed) {
+               SecTransformAttributeActionBlock validate = NULL;
+               CFErrorRef (^make_error_message)(SecTransformAttributeRef attr, CFTypeRef value, CFTypeID expected_type, Boolean null_allowed) = 
+               ^(SecTransformAttributeRef attr, CFTypeRef value, CFTypeID expected_type, Boolean null_allowed) {
+                       CFStringRef expected_type_name = CFCopyTypeIDDescription(expected_type);
+                       CFErrorRef error = NULL;
+                       if (value) {
+                               CFStringRef value_type_name = CFCopyTypeIDDescription(CFGetTypeID(value));
+                               error = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "%@ received value of type %@ (%@), expected%@ a %@%@",
+                                                                                                                         attr, value_type_name, value,
+                                                                                                                         null_allowed ? CFSTR(" either") : CFSTR(""),
+                                                                                                                         expected_type_name,
+                                                                                                                         null_allowed ? CFSTR(" or a NULL") : CFSTR(""));
+                               CFRelease(value_type_name);
+                       } else {
+                               error = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "%@ received NULL value, expected a %@",
+                                                                                                  attr, expected_type_name);
+                       }
+                       CFRelease(expected_type_name);
+                       
+                       return error;
+               };
+               
+               
+               if (null_allowed) {
+                       validate = ^(SecTransformAttributeRef attr, CFTypeRef value) {
+                               if (value == NULL || CFGetTypeID(value) == expected_type) {
+                                       return (CFTypeRef)NULL;
+                               }
+                               return (CFTypeRef)make_error_message(attr, value, expected_type, null_allowed);
+                       };
+               } else {
+                       validate = ^(SecTransformAttributeRef attr, CFTypeRef value) {
+                               if (value != NULL && CFGetTypeID(value) == expected_type) {
+                                       return (CFTypeRef)NULL;
+                               }
+                               return (CFTypeRef)make_error_message(attr, value, expected_type, null_allowed);
+                       };
+               }
+               
+               return Block_copy(validate);
+       }
+}
+
+Boolean SecTransformRegister(CFStringRef uniqueName, SecTransformCreateFP createFP, CFErrorRef *caller_error) 
+{
+       CFErrorRef error = NULL;
+       
+       CustomTransformFactory *tf = new CustomTransformFactory(uniqueName, createFP, &error);
+       if (error) 
+       {
+               delete tf;
+               if (caller_error) 
+               {
+                       *caller_error = error;
+               }
+               return FALSE;
+       } 
+       else 
+       {
+               return TRUE;
+       }
+}
+
+SecTransformRef SecTransformCreate(CFStringRef name, CFErrorRef *error) 
+{
+       SecTransformRef tr = TransformFactory::MakeTransformWithType(name, error);
+       return tr;
+}
+
+extern "C" {
+       Boolean SecExternalSourceSetValue(SecTransformRef xst, CFTypeRef value, CFErrorRef *error)
+       {
+               CustomTransform *ct = (CustomTransform *)CoreFoundationHolder::ObjectFromCFType(xst);
+               extern CFStringRef external_source_name;
+               if (CFEqual(ct->mTypeName, external_source_name)) {
+                       ct->SetAttribute(ct->input_ah, value);
+                       return true;
+               } else {
+                       if (error) {
+                               *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "SecExternalSourceSetValue called for %@, you need to pass in an ExternalSource transform not a %@", ct->GetName(), ct->mTypeName);
+                       }
+                       return false;
+               }
+       }
+}
+
+/* ==========================================================================
+       class:                  NoDataClass
+       description:    A Special CFType that signifies that no data is being 
+                                       returned
+   ==========================================================================*/
+#pragma mark NoDataClass
+
+class NoDataClass : public CoreFoundationObject
+{
+protected:
+       NoDataClass();
+
+public:
+       virtual ~NoDataClass();
+       std::string FormattingDescription(CFDictionaryRef options);
+       std::string DebugDescription();
+       static CFTypeRef Make();
+};
+
+CFTypeRef NoDataClass::Make() {
+       NoDataClass* obj = new NoDataClass();
+       return CoreFoundationHolder::MakeHolder(gInternalProtectedCFObjectName, obj);
+}
+
+
+NoDataClass::NoDataClass() : CoreFoundationObject(gInternalProtectedCFObjectName) {
+}
+
+NoDataClass::~NoDataClass()
+{
+}
+
+std::string NoDataClass::DebugDescription() 
+{
+       return CoreFoundationObject::DebugDescription() + " | SecTransformNoData";
+}
+
+std::string NoDataClass::FormattingDescription(CFDictionaryRef options) 
+{
+       return CoreFoundationObject::FormattingDescription(options) + " | SecTransformNoData";
+}
+
+CFTypeRef SecTransformNoData() 
+{
+       static dispatch_once_t inited;
+       static CFTypeRef no_data;
+       
+       dispatch_once(&inited, 
+       ^{
+               no_data = NoDataClass::Make();
+       });
+       
+       return no_data;
+}
+
+/* ==========================================================================
+       class Implementation    CustomTransform 
+   ==========================================================================*/
+
+#pragma mark CustomTransform
+
+void CustomTransform::AttributeChanged(CFStringRef name, CFTypeRef value) {
+#ifndef NDEBUG
+       // We really shouldn't get here, and this is the debug build so we can blow up on the spot so it is easy to look at the stack trace
+       abort();
+#else
+       // We really shouldn't get here, but this is a production build and recovery is easy to code (but costly to execute)
+       AttributeChanged(getAH(name, false), value);
+#endif
+}
+
+void CustomTransform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value) 
+{
+       transform_attribute *ta = ah2ta(ah);
+       SecTransformAttributeActionBlock attribute_set_notification = NULL;
+
+       SecTransformAttributeActionBlock attribute_validate = NULL;
+
+       attribute_validate = (SecTransformAttributeActionBlock)ta->attribute_validate_block;
+       if (!attribute_validate) {
+                       attribute_validate = generic_attribute_validation;
+       }
+       CFTypeRef vr = attribute_validate(ah, value);
+       if (vr) {
+                       if (CFGetTypeID(vr) == CFErrorGetTypeID()) {
+                                       SendAttribute(AbortAH, vr);
+                                       CFRelease(vr);
+                       } else {
+                                       CFErrorRef e = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Invalid return type from a validate action, expected a CFErrorRef got a %@ (%@)", CFCopyTypeIDDescription(CFGetTypeID(vr)), vr);
+                                       SendAttribute(AbortAH, e);
+                                       CFRelease(vr);
+                                       // XXX: this causes a core dump -- I think AbortAH doesn't take it's own reference!!   CFRelease(e);
+                       }
+                       return;
+       }
+
+       attribute_set_notification = (SecTransformAttributeActionBlock)ta->attribute_changed_block;
+
+       if ((!attribute_set_notification) && ah == input_ah) 
+       {
+               CFTypeID vtype = value ? CFGetTypeID(value) : CFDataGetTypeID();
+               if (vtype == CFDataGetTypeID()) 
+               {
+                       CFTypeRef output = process_data(value);
+                       if (output == NULL || output != SecTransformNoData()) 
+                       {
+                               SendAttribute(output_ah, output);
+
+                // if output == value, we are being asked to just
+                // forward the existing value.  No need to release.
+                // If they are different, we are being asked to
+                // send a new value which must be released.
+
+                if (output != value && output != NULL)
+                {
+                    CFRelease(output);
+                }
+                       }
+               } 
+               else if (vtype == CFErrorGetTypeID() && !ah2ta(ah)->direct_error_handling) 
+               {
+                       SendAttribute(output_ah, value);
+               } else 
+               {
+                       attribute_set_notification = attribute_set_notification ? attribute_set_notification : generic_attribute_set_notification;
+                       CFTypeRef new_value = attribute_set_notification(ah, value);
+                       if (new_value != value) 
+                       {
+                               SendAttribute(ah, new_value);
+                       }
+               }
+       } 
+       else 
+       {
+               CFTypeID vtype = value ? CFGetTypeID(value) : CFDataGetTypeID();
+               if (vtype != CFErrorGetTypeID() || ah2ta(ah)->direct_error_handling) 
+               {
+                       attribute_set_notification = attribute_set_notification ? attribute_set_notification : generic_attribute_set_notification;
+                       CFTypeRef new_value = attribute_set_notification(ah, value);
+                       if (new_value != value) 
+                       {
+                               SendAttribute(ah, new_value);
+                       }
+               } 
+               else 
+               {
+                       SendAttribute(output_ah, value);
+               }
+       }
+}
+
+CFTypeRef CustomTransform::rebind_data_action(CFStringRef action, 
+                       SecTransformDataBlock new_action) 
+{
+       CFTypeRef result = NULL;
+       if (kCFCompareEqualTo == CFStringCompare(kSecTransformActionProcessData, action, 0)) 
+       {
+               SetProcessData(new_action);
+       }
+       else if (kCFCompareEqualTo == CFStringCompare(kSecTransformActionInternalizeExtraData, action, 0))
+       {
+               SetInternalizeExtraData(new_action);
+       }
+       else 
+       {
+               result = (CFTypeRef)CreateSecTransformErrorRef(kSecTransformInvalidOverride, "Unkown override type");
+
+        // XXX: can we get a stackdump here too?
+        CFStringRef msg = CFStringCreateWithFormat(NULL, NULL,
+                                                   CFSTR("rebind_data_action (action %@, new_action %p, transform %s)"),
+                                                   action, (void*)new_action, DebugDescription().c_str());
+        char *utf8_message = utf8(msg);
+        syslog(LOG_ERR, "%s", utf8_message);
+        free(utf8_message);
+        CFRelease(msg);
+       }
+       return result;
+}
+
+CFTypeRef CustomTransform::rebind_transform_action(CFStringRef action, SecTransformActionBlock new_action) 
+{
+       CFErrorRef result = NULL;
+       
+       if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionCanExecute, 0)) 
+       {
+               SetCanExecute(new_action);
+       }
+       else if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionStartingExecution, 0)) 
+       {
+               SetExecuteStarting(new_action);
+       }
+       else if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionFinalize, 0))
+       {
+               SetFinalize(new_action);
+       }
+       else if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionExternalizeExtraData, 0))
+       {
+               SetExternalizeExtraData(new_action);
+       }
+       else
+       {
+               result = CreateSecTransformErrorRef(kSecTransformInvalidOverride, "Unkown override type");
+
+               char *action_utf8 = utf8(action);
+               syslog(LOG_ERR, "rebind_transform_action (action %s, all-attributes, block %p, transform %s)\n", action_utf8, (void*)new_action, DebugDescription().c_str());
+               free(action_utf8);
+       }
+               
+       return result;
+}
+
+CFTypeRef CustomTransform::rebind_attribute_action(
+                       CFStringRef action,
+                       SecTransformStringOrAttributeRef attribute, 
+                       SecTransformAttributeActionBlock new_action) 
+{
+       CFErrorRef result = NULL;
+       
+       if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionAttributeNotification, 0)) 
+       {
+               SetNotficationBlock(attribute, new_action);
+       }
+       else if (kCFCompareEqualTo == CFStringCompare(action, kSecTransformActionAttributeValidation, 0))
+       {
+               SetVerifyBlock(attribute, new_action);
+       }
+       else
+       {
+               result = CreateSecTransformErrorRef(kSecTransformInvalidOverride, "Unkown override type");
+               char *action_utf8 = utf8(action);
+               syslog(LOG_ERR, "rebind_attribute_action (action %s, all-attributes, block %p, transform %s)\n", action_utf8, (void*)new_action, DebugDescription().c_str());
+               free(action_utf8);
+       }
+       
+       return result;  
+}
+
+CustomTransform::CustomTransform(CFStringRef cfname, SecTransformCreateFP createFP) : 
+       Transform(cfname), 
+       createFuncPtr(createFP), 
+       instanceBlock(NULL),
+       can_run(Block_copy(default_can_run)), 
+       execute_starting(Block_copy(default_execute_starting)), 
+       finalize(Block_copy(default_finalize)), 
+       generic_attribute_set_notification(Block_copy(default_generic_attribute_set_notification)),
+       generic_attribute_validation(Block_copy(default_generic_attribute_validation)),
+       process_data(Block_copy(default_process_data)),
+       externalize_data(Block_copy(default_externalize_data)),
+       internalize_data(Block_copy(default_internalize_data))
+{
+       mAlwaysSelfNotify = true;
+       
+       input_ah = getAH(kSecTransformInputAttributeName, true);
+       output_ah = getAH(kSecTransformOutputAttributeName, true);
+       
+       parameters.version = kSecTransformCreateBlockParametersVersion;
+       parameters.send = Block_copy(^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type, CFTypeRef value) 
+       { 
+               return SendMetaAttribute(attribute, type, value); 
+       });
+       
+       parameters.pushback = Block_copy(^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 
+       { 
+               return Pushback(getAH(attribute), value); 
+       });
+       
+       parameters.get = Block_copy(^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type) 
+       { 
+               return GetMetaAttribute(attribute, type); 
+       });
+       
+       parameters.overrideTransform = Block_copy(^(CFStringRef action, SecTransformActionBlock new_action) 
+       { 
+               return rebind_transform_action(action, new_action); 
+       });
+       
+       parameters.overrideData = Block_copy(^(CFStringRef action, 
+                                                               SecTransformDataBlock new_action)
+       {
+               return rebind_data_action(action, new_action); 
+       });
+       
+       /*
+        CFTypeRef (^SecTransformOverrideAttributeAction)( 
+        CFStringRef action,
+        SecTransformStringOrAttributeRef attribute, 
+        SecTransformAttributeActionBlock newAction);
+       */
+       parameters.overrideAttribute = 
+               Block_copy(^(CFStringRef action, SecTransformStringOrAttributeRef attribute, SecTransformAttributeActionBlock new_action) 
+       { 
+               return rebind_attribute_action(action, attribute, new_action); 
+       });
+       
+       char *tname = const_cast<char*>(CFStringGetCStringPtr(cfname, kCFStringEncodingUTF8));
+       if (!tname) {
+               CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfname), kCFStringEncodingUTF8);
+               tname = static_cast<typeof(tname)>(alloca(sz));
+               CFStringGetCString(cfname, tname, sz, kCFStringEncodingUTF8);
+       }       
+       tr = CoreFoundationHolder::MakeHolder(kSecCustom, (CoreFoundationObject*)this);
+       
+       instanceBlock = (*createFuncPtr)(cfname, tr, &parameters);
+}
+
+void CustomTransform::Create()
+{
+       (void)instanceBlock();
+}
+
+
+CustomTransform::~CustomTransform() {
+       finalize();
+       
+       if (instanceBlock)
+       {
+               Block_release(instanceBlock);
+       }
+       
+       Block_release(can_run);
+       Block_release(execute_starting);
+       Block_release(finalize);
+       Block_release(generic_attribute_set_notification);
+       Block_release(process_data);
+       Block_release(externalize_data);
+       Block_release(internalize_data);
+
+       Block_release(parameters.send);
+       Block_release(parameters.pushback);
+       Block_release(parameters.get);
+       Block_release(parameters.overrideTransform);
+       Block_release(parameters.overrideData);
+       Block_release(parameters.overrideAttribute);
+       
+       // strictly speaking this isn't needed, but it can help track down some "use after free" bugs
+       tr = NULL;
+       createFuncPtr = NULL;
+       process_data = NULL;
+}
+
+CFErrorRef CustomTransform::TransformStartingExecution() 
+{
+       CFTypeRef result = execute_starting();
+       return (CFErrorRef)result;
+}
+
+
+CFDictionaryRef CustomTransform::GetCustomExternalData()
+{
+       CFTypeRef result = externalize_data();
+       if (NULL == result)
+       {
+               return NULL;
+       }
+       
+       if (CFGetTypeID(result) == CFErrorGetTypeID())
+       {
+               // Ouch!  we should deal with this
+               CFRelease(result);
+               return NULL;
+       }
+       
+       if (CFGetTypeID(result) == CFDictionaryGetTypeID())
+       {
+               return (CFDictionaryRef)result;
+       }
+       
+       CFRelease(result);
+       result = NULL;
+       return (CFDictionaryRef)result;
+}
+
+
+void CustomTransform::SetCustomExternalData(CFDictionaryRef customData)
+{
+       if (NULL != customData)
+       {
+               internalize_data(customData);           
+       }
+       return;
+}
+
+CFErrorRef SecTransformSetAttributeAction(SecTransformImplementationRef ref, 
+                                                               CFStringRef action,
+                                                               SecTransformStringOrAttributeRef attribute, 
+                                                               SecTransformAttributeActionBlock newAction)                                                             
+{
+       if (NULL == ref)
+       {
+                CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                       "SecTransformSetAttributeNotificationAction called with a NULL SecTransformImplementationRef ref");
+                       
+               return result;
+       }
+       
+       return (CFErrorRef)ref->overrideAttribute(action, attribute, newAction);
+}
+
+CFErrorRef SecTransformSetDataAction(SecTransformImplementationRef ref, 
+                                                                       CFStringRef action, 
+                                                                       SecTransformDataBlock newAction)
+{
+       if (NULL == ref)
+       {
+                CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                       "SecTransformSetAttributeNotificationAction called with a NULL SecTransformImplementationRef ref");
+                       
+               return result;
+       }
+       
+       return (CFErrorRef)ref->overrideData(action, newAction);        
+}
+
+CFErrorRef SecTransformSetTransformAction(SecTransformImplementationRef ref,
+                                                               CFStringRef action, 
+                                                               SecTransformActionBlock newAction)
+{
+       if (NULL == ref)
+       {
+                CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                       "SecTransformSetAttributeNotificationAction called with a NULL SecTransformImplementationRef ref");
+                       
+               return result;
+       }
+       
+       return (CFErrorRef)ref->overrideTransform(action, newAction);
+}
+
+CFTypeRef SecTranformCustomGetAttribute(SecTransformImplementationRef ref,
+                                        SecTransformStringOrAttributeRef attribute,
+                                        SecTransformMetaAttributeType type)
+{
+       if (NULL == ref)
+       {
+        CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                                                       "SecTransformCustomGetAttribute called with a NULL SecTransformImplementationRef ref");
+        
+               return result;
+       }
+       
+       return (CFErrorRef)ref->get(attribute, type);
+}
+
+CFTypeRef SecTransformCustomSetAttribute(SecTransformImplementationRef ref,
+                                                                       SecTransformStringOrAttributeRef attribute, 
+                                                                       SecTransformMetaAttributeType type, 
+                                                                       CFTypeRef value)
+{
+       if (NULL == ref)
+       {
+                CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                       "SecTransformCustomSetAttribute called with a NULL SecTransformImplementationRef ref");
+                       
+               return result;
+       }
+       
+       return (CFErrorRef)ref->send(attribute, type, value);
+       
+}
+
+CFTypeRef SecTransformPushbackAttribute(SecTransformImplementationRef ref,
+                                                               SecTransformStringOrAttributeRef attribute, 
+                                                               CFTypeRef value)
+{
+       if (NULL == ref)
+       {
+                CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, 
+                       "SecTransformPushbackAttribute called with a NULL SecTransformImplementationRef ref");
+                       
+               return (CFTypeRef)result;
+       }
+       
+       return ref->pushback(attribute, value);
+}