X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/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 index 00000000..e8a243e8 --- /dev/null +++ b/Security/libsecurity_transform/lib/SecCustomTransform.cpp @@ -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 +#include +#include +#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(CFStringGetCStringPtr(cfname, kCFStringEncodingUTF8)); + if (!tname) { + CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfname), kCFStringEncodingUTF8); + tname = static_cast(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); +}