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