--- /dev/null
+/*
+ * 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, ¶meters);
+}
+
+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);
+}