]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/Transform.cpp
Security-57740.1.18.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) {
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) {
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) {
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 CFRelease(ta->value);
65 }
66
67 // ta->q already released
68
69 if (ta->connections)
70 {
71 CFRelease(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 CFRelease(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 CFRelease(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 CFRelease(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 CFRelease(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 CFRelease(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 CFRelease(mAttributes);
478 if (mAbortError) {
479 CFRelease(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 CFRelease(mTypeName);
487
488 if (NULL != mPushedback)
489 {
490 CFRelease(mPushedback);
491 }
492 dispatch_release(mActivationPending);
493 }
494
495 CFStringRef Transform::GetName() {
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 break;
589 }
590
591 return NULL;
592 }
593
594
595
596 CFErrorRef Transform::RefactorErrorToIncludeAbortingTransform(CFErrorRef sourceError)
597 {
598 // pull apart the error
599 CFIndex code = CFErrorGetCode(sourceError);
600 CFStringRef domain = CFErrorGetDomain(sourceError);
601 CFDictionaryRef oldUserInfo = CFErrorCopyUserInfo(sourceError);
602 CFMutableDictionaryRef userInfo = CFDictionaryCreateMutableCopy(NULL, 0, oldUserInfo);
603 CFRelease(oldUserInfo);
604
605 // add the new key and value to the dictionary
606 CFDictionaryAddValue(userInfo, kSecTransformAbortOriginatorKey, GetCFObject());
607
608 // make a new CFError
609 CFErrorRef newError = CFErrorCreate(NULL, domain, code, userInfo);
610 CFRelease(userInfo);
611 return newError;
612 }
613
614 // NOTE: If called prior to execution will schedule a later call to AbortAllTransforms
615 void Transform::AbortJustThisTransform(CFErrorRef abortErr)
616 {
617 (void)transforms_assume(abortErr);
618 (void)transforms_assume(dispatch_get_specific(&dispatchQueueToTransformKey) == this);
619
620 Boolean wasActive = mIsActive;
621
622 if (OSAtomicCompareAndSwapPtr(NULL, (void *)abortErr, (void**)&mAbortError)) {
623 // send an abort message to the attribute so that it can shut down
624 // note that this bypasses the normal processes. The message sent is a notification
625 // that things aren't working well any more, the transform cannot make any other assumption.
626
627 // mAbortError is released in the destructor which is triggered (in part)
628 // by the dispatch queue finalizer so we don't need a retain/release of
629 // abortErr for the abortAction block, but we do need to retain it
630 // here to match with the release by the destructor.
631 CFRetain(abortErr);
632
633 dispatch_block_t abortAction = ^{
634 // This actually makes the abort happen, it needs to run on the transform's queue while the
635 // transform is executing.
636
637 if (!wasActive) {
638 // When this abort was first processed we were not executing, so
639 // additional transforms may have been added to our group (indeed,
640 // we may not have had a group at all), so we need to let everyone
641 // know about the problem. This will end up letting ourself (and
642 // maybe some others) know an additional time, but the CompareAndSwap
643 // prevents that from being an issue.
644 this->AbortAllTransforms(abortErr);
645 }
646
647 SecTransformAttributeRef GCC_BUG_WORKAROUND inputAttributeHandle = getAH(kSecTransformInputAttributeName, false);
648 // Calling AttributeChanged directly lets an error "skip ahead" of the input queue,
649 // and even execute if the input queue is suspended pending pushback retries.
650 AttributeChanged(inputAttributeHandle, abortErr);
651 try_pushbacks();
652 };
653
654 if (mIsActive) {
655 // This transform is running, so we use the normal queue (which we are
656 // already executing on)
657 abortAction();
658 } else {
659 // This transform hasn't run yet, do the work on the activation queue
660 // so it happens as soon as the transforms starts executing.
661 dispatch_async(mActivationQueue, abortAction);
662 }
663 } else {
664 Debug("%@ set to %@ while processing ABORT=%@, this extra set will be ignored", AbortAH, abortErr, mAbortError);
665 }
666 }
667
668 // abort all transforms in the root group & below
669 void Transform::AbortAllTransforms(CFTypeRef err)
670 {
671 Debug("%@ set to %@, aborting\n", AbortAH, err);
672 CFErrorRef error = NULL;
673
674 CFTypeRef replacementErr = NULL;
675
676 if (CFGetTypeID(err) != CFErrorGetTypeID())
677 {
678 replacementErr = err = CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "ABORT set to a %@ (%@) not a %@", CFCopyTypeIDDescription(CFGetTypeID(err)), err, CFCopyTypeIDDescription(CFErrorGetTypeID()));
679 }
680
681 error = RefactorErrorToIncludeAbortingTransform((CFErrorRef)err);
682
683 if (replacementErr)
684 {
685 CFRelease(replacementErr);
686 }
687
688 GroupTransform *root = GetRootGroup();
689 if (root)
690 {
691 // tell everyone in the (root) group to "AbortJustThisTransform"
692 dispatch_group_t all_aborted = dispatch_group_create();
693 root->ForAllNodesAsync(false, all_aborted, ^(Transform* t){
694 t->AbortJustThisTransform(error);
695 });
696 dispatch_group_notify(all_aborted, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
697 CFRelease(error);
698 dispatch_release(all_aborted);
699 });
700 }
701 else
702 {
703 // We are everyone so we AbortJustThisTransform "directly"
704 // NOTE: this can only happen prior to execution (execution always happens in a group)
705 (void)transforms_assume_zero(mIsActive);
706 this->AbortJustThisTransform(error);
707 }
708 }
709
710
711
712 CFErrorRef Transform::Disconnect(Transform* destinationTransform, CFStringRef myKey, CFStringRef hisKey)
713 {
714 //CFTypeRef thisTransform = (SecTransformRef) GetCFObject();
715
716 // find this transform in the backlinks for the destination
717 CFIndex i;
718
719 // now remove the link in the transform dictionary
720 transform_attribute *src = getTA(myKey, true);
721 SecTransformAttributeRef dst = destinationTransform->getAH(hisKey);
722
723 if (src->connections == NULL)
724 {
725 return CreateSecTransformErrorRef(kSecTransformErrorInvalidOperation, "Cannot find transform in destination.");
726 }
727
728 CFIndex numConnections = CFArrayGetCount(src->connections);
729 for (i = 0; i < numConnections; ++i)
730 {
731 if (CFArrayGetValueAtIndex(src->connections, i) == dst)
732 {
733 CFArrayRemoveValueAtIndex(src->connections, i);
734 numConnections = CFArrayGetCount(src->connections);
735 }
736
737 // clear the has_incoming_connection bit in the destination. We can do this because inputs can have only one connection.
738 transform_attribute* dstTA = ah2ta(dst);
739 dstTA->has_incoming_connection = false;
740 }
741
742 if (HasNoInboundConnections() && HasNoOutboundConnections())
743 {
744 // we have been orphaned, just remove us
745 mGroup->RemoveMemberFromGroup(GetCFObject());
746 mGroup = NULL;
747 }
748
749 return NULL;
750 }
751
752
753
754 CFErrorRef Transform::Connect(GroupTransform *group, Transform* destinationTransform, CFStringRef destAttr, CFStringRef srcAttr)
755 {
756 if (group == NULL)
757 {
758 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections without a specific group (do not call with group = NULL)");
759 return err;
760 }
761
762 GroupTransform *newSourceGroup = mGroup;
763 GroupTransform *newDestinationGroup = destinationTransform->mGroup;
764
765 if (mGroup == NULL || mGroup == this)
766 {
767 newSourceGroup = group;
768 }
769
770 if (destinationTransform->mGroup == NULL || destinationTransform->mGroup == destinationTransform)
771 {
772 newDestinationGroup = group;
773 }
774
775 if (newSourceGroup != newDestinationGroup && mGroup)
776 {
777 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make connections between transforms in different groups (%@ is in %@, %@ is in %@)", GetName(), newSourceGroup->GetName(), destinationTransform->GetName(), newDestinationGroup->GetName());
778 return err;
779 }
780
781 if (!validConnectionPoint(srcAttr)) {
782 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection from non-exported attribute %@ of %@", srcAttr, this->GetName());
783 return err;
784 }
785 if (!destinationTransform->validConnectionPoint(destAttr)) {
786 CFErrorRef err = CreateSecTransformErrorRef(kSecTransformErrorInvalidConnection, "Can not make a connection to non-exported attribute %@ of %@", destAttr, destinationTransform->GetName());
787 return err;
788 }
789
790 mGroup = newSourceGroup;
791 destinationTransform->mGroup = newDestinationGroup;
792
793 // NOTE: this fails on OOM
794 group->AddMemberToGroup(this->GetCFObject());
795 group->AddMemberToGroup(destinationTransform->GetCFObject());
796
797 transform_attribute *src = this->getTA(srcAttr, true);
798 SecTransformAttributeRef dst = destinationTransform->getAH(destAttr);
799
800 if (!src->connections)
801 {
802 src->connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
803 }
804 CFArrayAppendValue(src->connections, dst);
805
806 ah2ta(dst)->has_incoming_connection = 1;
807
808 return NULL;
809 }
810
811
812 bool Transform::validConnectionPoint(CFStringRef attributeName)
813 {
814 return true;
815 }
816
817 // NoCallback == don't call this transform's Do function, but DO call the Do functions of connected attributes
818 // SetAttribute eventually calls SetAttributeNoCallback
819 CFErrorRef Transform::SetAttributeNoCallback(SecTransformStringOrAttributeRef key, CFTypeRef value)
820 {
821 SecTransformAttributeRef ah = getAH(key, true);
822 if (!ah)
823 {
824 abort();
825 }
826 transform_attribute *ta = ah2ta(ah);
827
828 if (ah == AbortAH && value && (mIsActive || !ta->deferred))
829 {
830 AbortAllTransforms(value);
831 return CreateSecTransformErrorRef(kSecTransformErrorAbortInProgress, "Abort started");
832 }
833
834 bool do_propagate = true;
835
836 if (!ta->has_been_deferred)
837 {
838 bool doNotRetain = false;
839
840 if (value)
841 {
842 CFStringRef name = ta->name;
843 if (CFGetTypeID(value) == CFReadStreamGetTypeID())
844 {
845 CFTypeRef src = StreamSource::Make((CFReadStreamRef) value, this, name);
846 value = src;
847 do_propagate = false;
848 ta->has_been_deferred = 1;
849 doNotRetain = true;
850 }
851 else if (ta->deferred && !mIsActive)
852 {
853 if (ta->deferred)
854 {
855 Debug("%@ deferred value=%p\n", ah, value);
856 }
857
858 CFTypeRef src = SingleShotSource::Make(value, this, name);
859 ta->has_been_deferred = 1;
860
861 // the old value will be release when Transform::Do terminates
862
863 value = src;
864 do_propagate = false;
865 doNotRetain = true;
866 }
867 else
868 {
869 ta->has_been_deferred = 0;
870 }
871 }
872
873 if (ta->value != value) {
874 if (value && !doNotRetain) {
875 CFRetain(value);
876 }
877 if (ta->value) {
878 CFRelease(ta->value);
879 }
880 }
881
882 ta->value = value;
883 }
884
885 // propagate the changes out to all connections
886 if (ta->connections && mIsActive && do_propagate && !(mAbortError || mIsFinalizing))
887 {
888 Debug("Propagating from %@ to %@\n", ah, ta->connections);
889 CFIndex i, numConnections = CFArrayGetCount(ta->connections);
890 for(i = 0; i < numConnections; ++i) {
891 SecTransformAttributeRef ah = static_cast<SecTransformAttributeRef>(const_cast<void *>(CFArrayGetValueAtIndex(ta->connections, i)));
892 Transform *tt = ah2ta(ah)->transform;
893 if (NULL != tt)
894 {
895 if (tt->mIsActive)
896 {
897 tt->SetAttribute(ah, value);
898 }
899 else
900 {
901 dispatch_block_t setAttribute = ^{
902 tt->SetAttribute(ah, value);
903 };
904 // Here the target queue might not be activated yet, we can't
905 // look directly at the other transform's ActivationQueue as
906 // it might activate (or Finalize!) as we look, so just ask
907 // the other transform to deal with it.
908 dispatch_async(ah2ta(ah)->q, ^(void) {
909 // This time we are on the right queue to know this is the real deal
910 if (tt->mIsActive) {
911 setAttribute();
912 } else {
913 dispatch_async(ah2ta(ah)->transform->mActivationQueue, setAttribute);
914 }
915 });
916 }
917 }
918 }
919 }
920
921 return NULL;
922 }
923
924 // external sets normally fail if the transform is running
925 CFErrorRef Transform::ExternalSetAttribute(CFTypeRef key, CFTypeRef value)
926 {
927 if (!mIsActive)
928 {
929 return this->SetAttribute(key, value);
930 }
931 else
932 {
933 SecTransformAttributeRef ah = getAH(key, false);
934 if (ah != NULL && ah2ta(ah)->allow_external_sets)
935 {
936 return this->SetAttribute(static_cast<CFTypeRef>(ah), value);
937 }
938 else
939 {
940 return CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "%@ can not be set while %@ is executing", ah, this->GetName());
941 }
942 }
943 }
944
945
946 // queue up the setting of the key and value
947 CFErrorRef Transform::SetAttribute(CFTypeRef key, CFTypeRef value)
948 {
949 if (mAbortError)
950 {
951 return CreateSecTransformErrorRef(kSecTransformErrorAborted, "ABORT has been sent to the transform (%@)", mAbortError);
952 }
953
954 // queue up the setting of the key and value
955 SecTransformAttributeRef ah;
956 if (CFGetTypeID(key) == transform_attribute::cftype)
957 {
958 ah = key;
959 }
960 else if (CFGetTypeID(key) == CFStringGetTypeID())
961 {
962 ah = getAH(static_cast<CFStringRef>(key));
963 if (!ah)
964 {
965 return CreateSecTransformErrorRef(kSecTransformErrorUnsupportedAttribute, "Can't set attribute %@ in transform %@", key, GetName());
966 }
967 }
968 else
969 {
970 return CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "Transform::SetAttribute called with %@, requires a string or an AttributeHandle", key);
971 }
972
973 // Do this after the error check above so we don't leak
974 if (value != NULL)
975 {
976 CFRetain(value); // if we use dispatch_async we need to own the value (the matching release is in the set block)
977 }
978
979
980 transform_attribute *ta = ah2ta(ah);
981
982 dispatch_block_t set = ^{
983 Do(ah, value);
984
985 dispatch_semaphore_signal(ta->semaphore);
986
987 if (value != NULL)
988 {
989 CFRelease(value);
990 }
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 CFRetain(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 CFRelease(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 CFRelease(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 auto GCC_BUG_WORKAROUND CFURLRef GCC_BUG_WORKAROUND p = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/stderr"), kCFURLPOSIXPathStyle, FALSE);
1132 StdErrWriteStream = CFWriteStreamCreateWithFile(NULL, p);
1133 CFWriteStreamOpen(StdErrWriteStream);
1134 CFRelease(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 CFRelease(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 CFRelease(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 CFRelease(g);
1257 });
1258 }
1259 };
1260
1261 CFTypeRef ret = this->Execute(deliveryQueue, deliveryBlock ? smb : (SecMessageBlock) NULL, errorRef);
1262
1263 if (!deliveryBlock)
1264 {
1265 CFRelease(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 CFRetain(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 CFRelease(rootGroup->GetCFObject());
1316 CFRelease(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 CFRelease(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 CFRelease(elist);
1416 CFRelease(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 if (value != NULL) {
1519 return CFRetain(value);
1520 } else {
1521 return value;
1522 }
1523 }
1524
1525 static void CFTypeOrNULLRelease(CFAllocatorRef allocator, const void *value) {
1526 if (value != NULL) {
1527 CFRelease(value);
1528 }
1529 }
1530
1531 static CFStringRef CFTypeOrNULLCopyDescription (const void *value) {
1532 if (value != NULL) {
1533 return CFCopyDescription(value);
1534 } else {
1535 return CFSTR("NULL");
1536 }
1537 }
1538
1539 static Boolean CFTypeOrNULLEqual(const void *value1, const void *value2) {
1540 if (value1 == NULL && value2 == NULL) {
1541 return TRUE;
1542 } else {
1543 if (value1 == NULL || value2 == NULL) {
1544 return FALSE;
1545 } else {
1546 return CFEqual(value1, value2);
1547 }
1548 }
1549 }
1550
1551 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1552 CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key)
1553 {
1554 SecTransformMetaAttributeType types[] =
1555 {
1556 kSecTransformMetaAttributeRequired,
1557 kSecTransformMetaAttributeRequiresOutboundConnection,
1558 kSecTransformMetaAttributeDeferred,
1559 kSecTransformMetaAttributeStream,
1560 kSecTransformMetaAttributeCanCycle,
1561 kSecTransformMetaAttributeValue
1562 };
1563
1564 CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType);
1565 CFTypeRef values[cnt];
1566 CFNumberRef keys[cnt];
1567 key = getAH(key);
1568
1569 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1570 // default may change in the future and we definitely want to restore the default values at
1571 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1572 // default we want to load all old transforms with stream=1, the simplest way to do that is
1573 // to store all values, not just non-default values)
1574 for(i = 0; i < cnt; ++i)
1575 {
1576 values[i] = GetMetaAttribute(key, types[i]);
1577 int tmp = (int)types[i];
1578 keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
1579 }
1580
1581 static CFDictionaryValueCallBacks CFTypeOrNULL;
1582 static dispatch_once_t once;
1583 dispatch_block_t b =
1584 ^{
1585 CFTypeOrNULL.version = 0;
1586 CFTypeOrNULL.retain = CFTypeOrNULLRetain;
1587 CFTypeOrNULL.release = CFTypeOrNULLRelease;
1588 CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription;
1589 CFTypeOrNULL.equal = CFTypeOrNULLEqual;
1590 };
1591 dispatch_once(&once, b);
1592
1593 CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL);
1594
1595 for(i = 0; i < cnt; ++i)
1596 {
1597 CFRelease(keys[i]);
1598 }
1599
1600 return ret;
1601 }
1602
1603 // return everything that doesn't have ignore_while_externalizing set
1604 CFDictionaryRef Transform::CopyState()
1605 {
1606 CFIndex i, j, cnt = CFSetGetCount(mAttributes);
1607 transform_attribute *attrs[cnt];
1608 CFStringRef names[cnt];
1609 CFDictionaryRef values[cnt];
1610 TAGetAll(attrs);
1611 for(i = j = 0; i < cnt; ++i)
1612 {
1613 transform_attribute *ta = attrs[i];
1614 if (!ta->ignore_while_externalizing)
1615 {
1616 names[j] = ta->name;
1617 values[j++] = GetAHDictForSaveState(ta->name);
1618 }
1619 }
1620
1621 CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1622
1623 for(i = j = 0; i < cnt; ++i)
1624 {
1625 transform_attribute *ta = attrs[i];
1626 if (!ta->ignore_while_externalizing)
1627 {
1628 CFRelease(values[j++]);
1629 }
1630 }
1631
1632 return result;
1633 }
1634
1635
1636
1637 void Transform::RestoreState(CFDictionaryRef state)
1638 {
1639 CFIndex i, cnt = CFDictionaryGetCount(state);
1640 const void
1641 **keys = (const void **)alloca(sizeof(void*)*cnt),
1642 **values = (const void **)alloca(sizeof(void*)*cnt);
1643
1644 CFDictionaryGetKeysAndValues(state, keys, values);
1645
1646 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1647 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1648 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1649
1650 for(i = 0; i < cnt; i++)
1651 {
1652 SecTransformAttributeRef ah = getAH(keys[i]);
1653
1654 if (NULL == ah)
1655 {
1656 continue;
1657 }
1658
1659 CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]);
1660 const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt);
1661 CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values);
1662
1663 int t;
1664 for(j = 0; j < meta_cnt; ++j)
1665 {
1666 CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t);
1667 if (t == kSecTransformMetaAttributeValue)
1668 {
1669 if (meta_values[j]) {
1670 // SendMetaAttribute doesn't activate the callbacks
1671 SetAttribute(ah, meta_values[j]);
1672 }
1673 }
1674 else
1675 {
1676 CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]);
1677 if (result)
1678 {
1679 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1680 }
1681 }
1682 }
1683
1684 CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue);
1685 if (result)
1686 {
1687 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1688 }
1689 }
1690 }
1691
1692 GroupTransform* Transform::GetRootGroup()
1693 {
1694 GroupTransform *g = mGroup;
1695 if (g) {
1696 while (g->mGroup) {
1697 g = g->mGroup;
1698 }
1699 } else {
1700 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1701 return (GroupTransform *)this;
1702 }
1703 }
1704 return g;
1705 }
1706
1707 CFDictionaryRef Transform::GetCustomExternalData()
1708 {
1709 return NULL;
1710 }
1711
1712 void Transform::SetCustomExternalData(CFDictionaryRef customData)
1713 {
1714 return;
1715 }
1716
1717 CFDictionaryRef Transform::Externalize(CFErrorRef* error)
1718 {
1719 if (mIsActive)
1720 {
1721 return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1722 }
1723
1724 // make arrays to hold the transforms and the connections
1725 __block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1726 __block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1727 GroupTransform *root = GetRootGroup();
1728
1729 CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) {
1730 if (t != root) {
1731 return t->ProcessExternalize(transforms, connections);
1732 }
1733 return (CFErrorRef)NULL;
1734 });
1735
1736 if (NULL != err)
1737 {
1738 // Really? This just seems like a bad idea
1739 if (NULL != error)
1740 {
1741 *error = err;
1742 }
1743 return NULL;
1744
1745 }
1746
1747 // make a dictionary to hold the output
1748 CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1749 CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms);
1750 CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections);
1751
1752 // clean up
1753 CFRelease(connections);
1754 CFRelease(transforms);
1755
1756 return output;
1757 }
1758
1759 CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections)
1760 {
1761 if (!IsExternalizable()) {
1762 return NULL;
1763 }
1764
1765 CFDictionaryRef state = CopyState();
1766 if (state && CFGetTypeID(state) == CFErrorGetTypeID()) {
1767 return (CFErrorRef)state;
1768 }
1769
1770 // make a dictionary to hold the name, type, and state of this node
1771 CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1772 CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName());
1773
1774 CFTypeRef type = CFStringCreateCopy(NULL, mTypeName);
1775 CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type);
1776 CFRelease(type);
1777
1778 if (state != NULL)
1779 {
1780 CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state);
1781 CFRelease(state);
1782 }
1783
1784 CFDictionaryRef customItems = GetCustomExternalData();
1785 if (NULL != customItems)
1786 {
1787 CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems);
1788 CFRelease(customItems);
1789 }
1790
1791 // append the resulting dictionary to the node list
1792 CFArrayAppendValue(transforms, node);
1793 CFRelease(node);
1794
1795 // now walk the attribute list
1796 CFIndex numAttributes = CFSetGetCount(mAttributes);
1797 transform_attribute *attributes[numAttributes];
1798 TAGetAll(attributes);
1799
1800 CFIndex i;
1801
1802 // walk the forward links
1803 for (i = 0; i < numAttributes; ++i)
1804 {
1805 CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
1806 if (arraySize != 0)
1807 {
1808 CFIndex j;
1809 for (j = 0; j < arraySize; ++j)
1810 {
1811 transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j));
1812
1813 if (!ta->transform->IsExternalizable()) {
1814 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1815 continue;
1816 }
1817
1818 // add this forward connection to the array -- make a dictionary
1819 CFMutableDictionaryRef connection =
1820 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1821
1822 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName());
1823 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name);
1824 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName());
1825 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name);
1826
1827 CFArrayAppendValue(connections, connection);
1828 CFRelease(connection);
1829 }
1830 }
1831 }
1832
1833 return NULL;
1834 }