]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/SecTransform.cpp
Security-58286.1.32.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 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 }