1/* 2 * Copyright (c) 2010-2011 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include "Transform.h" 25#include "SecTransform.h" 26#include "SecCollectTransform.h" 27#include "SecCustomTransform.h" 28#include "misc.h" 29#include "c++utils.h" 30#include "Utilities.h" 31 32static CFStringRef kCollectTransformName = CFSTR("com.apple.security.seccollecttransform"); 33 34static SecTransformInstanceBlock CollectTransform(CFStringRef name, 35 SecTransformRef newTransform, 36 SecTransformImplementationRef ref) 37{ 38 SecTransformInstanceBlock instanceBlock = 39 ^{ 40 __block CFMutableArrayRef allValues = 41 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 42 __block Boolean isSameType = TRUE; 43 CFTypeRef input_ah = SecTranformCustomGetAttribute(ref, kSecTransformInputAttributeName, kSecTransformMetaAttributeRef); 44 ah2ta(input_ah)->direct_error_handling = 1; 45 46 dispatch_block_t no_more_output = ^ 47 { 48 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, input_ah, ^(SecTransformStringOrAttributeRef a, CFTypeRef v) { return v; }); 49 }; 50 51 // Create a block to deal with out of memory errors 52 dispatch_block_t oom = ^ 53 { 54 CFTypeRefHolder localErr(GetNoMemoryError()); 55 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 56 kSecTransformMetaAttributeValue, localErr.Get()); 57 no_more_output(); 58 }; 59 60 SecTransformSetTransformAction(ref, kSecTransformActionFinalize, 61 ^() 62 { 63 if (NULL != allValues) 64 { 65 CFRelease(allValues); 66 } 67 68 return (CFTypeRef) NULL; 69 }); 70 71 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, 72 input_ah, 73 ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) 74 { 75 CFIndex len = CFArrayGetCount(allValues); 76 77#if 0 78 if (NULL == value && 0 == len) 79 { 80 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 81 kSecTransformMetaAttributeValue, NULL); 82 no_more_output(); 83 return value; 84 } 85#endif 86 87 if (value && isSameType && len > 0) 88 { 89 isSameType = CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) == CFGetTypeID(value); 90 } 91 92 if (value) 93 { 94 95 if (CFGetTypeID(value) == CFErrorGetTypeID()) 96 { 97 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 98 kSecTransformMetaAttributeValue, value); 99 no_more_output(); 100 return value; 101 } 102 103 // For mutable types, we want an immutable copy. 104 /// XXX: write a more general CFImutableCopy and use it here. 105 if (CFGetTypeID(value) == CFDataGetTypeID()) 106 { 107 CFDataRef copy = CFDataCreateCopy(NULL, (CFDataRef)value); 108 109 CFArrayAppendValue(allValues, copy); 110 CFRelease(copy); 111 } 112 else 113 { 114 CFArrayAppendValue(allValues, value); 115 } 116 117 if (CFArrayGetCount(allValues) != len +1) 118 { 119 oom(); 120 return value; 121 } 122 } 123 else 124 { 125 if (isSameType) 126 { 127 // Deal with data or no items at all 128 CFTypeID type = CFArrayGetCount(allValues) ? 129 CFGetTypeID(CFArrayGetValueAtIndex(allValues, 0)) : CFDataGetTypeID(); 130 if (CFDataGetTypeID() == type) 131 { 132 CFIndex total_len = 0; 133 CFIndex prev_total_len = 0; 134 CFIndex i; 135 const CFIndex n_datas = CFArrayGetCount(allValues); 136 137 for(i = 0; i < n_datas; i++) 138 { 139 total_len += 140 CFDataGetLength((CFDataRef)CFArrayGetValueAtIndex(allValues, i)); 141 if (total_len < prev_total_len) 142 { 143 oom(); 144 return value; 145 } 146 prev_total_len = total_len; 147 } 148 149 CFMutableDataRef result = CFDataCreateMutable(NULL, total_len); 150 if (!result) 151 { 152 oom(); 153 return value; 154 } 155 156 for(i = 0; i < n_datas; i++) 157 { 158 CFDataRef d = (CFDataRef)CFArrayGetValueAtIndex(allValues, i); 159 CFDataAppendBytes(result, CFDataGetBytePtr(d), CFDataGetLength(d)); 160 } 161 162 if (CFDataGetLength(result) != total_len) 163 { 164 oom(); 165 return value; 166 } 167 168 CFDataRef resultData = CFDataCreateCopy(NULL, result); 169 170 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 171 kSecTransformMetaAttributeValue, (CFTypeRef)resultData); 172 173 CFRelease(resultData); 174 175 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 176 kSecTransformMetaAttributeValue, (CFTypeRef)value); 177 no_more_output(); 178 179 CFRelease(result); 180 return value; 181 } 182 else if (CFStringGetTypeID() == type) 183 { 184 // deal with strings 185 CFStringRef resultStr = CFStringCreateByCombiningStrings(NULL, allValues, CFSTR("")); 186 187 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 188 kSecTransformMetaAttributeValue, (CFTypeRef)resultStr); 189 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 190 kSecTransformMetaAttributeValue, (CFTypeRef)value); 191 no_more_output(); 192 193 return value; 194 } 195 else 196 { 197 // special case the singleton 198 if (1 == CFArrayGetCount(allValues)) 199 { 200 CFTypeRef result = (CFTypeRef)CFRetain(CFArrayGetValueAtIndex(allValues, 0)); 201 202 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 203 kSecTransformMetaAttributeValue, (CFTypeRef)result); 204 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 205 kSecTransformMetaAttributeValue, (CFTypeRef)value); 206 no_more_output(); 207 208 return value; 209 } 210 } 211 } 212 // Fall through for non-homogenous or un-mergable type 213 CFArrayRef resultArray = CFArrayCreateCopy(kCFAllocatorDefault, allValues); 214 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 215 kSecTransformMetaAttributeValue, (CFTypeRef)resultArray); 216 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 217 kSecTransformMetaAttributeValue, (CFTypeRef)value); 218 no_more_output(); 219 220 return value; 221 } 222 223 return value; 224 225 }); 226 227 return (CFErrorRef)NULL; 228 }; 229 230 return Block_copy(instanceBlock); 231} 232 233SecTransformRef SecCreateCollectTransform(CFErrorRef* error) 234{ 235 static dispatch_once_t once; 236 __block Boolean ok = TRUE; 237 238 dispatch_block_t aBlock = ^ 239 { 240 ok = SecTransformRegister(kCollectTransformName, &CollectTransform, error); 241 }; 242 243 dispatch_once(&once, aBlock); 244 245 if (!ok) 246 { 247 return NULL; 248 } 249 250 SecTransformRef yatz = SecTransformCreate(kCollectTransformName, error); 251 return yatz; 252} 253