]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/Transform.cpp
Security-57740.51.3.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 }
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, (void *)abortErr, (void**)&mAbortError)) {
622 // send an abort message to the attribute so that it can shut down
623 // note that this bypasses the normal processes. The message sent is a notification
624 // that things aren't working well any more, the transform cannot make any other assumption.
625
626 // mAbortError is released in the destructor which is triggered (in part)
627 // by the dispatch queue finalizer so we don't need a retain/release of
628 // abortErr for the abortAction block, but we do need to retain it
629 // here to match with the release by the destructor.
630 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 CFURLRef 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 CFIndex 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 // Returns a dictionary of all the meta attributes that will need to be reset on a RestoreState
1551 CFDictionaryRef Transform::GetAHDictForSaveState(SecTransformStringOrAttributeRef key)
1552 {
1553 SecTransformMetaAttributeType types[] =
1554 {
1555 kSecTransformMetaAttributeRequired,
1556 kSecTransformMetaAttributeRequiresOutboundConnection,
1557 kSecTransformMetaAttributeDeferred,
1558 kSecTransformMetaAttributeStream,
1559 kSecTransformMetaAttributeCanCycle,
1560 kSecTransformMetaAttributeValue
1561 };
1562
1563 CFIndex i, cnt = sizeof(types)/sizeof(SecTransformMetaAttributeType);
1564 CFTypeRef values[cnt];
1565 CFNumberRef keys[cnt];
1566 key = getAH(key);
1567
1568 // NOTE: we save meta attributes that are in their "default" state on purpose because the
1569 // default may change in the future and we definitely want to restore the default values at
1570 // time of save (i.e. if "stream=1" is the 10.7 default, but "stream=0" becomes the 10.8
1571 // default we want to load all old transforms with stream=1, the simplest way to do that is
1572 // to store all values, not just non-default values)
1573 for(i = 0; i < cnt; ++i)
1574 {
1575 values[i] = GetMetaAttribute(key, types[i]);
1576 int tmp = (int)types[i];
1577 keys[i] = CFNumberCreate(NULL, kCFNumberIntType, &tmp);
1578 }
1579
1580 static CFDictionaryValueCallBacks CFTypeOrNULL;
1581 static dispatch_once_t once;
1582 dispatch_block_t b =
1583 ^{
1584 CFTypeOrNULL.version = 0;
1585 CFTypeOrNULL.retain = CFTypeOrNULLRetain;
1586 CFTypeOrNULL.release = CFTypeOrNULLRelease;
1587 CFTypeOrNULL.copyDescription = CFTypeOrNULLCopyDescription;
1588 CFTypeOrNULL.equal = CFTypeOrNULLEqual;
1589 };
1590 dispatch_once(&once, b);
1591
1592 CFDictionaryRef ret = CFDictionaryCreate(NULL, (const void**)&keys, (const void**)&values, cnt, &kCFTypeDictionaryKeyCallBacks, &CFTypeOrNULL);
1593
1594 for(i = 0; i < cnt; ++i)
1595 {
1596 CFRelease(keys[i]);
1597 }
1598
1599 return ret;
1600 }
1601
1602 // return everything that doesn't have ignore_while_externalizing set
1603 CFDictionaryRef Transform::CopyState()
1604 {
1605 CFIndex i, j, cnt = CFSetGetCount(mAttributes);
1606 transform_attribute *attrs[cnt];
1607 CFStringRef names[cnt];
1608 CFDictionaryRef values[cnt];
1609 TAGetAll(attrs);
1610 for(i = j = 0; i < cnt; ++i)
1611 {
1612 transform_attribute *ta = attrs[i];
1613 if (!ta->ignore_while_externalizing)
1614 {
1615 names[j] = ta->name;
1616 values[j++] = GetAHDictForSaveState(ta->name);
1617 }
1618 }
1619
1620 CFDictionaryRef result = CFDictionaryCreate(NULL, (const void**)&names, (const void**)&values, j, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1621
1622 for(i = j = 0; i < cnt; ++i)
1623 {
1624 transform_attribute *ta = attrs[i];
1625 if (!ta->ignore_while_externalizing)
1626 {
1627 CFRelease(values[j++]);
1628 }
1629 }
1630
1631 return result;
1632 }
1633
1634
1635
1636 void Transform::RestoreState(CFDictionaryRef state)
1637 {
1638 CFIndex i, cnt = CFDictionaryGetCount(state);
1639 const void
1640 **keys = (const void **)alloca(sizeof(void*)*cnt),
1641 **values = (const void **)alloca(sizeof(void*)*cnt);
1642
1643 CFDictionaryGetKeysAndValues(state, keys, values);
1644
1645 // Open issue -- do we need to do anything to values that are already set, but are not in "state"?
1646 // this isn't an issue right now, which is only used on the SecTransformCopyExternalRepresentation path which starts with brand new objects,
1647 // it only becomes an issue if we add a ResetFromState, or use it internally in that role.
1648
1649 for(i = 0; i < cnt; i++)
1650 {
1651 SecTransformAttributeRef ah = getAH(keys[i]);
1652
1653 if (NULL == ah)
1654 {
1655 continue;
1656 }
1657
1658 CFIndex j, meta_cnt = CFDictionaryGetCount((CFDictionaryRef)values[i]);
1659 const void **types = (const void**)alloca(sizeof(void*)*meta_cnt), **meta_values = (const void**)alloca(sizeof(void*)*meta_cnt);
1660 CFDictionaryGetKeysAndValues((CFDictionaryRef)values[i], types, meta_values);
1661
1662 int t;
1663 for(j = 0; j < meta_cnt; ++j)
1664 {
1665 CFNumberGetValue((CFNumberRef)types[j], kCFNumberIntType, &t);
1666 if (t == kSecTransformMetaAttributeValue)
1667 {
1668 if (meta_values[j]) {
1669 // SendMetaAttribute doesn't activate the callbacks
1670 SetAttribute(ah, meta_values[j]);
1671 }
1672 }
1673 else
1674 {
1675 CFErrorRef result = SendMetaAttribute(ah, (SecTransformMetaAttributeType)t, meta_values[j]);
1676 if (result)
1677 {
1678 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1679 }
1680 }
1681 }
1682
1683 CFErrorRef result = SendMetaAttribute(ah, kSecTransformMetaAttributeExternalize, kCFBooleanTrue);
1684 if (result)
1685 {
1686 CFRelease(result); // see <rdar://problem/8741628> Transform::RestoreState is ignoring error returns
1687 }
1688 }
1689 }
1690
1691 GroupTransform* Transform::GetRootGroup()
1692 {
1693 GroupTransform *g = mGroup;
1694 if (g) {
1695 while (g->mGroup) {
1696 g = g->mGroup;
1697 }
1698 } else {
1699 if (CFGetTypeID(this->GetCFObject()) == SecGroupTransformGetTypeID()) {
1700 return (GroupTransform *)this;
1701 }
1702 }
1703 return g;
1704 }
1705
1706 CFDictionaryRef Transform::GetCustomExternalData()
1707 {
1708 return NULL;
1709 }
1710
1711 void Transform::SetCustomExternalData(CFDictionaryRef customData)
1712 {
1713 return;
1714 }
1715
1716 CFDictionaryRef Transform::Externalize(CFErrorRef* error)
1717 {
1718 if (mIsActive)
1719 {
1720 return (CFDictionaryRef)CreateSecTransformErrorRef(kSecTransformTransformIsExecuting, "The %@ transform is executing, you need to externalize it prior to execution", GetName());
1721 }
1722
1723 // make arrays to hold the transforms and the connections
1724 __block CFMutableArrayRef transforms = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1725 __block CFMutableArrayRef connections = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1726 GroupTransform *root = GetRootGroup();
1727
1728 CFErrorRef err = ForAllNodes(false, true, ^(Transform *t) {
1729 if (t != root) {
1730 return t->ProcessExternalize(transforms, connections);
1731 }
1732 return (CFErrorRef)NULL;
1733 });
1734
1735 if (NULL != err)
1736 {
1737 // Really? This just seems like a bad idea
1738 if (NULL != error)
1739 {
1740 *error = err;
1741 }
1742 return NULL;
1743
1744 }
1745
1746 // make a dictionary to hold the output
1747 CFMutableDictionaryRef output = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1748 CFDictionaryAddValue(output, EXTERN_TRANSFORM_TRANSFORM_ARRAY, transforms);
1749 CFDictionaryAddValue(output, EXTERN_TRANSFORM_CONNECTION_ARRAY, connections);
1750
1751 // clean up
1752 CFRelease(connections);
1753 CFRelease(transforms);
1754
1755 return output;
1756 }
1757
1758 CFErrorRef Transform::ProcessExternalize(CFMutableArrayRef transforms, CFMutableArrayRef connections)
1759 {
1760 if (!IsExternalizable()) {
1761 return NULL;
1762 }
1763
1764 CFDictionaryRef state = CopyState();
1765 if (state && CFGetTypeID(state) == CFErrorGetTypeID()) {
1766 return (CFErrorRef)state;
1767 }
1768
1769 // make a dictionary to hold the name, type, and state of this node
1770 CFMutableDictionaryRef node = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1771 CFDictionaryAddValue(node, EXTERN_TRANSFORM_NAME, GetName());
1772
1773 CFTypeRef type = CFStringCreateCopy(NULL, mTypeName);
1774 CFDictionaryAddValue(node, EXTERN_TRANSFORM_TYPE, type);
1775 CFRelease(type);
1776
1777 if (state != NULL)
1778 {
1779 CFDictionaryAddValue(node, EXTERN_TRANSFORM_STATE, state);
1780 CFRelease(state);
1781 }
1782
1783 CFDictionaryRef customItems = GetCustomExternalData();
1784 if (NULL != customItems)
1785 {
1786 CFDictionaryAddValue(node, EXTERN_TRANSFORM_CUSTOM_EXPORTS_DICTIONARY, customItems);
1787 CFRelease(customItems);
1788 }
1789
1790 // append the resulting dictionary to the node list
1791 CFArrayAppendValue(transforms, node);
1792 CFRelease(node);
1793
1794 // now walk the attribute list
1795 CFIndex numAttributes = CFSetGetCount(mAttributes);
1796 transform_attribute *attributes[numAttributes];
1797 TAGetAll(attributes);
1798
1799 CFIndex i;
1800
1801 // walk the forward links
1802 for (i = 0; i < numAttributes; ++i)
1803 {
1804 CFIndex arraySize = attributes[i]->connections ? CFArrayGetCount(attributes[i]->connections) : 0;
1805 if (arraySize != 0)
1806 {
1807 CFIndex j;
1808 for (j = 0; j < arraySize; ++j)
1809 {
1810 transform_attribute *ta = ah2ta((SecTransformAttributeRef)CFArrayGetValueAtIndex(attributes[i]->connections, j));
1811
1812 if (!ta->transform->IsExternalizable()) {
1813 // just pretend non-externalizable transforms don't even exist. Don't write out connections, and don't talk to them about externalizing.
1814 continue;
1815 }
1816
1817 // add this forward connection to the array -- make a dictionary
1818 CFMutableDictionaryRef connection =
1819 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1820
1821 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_NAME, GetName());
1822 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_FROM_ATTRIBUTE, attributes[i]->name);
1823 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_NAME, ta->transform->GetName());
1824 CFDictionaryAddValue(connection, EXTERN_TRANSFORM_TO_ATTRIBUTE, ta->name);
1825
1826 CFArrayAppendValue(connections, connection);
1827 CFRelease(connection);
1828 }
1829 }
1830 }
1831
1832 return NULL;
1833 }