/ OSX / libsecurity_transform / lib / SecTransform.cpp
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  }