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