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