]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/SecTransform.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / 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 #pragma clang diagnostic push
169 #pragma clang diagnostic ignored "-Wunused-function"
170 static inline GroupTransform* MakeGroupTransformFromTransformRef(SecTransformRef tr)
171 {
172 GroupTransform* gt = (GroupTransform*) CoreFoundationHolder::ObjectFromCFType(tr);
173 return gt;
174 }
175 #pragma clang diagnostic pop
176
177 static 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
210 CFTypeRef 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
380 void 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
400 CFDictionaryRef SecTransformCopyExternalRepresentation(SecTransformRef transformRef)
401 {
402
403 Transform* tr = (Transform*) CoreFoundationHolder::ObjectFromCFType(transformRef);
404 return tr->Externalize(NULL);
405 }
406
407 CFStringRef 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
417 SecTransformRef 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
523 SecTransformRef 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
538 CFTypeID SecGroupTransformGetTypeID()
539 {
540 return GroupTransform::GetCFTypeID();
541 }
542
543 CFTypeID SecTransformGetTypeID()
544 {
545 // Obviously this is wrong (returns same CFTypeID as SecTransformGetTypeID) Needs to be fixed
546 return GroupTransform::GetCFTypeID();
547 }