]> git.saurik.com Git - apple/security.git/blob - libsecurity_transform/lib/SecTransform.cpp
Security-55178.0.1.tar.gz
[apple/security.git] / libsecurity_transform / lib / 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 *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
102 Boolean 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
151 CFTypeRef 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
169 static inline GroupTransform* MakeGroupTransformFromTransformRef(SecTransformRef tr)
170 {
171 GroupTransform* gt = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(tr);
172 return gt;
173 }
174
175 static CFTypeRef InternalSecTransformExecute(SecTransformRef transformRef,
176 CFErrorRef* errorRef,
177 dispatch_queue_t deliveryQueue,
178 SecMessageBlock deliveryBlock)
179 {
180 if (NULL == transformRef || (deliveryBlock && !deliveryQueue))
181 {
182 CFErrorRef localError = CFErrorCreate(kCFAllocatorDefault, kSecTransformErrorDomain,
183 kSecTransformInvalidArgument, NULL);
184
185 if (NULL != errorRef)
186 {
187 *errorRef = localError;
188 }
189 else
190 {
191 CFRelease(localError);
192 }
193
194 return (CFTypeRef)NULL;
195 }
196
197 // if our transform is a group, connect to its first member instead
198 if (CFGetTypeID(transformRef) == GroupTransform::GetCFTypeID())
199 {
200 GroupTransform* gtsrc = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
201 transformRef = gtsrc->GetAnyMember();
202 }
203
204 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
205 return transform->Execute(deliveryQueue, deliveryBlock, errorRef);
206 }
207
208 CFTypeRef SecTransformExecute(SecTransformRef transformRef, CFErrorRef* errorRef)
209 {
210 if (NULL == transformRef)
211 {
212 if (errorRef)
213 {
214 *errorRef = CreateSecTransformErrorRef(kSecTransformInvalidArgument, "NULL transform can not be executed");
215 }
216 return NULL;
217 }
218
219 Transform* transform = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
220
221 // transform->Execute will check this, but by then we have attached a collector which causes all manner of issues.
222 if (transform->mIsActive)
223 {
224 if (errorRef)
225 {
226 *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", transform->GetName());
227 }
228 return NULL;
229 }
230
231 SecTransformRef collectTransform = transforms_assume(SecCreateCollectTransform(errorRef));
232 SecGroupTransformRef theGroup = NULL;
233 Boolean releaseTheGroup = false;
234 GroupTransform* myGroup = NULL;
235 Boolean needConnection = true;
236
237 // Sniff the type of the transformRef to see if it is a group
238 if (SecGroupTransformGetTypeID() == CFGetTypeID(transformRef))
239 {
240 theGroup = (SecGroupTransformRef)transformRef;
241 }
242 else
243 {
244 // Ok TransformRef is a TransformRef so get's it group
245
246 myGroup = transform->mGroup;
247
248 if (NULL == myGroup)
249 {
250 theGroup = SecTransformCreateGroupTransform();
251 if (NULL == theGroup)
252 {
253 if (NULL != errorRef)
254 {
255 *errorRef = GetNoMemoryErrorAndRetain();
256 }
257
258 return (CFTypeRef)NULL;
259
260 }
261
262 releaseTheGroup = true;
263
264 SecGroupTransformRef connectResult =
265 SecTransformConnectTransforms(transformRef,
266 kSecTransformOutputAttributeName,
267 collectTransform,
268 kSecTransformInputAttributeName,
269 theGroup, errorRef);
270
271 if (NULL == connectResult)
272 {
273 return (CFTypeRef)NULL;
274 }
275
276 needConnection = false;
277
278 }
279 else
280 {
281 theGroup = (SecGroupTransformRef)myGroup->GetCFObject();
282 }
283 }
284
285 if (NULL == theGroup || (SecGroupTransformGetTypeID() != CFGetTypeID(theGroup)))
286 {
287 if (NULL != errorRef)
288 {
289 *errorRef = GetNoMemoryErrorAndRetain();
290 }
291
292 return (CFTypeRef)NULL;
293
294 }
295
296
297 if (needConnection)
298 {
299 // Connect the collectTransform to the group
300 myGroup = ((GroupTransform*)CoreFoundationHolder::ObjectFromCFType(theGroup))->GetRootGroup();
301 if (NULL == myGroup)
302 {
303 if (NULL != errorRef)
304 {
305 *errorRef = GetNoMemoryErrorAndRetain();
306 }
307
308 return (CFTypeRef)NULL;
309 }
310
311 SecTransformRef outputTransform = myGroup->FindLastTransform();
312
313 SecGroupTransformRef connectResult =
314 SecTransformConnectTransforms(outputTransform,
315 kSecTransformOutputAttributeName,
316 collectTransform,
317 kSecTransformInputAttributeName,
318 myGroup->GetCFObject(), errorRef);
319
320 if (NULL == connectResult)
321 {
322 CFRelease(collectTransform);
323 if (releaseTheGroup)
324 {
325 CFRelease(theGroup);
326 }
327 return (CFTypeRef)NULL;
328 }
329 }
330
331 __block CFTypeRef myResult = NULL;
332 dispatch_semaphore_t mySem = dispatch_semaphore_create(0L);
333 dispatch_queue_t myQueue = dispatch_queue_create("com.apple.security.sectransfrom.SecTransformExecute", NULL);
334 SecMessageBlock myBlock = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
335 {
336 if (NULL != error)
337 {
338 if (NULL != errorRef)
339 {
340 CFRetain(error);
341 *errorRef = error;
342 }
343
344 if (NULL != myResult)
345 {
346 CFRelease(myResult);
347 myResult = NULL;
348 }
349 }
350
351 if (NULL != message)
352 {
353 myResult = message;
354 CFRetain(myResult);
355 }
356
357 if (isFinal)
358 {
359 dispatch_semaphore_signal(mySem);
360 }
361 };
362
363 SecTransformExecuteAsync(theGroup, myQueue, myBlock);
364 dispatch_semaphore_wait(mySem, DISPATCH_TIME_FOREVER);
365 dispatch_release(mySem);
366 dispatch_release(myQueue);
367
368 if (releaseTheGroup)
369 {
370 CFRelease(theGroup);
371 theGroup = NULL;
372 }
373 CFRelease(collectTransform);
374
375 return myResult;
376 }
377
378 void SecTransformExecuteAsync(SecTransformRef transformRef,
379 dispatch_queue_t deliveryQueue,
380 SecMessageBlock deliveryBlock)
381 {
382 CFErrorRef localError = NULL;
383 InternalSecTransformExecute(transformRef, &localError, deliveryQueue, deliveryBlock);
384
385 // if we got an error (usually a transform startup error), we must deliver it
386 if (localError != NULL)
387 {
388 // The monitor treats a NULL queue as running on it's own queue, which from an appication's point of view is
389 // the same as a default global queue. Chances are there is no monitor at this point, so it is best to just use the
390 // global queue for this.
391 dispatch_queue_t effectiveQueue = deliveryQueue ? deliveryQueue : dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL);
392 dispatch_async(effectiveQueue, ^{
393 deliveryBlock(NULL, localError, true);
394 });
395 }
396 }
397
398 CFDictionaryRef SecTransformCopyExternalRepresentation(SecTransformRef transformRef)
399 {
400
401 Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
402 return tr->Externalize(NULL);
403 }
404
405 CFStringRef SecTransformDotForDebugging(SecTransformRef transformRef)
406 {
407 if (CFGetTypeID(transformRef) == SecGroupTransformGetTypeID()) {
408 GroupTransform* tr = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
409 return tr->DotForDebugging();
410 } else {
411 return CFSTR("Can only dot debug a group");
412 }
413 }
414
415 SecTransformRef SecTransformCreateFromExternalRepresentation(
416 CFDictionaryRef dictionary,
417 CFErrorRef *error)
418 {
419 // The incoming dictionary consists of a list of transforms and
420 // a list of connections. We start by making the individual
421 // transforms and storing them in a dictionary so that we can
422 // efficiently make connections
423
424 CFArrayRef transforms = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_TRANSFORM_ARRAY);
425 if (transforms == NULL)
426 {
427 // The dictionary we got is massively malformed!
428 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_TRANSFORM_ARRAY);
429 return NULL;
430 }
431
432 CFArrayRef connections = (CFArrayRef) CFDictionaryGetValue(dictionary, EXTERN_TRANSFORM_CONNECTION_ARRAY);
433 if (connections == NULL)
434 {
435 // The dictionary we got is massively malformed!
436 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "%@ is missing from the dictionary. The dictionary is malformed.", EXTERN_TRANSFORM_CONNECTION_ARRAY);
437 return NULL;
438 }
439
440 CFMutableDictionaryRef transformHolder = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
441 CFTypeRefHolder _(transformHolder);
442
443 CFIndex numTransforms = CFArrayGetCount(transforms);
444 CFIndex n;
445
446 SecTransformRef aTransform;
447
448 for (n = 0; n < numTransforms; ++n)
449 {
450 // get the basic info we need
451 CFDictionaryRef xTransform = (CFDictionaryRef) CFArrayGetValueAtIndex(transforms, n);
452
453 CFStringRef xName = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_NAME);
454
455 CFStringRef xType = (CFStringRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_TYPE);
456
457 // reconstruct the transform
458 aTransform = TransformFactory::MakeTransformWithType(xType, error);
459 SecTransformSetAttribute(aTransform, kSecTransformTransformName, xName, NULL);
460
461 // restore the transform state
462 Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(aTransform);
463 tr->RestoreState((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_STATE));
464 tr->SetCustomExternalData((CFDictionaryRef) CFDictionaryGetValue(xTransform, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY));
465
466 CFIndex cnt = CFDictionaryGetCount(transformHolder);
467
468 // add the transform to the dictionary
469 CFDictionaryAddValue(transformHolder, xName, aTransform);
470
471 if (CFDictionaryGetCount(transformHolder) <= cnt)
472 {
473 if (error)
474 {
475 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary,
476 "Out of memory, or damaged input dictonary (duplicate label %@?)", xName);
477 }
478 return NULL;
479 }
480 }
481
482 CFIndex numConnections = CFArrayGetCount(connections);
483 if (numConnections == 0)
484 {
485 return aTransform;
486 }
487
488 SecGroupTransformRef gt = SecTransformCreateGroupTransform();
489
490 for (n = 0; n < numConnections; ++n)
491 {
492 CFDictionaryRef connection = (CFDictionaryRef) CFArrayGetValueAtIndex(connections, n);
493 CFStringRef fromTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_NAME);
494 CFStringRef fromAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE);
495 CFStringRef toTransformName = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_NAME);
496 CFStringRef toAttribute = (CFStringRef) CFDictionaryGetValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE);
497
498 SecTransformRef fromTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, fromTransformName);
499 if (!fromTransform) {
500 if (error) {
501 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, fromTransformName);
502 }
503 return NULL;
504 }
505 SecTransformRef toTransform = (SecTransformRef) CFDictionaryGetValue(transformHolder, toTransformName);
506 if (!toTransform) {
507 if (error) {
508 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInputDictionary, "Can't connect %@ to %@ because %@ was not found", fromTransformName, toTransformName, toTransformName);
509 }
510 return NULL;
511 }
512
513 aTransform = SecTransformConnectTransforms(fromTransform, fromAttribute, toTransform, toAttribute, gt, error);
514 }
515
516 return gt;
517 }
518
519
520
521 SecTransformRef SecTransformFindByName(SecTransformRef transform, CFStringRef name)
522 {
523 Transform *t = (Transform*)CoreFoundationHolder::ObjectFromCFType(transform);
524 GroupTransform *g = t->GetRootGroup();
525
526 if (g) {
527 return g->FindByName(name);
528 } else {
529 // There is no group, so if transform isn't our guy nobody is.
530 return (CFStringCompare(name, t->GetName(), 0) == kCFCompareEqualTo) ? transform : NULL;
531 }
532 }
533
534
535
536 CFTypeID SecGroupTransformGetTypeID()
537 {
538 return GroupTransform::GetCFTypeID();
539 }
540
541 CFTypeID SecTransformGetTypeID()
542 {
543 // Obviously this is wrong (returns same CFTypeID as SecTransformGetTypeID) Needs to be fixed
544 return GroupTransform::GetCFTypeID();
545 }