]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/Transform.cpp
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_transform / lib / Transform.cpp
1 #include <CoreServices/CoreServices.h>
2 #include <Block.h>
3 #include <libkern/OSAtomic.h>
4 #include <syslog.h>
5 #include "Transform.h"
6 #include "StreamSource.h"
7 #include "SingleShotSource.h"
8 #include "Monitor.h"
9 #include "Utilities.h"
10 #include "c++utils.h"
11 #include "misc.h"
12 #include "SecTransformInternal.h"
13 #include "GroupTransform.h"
14 #include "GroupTransform.h"
15 #include <pthread.h>
16
17 static const int kMaxPendingTransactions = 20;
18
19 static CFTypeID internalID = _kCFRuntimeNotATypeID;
20
21 // Use &dispatchQueueToTransformKey as a key to dispatch_get_specific to map from
22 // a transforms master, activation, or any attribute queue to the Transform*
23 static unsigned char dispatchQueueToTransformKey;
24
25 static char RandomChar()
26 {
27 return arc4random() % 26 + 'A'; // good enough
28 }
29
30
31 static CFStringRef ah_set_describe(const void *v) CF_RETURNS_RETAINED {
32 transform_attribute *ta = ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)));
33 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@=%@ (conn: %@)"), ta->transform->GetName(), ta->name, ta->value ? ta->value : CFSTR("NULL"), ta->connections ? static_cast<CFTypeRef>(ta->connections) : static_cast<CFTypeRef>(CFSTR("NONE")));
34 }
35
36 static CFStringRef AttributeHandleFormat(CFTypeRef ah, CFDictionaryRef dict) CF_RETURNS_RETAINED {
37 transform_attribute *ta = ah2ta(ah);
38 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), ta->transform->GetName(), ta->name);
39 }
40
41 static CFStringRef AttributeHandleDebugFormat(CFTypeRef ah) CF_RETURNS_RETAINED {
42 transform_attribute *ta = ah2ta(ah);
43 return CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@ (%p)"), ta->transform->GetName(), ta->name, ta);
44 }
45
46 static void AttributeHandleFinalize(CFTypeRef ah)
47 {
48 transform_attribute *ta = ah2ta(ah);
49 if (!ta)
50 {
51 return;
52 }
53
54 if (ta->transform)
55 {
56 // When we release AH's we clear out the transform pointer, so if we get here with transform!=NULL somebody
57 // has released an AH we are still using and we will crash very very soon (in our code) if we don't abort here
58 syslog(LOG_ERR, "over release of SecTransformAttributeRef at %p\n", ah);
59 abort();
60 }
61
62 if (ta->value)
63 {
64 CFReleaseNull(ta->value);
65 }
66
67 // ta->q already released
68
69 if (ta->connections)
70 {
71 CFReleaseNull(ta->connections);
72 }
73
74 if (ta->semaphore)
75 {
76 dispatch_release(ta->semaphore);
77 }
78
79 if (ta->attribute_changed_block)
80 {
81 Block_release(ta->attribute_changed_block);
82 }
83
84 if (ta->attribute_validate_block)
85 {
86 Block_release(ta->attribute_validate_block);
87 }
88
89 free(ta);
90 }
91
92
93
94 static CFHashCode ah_set_hash(const void *v) {
95 return CFHash(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v)))->name);
96 }
97
98 static Boolean ah_set_equal(const void *v1, const void *v2) {
99 return CFEqual(ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v1)))->name, ah2ta(static_cast<SecTransformAttributeRef>(const_cast<void*>(v2)))->name);
100 }
101
102 CFTypeID transform_attribute::cftype;
103
104 SecTransformAttributeRef Transform::makeAH(transform_attribute *ta) {
105 if (ta) {
106 SecTransformAttributeRef ah = _CFRuntimeCreateInstance(NULL, transform_attribute::cftype, sizeof(struct transform_attribute*), NULL);
107 if (!ah) {
108 return NULL;
109 }
110 *(struct transform_attribute **)(1 + (CFRuntimeBase*)ah) = ta;
111 return ah;
112 } else {
113 return NULL;
114 }
115 }
116
117 static pthread_key_t ah_search_key_slot;
118
119 static void destroy_ah_search_key(void *ah) {
120 CFReleaseNull(ah);
121 pthread_setspecific(ah_search_key_slot, NULL);
122 }
123
124
125
126 SecTransformAttributeRef Transform::getAH(SecTransformStringOrAttributeRef attrib, bool create_ok, bool create_underscore_ok)
127 {
128 if (CFGetTypeID(attrib) == transform_attribute::cftype)
129 {
130 return (SecTransformAttributeRef)attrib;
131 }
132
133 CFStringRef label = (CFStringRef)attrib;
134 static dispatch_once_t once = 0;
135 const char *name = (const char *)"SecTransformAttributeRef";
136 static CFRuntimeClass ahclass;
137 static CFSetCallBacks tasetcb;
138
139 dispatch_once(&once, ^{
140 ahclass.className = name;
141 ahclass.copyFormattingDesc = AttributeHandleFormat;
142 ahclass.copyDebugDesc = AttributeHandleDebugFormat;
143 ahclass.finalize = AttributeHandleFinalize;
144 transform_attribute::cftype = _CFRuntimeRegisterClass(&ahclass);
145 if (transform_attribute::cftype == _kCFRuntimeNotATypeID) {
146 abort();
147 }
148
149 tasetcb.equal = ah_set_equal;
150 tasetcb.hash = ah_set_hash;
151 tasetcb.copyDescription = ah_set_describe;
152
153 pthread_key_create(&ah_search_key_slot, destroy_ah_search_key);
154 });
155
156 SecTransformAttributeRef search_for = pthread_getspecific(ah_search_key_slot);
157 if (!search_for)
158 {
159 transform_attribute* ta = (transform_attribute*)malloc(sizeof(transform_attribute));
160 search_for = makeAH(ta);
161 if (!search_for)
162 {
163 free(ta);
164 return NULL;
165 }
166
167 bzero(ah2ta(search_for), sizeof(transform_attribute));
168 pthread_setspecific(ah_search_key_slot, search_for);
169 }
170
171 if (!mAttributes)
172 {
173 mAttributes = CFSetCreateMutable(NULL, 0, &tasetcb);
174 }
175
176 ah2ta(search_for)->name = label;
177 SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void*>(CFSetGetValue(mAttributes, search_for)));
178 if (ah == NULL && create_ok)
179 {
180 if (CFStringGetLength(label) && L'_' == CFStringGetCharacterAtIndex(label, 0) && !create_underscore_ok)
181 {
182 // Attributes with a leading _ belong to the Transform system only, not to random 3rd party transforms.
183 return NULL;
184 }
185
186 transform_attribute *ta = static_cast<transform_attribute *>(malloc(sizeof(transform_attribute)));
187 ah = makeAH(ta);
188 if (!ah)
189 {
190 free(ta);
191 return NULL;
192 }
193
194 ta->name = CFStringCreateCopy(NULL, label);
195 if (!ta->name)
196 {
197 CFRelease(ah);
198 free(ta);
199 return NULL;
200 }
201 CFIndex cnt = CFSetGetCount(mAttributes);
202 CFSetAddValue(mAttributes, ah);
203 if (CFSetGetCount(mAttributes) != cnt+1)
204 {
205 CFReleaseNull(ta->name);
206 free(ta);
207 CFRelease(ah);
208 return NULL;
209 }
210
211 CFStringRef qname = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s/%@"), dispatch_queue_get_label(this->mDispatchQueue), label);
212 CFIndex used, sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(qname), kCFStringEncodingUTF8);
213 UInt8 *qnbuf = (UInt8 *)alloca(sz);
214 CFStringGetBytes(qname, CFRangeMake(0, CFStringGetLength(qname)), kCFStringEncodingUTF8, '?', FALSE, qnbuf, sz, &used);
215 qnbuf[used] = '\0';
216 ta->q = dispatch_queue_create((char*)qnbuf, NULL);
217 CFReleaseNull(qname);
218 ta->semaphore = dispatch_semaphore_create(kMaxPendingTransactions);
219
220
221 ta->pushback_state = transform_attribute::pb_empty;
222 ta->pushback_value = NULL;
223 ta->value = NULL;
224 ta->connections = NULL;
225 ta->transform = this;
226
227 dispatch_set_target_queue(ta->q, mDispatchQueue);
228 ta->required = 0;
229 ta->requires_outbound_connection = 0;
230 ta->deferred = 0;
231 ta->stream = 0;
232 ta->ignore_while_externalizing = 0;
233 ta->has_incoming_connection = 0;
234 ta->direct_error_handling = 0;
235 ta->allow_external_sets = 0;
236 ta->has_been_deferred = 0;
237 ta->attribute_changed_block = NULL;
238 ta->attribute_validate_block = NULL;
239 }
240
241 return ah;
242 }
243
244 transform_attribute *Transform::getTA(SecTransformStringOrAttributeRef attrib, bool create_ok)
245 {
246 SecTransformAttributeRef ah = getAH(attrib, create_ok);
247 if (ah)
248 {
249 return ah2ta(ah);
250 }
251 else
252 {
253 return NULL;
254 }
255 }
256
257
258
259 void Transform::TAGetAll(transform_attribute **attributes) {
260 CFSetGetValues(mAttributes, (const void**)attributes);
261 CFIndex i, n = CFSetGetCount(mAttributes);
262 for(i = 0; i < n; ++i) {
263 attributes[i] = ah2ta(attributes[i]);
264 }
265 }
266
267
268
269 bool Transform::HasNoOutboundConnections()
270 {
271 // make an array big enough to hold all of the attributes
272 CFIndex numAttributes = CFSetGetCount(mAttributes);
273 transform_attribute **attributes = (transform_attribute**)malloc(numAttributes*sizeof(transform_attribute*));
274
275 if (attributes == NULL) {
276 // No more memory, we assume it's orphaned
277 return true;
278 }
279
280 TAGetAll(attributes);
281
282 // check all of the attributes
283 CFIndex i;
284 for (i = 0; i < numAttributes; ++i)
285 {
286 if (attributes[i]->connections && CFArrayGetCount(attributes[i]->connections) != 0)
287 {
288 free(attributes);
289 return false;
290 }
291 }
292
293 free(attributes);
294
295 return true;
296 }
297
298
299
300 bool Transform::HasNoInboundConnections()
301 {
302 // make an array big enough to hold all of the attributes
303 CFIndex numAttributes = CFSetGetCount(mAttributes);
304 transform_attribute **attributes = (transform_attribute**)malloc(numAttributes*sizeof(transform_attribute*));
305
306 if (attributes == NULL) {
307 // No more memory, we assume it's orphaned
308 return true;
309 }
310
311 TAGetAll(attributes);
312
313 // check all of the attributes
314 CFIndex i;
315 for (i = 0; i < numAttributes; ++i)
316 {
317 if (attributes[i]->has_incoming_connection)
318 {
319 free(attributes);
320 return false;
321 }
322 }
323
324 free(attributes);
325
326 return true;
327 }
328
329
330
331 CFIndex Transform::GetAttributeCount()
332 {
333 return CFSetGetCount(mAttributes);
334 }
335
336 Transform::Transform(CFStringRef transformType, CFStringRef CFobjectType) :
337 CoreFoundationObject(CFobjectType),
338 mIsActive(false),
339 mIsFinalizing(false),
340 mAlwaysSelfNotify(false),
341 mGroup(NULL),
342 mAbortError(NULL),
343 mTypeName(CFStringCreateCopy(NULL, transformType))
344 {
345 mAttributes = NULL;
346 mPushedback = NULL;
347 mProcessingPushbacks = FALSE;
348
349 if (internalID == _kCFRuntimeNotATypeID) {
350 (void)SecTransformNoData();
351 internalID = CoreFoundationObject::FindObjectType(gInternalCFObjectName);
352 }
353
354 // create a name for the transform
355 char rname[10];
356 unsigned i;
357 for (i = 0; i < sizeof(rname) - 1; ++i)
358 {
359 rname[i] = RandomChar();
360 }
361
362 rname[i] = 0;
363
364 char *tname = const_cast<char*>(CFStringGetCStringPtr(transformType, kCFStringEncodingUTF8));
365 if (!tname) {
366 CFIndex sz = 1+CFStringGetMaximumSizeForEncoding(CFStringGetLength(transformType), kCFStringEncodingUTF8);
367 tname = static_cast<typeof(tname)>(alloca(sz));
368 if (tname) {
369 CFStringGetCString(transformType, tname, sz, kCFStringEncodingUTF8);
370 } else {
371 tname = const_cast<char*>("-");
372 }
373 }
374
375 char* name;
376 asprintf(&name, "%s-%s", rname, tname);
377
378 char *dqName;
379 asprintf(&dqName, "%s-%s", rname, tname);
380
381 char *aqName;
382 asprintf(&aqName, "aq-%s-%s", rname, tname);
383
384 mDispatchQueue = dispatch_queue_create(dqName, NULL);
385 dispatch_queue_set_specific(mDispatchQueue, &dispatchQueueToTransformKey, this, NULL);
386 // mActivationQueue's job in life is to be suspended until just after this transform is made active.
387 // It's primary use is when a value flowing across a connection isn't sure if the target transform is active yet.
388 mActivationQueue = dispatch_queue_create(aqName, NULL);
389 dispatch_set_target_queue(mActivationQueue, mDispatchQueue);
390 dispatch_suspend(mActivationQueue);
391 mActivationPending = dispatch_group_create();
392
393 // set up points for ABORT, DEBUG, INPUT, and OUTPUT
394 AbortAH = getAH(kSecTransformAbortAttributeName, true);
395 transform_attribute *ta = ah2ta(AbortAH);
396 ta->ignore_while_externalizing = 1;
397 CFStringRef attributeName = CFStringCreateWithCStringNoCopy(NULL, name, 0, kCFAllocatorMalloc);
398 SetAttributeNoCallback(kSecTransformTransformName, attributeName);
399 CFReleaseNull(attributeName);
400
401 free(dqName);
402 free(aqName);
403
404 DebugAH = getAH(kSecTransformDebugAttributeName, true);
405 ah2ta(DebugAH)->ignore_while_externalizing = 1;
406
407 ta = getTA(kSecTransformInputAttributeName, true);
408 ta->required = ta->deferred = ta->stream = 1;
409 ta->allow_external_sets = 0;
410 ta->value = NULL;
411 ta->has_been_deferred = 0;
412 ta = getTA(kSecTransformOutputAttributeName, true);
413 ta->requires_outbound_connection = ta->stream = 1;
414 }
415
416 static void run_and_release_finalizer(void *finalizer_block)
417 {
418 ((dispatch_block_t)finalizer_block)();
419 Block_release(finalizer_block);
420 }
421
422 static void set_dispatch_finalizer(dispatch_object_t object, dispatch_block_t finalizer)
423 {
424 finalizer = Block_copy(finalizer);
425 dispatch_set_context(object, finalizer);
426 dispatch_set_finalizer_f(object, run_and_release_finalizer);
427 }
428
429 void Transform::FinalizePhase2()
430 {
431 delete this;
432 }
433
434 void Transform::FinalizeForClang()
435 {
436 CFIndex numAttributes = CFSetGetCount(mAttributes);
437 SecTransformAttributeRef *handles = (const void**)malloc(numAttributes*sizeof(SecTransformAttributeRef));
438
439 if (handles == NULL) {
440 syslog(LOG_ERR, "Unable to allocate SecTransformAttributeRef handles in FinalizeForClang");
441 return;
442 }
443
444 CFSetGetValues(mAttributes, handles);
445
446 for(CFIndex i = 0; i < numAttributes; ++i) {
447 SecTransformAttributeRef ah = handles[i];
448 transform_attribute *ta = ah2ta(ah);
449
450 set_dispatch_finalizer(ta->q, ^{
451 // NOTE: not done until all pending use of the attribute queue has ended AND retain count is zero
452 ta->transform = NULL;
453 CFReleaseSafe(ah);
454 });
455 // If there is a pending pushback the attribute queue will be suspended, and needs a kick before it can be destructed.
456 if (__sync_bool_compare_and_swap(&ta->pushback_state, transform_attribute::pb_value, transform_attribute::pb_discard)) {
457 dispatch_resume(ta->q);
458 }
459 dispatch_release(ta->q);
460 }
461
462 // We might be finalizing a transform as it is being activated, make sure that is complete before we do the rest
463 dispatch_group_notify(mActivationPending, mDispatchQueue, ^{
464 if (mActivationQueue != NULL) {
465 // This transform has not been activated (and does not have a activation pending), so we need to resume to activation queue before we can release it
466 dispatch_resume(mActivationQueue);
467 dispatch_release(mActivationQueue);
468 }
469
470 set_dispatch_finalizer(mDispatchQueue, ^{
471 // NOTE: delayed until all pending work items on the transform's queue are complete, and all of the attribute queues have been finalized, and the retain count is zero
472 FinalizePhase2();
473 });
474 dispatch_release(mDispatchQueue);
475 });
476
477 free(handles);
478 }
479
480 void Transform::Finalize()
481 {
482 // When _all_ transforms in the group have been marked as finalizing we can tear down our own context without anyone else in the group sending us values
483 // (NOTE: moved block into member function as clang hits an internal error and declines to compile)
484 dispatch_block_t continue_finalization = ^{ this->FinalizeForClang(); };
485 dispatch_block_t mark_as_finalizing = ^{ this->mIsFinalizing = true; };
486
487 // Mark the transform as "finalizing" so it knows not to propagate values across connections
488 if (this == dispatch_get_specific(&dispatchQueueToTransformKey)) {
489 mark_as_finalizing();
490 } else {
491 dispatch_sync(mDispatchQueue, mark_as_finalizing);
492 }
493
494 if (mGroup) {
495 (void)transforms_assume(mGroup->mIsFinalizing); // under retain?
496 mGroup->AddAllChildrenFinalizedCallback(mDispatchQueue, continue_finalization);
497 mGroup->ChildStartedFinalization(this);
498 } else {
499 // a "bare" transform (normally itself a group) still needs to be deconstructed
500 dispatch_async(mDispatchQueue, continue_finalization);
501 }
502 }
503
504 Transform::~Transform()
505 {
506 CFReleaseNull(mAttributes);
507 if (mAbortError) {
508 CFReleaseNull(mAbortError);
509 mAbortError = NULL;
510 }
511
512 // See if we can catch anything using us after our death
513 mDispatchQueue = (dispatch_queue_t)0xdeadbeef;
514
515 CFReleaseNull(mTypeName);
516
517 if (NULL != mPushedback)
518 {
519 CFReleaseNull(mPushedback);
520 }
521 dispatch_release(mActivationPending);
522 }
523
524 CFStringRef Transform::GetName() CF_RETURNS_NOT_RETAINED {
525 return (CFStringRef)GetAttribute(kSecTransformTransformName);
526 }
527
528 CFTypeID Transform::GetCFTypeID()
529 {
530 return CoreFoundationObject::FindObjectType(CFSTR("SecTransform"));
531 }
532
533 std::string Transform::DebugDescription()
534 {
535 return CoreFoundationObject::DebugDescription() + "|SecTransform|" + StringFromCFString(this->GetName());
536 }
537
538 CFErrorRef Transform::SendMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type, CFTypeRef value)
539 {
540 SecTransformAttributeRef ah = getAH(key, true);
541 transform_attribute *ta = ah2ta(ah);
542 switch (type)
543 {
544 case kSecTransformMetaAttributeRequired:
545 ta->required = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
546 break;
547
548 case kSecTransformMetaAttributeRequiresOutboundConnection:
549 ta->requires_outbound_connection = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
550 break;
551
552 case kSecTransformMetaAttributeDeferred:
553 ta->deferred = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
554 break;
555
556 case kSecTransformMetaAttributeStream:
557 ta->stream = CFBooleanGetValue((CFBooleanRef)value) ? 1 : 0;
558 break;
559
560 case kSecTransformMetaAttributeHasOutboundConnections:
561 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasOutboundConnections for %@ (or any other attribute)", ah);
562
563 case kSecTransformMetaAttributeHasInboundConnection:
564 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeHasInboundConnection for %@ (or any other attribute)", ah);
565
566 case kSecTransformMetaAttributeCanCycle:
567 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "kSecTransformMetaAttributeCanCycle not yet supported (%@)", ah);
568
569 case kSecTransformMetaAttributeExternalize:
570 ta->ignore_while_externalizing = CFBooleanGetValue((CFBooleanRef)value) ? 0 : 1;
571 break;
572
573 case kSecTransformMetaAttributeValue:
574 return SetAttributeNoCallback(ah, value);
575
576 case kSecTransformMetaAttributeRef:
577 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeRef for %@ (or any other attribute)", ah);
578
579 case kSecTransformMetaAttributeName:
580 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set kSecTransformMetaAttributeName for %@ (or any other attribute)", ah);
581
582 default:
583 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't set unknown meta attribute #%d to %@ on %@", type, value, key);
584 }
585
586 return NULL;
587 }
588
589 CFTypeRef Transform::GetMetaAttribute(SecTransformStringOrAttributeRef key, SecTransformMetaAttributeType type) {
590 SecTransformAttributeRef ah = getAH(key, true);
591 transform_attribute *ta = ah2ta(ah);
592 switch (type) {
593 case kSecTransformMetaAttributeRequired:
594 return (CFTypeRef)(ta->required ? kCFBooleanTrue : kCFBooleanFalse);
595 case kSecTransformMetaAttributeRequiresOutboundConnection:
596 return (CFTypeRef)(ta->requires_outbound_connection ? kCFBooleanTrue : kCFBooleanFalse);
597 case kSecTransformMetaAttributeDeferred:
598 return (CFTypeRef)(ta->deferred ? kCFBooleanTrue : kCFBooleanFalse);
599 case kSecTransformMetaAttributeStream:
600 return (CFTypeRef)(ta->stream ? kCFBooleanTrue : kCFBooleanFalse);
601 case kSecTransformMetaAttributeHasOutboundConnections:
602 return (CFTypeRef)((ta->connections && CFArrayGetCount(ta->connections)) ? kCFBooleanTrue : kCFBooleanFalse);
603 case kSecTransformMetaAttributeHasInboundConnection:
604 return (CFTypeRef)(ta->has_incoming_connection ? kCFBooleanTrue : kCFBooleanFalse);
605 case kSecTransformMetaAttributeCanCycle:
606 return (CFTypeRef)kCFBooleanFalse;
607 case kSecTransformMetaAttributeExternalize:
608 return (CFTypeRef)(ta->ignore_while_externalizing ? kCFBooleanFalse : kCFBooleanTrue);
609 case kSecTransformMetaAttributeRef:
610 return ah;
611 case kSecTransformMetaAttributeValue:
612 return ta->value;
613 case kSecTransformMetaAttributeName:
614 return ta->name;
615 default:
616 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Can't get unknown meta attribute #%d from %@", type, key);
617 }
618
619 return NULL;
620 }
621
622
623
624 CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError)
625 {
626 // pull apart the error
627 CFIndex code = CFErrorGetCode(sourceError);
628 CFStringRef domain = CFErrorGetDomain(sourceError);
629 CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(sourceError);
630 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutableCopy(NULL, 0, oldUserInfo);
631 CFReleaseNull(oldUserInfo);
632
633 // add the new key and value to the dictionary
634 CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject());
635
636 // make a new CFError
637 CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo);
638 CFReleaseNull(userInfo);
639 return newError;
640 }
641
642 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
643 void Transform::AbortJustThisTransform(CFErrorRef abortErr)
644 {
645 (void)transforms_assume(abortErr);
646 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this);
647
648 Boolean wasActive = mIsActive;
649
650 if (OSAtomicCompareAndSwapPtr(NULL, (void *)abortErr, (void**)&mAbortError)) {
651 // send an abort message to the attribute so that it can shut down
652 // note that this bypasses the normal processes. The message sent is a notification
653 // that things aren't working well any more, the transform cannot make any other assumption.
654
655 // mAbortError is released in the destructor which is triggered (in part)
656 // by the dispatch queue finalizer so we don't need a retain/release of
657 // abortErr for the abortAction block, but we do need to retain it
658 // here to match with the release by the destructor.
659 CFRetainSafe(abortErr);
660
661 dispatch_block_t abortAction = ^{
662 // This actually makes the abort happen, it needs to run on the transform's queue while the
663 // transform is executing.
664
665 if (!wasActive) {
666 // When this abort was first processed we were not executing, so
667 // additional transforms may have been added to our group (indeed,
668 // we may not have had a group at all), so we need to let everyone
669 // know about the problem. This will end up letting ourself (and
670 // maybe some others) know an additional time, but the CompareAndSwap
671 // prevents that from being an issue.
672 this->AbortAllTransforms(abortErr);
673 }
674
675 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle = getAH(kSecTransformInputAttributeName, false);
676 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
677 // and even execute if the input queue is suspended pending pushback retries.
678 AttributeChanged(inputAttributeHandle, abortErr);
679 try_pushbacks();
680 };
681
682 if (mIsActive) {
683 // This transform is running, so we use the normal queue (which we are
684 // already executing on)
685 abortAction();
686 } else {
687 // This transform hasn't run yet, do the work on the activation queue
688 // so it happens as soon as the transforms starts executing.
689 dispatch_async(mActivationQueue, abortAction);
690 }
691 } else {
692 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError);
693 }
694 }
695
696 // abort all transforms in the root group & below
697 void Transform::AbortAllTransforms(CFTypeRef err)
698 {
699 Debug("%@ set to %@, aborting\n", AbortAH, err);
700 CFErrorRef error = NULL;
701
702 CFTypeRef replacementErr = NULL;
703
704 if (CFGetTypeID(err) != CFErrorGetTypeID())
705 {
706 CFStringRef thisErrorTypeDescription = CFCopyTypeIDDescription(CFGetTypeID(err));
707 CFStringRef regularErrorTypeDescription = CFCopyTypeIDDescription(CFErrorGetTypeID());
708 replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", thisErrorTypeDescription, err, regularErrorTypeDescription);
709 CFReleaseNull(thisErrorTypeDescription);
710 CFReleaseNull(regularErrorTypeDescription);
711 }
712
713 error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err);
714
715 if (replacementErr)
716 {
717 CFReleaseNull(replacementErr);
718 }
719
720 GroupTransform *root = GetRootGroup();
721 if (root)
722 {
723 // tell everyone in the (root) group to "AbortJustThisTransform"
724 dispatch_group_t all_aborted = dispatch_group_create();
725 root->ForAllNodesAsync(false, all_aborted, ^(Transform* t){
726 t->AbortJustThisTransform(error);
727 });
728 dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
729 CFReleaseSafe(error);
730 dispatch_release(all_aborted);
731 });
732 }
733 else
734 {
735 // We are everyone so we AbortJustThisTransform "directly"
736 // NOTE: this can only happen prior to execution (execution always happens in a group)
737 (void)transforms_assume_zero(mIsActive);
738 this->AbortJustThisTransform(error);
739 }
740 }
741
742
743
744 CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey)
745 {
746 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
747
748 // find this transform in the backlinks for the destination
749 CFIndex i;
750
751 // now remove the link in the transform dictionary
752 transform_attribute *src = getTA(myKey, true);
753 SecTransformAttributeRef dst = destinationTransform->getAH(hisKey);
754
755 if (src->connections == NULL)
756 {
757 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination.");
758 }
759
760 CFIndex numConnections = CFArrayGetCount(src->connections);
761 for (i = 0; i < numConnections; ++i)
762 {
763 if (CFArrayGetValueAtIndex(src->connections, i) == dst)
764 {
765 CFArrayRemoveValueAtIndex(src->connections, i);
766 numConnections = CFArrayGetCount(src->connections);
767 }
768
769 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
770 transform_attribute* dstTA = ah2ta(dst);
771 dstTA->has_incoming_connection = false;
772 }
773
774 if (HasNoInboundConnections() && HasNoOutboundConnections())
775 {
776 // we have been orphaned, just remove us
777 mGroup->RemoveMemberFromGroup(GetCFObject());
778 mGroup = NULL;
779 }
780
781 return NULL;
782 }
783
784
785
786 CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr)
787 {
788 if (group == NULL)
789 {
790 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)");
791 return err;
792 }
793
794 GroupTransform *newSourceGroup = mGroup;
795 GroupTransform *newDestinationGroup = destinationTransform->mGroup;
796
797 if (mGroup == NULL || mGroup == this)
798 {
799 newSourceGroup = group;
800 }
801
802 if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform)
803 {
804 newDestinationGroup = group;
805 }
806
807 if (newSourceGroup != newDestinationGroup && mGroup)
808 {
809 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup->GetName(), destinationTransform->GetName(), newDestinationGroup->GetName());
810 return err;
811 }
812
813 if (!validConnectionPoint(srcAttr)) {
814 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName());
815 return err;
816 }
817 if (!destinationTransform->validConnectionPoint(destAttr)) {
818 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName());
819 return err;
820 }
821
822 mGroup = newSourceGroup;
823 destinationTransform->mGroup = newDestinationGroup;
824
825 // NOTE: this fails on OOM
826 group->AddMemberToGroup(this->GetCFObject());
827 group->AddMemberToGroup(destinationTransform->GetCFObject());
828
829 transform_attribute *src = this->getTA(srcAttr, true);
830 SecTransformAttributeRef dst = destinationTransform->getAH(destAttr);
831
832 if (!src->connections)
833 {
834 src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
835 }
836 CFArrayAppendValue(src->connections, dst);
837
838 ah2ta(dst)->has_incoming_connection = 1;
839
840 return NULL;
841 }
842
843
844 bool Transform::validConnectionPoint(CFStringRef attributeName)
845 {
846 return true;
847 }
848
849 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
850 // SetAttribute eventually calls SetAttributeNoCallback
851 CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value)
852 {
853 SecTransformAttributeRef ah = getAH(key, true);
854 if (!ah)
855 {
856 abort();
857 }
858 transform_attribute *ta = ah2ta(ah);
859
860 if (ah == AbortAH && value && (mIsActive || !ta->deferred))
861 {
862 AbortAllTransforms(value);
863 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started");
864 }
865
866 bool do_propagate = true;
867
868 if (!ta->has_been_deferred)
869 {
870 bool doNotRetain = false;
871
872 if (value)
873 {
874 CFStringRef name = ta->name;
875 if (CFGetTypeID(value) == CFReadStreamGetTypeID())
876 {
877 CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name);
878 value = src;
879 do_propagate = false;
880 ta->has_been_deferred = 1;
881 doNotRetain = true;
882 }
883 else if (ta->deferred && !mIsActive)
884 {
885 if (ta->deferred)
886 {
887 Debug("%@ deferred value=%p\n", ah, value);
888 }
889
890 CFTypeRef src = SingleShotSource::Make(value, this, name);
891 ta->has_been_deferred = 1;
892
893 // the old value will be release when Transform::Do terminates
894
895 value = src;
896 do_propagate = false;
897 doNotRetain = true;
898 }
899 else
900 {
901 ta->has_been_deferred = 0;
902 }
903 }
904
905 if (ta->value != value) {
906 if (value && !doNotRetain) {
907 CFRetainSafe(value);
908 }
909 if (ta->value) {
910 CFReleaseNull(ta->value);
911 }
912 }
913
914 ta->value = value;
915 }
916
917 // propagate the changes out to all connections
918 if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing))
919 {
920 Debug("Propagating from %@ to %@\n", ah, ta->connections);
921 CFIndex i, numConnections = CFArrayGetCount(ta->connections);
922 for(i = 0; i < numConnections; ++i) {
923 SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void *>(CFArrayGetValueAtIndex(ta->connections, i)));
924 Transform *tt = ah2ta(ah)->transform;
925 if (NULL != tt)
926 {
927 if (tt->mIsActive)
928 {
929 tt->SetAttribute(ah, value);
930 }
931 else
932 {
933 dispatch_block_t setAttribute = ^{
934 tt->SetAttribute(ah, value);
935 };
936 // Here the target queue might not be activated yet, we can't
937 // look directly at the other transform's ActivationQueue as
938 // it might activate (or Finalize!) as we look, so just ask
939 // the other transform to deal with it.
940 dispatch_async(ah2ta(ah)->q, ^(void) {
941 // This time we are on the right queue to know this is the real deal
942 if (tt->mIsActive) {
943 setAttribute();
944 } else {
945 dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute);
946 }
947 });
948 }
949 }
950 }
951 }
952
953 return NULL;
954 }
955
956 // external sets normally fail if the transform is running
957 CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value)
958 {
959 if (!mIsActive)
960 {
961 return this->SetAttribute(key, value);
962 }
963 else
964 {
965 SecTransformAttributeRef ah = getAH(key, false);
966 if (ah != NULL && ah2ta(ah)->allow_external_sets)
967 {
968 return this->SetAttribute(static_cast<CFTypeRef>(ah), value);
969 }
970 else
971 {
972 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName());
973 }
974 }
975 }
976
977
978 // queue up the setting of the key and value
979 CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value)
980 {
981 if (mAbortError)
982 {
983 CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError);
984 CFAutorelease(result);
985 return result;
986 }
987
988 // queue up the setting of the key and value
989 SecTransformAttributeRef ah;
990 if (CFGetTypeID(key) == transform_attribute::cftype)
991 {
992 ah = key;
993 }
994 else if (CFGetTypeID(key) == CFStringGetTypeID())
995 {
996 ah = getAH(static_cast<CFStringRef>(key));
997 if (!ah)
998 {
999 CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName());
1000 CFAutorelease(result);
1001 return result;
1002 }
1003 }
1004 else
1005 {
1006 CFErrorRef result = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key);
1007 CFAutorelease(result);
1008 return result;
1009 }
1010
1011 // Do this after the error check above so we don't leak
1012 CFRetainSafe(value); // if we use dispatch_async we need to own the value (the matching release is in the set block)
1013
1014 transform_attribute *ta = ah2ta(ah);
1015
1016 dispatch_block_t set = ^{
1017 Do(ah, value);
1018 dispatch_semaphore_signal(ta->semaphore);
1019 CFReleaseSafe(value);
1020 };
1021
1022
1023 // when the transform is active, set attributes asynchronously. Otherwise, we are doing
1024 // initialization and must wait for the operation to complete.
1025 if (mIsActive)
1026 {
1027 dispatch_async(ta->q, set);
1028 }
1029 else
1030 {
1031 dispatch_sync(ta->q, set);
1032 }
1033 if (dispatch_semaphore_wait(ta->semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC))) {
1034 Debug("Send from %@ to %@ is still waiting\n", GetName(), ah);
1035 dispatch_semaphore_wait(ta->semaphore, DISPATCH_TIME_FOREVER);
1036 }
1037
1038 // Return the best available status (which will be NULL if we haven't aborted, or stated an
1039 // intent to abort when execution starts)
1040 //
1041 // The value of the ABORT attribute can differ from mAbortError, first if a transform is aborted
1042 // prior to running the general abort mechanic is deferred until execution. Second during
1043 // execution the abort logic avoids most of the normal processing. Third, and most importantly
1044 // during an abort the exact error that gets generated will differ from the value sent to ABORT
1045 // (for example if a non-CFError was sent...plus even if it was a CFError we annotate that error).
1046
1047 return mAbortError;
1048 }
1049
1050 CFErrorRef Transform::SendAttribute(SecTransformStringOrAttributeRef key, CFTypeRef value)
1051 {
1052 return SetAttributeNoCallback(key, value);
1053 }
1054
1055
1056
1057 CFTypeRef Transform::GetAttribute(SecTransformStringOrAttributeRef key)
1058 {
1059 struct transform_attribute *ta = getTA(key, false);
1060 if (ta == NULL || ta->value == NULL) {
1061 return NULL;
1062 }
1063
1064 if (CFGetTypeID(ta->value) == internalID)
1065 {
1066 // this is one of our internal objects, so get the value from it
1067 Source* source = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
1068 return source->GetValue();
1069 }
1070 else
1071 {
1072 return ta->value;
1073 }
1074 }
1075
1076 CFErrorRef Transform::Pushback(SecTransformAttributeRef ah, CFTypeRef value)
1077 {
1078 CFErrorRef result = NULL;
1079 transform_attribute *ta = ah2ta(ah);
1080 if (!(ta->pushback_state == transform_attribute::pb_empty || ta->pushback_state == transform_attribute::pb_repush))
1081 {
1082 CFErrorRef error = fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidOperation, CFSTR("Can not pushback new value until old value has been processed"));
1083 SetAttribute(kSecTransformAbortAttributeName, error);
1084 return error;
1085 }
1086 if (value == NULL && ta->pushback_value == NULL && ta->pushback_state == transform_attribute::pb_repush)
1087 {
1088 ta->pushback_state = transform_attribute::pb_presented_once;
1089 } else
1090 {
1091 ta->pushback_state = transform_attribute::pb_value;
1092 }
1093 if (value)
1094 {
1095 CFRetainSafe(value);
1096 }
1097 ta->pushback_value = value;
1098 dispatch_suspend(ta->q);
1099 if (!mPushedback)
1100 {
1101 mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1102 }
1103 CFArrayAppendValue(mPushedback, ah);
1104 return result;
1105 }
1106
1107 void Transform::try_pushbacks() {
1108 if (!mPushedback || !CFArrayGetCount(mPushedback)) {
1109 mProcessingPushbacks = FALSE;
1110 return;
1111 }
1112
1113 CFArrayRef pb = (CFArrayRef)mPushedback;
1114 mPushedback = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1115 CFIndex i, n = CFArrayGetCount(pb);
1116 int succeeded = 0;
1117 for(i = 0; i < n; ++i)
1118 {
1119 SecTransformAttributeRef ah = CFArrayGetValueAtIndex(pb, i);
1120 transform_attribute *ta = ah2ta(ah);
1121 ta->pushback_state = transform_attribute::pb_repush;
1122 CFTypeRef v = ta->pushback_value;
1123 ta->pushback_value = NULL;
1124 Do(ah, v);
1125 if (v)
1126 {
1127 CFReleaseNull(v);
1128 }
1129 if (ta->pushback_state == transform_attribute::pb_repush) {
1130 ta->pushback_state = transform_attribute::pb_empty;
1131 succeeded++;
1132 }
1133 // NOTE: a successful repush needs the queue unsuspended so it can run.
1134 // A failed repush has suspended the queue an additional time, so we
1135 // still need to resume it.
1136 dispatch_resume(ta->q);
1137 }
1138
1139 CFReleaseNull(pb);
1140
1141 if (succeeded && CFArrayGetCount(mPushedback)) {
1142 // some attribute changed while we proceeded the last batch of pushbacks, so any "new" pushbacks are eligible to run again.
1143 // In theory the ones that were pushed after the last success don't need to be re-run but that isn't a big deal.
1144 dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
1145 } else {
1146 mProcessingPushbacks = FALSE;
1147 }
1148 }
1149
1150 void Transform::Debug(const char *cfmt, ...) {
1151 CFTypeRef d = ah2ta(DebugAH)->value;
1152 if (d) {
1153 CFWriteStreamRef out = NULL;
1154 if (CFGetTypeID(d) == CFWriteStreamGetTypeID()) {
1155 out = (CFWriteStreamRef)d;
1156 } else {
1157 static dispatch_once_t once;
1158 static CFWriteStreamRef StdErrWriteStream;
1159 dispatch_once(&once, ^{
1160 CFURLRef p = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle, FALSE);
1161 StdErrWriteStream = CFWriteStreamCreateWithFile(NULL, p);
1162 CFWriteStreamOpen(StdErrWriteStream);
1163 CFReleaseNull(p);
1164 });
1165 out = StdErrWriteStream;
1166 }
1167
1168 va_list ap;
1169 va_start(ap, cfmt);
1170
1171 CFStringRef fmt = CFStringCreateWithCString(NULL, cfmt, kCFStringEncodingUTF8);
1172 CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, fmt, ap);
1173 CFReleaseNull(fmt);
1174 va_end(ap);
1175
1176
1177 CFIndex sz = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8);
1178 sz += 1;
1179 CFIndex used = 0;
1180 unsigned char *buf;
1181 bool needs_free = true;
1182 buf = (unsigned char*)malloc(sz);
1183 if (buf) {
1184 CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)), kCFStringEncodingUTF8, '?', FALSE, buf, sz, &used);
1185 } else {
1186 buf = (unsigned char *)"malloc failure during Transform::Debug\n";
1187 needs_free = false;
1188 }
1189
1190 static dispatch_once_t once;
1191 static dispatch_queue_t print_q;
1192 dispatch_once(&once, ^{
1193 print_q = dispatch_queue_create("com.apple.security.debug.print_queue", 0);
1194 dispatch_set_target_queue((dispatch_object_t)print_q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
1195 });
1196
1197 dispatch_async(print_q, ^{
1198 CFWriteStreamWrite(out, buf, used);
1199 if (needs_free) {
1200 free(buf);
1201 }
1202 });
1203
1204 CFReleaseNull(str);
1205 }
1206 }
1207
1208 void Transform::Do(SecTransformAttributeRef ah, CFTypeRef value)
1209 {
1210 transform_attribute *ta = ah2ta(ah);
1211 if (ta->pushback_state == transform_attribute::pb_discard)
1212 {
1213 return;
1214 }
1215 (void)transforms_assume(dispatch_get_current_queue() == ((ta->pushback_state == transform_attribute::pb_repush) ? mDispatchQueue : ta->q));
1216
1217 if (mIsFinalizing)
1218 {
1219 Debug("Ignoring value %p sent to %@ (on queue %s) during finalization", value, ah, dispatch_queue_get_label(dispatch_get_current_queue()));
1220 return;
1221 }
1222
1223 SetAttributeNoCallback(ah, value);
1224 // While an abort is in progress things can get into bad
1225 // states if we allow normal processing so we throw anything
1226 // on the floor except CFErrorRef or NULL vales sent to
1227 // ABORT or INPUT (we need to process them to let the
1228 // transform shut down correctly)
1229 if (mAbortError && (!(ah == this->AbortAH || ah == getTA(CFSTR("INPUT"), true)) && (value == NULL || CFGetTypeID(value) != CFErrorGetTypeID())))
1230 {
1231 if (value) {
1232 Debug("Ignoring value (%@) sent to %@ during abort\n", value, ah);
1233 } else {
1234 Debug("Ignoring NULL sent to %@ during abort\n", ah);
1235 }
1236 return;
1237 }
1238
1239 if (mIsActive || (mAlwaysSelfNotify && !ta->deferred))
1240 {
1241 Debug("AttributeChanged: %@ (%s) = %@\n", ah, mIsActive ? "is executing" : "self notify set", value ? value : (CFTypeRef)CFSTR("(NULL)"));
1242 AttributeChanged(ah, value);
1243 }
1244
1245 if (mPushedback && CFArrayGetCount(mPushedback) && !mProcessingPushbacks)
1246 {
1247 Debug("will process pushbacks (%@) later\n", mPushedback);
1248 mProcessingPushbacks = TRUE;
1249 dispatch_async(mDispatchQueue, ^{ try_pushbacks(); });
1250 }
1251
1252 return;
1253 }
1254
1255
1256 void Transform::AttributeChanged(CFStringRef name, CFTypeRef value)
1257 {
1258 }
1259
1260 void Transform::AttributeChanged(SecTransformAttributeRef ah, CFTypeRef value)
1261 {
1262 AttributeChanged(ah2ta(ah)->name, value);
1263 }
1264
1265 CFArrayRef Transform::GetAllAH() {
1266 CFIndex cnt = CFSetGetCount(mAttributes);
1267 const void **values = (const void **)alloca(sizeof(void*)*cnt);
1268 CFSetGetValues(mAttributes, values);
1269 return CFArrayCreate(NULL, values, cnt, &kCFTypeArrayCallBacks);
1270 }
1271
1272 CFTypeRef Transform::Execute(dispatch_queue_t deliveryQueue, SecMessageBlock deliveryBlock, CFErrorRef* errorRef)
1273 {
1274 if (!mGroup)
1275 {
1276 CFTypeRef g = GroupTransform::Make();
1277 mGroup = (GroupTransform*)CoreFoundationHolder::ObjectFromCFType(g);
1278 mGroup->AddMemberToGroup(this->GetCFObject());
1279 SecMessageBlock smb = ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
1280 {
1281 deliveryBlock(message, error, isFinal);
1282 if (isFinal)
1283 {
1284 dispatch_async(this->mDispatchQueue, ^{
1285 CFReleaseSafe(g);
1286 });
1287 }
1288 };
1289
1290 CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef);
1291
1292 if (!deliveryBlock)
1293 {
1294 CFReleaseNull(g);
1295 }
1296
1297 return ret;
1298 }
1299
1300 if (mIsActive)
1301 {
1302 if (errorRef)
1303 {
1304 *errorRef = CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform has already executed, it may not be executed again.", GetName());
1305 }
1306
1307 return NULL;
1308 }
1309
1310 // Do a retain on our parent since we are using it
1311 GroupTransform *rootGroup = GetRootGroup();
1312 CFRetainSafe(rootGroup->GetCFObject());
1313
1314 CFTypeRef result = NULL;
1315
1316 CFTypeRef monitorRef = BlockMonitor::Make(deliveryQueue, deliveryBlock);
1317
1318 __block CFStringRef outputAttached = NULL;
1319
1320 dispatch_queue_t p2 = dispatch_queue_create("activate phase2", NULL);
1321 dispatch_queue_t p3 = dispatch_queue_create("activate phase3", NULL);
1322 dispatch_suspend(p2);
1323 dispatch_suspend(p3);
1324 // walk the transform, doing phase1 activating as we go, and queueing phase2 and phase3 work
1325 CFErrorRef temp = TraverseTransform(NULL, ^(Transform *t){
1326 return t->ExecuteOperation(outputAttached, (SecMonitorRef)monitorRef, p2, p3);
1327 });
1328 // ExecuteOperation is not called for the outer group, so we need to manually set mISActive for it.
1329 rootGroup->mIsActive = true;
1330 rootGroup->StartingExecutionInGroup();
1331 dispatch_resume(p2);
1332 dispatch_sync(p2, ^{ dispatch_resume(p3); });
1333 dispatch_sync(p3, ^{ dispatch_release(p2); });
1334 dispatch_release(p3);
1335
1336 if (errorRef)
1337 {
1338 *errorRef = temp;
1339 }
1340 if (temp) {
1341 // It is safe to keep the monitors attached, because it is invalid to try to execute again, BUT
1342 // we do need to release the reference to the group that the monitor would normally release
1343 // when it processes the final message.
1344 CFReleaseSafe(rootGroup->GetCFObject());
1345 CFReleaseNull(monitorRef);
1346 rootGroup->StartedExecutionInGroup(false);
1347 return NULL;
1348 }
1349
1350 dispatch_group_t initialized = dispatch_group_create();
1351 rootGroup->ForAllNodesAsync(true, initialized, ^(Transform*t) {
1352 t->Initialize();
1353 });
1354
1355 dispatch_group_notify(initialized, rootGroup->mDispatchQueue, ^{
1356 dispatch_release(initialized);
1357 dispatch_group_t activated = dispatch_group_create();
1358 dispatch_group_enter(activated);
1359 dispatch_async(rootGroup->mDispatchQueue, ^{
1360 rootGroup->ForAllNodesAsync(true, activated, ^(Transform*t) {
1361 t->ActivateInputs();
1362 });
1363 dispatch_group_leave(activated);
1364 });
1365 dispatch_group_notify(activated, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
1366 dispatch_release(activated);
1367 // once we have been activated (but not before!), the monitor belongs to the group, and we can drop our claim
1368 CFReleaseSafe(monitorRef);
1369 rootGroup->StartedExecutionInGroup(true);
1370 });
1371 });
1372
1373 return result;
1374 }
1375
1376
1377 void Transform::Initialize()
1378 {
1379 }
1380
1381 static void ActivateInputs_set(const void *v, void *unused) {
1382 transform_attribute *ta = static_cast<transform_attribute *>(ah2ta(const_cast<void *>(v)));
1383 if (ta->value && internalID == CFGetTypeID(ta->value)) {
1384 Source* s = (Source*) CoreFoundationHolder::ObjectFromCFType(ta->value);
1385 s->Activate();
1386 }
1387 }
1388
1389 void Transform::ActivateInputs()
1390 {
1391 (void)transforms_assume_zero(mIsActive && this != dispatch_get_specific(&dispatchQueueToTransformKey));
1392
1393 // now run all of the forward links
1394 if (!mIsFinalizing) {
1395 CFSetApplyFunction(mAttributes, ActivateInputs_set, NULL);
1396 }
1397 }
1398
1399 CFErrorRef Transform::ForAllNodes(bool parallel, bool includeOwningGroup, Transform::TransformOperation op)
1400 {
1401 GroupTransform *g = GetRootGroup();
1402 if (g) {
1403 return g->ForAllNodes(parallel, includeOwningGroup, op);
1404 } else {
1405 return op(this);
1406 }
1407 }
1408
1409 CFErrorRef Transform::TraverseTransform(CFMutableSetRef visited, TransformOperation t)
1410 {
1411 return ForAllNodes(true, true, t);
1412 }
1413
1414 CFErrorRef Transform::ExecuteOperation(CFStringRef &outputAttached, SecMonitorRef output, dispatch_queue_t phase2, dispatch_queue_t phase3)
1415 {
1416 if (!mGroup) {
1417 // top level groups are special, and don't go through this path.
1418 return NULL;
1419 }
1420
1421 if (!TransformCanExecute())
1422 {
1423 // oops, this transform isn't ready to go
1424 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "The transform %@ was not ready for execution.", GetName());
1425 }
1426
1427 // check to see if required attributes are connected or set
1428 CFIndex i, numAttributes = CFSetGetCount(mAttributes);
1429 transform_attribute **attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
1430 TAGetAll(attributes);
1431 CFMutableArrayRef still_need = NULL;
1432 for(i = 0; i < numAttributes; ++i) {
1433 transform_attribute *ta = attributes[i];
1434 if (ta->required && ta->value == NULL && !ta->has_incoming_connection) {
1435 if (!still_need) {
1436 still_need = CFArrayCreateMutable(NULL, i, &kCFTypeArrayCallBacks);
1437 }
1438 CFArrayAppendValue(still_need, ta->name);
1439 }
1440 }
1441 if (still_need) {
1442 CFStringRef elist = CFStringCreateByCombiningStrings(NULL, still_need, CFSTR(", "));
1443 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorMissingParameter, "Can not execute %@, missing required attributes: %@", GetName(), elist);
1444 CFReleaseNull(elist);
1445 CFReleaseNull(still_need);
1446 return err;
1447 }
1448
1449 // see if we can attach our output here (note mAttributes may have changed)
1450 numAttributes = CFSetGetCount(mAttributes);
1451 attributes = (transform_attribute **)alloca(numAttributes * sizeof(transform_attribute *));
1452 TAGetAll(attributes);
1453 for (i = 0; i < numAttributes; ++i)
1454 {
1455 transform_attribute *ta = attributes[i];
1456 CFIndex arraySize = ta->connections ? CFArrayGetCount(ta->connections) : 0;
1457 if (arraySize == 0 && ta->requires_outbound_connection)
1458 {
1459 if (CFStringCompare(ta->name, kSecTransformOutputAttributeName, 0) == kCFCompareEqualTo) {
1460 // this is a place where we can hook up our output -- maybe
1461 if (outputAttached)
1462 {
1463 // oops, we've already done that.
1464 return CreateSecTransformErrorRef(kSecTransformErrorMoreThanOneOutput, "Both %@ and %@ have loose outputs, attach one to something", outputAttached, ta->transform->GetName());
1465 }
1466 // Delay the connect until after ForAllNodes returns
1467 dispatch_async(phase2, ^{
1468 SecTransformConnectTransformsInternal(mGroup->GetCFObject(),
1469 GetCFObject(), kSecTransformOutputAttributeName,
1470 output, kSecTransformInputAttributeName);
1471 });
1472 outputAttached = ta->transform->GetName();
1473
1474 // activate the attached monitor
1475 Monitor* m = (Monitor*) CoreFoundationHolder::ObjectFromCFType(output);
1476 m->mIsActive = true;
1477
1478 // add the monitor to the output so that it doesn't get activated twice
1479 } else {
1480 return CreateSecTransformErrorRef(kSecTransformErrorNotInitializedCorrectly, "Attribute %@ (in %@) requires an outbound connection and doesn't have one", ta->name, GetName());
1481 }
1482
1483 break;
1484 }
1485 }
1486
1487 // Delay activation until after the Monitor is connected
1488 dispatch_async(phase3, ^{
1489 phase3Activation();
1490 });
1491
1492 return NULL;
1493 }
1494
1495
1496
1497 void Transform::DoPhase3Activation()
1498 {
1499 this->mIsActive = true;
1500 // execution has now truly started ("mIsActive is true")
1501 CFErrorRef initError = TransformStartingExecution();
1502 if (initError)
1503 {
1504 // Oops, now execution is about to grind to a halt
1505 this->SendAttribute(AbortAH, initError);
1506 }
1507
1508 dispatch_resume(this->mActivationQueue);
1509 dispatch_group_async(this->mActivationPending, this->mActivationQueue, ^{
1510 dispatch_release(this->mActivationQueue);
1511 this->mActivationQueue = NULL;
1512 });
1513 }
1514
1515
1516
1517 // This would be best expressed as a block, but we seem to run into compiler errors
1518 void Transform::phase3Activation()
1519 {
1520 dispatch_async(this->mDispatchQueue, ^
1521 {
1522 DoPhase3Activation();
1523 });
1524 }
1525
1526
1527 Boolean Transform::TransformCanExecute()
1528 {
1529 return true;
1530 }
1531
1532
1533
1534 CFErrorRef Transform::TransformStartingExecution()
1535 {
1536 return NULL;
1537 }
1538
1539
1540
1541 bool Transform::IsExternalizable()
1542 {
1543 return true;
1544 }
1545
1546 static const void *CFTypeOrNULLRetain(CFAllocatorRef allocator, const void *value) {
1547 return CFRetainSafe(value);
1548 }
1549
1550 static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) {
1551 CFReleaseNull(value);
1552 }
1553
1554 static CFStringRef CFTypeOrNULLCopyDescription (const void *value) {
1555 if (value != NULL) {
1556 return CFCopyDescription(value);
1557 } else {
1558 return CFSTR("NULL");
1559 }
1560 }
1561
1562 static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) {
1563 if (value1 == NULL && value2 == NULL) {
1564 return TRUE;
1565 } else {
1566 if (value1 == NULL || value2 == NULL) {
1567 return FALSE;
1568 } else {
1569 return CFEqual(value1, value2);
1570 }
1571 }
1572 }
1573
1574 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1575 CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key)
1576 {
1577 SecTransformMetaAttributeType types[] =
1578 {
1579 kSecTransformMetaAttributeRequired,
1580 kSecTransformMetaAttributeRequiresOutboundConnection,
1581 kSecTransformMetaAttributeDeferred,
1582 kSecTransformMetaAttributeStream,
1583 kSecTransformMetaAttributeCanCycle,
1584 kSecTransformMetaAttributeValue
1585 };
1586
1587 CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType);
1588 CFTypeRef values[cnt];
1589 CFNumberRef keys[cnt];
1590 key = getAH(key);
1591
1592 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1593 // default may change in the future and we definitely want to restore the default values at
1594 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1595 // default we want to load all old transforms with stream=1, the simplest way to do that is
1596 // to store all values, not just non-default values)
1597 for(i = 0; i < cnt; ++i)
1598 {
1599 values[i] = GetMetaAttribute(key, types[i]);
1600 int tmp = (int)types[i];
1601 keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
1602 }
1603
1604 static CFDictionaryValueCallBacks CFTypeOrNULL;
1605 static dispatch_once_t once;
1606 dispatch_block_t b =
1607 ^{
1608 CFTypeOrNULL.version = 0;
1609 CFTypeOrNULL.retain = CFTypeOrNULLRetain;
1610 CFTypeOrNULL.release = CFTypeOrNULLRelease;
1611 CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription;
1612 CFTypeOrNULL.equal = CFTypeOrNULLEqual;
1613 };
1614 dispatch_once(&once, b);
1615
1616 CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL);
1617
1618 for(i = 0; i < cnt; ++i)
1619 {
1620 CFReleaseNull(keys[i]);
1621 }
1622
1623 return ret;
1624 }
1625
1626 // return everything that doesn't have ignore_while_externalizing set
1627 CFDictionaryRef Transform::CopyState()
1628 {
1629 CFIndex i, j, cnt = CFSetGetCount(mAttributes);
1630 transform_attribute **attrs = (transform_attribute**)malloc(cnt*sizeof(transform_attribute*));
1631 CFStringRef *names = (CFStringRef*)malloc(cnt*sizeof(CFStringRef));
1632 CFDictionaryRef *values = (CFDictionaryRef*)malloc(sizeof(CFDictionaryRef) * cnt);
1633
1634 if (attrs == NULL || names == NULL || values == NULL) {
1635 free(attrs);
1636 free(names);
1637 free(values);
1638 return NULL;
1639 }
1640
1641 TAGetAll(attrs);
1642 for(i = j = 0; i < cnt; ++i)
1643 {
1644 transform_attribute *ta = attrs[i];
1645 if (!ta->ignore_while_externalizing)
1646 {
1647 names[j] = ta->name;
1648 values[j++] = GetAHDictForSaveState(ta->name);
1649 }
1650 }
1651
1652 CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1653
1654 free(names);
1655
1656 for(i = j = 0; i < cnt; ++i)
1657 {
1658 transform_attribute *ta = attrs[i];
1659 if (!ta->ignore_while_externalizing)
1660 {
1661 CFReleaseNull(values[j++]);
1662 }
1663 }
1664
1665 free(attrs);
1666 free(values);
1667 return result;
1668 }
1669
1670
1671
1672 void Transform::RestoreState(CFDictionaryRef state)
1673 {
1674 CFIndex i, cnt = CFDictionaryGetCount(state);
1675 const void
1676 **keys = (const void **)alloca(sizeof(void*)*cnt),
1677 **values = (const void **)alloca(sizeof(void*)*cnt);
1678
1679 CFDictionaryGetKeysAndValues(state, keys, values);
1680
1681 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1682 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1683 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1684
1685 for(i = 0; i < cnt; i++)
1686 {
1687 SecTransformAttributeRef ah = getAH(keys[i]);
1688
1689 if (NULL == ah)
1690 {
1691 continue;
1692 }
1693
1694 CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]);
1695 const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt);
1696 CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values);
1697
1698 int t;
1699 for(j = 0; j < meta_cnt; ++j)
1700 {
1701 CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t);
1702 if (t == kSecTransformMetaAttributeValue)
1703 {
1704 if (meta_values[j]) {
1705 // SendMetaAttribute doesn't activate the callbacks
1706 SetAttribute(ah, meta_values[j]);
1707 }
1708 }
1709 else
1710 {
1711 CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]);
1712 CFReleaseNull(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1713 }
1714 }
1715
1716 CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue);
1717 CFReleaseNull(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1718 }
1719 }
1720
1721 GroupTransform* Transform::GetRootGroup()
1722 {
1723 GroupTransform *g = mGroup;
1724 if (g) {
1725 while (g->mGroup) {
1726 g = g->mGroup;
1727 }
1728 } else {
1729 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1730 return (GroupTransform *)this;
1731 }
1732 }
1733 return g;
1734 }
1735
1736 CFDictionaryRef Transform::GetCustomExternalData()
1737 {
1738 return NULL;
1739 }
1740
1741 void Transform::SetCustomExternalData(CFDictionaryRef customData)
1742 {
1743 return;
1744 }
1745
1746 CFDictionaryRef Transform::Externalize(CFErrorRef* error)
1747 {
1748 if (mIsActive)
1749 {
1750 return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1751 }
1752
1753 // make arrays to hold the transforms and the connections
1754 __block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1755 __block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1756 GroupTransform *root = GetRootGroup();
1757
1758 CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) {
1759 if (t != root) {
1760 return t->ProcessExternalize(transforms, connections);
1761 }
1762 return (CFErrorRef)NULL;
1763 });
1764
1765 if (NULL != err)
1766 {
1767 // Really? This just seems like a bad idea
1768 if (NULL != error)
1769 {
1770 CFRetainSafe(err);
1771 *error = err;
1772 }
1773 return NULL;
1774
1775 }
1776
1777 // make a dictionary to hold the output
1778 CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1779 CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms);
1780 CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections);
1781
1782 // clean up
1783 CFReleaseNull(connections);
1784 CFReleaseNull(transforms);
1785
1786 return output;
1787 }
1788
1789 CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections)
1790 {
1791 if (!IsExternalizable()) {
1792 return NULL;
1793 }
1794
1795 CFDictionaryRef state = CopyState();
1796 if (state && CFGetTypeID(state) == CFErrorGetTypeID()) {
1797 return (CFErrorRef)state;
1798 }
1799
1800 // make a dictionary to hold the name, type, and state of this node
1801 CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1802 CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName());
1803
1804 CFTypeRef type = CFStringCreateCopy(NULL, mTypeName);
1805 CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type);
1806 CFReleaseNull(type);
1807
1808 if (state != NULL)
1809 {
1810 CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state);
1811 CFReleaseNull(state);
1812 }
1813
1814 CFDictionaryRef customItems = GetCustomExternalData();
1815 if (NULL != customItems)
1816 {
1817 CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems);
1818 CFReleaseNull(customItems);
1819 }
1820
1821 // append the resulting dictionary to the node list
1822 CFArrayAppendValue(transforms, node);
1823 CFReleaseNull(node);
1824
1825 // now walk the attribute list
1826 CFIndex numAttributes = CFSetGetCount(mAttributes);
1827 transform_attribute **attributes = (transform_attribute**)malloc(numAttributes*sizeof(transform_attribute*));
1828
1829 if (attributes == NULL) {
1830 return GetNoMemoryErrorAndRetain();
1831 }
1832
1833 TAGetAll(attributes);
1834
1835 CFIndex i;
1836
1837 // walk the forward links
1838 for (i = 0; i < numAttributes; ++i)
1839 {
1840 CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
1841 if (arraySize != 0)
1842 {
1843 CFIndex j;
1844 for (j = 0; j < arraySize; ++j)
1845 {
1846 transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j));
1847
1848 if (!ta->transform->IsExternalizable()) {
1849 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1850 continue;
1851 }
1852
1853 // add this forward connection to the array -- make a dictionary
1854 CFMutableDictionaryRef connection =
1855 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1856
1857 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName());
1858 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name);
1859 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName());
1860 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name);
1861
1862 CFArrayAppendValue(connections, connection);
1863 CFReleaseNull(connection);
1864 }
1865 }
1866 }
1867
1868 free(attributes);
1869
1870 return NULL;
1871 }