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