SecCollectTransform.cpp
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 32 static CFStringRef kCollectTransformName = CFSTR("com.apple.security.seccollecttransform"); 33 34 static 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 CFReleaseNull(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 CFReleaseNull(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 CFReleaseNull(result); 166 return value; 167 } 168 169 CFDataRef resultData = CFDataCreateCopy(NULL, result); 170 171 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 172 kSecTransformMetaAttributeValue, (CFTypeRef)resultData); 173 174 CFReleaseNull(resultData); 175 176 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 177 kSecTransformMetaAttributeValue, (CFTypeRef)value); 178 no_more_output(); 179 180 CFReleaseNull(result); 181 return value; 182 } 183 else if (CFStringGetTypeID() == type) 184 { 185 // deal with strings 186 CFStringRef resultStr = CFStringCreateByCombiningStrings(NULL, allValues, CFSTR("")); 187 188 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 189 kSecTransformMetaAttributeValue, (CFTypeRef)resultStr); 190 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 191 kSecTransformMetaAttributeValue, (CFTypeRef)value); 192 no_more_output(); 193 CFReleaseNull(resultStr); 194 195 return value; 196 } 197 else 198 { 199 // special case the singleton 200 if (1 == CFArrayGetCount(allValues)) 201 { 202 CFTypeRef result = (CFTypeRef)CFRetainSafe(CFArrayGetValueAtIndex(allValues, 0)); 203 204 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 205 kSecTransformMetaAttributeValue, (CFTypeRef)result); 206 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 207 kSecTransformMetaAttributeValue, (CFTypeRef)value); 208 no_more_output(); 209 CFReleaseNull(result); 210 211 return value; 212 } 213 } 214 } 215 // Fall through for non-homogenous or un-mergable type 216 CFArrayRef resultArray = CFArrayCreateCopy(kCFAllocatorDefault, allValues); 217 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 218 kSecTransformMetaAttributeValue, (CFTypeRef)resultArray); 219 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 220 kSecTransformMetaAttributeValue, (CFTypeRef)value); 221 no_more_output(); 222 CFReleaseNull(resultArray); 223 224 return value; 225 } 226 227 return value; 228 229 }); 230 231 return (CFErrorRef)NULL; 232 }; 233 234 return Block_copy(instanceBlock); 235 } 236 237 SecTransformRef SecCreateCollectTransform(CFErrorRef* error) 238 { 239 static dispatch_once_t once; 240 __block Boolean ok = TRUE; 241 242 dispatch_block_t aBlock = ^ 243 { 244 ok = SecTransformRegister(kCollectTransformName, &CollectTransform, error); 245 }; 246 247 dispatch_once(&once, aBlock); 248 249 if (!ok) 250 { 251 return NULL; 252 } 253 254 SecTransformRef yatz = SecTransformCreate(kCollectTransformName, error); 255 return yatz; 256 }