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