SecTransform.cpp
1 #include "SecTransform.h" 2 #include "SecTransformInternal.h" 3 4 #include "Transform.h" 5 #include "Utilities.h" 6 #include "TransformFactory.h" 7 #include "GroupTransform.h" 8 #include "c++utils.h" 9 #include "SecCollectTransform.h" 10 11 12 #include <string> 13 14 using namespace std; 15 16 const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT"); 17 const CFStringRef kSecTransformOutputAttributeName = CFSTR("OUTPUT"); 18 const CFStringRef kSecTransformDebugAttributeName = CFSTR("DEBUG"); 19 const CFStringRef kSecTransformTransformName = CFSTR("NAME"); 20 //const CFStringRef kSecTransformErrorTransform = CFSTR("TRANSFORM"); 21 const CFStringRef kSecTransformErrorDomain = CFSTR("com.apple.security.transforms.error"); 22 const CFStringRef kSecTransformAbortAttributeName = CFSTR("ABORT"); 23 24 CFErrorRef SecTransformConnectTransformsInternal(SecGroupTransformRef groupRef, 25 SecTransformRef sourceTransformRef, 26 CFStringRef sourceAttributeName, 27 SecTransformRef destinationTransformRef, 28 CFStringRef destinationAttributeName) 29 { 30 Transform* destination = (Transform*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef); 31 Transform* source = (Transform*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef); 32 33 GroupTransform* group = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(groupRef); 34 CFErrorRef temp = source->Connect(group, destination, destinationAttributeName, sourceAttributeName); 35 return temp; 36 } 37 38 39 CFErrorRef SecTransformDisconnectTransforms(SecTransformRef sourceTransformRef, CFStringRef sourceAttributeName, 40 SecTransformRef destinationTransformRef, CFStringRef destinationAttributeName) 41 { 42 Transform* destination = (Transform*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef); 43 Transform* source = (Transform*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef); 44 return source->Disconnect(destination, sourceAttributeName, destinationAttributeName); 45 } 46 47 SecGroupTransformRef SecTransformCreateGroupTransform() 48 { 49 return (SecGroupTransformRef) GroupTransform::Make(); 50 } 51 52 SecGroupTransformRef SecTransformConnectTransforms(SecTransformRef sourceTransformRef, 53 CFStringRef sourceAttributeName, 54 SecTransformRef destinationTransformRef, 55 CFStringRef destinationAttributeName, 56 SecGroupTransformRef group, 57 CFErrorRef *error) 58 { 59 if (group == NULL) 60 { 61 if (error) 62 { 63 *error = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Group must not be NULL."); 64 } 65 66 return NULL; 67 } 68 69 if (destinationAttributeName == NULL) 70 { 71 destinationAttributeName = kSecTransformInputAttributeName; 72 } 73 74 if (sourceAttributeName == NULL) 75 { 76 sourceAttributeName = kSecTransformOutputAttributeName; 77 } 78 79 GroupTransform* gtr = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(group); 80 81 CFErrorRef temp = SecTransformConnectTransformsInternal(gtr->GetCFObject(), 82 sourceTransformRef, sourceAttributeName, 83 destinationTransformRef, destinationAttributeName); 84 85 if (error) 86 { 87 CFRetainSafe(temp); 88 *error = temp; 89 } 90 91 if (temp) // an error happened? 92 { 93 return NULL; 94 } 95 else 96 { 97 return group; 98 } 99 } 100 101 102 103 Boolean SecTransformSetAttribute(SecTransformRef transformRef, 104 CFStringRef key, 105 CFTypeRef value, 106 CFErrorRef *error) 107 { 108 Boolean result = false; // Guilty until proven 109 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 110 111 if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID() && !transform->getAH(key, false)) 112 { 113 if (error) 114 { 115 *error = CreateSecTransformErrorRef(kSecTransformOperationNotSupportedOnGroup, "SecTransformSetAttribute on non-exported attribute: %@ (exported attributes are: %@).", key, transform->GetAllAH()); 116 } 117 118 return result; 119 } 120 121 // if the caller is setting the abort attribute, a value must be supplied 122 if (NULL == value && CFStringCompare(key, kSecTransformAbortAttributeName, 0) == kCFCompareEqualTo) 123 { 124 if (error) 125 { 126 // XXX: "a parameter"? It has one: NULL. What it requires is a non-NULL value. 127 *error = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "ABORT requires a parameter."); 128 } 129 130 return result; 131 } 132 133 CFErrorRef temp = transform->ExternalSetAttribute(key, value); 134 result = (temp == NULL); 135 if (error) 136 { 137 CFRetainSafe(temp); 138 *error = temp; 139 } 140 141 return result; 142 } 143 144 145 146 CFTypeRef SecTransformGetAttribute(SecTransformRef transformRef, 147 CFStringRef key) 148 { 149 // if the transform is a group, we really want to operation on its first object 150 if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID()) 151 { 152 return NULL; 153 } 154 155 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 156 if (transform->mIsActive) { 157 CFErrorRef error = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "Can not get the value of attributes during execution (attempt to fetch %@/%@)", transform->GetName(), key); 158 CFAutorelease(error); 159 return error; 160 } 161 return transform->GetAttribute(key); 162 } 163 164 165 #pragma clang diagnostic push 166 #pragma clang diagnostic ignored "-Wunused-function" 167 static inline GroupTransform* MakeGroupTransformFromTransformRef(SecTransformRef tr) 168 { 169 GroupTransform* gt = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(tr); 170 return gt; 171 } 172 #pragma clang diagnostic pop 173 174 static CFTypeRef InternalSecTransformExecute(SecTransformRef transformRef, 175 CFErrorRef* errorRef, 176 dispatch_queue_t deliveryQueue, 177 SecMessageBlock deliveryBlock) 178 { 179 if (NULL == transformRef || (deliveryBlock && !deliveryQueue)) 180 { 181 CFErrorRef localError = CFErrorCreate(kCFAllocatorDefault, kSecTransformErrorDomain, 182 kSecTransformInvalidArgument, NULL); 183 184 if (NULL != errorRef) 185 { 186 *errorRef = localError; 187 } 188 else 189 { 190 CFReleaseNull(localError); 191 } 192 193 return (CFTypeRef)NULL; 194 } 195 196 // if our transform is a group, connect to its first member instead 197 if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID()) 198 { 199 GroupTransform* gtsrc = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 200 transformRef = gtsrc->GetAnyMember(); 201 } 202 203 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 204 return transform->Execute(deliveryQueue, deliveryBlock, errorRef); 205 } 206 207 CFTypeRef SecTransformExecute(SecTransformRef transformRef, CFErrorRef* errorRef) 208 { 209 if (NULL == transformRef) 210 { 211 if (errorRef) 212 { 213 *errorRef = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "NULL transform can not be executed"); 214 } 215 return NULL; 216 } 217 218 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 219 220 // transform->Execute will check this, but by then we have attached a collector which causes all manner of issues. 221 if (transform->mIsActive) 222 { 223 if (errorRef) 224 { 225 *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", transform->GetName()); 226 } 227 return NULL; 228 } 229 230 SecTransformRef collectTransform = transforms_assume(SecCreateCollectTransform(errorRef)); 231 SecGroupTransformRef theGroup = NULL; 232 Boolean releaseTheGroup = false; 233 GroupTransform* myGroup = NULL; 234 Boolean needConnection = true; 235 236 // Sniff the type of the transformRef to see if it is a group 237 if (SecGroupTransformGetTypeID() == CFGetTypeID(transformRef)) 238 { 239 theGroup = (SecGroupTransformRef)transformRef; 240 } 241 else 242 { 243 // Ok TransformRef is a TransformRef so get's it group 244 245 myGroup = transform->mGroup; 246 247 if (NULL == myGroup) 248 { 249 theGroup = SecTransformCreateGroupTransform(); 250 if (NULL == theGroup) 251 { 252 if (NULL != errorRef) 253 { 254 *errorRef = GetNoMemoryErrorAndRetain(); 255 } 256 257 return (CFTypeRef)NULL; 258 259 } 260 261 releaseTheGroup = true; 262 263 #ifdef __clang_analyzer__ 264 // we've already asserted that collectTransform is non-NULL, but clang doesn't know that, we skip use of it for the analyzer 265 SecGroupTransformRef connectResult = NULL; 266 #else 267 SecGroupTransformRef connectResult = 268 SecTransformConnectTransforms(transformRef, 269 kSecTransformOutputAttributeName, 270 collectTransform, 271 kSecTransformInputAttributeName, 272 theGroup, errorRef); 273 #endif 274 275 if (NULL == connectResult) 276 { 277 return (CFTypeRef)NULL; 278 } 279 280 needConnection = false; 281 282 } 283 else 284 { 285 theGroup = (SecGroupTransformRef)myGroup->GetCFObject(); 286 } 287 } 288 289 if (NULL == theGroup || (SecGroupTransformGetTypeID() != CFGetTypeID(theGroup))) 290 { 291 if (NULL != errorRef) 292 { 293 *errorRef = GetNoMemoryErrorAndRetain(); 294 } 295 296 return (CFTypeRef)NULL; 297 298 } 299 300 301 if (needConnection) 302 { 303 // Connect the collectTransform to the group 304 myGroup = ((GroupTransform*)CoreFoundationHolder::ObjectFromCFType(theGroup))->GetRootGroup(); 305 if (NULL == myGroup) 306 { 307 if (NULL != errorRef) 308 { 309 *errorRef = GetNoMemoryErrorAndRetain(); 310 } 311 312 return (CFTypeRef)NULL; 313 } 314 315 SecTransformRef outputTransform = myGroup->FindLastTransform(); 316 317 #ifdef __clang_analyzer__ 318 // we've already asserted that collectTransform is non-NULL, but clang doesn't know that, we skip use of it for the analyzer 319 SecGroupTransformRef connectResult = NULL; 320 #else 321 SecGroupTransformRef connectResult = 322 SecTransformConnectTransforms(outputTransform, 323 kSecTransformOutputAttributeName, 324 collectTransform, 325 kSecTransformInputAttributeName, 326 myGroup->GetCFObject(), errorRef); 327 328 if (NULL == connectResult) 329 { 330 CFReleaseNull(collectTransform); 331 if (releaseTheGroup) 332 { 333 CFReleaseNull(theGroup); 334 } 335 return (CFTypeRef)NULL; 336 } 337 #endif // __clang_analyzer__ 338 } 339 340 __block CFTypeRef myResult = NULL; 341 dispatch_semaphore_t mySem = dispatch_semaphore_create(0L); 342 dispatch_queue_t myQueue = dispatch_queue_create("com.apple.security.sectransfrom.SecTransformExecute", NULL); 343 SecMessageBlock myBlock = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) 344 { 345 if (NULL != error) 346 { 347 if (NULL != errorRef) 348 { 349 CFRetainSafe(error); 350 *errorRef = error; 351 } 352 353 if (NULL != myResult) 354 { 355 CFReleaseNull(myResult); 356 myResult = NULL; 357 } 358 } 359 360 if (NULL != message) 361 { 362 myResult = message; 363 CFRetainSafe(myResult); 364 } 365 366 if (isFinal) 367 { 368 dispatch_semaphore_signal(mySem); 369 } 370 }; 371 372 SecTransformExecuteAsync(theGroup, myQueue, myBlock); 373 dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER); 374 dispatch_release(mySem); 375 dispatch_release(myQueue); 376 377 if (releaseTheGroup) 378 { 379 CFRelease(theGroup); 380 theGroup = NULL; 381 } 382 CFRelease(collectTransform); 383 384 return myResult; 385 } 386 387 void SecTransformExecuteAsync(SecTransformRef transformRef, 388 dispatch_queue_t deliveryQueue, 389 SecMessageBlock deliveryBlock) 390 { 391 CFErrorRef localError = NULL; 392 InternalSecTransformExecute(transformRef, &localError, deliveryQueue, deliveryBlock); 393 394 // if we got an error (usually a transform startup error), we must deliver it 395 if (localError != NULL) 396 { 397 // The monitor treats a NULL queue as running on it's own queue, which from an appication's point of view is 398 // the same as a default global queue. Chances are there is no monitor at this point, so it is best to just use the 399 // global queue for this. 400 dispatch_queue_t effectiveQueue = deliveryQueue ? deliveryQueue : dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL); 401 dispatch_async(effectiveQueue, ^{ 402 deliveryBlock(NULL, localError, true); 403 }); 404 } 405 } 406 407 CFDictionaryRef SecTransformCopyExternalRepresentation(SecTransformRef transformRef) 408 { 409 410 Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 411 return tr->Externalize(NULL); 412 } 413 414 CFStringRef SecTransformDotForDebugging(SecTransformRef transformRef) 415 { 416 if (CFGetTypeID(transformRef) == SecGroupTransformGetTypeID()) { 417 GroupTransform* tr = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef); 418 return tr->DotForDebugging(); 419 } else { 420 return CFSTR("Can only dot debug a group"); 421 } 422 } 423 424 SecTransformRef SecTransformCreateFromExternalRepresentation( 425 CFDictionaryRef dictionary, 426 CFErrorRef *error) 427 { 428 // The incoming dictionary consists of a list of transforms and 429 // a list of connections. We start by making the individual 430 // transforms and storing them in a dictionary so that we can 431 // efficiently make connections 432 433 CFArrayRef transforms = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_TRANSFORM_ARRAY); 434 if (transforms == NULL) 435 { 436 // The dictionary we got is massively malformed! 437 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_TRANSFORM_ARRAY); 438 return NULL; 439 } 440 441 CFArrayRef connections = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_CONNECTION_ARRAY); 442 if (connections == NULL) 443 { 444 // The dictionary we got is massively malformed! 445 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_CONNECTION_ARRAY); 446 return NULL; 447 } 448 449 CFMutableDictionaryRef transformHolder = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 450 CFTypeRefHolder _(transformHolder); 451 452 CFIndex numTransforms = CFArrayGetCount(transforms); 453 CFIndex n; 454 455 SecTransformRef aTransform = NULL; 456 457 for (n = 0; n < numTransforms; ++n) 458 { 459 // get the basic info we need 460 CFDictionaryRef xTransform = (CFDictionaryRef) CFArrayGetValueAtIndex(transforms, n); 461 462 CFStringRef xName = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_NAME); 463 464 CFStringRef xType = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_TYPE); 465 466 // reconstruct the transform 467 aTransform = TransformFactory::MakeTransformWithType(xType, error); 468 SecTransformSetAttribute(aTransform, kSecTransformTransformName, xName, NULL); 469 470 // restore the transform state 471 Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(aTransform); 472 tr->RestoreState((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_STATE)); 473 tr->SetCustomExternalData((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY)); 474 475 CFIndex cnt = CFDictionaryGetCount(transformHolder); 476 477 // add the transform to the dictionary 478 CFDictionaryAddValue(transformHolder, xName, aTransform); 479 480 if (CFDictionaryGetCount(transformHolder) <= cnt) 481 { 482 if (error) 483 { 484 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, 485 "Out of memory, or damaged input dictonary (duplicate label %@?)", xName); 486 } 487 CFSafeRelease(aTransform); 488 return NULL; 489 } 490 } 491 492 CFIndex numConnections = CFArrayGetCount(connections); 493 if (numConnections == 0) 494 { 495 return aTransform; 496 } 497 498 SecGroupTransformRef gt = SecTransformCreateGroupTransform(); 499 500 for (n = 0; n < numConnections; ++n) 501 { 502 CFDictionaryRef connection = (CFDictionaryRef) CFArrayGetValueAtIndex(connections, n); 503 CFStringRef fromTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_NAME); 504 CFStringRef fromAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE); 505 CFStringRef toTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_NAME); 506 CFStringRef toAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE); 507 508 SecTransformRef fromTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, fromTransformName); 509 if (!fromTransform) { 510 if (error) { 511 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, fromTransformName); 512 } 513 return NULL; 514 } 515 SecTransformRef toTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, toTransformName); 516 if (!toTransform) { 517 if (error) { 518 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, toTransformName); 519 } 520 return NULL; 521 } 522 523 aTransform = SecTransformConnectTransforms(fromTransform, fromAttribute, toTransform, toAttribute, gt, error); 524 } 525 526 return gt; 527 } 528 529 530 531 SecTransformRef SecTransformFindByName(SecTransformRef transform, CFStringRef name) 532 { 533 Transform *t = (Transform*)CoreFoundationHolder::ObjectFromCFType(transform); 534 GroupTransform *g = t->GetRootGroup(); 535 536 if (g) { 537 return g->FindByName(name); 538 } else { 539 // There is no group, so if transform isn't our guy nobody is. 540 return (CFStringCompare(name, t->GetName(), 0) == kCFCompareEqualTo) ? transform : NULL; 541 } 542 } 543 544 545 546 CFTypeID SecGroupTransformGetTypeID() 547 { 548 return GroupTransform::GetCFTypeID(); 549 } 550 551 CFTypeID SecTransformGetTypeID() 552 { 553 // Obviously this is wrong (returns same CFTypeID as SecTransformGetTypeID) Needs to be fixed 554 return GroupTransform::GetCFTypeID(); 555 }