X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..e0e0d90ebff497686991a933ae2f7db24e7d8e0f:/OSX/include/security_codesigning/evaluationmanager.cpp diff --git a/OSX/include/security_codesigning/evaluationmanager.cpp b/OSX/include/security_codesigning/evaluationmanager.cpp deleted file mode 100644 index d64d6e1a..00000000 --- a/OSX/include/security_codesigning/evaluationmanager.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -#include "evaluationmanager.h" -#include "policyengine.h" -#include -#include -#include -#include - - - - -namespace Security { -namespace CodeSigning { - - - - -#pragma mark - EvaluationTask - - -// -// An evaluation task object manages the assessment - either directly, or in the -// form of waiting for another evaluation task to finish an assessment on the -// same target. -// -class EvaluationTask -{ -public: - CFURLRef path() const { return mPath.get(); } - AuthorityType type() const { return mType; } - bool isSharable() const { return mSharable; } - void setUnsharable() { mSharable = false; } - -private: - EvaluationTask(PolicyEngine *engine, CFURLRef path, AuthorityType type); - virtual ~EvaluationTask(); - void performEvaluation(SecAssessmentFlags flags, CFDictionaryRef context); - void waitForCompletion(SecAssessmentFlags flags, CFMutableDictionaryRef result); - - PolicyEngine *mPolicyEngine; - AuthorityType mType; - dispatch_queue_t mWorkQueue; - dispatch_queue_t mFeedbackQueue; - dispatch_semaphore_t mAssessmentLock; - __block dispatch_once_t mAssessmentKicked; - int32_t mReferenceCount; - int32_t mEvalCount; -// This whole thing is a pre-existing crutch and must be fixed soon. -#define UNOFFICIAL_MAX_XPC_ID_LENGTH 127 - char mXpcActivityName[UNOFFICIAL_MAX_XPC_ID_LENGTH]; - bool mSharable; - - CFCopyRef mPath; - CFCopyRef mResult; - std::vector mFeedback; - - std::exception_ptr mExceptionToRethrow; - - friend class EvaluationManager; -}; - - -EvaluationTask::EvaluationTask(PolicyEngine *engine, CFURLRef path, AuthorityType type) : - mPolicyEngine(engine), mType(type), mAssessmentLock(dispatch_semaphore_create(0)), - mAssessmentKicked(0), mReferenceCount(0), mEvalCount(0), mSharable(true), - mExceptionToRethrow(0) -{ - mXpcActivityName[0] = 0; - - mWorkQueue = dispatch_queue_create("EvaluationTask", 0); - mFeedbackQueue = dispatch_queue_create("EvaluationTaskFeedback", 0); - - mPath = path; - mResult.take(makeCFMutableDictionary()); -} - - -EvaluationTask::~EvaluationTask() -{ - dispatch_release(mFeedbackQueue); - dispatch_release(mWorkQueue); - dispatch_release(mAssessmentLock); -} - - -void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef context) -{ - bool performTheEvaluation = false; - bool lowPriority = flags & kSecAssessmentFlagLowPriority; - - // each evaluation task performs at most a single evaluation - if (OSAtomicIncrement32Barrier(&mEvalCount) == 1) - performTheEvaluation = true; - - // define a block to run when the assessment has feedback available - SecAssessmentFeedback relayFeedback = ^Boolean(CFStringRef type, CFDictionaryRef information) { - - __block Boolean proceed = true; - dispatch_sync(mFeedbackQueue, ^{ - if (mFeedback.size() > 0) { - proceed = false; // we need at least one interested party to proceed - // forward the feedback to all registered listeners - for (int i = 0; i < mFeedback.size(); ++i) { - proceed |= mFeedback[i](type, information); - } - } - }); - if (!proceed) - this->setUnsharable(); // don't share an expiring evaluation task - return proceed; - }; - - - // if the calling context has a feedback block, register it to listen to - // our feedback relay - dispatch_sync(mFeedbackQueue, ^{ - SecAssessmentFeedback feedback = (SecAssessmentFeedback)CFDictionaryGetValue(context, kSecAssessmentContextKeyFeedback); - if (feedback && CFGetTypeID(feedback) == CFGetTypeID(relayFeedback)) - mFeedback.push_back(feedback); - }); - - // if we haven't already started the evaluation (we're the first interested - // party), do it now - if (performTheEvaluation) { - dispatch_semaphore_t startLock = dispatch_semaphore_create(0); - - // create the assessment block - dispatch_async(mWorkQueue, dispatch_block_create_with_qos_class(DISPATCH_BLOCK_ENFORCE_QOS_CLASS, QOS_CLASS_UTILITY, 0, ^{ - // signal that the assessment is ready to start - dispatch_semaphore_signal(startLock); - - // wait until we're permitted to start the assessment. if we're in low - // priority mode, this will not happen until we're on AC power. if not - // in low priority mode, we're either already free to perform the - // assessment or we will be quite soon - dispatch_semaphore_wait(mAssessmentLock, DISPATCH_TIME_FOREVER); - - // Unregister a possibly still scheduled activity, as it lost its point. - if (strlen(mXpcActivityName)) { - xpc_activity_unregister(mXpcActivityName); - } - - // copy the original context into our own mutable dictionary and replace - // (or assign) the feedback entry within it to our multi-receiver - // feedback relay block - CFRef contextOverride = makeCFMutableDictionary(context); - CFDictionaryRemoveValue(contextOverride.get(), kSecAssessmentContextKeyFeedback); - CFDictionaryAddValue(contextOverride.get(), kSecAssessmentContextKeyFeedback, relayFeedback); - - try { - // perform the evaluation - switch (mType) { - case kAuthorityExecute: - mPolicyEngine->evaluateCode(mPath.get(), kAuthorityExecute, flags, contextOverride.get(), mResult.get(), true); - break; - case kAuthorityInstall: - mPolicyEngine->evaluateInstall(mPath.get(), flags, contextOverride.get(), mResult.get()); - break; - case kAuthorityOpenDoc: - mPolicyEngine->evaluateDocOpen(mPath.get(), flags, contextOverride.get(), mResult.get()); - break; - default: - MacOSError::throwMe(errSecCSInvalidAttributeValues); - break; - } - } catch(...) { - mExceptionToRethrow = std::current_exception(); - } - - })); - - // wait for the assessment to start - dispatch_semaphore_wait(startLock, DISPATCH_TIME_FOREVER); - dispatch_release(startLock); - - if (lowPriority) { - // This whole thing is a crutch and should be handled differently. - // Maybe by having just one activity that just kicks off all remaining - // background assessments, CTS determines that it's a good time. - - // reduce the bundle path name to just the app component and generate an - // xpc_activity identifier from it. this identifier should be smaller than - // 128 characters due to rdar://problem/20094806 - string path = cfString(mPath); - size_t bundleNamePosition = path.rfind('/'); - const char *bundleName = "/default"; - if (bundleNamePosition != string::npos) - bundleName = path.c_str() + bundleNamePosition; - snprintf(mXpcActivityName, UNOFFICIAL_MAX_XPC_ID_LENGTH, "com.apple.security.assess%s", bundleName); - - // schedule the assessment to be permitted to run (beyond start) -- this - // will either happen once we're no longer on battery power, or - // immediately, based on the flag value of kSecAssessmentFlagLowPriority - xpc_object_t criteria = xpc_dictionary_create(NULL, NULL, 0); - xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_REPEATING, false); - xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_DELAY, 0); - xpc_dictionary_set_int64(criteria, XPC_ACTIVITY_GRACE_PERIOD, 0); - - xpc_dictionary_set_string(criteria, XPC_ACTIVITY_PRIORITY, XPC_ACTIVITY_PRIORITY_MAINTENANCE); - xpc_dictionary_set_bool(criteria, XPC_ACTIVITY_ALLOW_BATTERY, false); - - xpc_activity_register(mXpcActivityName, criteria, ^(xpc_activity_t activity) { - dispatch_once(&mAssessmentKicked, ^{ - dispatch_semaphore_signal(mAssessmentLock); - }); - }); - xpc_release(criteria); - } - } - - // If this is a foreground assessment to begin with, or if an assessment - // with an existing task has been requested in the foreground, kick it - // immediately. - if (!lowPriority) { - dispatch_once(&mAssessmentKicked, ^{ - dispatch_semaphore_signal(mAssessmentLock); - }); - } -} - - - -void EvaluationTask::waitForCompletion(SecAssessmentFlags flags, CFMutableDictionaryRef result) -{ - // if the caller didn't request low priority we will elevate the dispatch - // queue priority via our wait block - dispatch_qos_class_t qos_class = QOS_CLASS_USER_INITIATED; - if (flags & kSecAssessmentFlagLowPriority) - qos_class = QOS_CLASS_UTILITY; - - // wait for the assessment to complete; our wait block will queue up behind - // the assessment and the copy its results - dispatch_sync(mWorkQueue, dispatch_block_create_with_qos_class (DISPATCH_BLOCK_ENFORCE_QOS_CLASS, qos_class, 0, ^{ - // copy the class result back to the caller - cfDictionaryApplyBlock(mResult.get(), ^(const void *key, const void *value){ - CFDictionaryAddValue(result, key, value); - }); - })); - - if (mExceptionToRethrow) std::rethrow_exception(mExceptionToRethrow); -} - - - -#pragma mark - - - -static Boolean evaluationTasksAreEqual(const EvaluationTask *task1, const EvaluationTask *task2) -{ - if (!task1->isSharable() || !task2->isSharable()) return false; - if ((task1->type() != task2->type()) || - (cfString(task1->path()) != cfString(task2->path()))) - return false; - - return true; -} - - - - -#pragma mark - EvaluationManager - - -EvaluationManager *EvaluationManager::globalManager() -{ - static EvaluationManager *singleton; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - singleton = new EvaluationManager(); - }); - return singleton; -} - - -EvaluationManager::EvaluationManager() -{ - static CFDictionaryValueCallBacks evalTaskValueCallbacks = kCFTypeDictionaryValueCallBacks; - evalTaskValueCallbacks.equal = (CFDictionaryEqualCallBack)evaluationTasksAreEqual; - evalTaskValueCallbacks.retain = NULL; - evalTaskValueCallbacks.release = NULL; - mCurrentEvaluations.take( - CFDictionaryCreateMutable(NULL, - 0, - &kCFTypeDictionaryKeyCallBacks, - &evalTaskValueCallbacks)); - - mListLockQueue = dispatch_queue_create("EvaluationManagerSyncronization", 0); -} - - -EvaluationManager::~EvaluationManager() -{ - dispatch_release(mListLockQueue); -} - - -EvaluationTask *EvaluationManager::evaluationTask(PolicyEngine *engine, CFURLRef path, AuthorityType type, SecAssessmentFlags flags, CFDictionaryRef context, CFMutableDictionaryRef result) -{ - __block EvaluationTask *evalTask = NULL; - - dispatch_sync(mListLockQueue, ^{ - // is path already being evaluated? - if (!(flags & kSecAssessmentFlagIgnoreActiveAssessments)) - evalTask = (EvaluationTask *)CFDictionaryGetValue(mCurrentEvaluations.get(), path); - if (!evalTask) { - // create a new task for the evaluation - evalTask = new EvaluationTask(engine, path, type); - if (flags & kSecAssessmentFlagIgnoreActiveAssessments) - evalTask->setUnsharable(); - CFDictionaryAddValue(mCurrentEvaluations.get(), path, evalTask); - } - evalTask->mReferenceCount++; - }); - - if (evalTask) - evalTask->performEvaluation(flags, context); - - return evalTask; -} - - -void EvaluationManager::waitForCompletion(EvaluationTask *task, SecAssessmentFlags flags, CFMutableDictionaryRef result) -{ - task->waitForCompletion(flags, result); -} - - -void EvaluationManager::removeTask(EvaluationTask *task) -{ - dispatch_sync(mListLockQueue, ^{ - // are we done with this evaluation task? - if (--task->mReferenceCount == 0) { - // yes -- remove it from our list and delete the object - CFDictionaryRemoveValue(mCurrentEvaluations.get(), task->path()); - delete task; - } - }); -} - - - -} // end namespace CodeSigning -} // end namespace Security -