1 #include "SecTransform.h"
2 #include "SecTransformInternal.h"
6 #include "TransformFactory.h"
7 #include "GroupTransform.h"
9 #include "SecCollectTransform.h"
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");
24 CFErrorRef
SecTransformConnectTransformsInternal(SecGroupTransformRef groupRef
,
25 SecTransformRef sourceTransformRef
,
26 CFStringRef sourceAttributeName
,
27 SecTransformRef destinationTransformRef
,
28 CFStringRef destinationAttributeName
)
30 Transform
* destination
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef
);
31 Transform
* source
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef
);
33 GroupTransform
* group
= (GroupTransform
*) CoreFoundationHolder::ObjectFromCFType(groupRef
);
34 CFErrorRef temp
= source
->Connect(group
, destination
, destinationAttributeName
, sourceAttributeName
);
39 CFErrorRef
SecTransformDisconnectTransforms(SecTransformRef sourceTransformRef
, CFStringRef sourceAttributeName
,
40 SecTransformRef destinationTransformRef
, CFStringRef destinationAttributeName
)
42 Transform
* destination
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(destinationTransformRef
);
43 Transform
* source
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(sourceTransformRef
);
44 return source
->Disconnect(destination
, sourceAttributeName
, destinationAttributeName
);
47 SecGroupTransformRef
SecTransformCreateGroupTransform()
49 return (SecGroupTransformRef
) GroupTransform::Make();
52 SecGroupTransformRef
SecTransformConnectTransforms(SecTransformRef sourceTransformRef
,
53 CFStringRef sourceAttributeName
,
54 SecTransformRef destinationTransformRef
,
55 CFStringRef destinationAttributeName
,
56 SecGroupTransformRef group
,
63 *error
= CreateSecTransformErrorRef(kSecTransformErrorMissingParameter
, "Group must not be NULL.");
69 if (destinationAttributeName
== NULL
)
71 destinationAttributeName
= kSecTransformInputAttributeName
;
74 if (sourceAttributeName
== NULL
)
76 sourceAttributeName
= kSecTransformOutputAttributeName
;
79 GroupTransform
* gtr
= (GroupTransform
*) CoreFoundationHolder::ObjectFromCFType(group
);
81 CFErrorRef temp
= SecTransformConnectTransformsInternal(gtr
->GetCFObject(),
82 sourceTransformRef
, sourceAttributeName
,
83 destinationTransformRef
, destinationAttributeName
);
91 if (temp
) // an error happened?
103 Boolean
SecTransformSetAttribute(SecTransformRef transformRef
,
108 Boolean result
= false; // Guilty until proven
109 Transform
* transform
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
111 if (CFGetTypeID(transformRef
) == GroupTransform::GetCFTypeID() && !transform
->getAH(key
, false))
115 *error
= CreateSecTransformErrorRef(kSecTransformOperationNotSupportedOnGroup
, "SecTransformSetAttribute on non-exported attribute: %@ (exported attributes are: %@).", key
, transform
->GetAllAH());
121 // if the caller is setting the abort attribute, a value must be supplied
122 if (NULL
== value
&& CFStringCompare(key
, kSecTransformAbortAttributeName
, 0) == kCFCompareEqualTo
)
126 // XXX: "a parameter"? It has one: NULL. What it requires is a non-NULL value.
127 *error
= CreateSecTransformErrorRef(kSecTransformInvalidArgument
, "ABORT requires a parameter.");
133 CFErrorRef temp
= transform
->ExternalSetAttribute(key
, value
);
134 result
= (temp
== NULL
);
146 CFTypeRef
SecTransformGetAttribute(SecTransformRef transformRef
,
149 // if the transform is a group, we really want to operation on its first object
150 if (CFGetTypeID(transformRef
) == GroupTransform::GetCFTypeID())
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
);
161 return transform
->GetAttribute(key
);
165 #pragma clang diagnostic push
166 #pragma clang diagnostic ignored "-Wunused-function"
167 static inline GroupTransform
* MakeGroupTransformFromTransformRef(SecTransformRef tr
)
169 GroupTransform
* gt
= (GroupTransform
*) CoreFoundationHolder::ObjectFromCFType(tr
);
172 #pragma clang diagnostic pop
174 static CFTypeRef
InternalSecTransformExecute(SecTransformRef transformRef
,
175 CFErrorRef
* errorRef
,
176 dispatch_queue_t deliveryQueue
,
177 SecMessageBlock deliveryBlock
)
179 if (NULL
== transformRef
|| (deliveryBlock
&& !deliveryQueue
))
181 CFErrorRef localError
= CFErrorCreate(kCFAllocatorDefault
, kSecTransformErrorDomain
,
182 kSecTransformInvalidArgument
, NULL
);
184 if (NULL
!= errorRef
)
186 *errorRef
= localError
;
190 CFReleaseNull(localError
);
193 return (CFTypeRef
)NULL
;
196 // if our transform is a group, connect to its first member instead
197 if (CFGetTypeID(transformRef
) == GroupTransform::GetCFTypeID())
199 GroupTransform
* gtsrc
= (GroupTransform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
200 transformRef
= gtsrc
->GetAnyMember();
203 Transform
* transform
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
204 return transform
->Execute(deliveryQueue
, deliveryBlock
, errorRef
);
207 CFTypeRef
SecTransformExecute(SecTransformRef transformRef
, CFErrorRef
* errorRef
)
209 if (NULL
== transformRef
)
213 *errorRef
= CreateSecTransformErrorRef(kSecTransformInvalidArgument
, "NULL transform can not be executed");
218 Transform
* transform
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
220 // transform->Execute will check this, but by then we have attached a collector which causes all manner of issues.
221 if (transform
->mIsActive
)
225 *errorRef
= CreateSecTransformErrorRef(kSecTransformTransformIsExecuting
, "The %@ transform has already executed, it may not be executed again.", transform
->GetName());
230 SecTransformRef collectTransform
= transforms_assume(SecCreateCollectTransform(errorRef
));
231 SecGroupTransformRef theGroup
= NULL
;
232 Boolean releaseTheGroup
= false;
233 GroupTransform
* myGroup
= NULL
;
234 Boolean needConnection
= true;
236 // Sniff the type of the transformRef to see if it is a group
237 if (SecGroupTransformGetTypeID() == CFGetTypeID(transformRef
))
239 theGroup
= (SecGroupTransformRef
)transformRef
;
243 // Ok TransformRef is a TransformRef so get's it group
245 myGroup
= transform
->mGroup
;
249 theGroup
= SecTransformCreateGroupTransform();
250 if (NULL
== theGroup
)
252 if (NULL
!= errorRef
)
254 *errorRef
= GetNoMemoryErrorAndRetain();
257 return (CFTypeRef
)NULL
;
261 releaseTheGroup
= true;
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
;
267 SecGroupTransformRef connectResult
=
268 SecTransformConnectTransforms(transformRef
,
269 kSecTransformOutputAttributeName
,
271 kSecTransformInputAttributeName
,
275 if (NULL
== connectResult
)
277 return (CFTypeRef
)NULL
;
280 needConnection
= false;
285 theGroup
= (SecGroupTransformRef
)myGroup
->GetCFObject();
289 if (NULL
== theGroup
|| (SecGroupTransformGetTypeID() != CFGetTypeID(theGroup
)))
291 if (NULL
!= errorRef
)
293 *errorRef
= GetNoMemoryErrorAndRetain();
296 return (CFTypeRef
)NULL
;
303 // Connect the collectTransform to the group
304 myGroup
= ((GroupTransform
*)CoreFoundationHolder::ObjectFromCFType(theGroup
))->GetRootGroup();
307 if (NULL
!= errorRef
)
309 *errorRef
= GetNoMemoryErrorAndRetain();
312 return (CFTypeRef
)NULL
;
315 SecTransformRef outputTransform
= myGroup
->FindLastTransform();
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
;
321 SecGroupTransformRef connectResult
=
322 SecTransformConnectTransforms(outputTransform
,
323 kSecTransformOutputAttributeName
,
325 kSecTransformInputAttributeName
,
326 myGroup
->GetCFObject(), errorRef
);
328 if (NULL
== connectResult
)
330 CFReleaseNull(collectTransform
);
333 CFReleaseNull(theGroup
);
335 return (CFTypeRef
)NULL
;
337 #endif // __clang_analyzer__
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
)
347 if (NULL
!= errorRef
)
353 if (NULL
!= myResult
)
355 CFReleaseNull(myResult
);
363 CFRetainSafe(myResult
);
368 dispatch_semaphore_signal(mySem
);
372 SecTransformExecuteAsync(theGroup
, myQueue
, myBlock
);
373 dispatch_semaphore_wait(mySem
, DISPATCH_TIME_FOREVER
);
374 dispatch_release(mySem
);
375 dispatch_release(myQueue
);
382 CFRelease(collectTransform
);
387 void SecTransformExecuteAsync(SecTransformRef transformRef
,
388 dispatch_queue_t deliveryQueue
,
389 SecMessageBlock deliveryBlock
)
391 CFErrorRef localError
= NULL
;
392 InternalSecTransformExecute(transformRef
, &localError
, deliveryQueue
, deliveryBlock
);
394 // if we got an error (usually a transform startup error), we must deliver it
395 if (localError
!= NULL
)
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);
407 CFDictionaryRef
SecTransformCopyExternalRepresentation(SecTransformRef transformRef
)
410 Transform
* tr
= (Transform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
411 return tr
->Externalize(NULL
);
414 CFStringRef
SecTransformDotForDebugging(SecTransformRef transformRef
)
416 if (CFGetTypeID(transformRef
) == SecGroupTransformGetTypeID()) {
417 GroupTransform
* tr
= (GroupTransform
*) CoreFoundationHolder::ObjectFromCFType(transformRef
);
418 return tr
->DotForDebugging();
420 return CFSTR("Can only dot debug a group");
424 SecTransformRef
SecTransformCreateFromExternalRepresentation(
425 CFDictionaryRef dictionary
,
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
433 CFArrayRef transforms
= (CFArrayRef
) CFDictionaryGetValue(dictionary
, EXTERN_TRANSFORM_TRANSFORM_ARRAY
);
434 if (transforms
== NULL
)
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
);
441 CFArrayRef connections
= (CFArrayRef
) CFDictionaryGetValue(dictionary
, EXTERN_TRANSFORM_CONNECTION_ARRAY
);
442 if (connections
== NULL
)
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
);
449 CFMutableDictionaryRef transformHolder
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
450 CFTypeRefHolder
_(transformHolder
);
452 CFIndex numTransforms
= CFArrayGetCount(transforms
);
455 SecTransformRef aTransform
= NULL
;
457 for (n
= 0; n
< numTransforms
; ++n
)
459 // get the basic info we need
460 CFDictionaryRef xTransform
= (CFDictionaryRef
) CFArrayGetValueAtIndex(transforms
, n
);
462 CFStringRef xName
= (CFStringRef
) CFDictionaryGetValue(xTransform
, EXTERN_TRANSFORM_NAME
);
464 CFStringRef xType
= (CFStringRef
) CFDictionaryGetValue(xTransform
, EXTERN_TRANSFORM_TYPE
);
466 // reconstruct the transform
467 aTransform
= TransformFactory::MakeTransformWithType(xType
, error
);
468 SecTransformSetAttribute(aTransform
, kSecTransformTransformName
, xName
, NULL
);
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
));
475 CFIndex cnt
= CFDictionaryGetCount(transformHolder
);
477 // add the transform to the dictionary
478 CFDictionaryAddValue(transformHolder
, xName
, aTransform
);
480 if (CFDictionaryGetCount(transformHolder
) <= cnt
)
484 *error
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary
,
485 "Out of memory, or damaged input dictonary (duplicate label %@?)", xName
);
487 CFSafeRelease(aTransform
);
492 CFIndex numConnections
= CFArrayGetCount(connections
);
493 if (numConnections
== 0)
498 SecGroupTransformRef gt
= SecTransformCreateGroupTransform();
500 for (n
= 0; n
< numConnections
; ++n
)
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
);
508 SecTransformRef fromTransform
= (SecTransformRef
) CFDictionaryGetValue(transformHolder
, fromTransformName
);
509 if (!fromTransform
) {
511 *error
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary
, "Can't connect %@ to %@ because %@ was not found", fromTransformName
, toTransformName
, fromTransformName
);
515 SecTransformRef toTransform
= (SecTransformRef
) CFDictionaryGetValue(transformHolder
, toTransformName
);
518 *error
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary
, "Can't connect %@ to %@ because %@ was not found", fromTransformName
, toTransformName
, toTransformName
);
523 aTransform
= SecTransformConnectTransforms(fromTransform
, fromAttribute
, toTransform
, toAttribute
, gt
, error
);
531 SecTransformRef
SecTransformFindByName(SecTransformRef transform
, CFStringRef name
)
533 Transform
*t
= (Transform
*)CoreFoundationHolder::ObjectFromCFType(transform
);
534 GroupTransform
*g
= t
->GetRootGroup();
537 return g
->FindByName(name
);
539 // There is no group, so if transform isn't our guy nobody is.
540 return (CFStringCompare(name
, t
->GetName(), 0) == kCFCompareEqualTo
) ? transform
: NULL
;
546 CFTypeID
SecGroupTransformGetTypeID()
548 return GroupTransform::GetCFTypeID();
551 CFTypeID
SecTransformGetTypeID()
553 // Obviously this is wrong (returns same CFTypeID as SecTransformGetTypeID) Needs to be fixed
554 return GroupTransform::GetCFTypeID();