/* * 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); }