X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..ee5f17c73ddf6cea151be3383378b7972c71f538:/OSX/libsecurity_codesigning/lib/evaluationmanager.cpp diff --git a/OSX/libsecurity_codesigning/lib/evaluationmanager.cpp b/OSX/libsecurity_codesigning/lib/evaluationmanager.cpp index d64d6e1a..22317b56 100644 --- a/OSX/libsecurity_codesigning/lib/evaluationmanager.cpp +++ b/OSX/libsecurity_codesigning/lib/evaluationmanager.cpp @@ -24,6 +24,8 @@ #include "evaluationmanager.h" #include "policyengine.h" #include +#include +#include #include #include #include @@ -34,8 +36,49 @@ namespace Security { namespace CodeSigning { +#pragma mark - +static CFStringRef EvaluationTaskCreateKey(CFURLRef path, AuthorityType type) +{ + CFErrorRef errors = NULL; + /* concatenate the type and the path before hashing */ + string pathString = std::to_string(type)+cfString(path); + CFRef data = makeCFData(pathString.c_str(), pathString.size()); + CFRef group = SecTransformCreateGroupTransform(); + + CFRef sha1 = SecDigestTransformCreate(kSecDigestSHA2, 256, &errors); + if( errors ) + { + CFError::throwMe(); + } + + CFRef b64 = SecEncodeTransformCreate(kSecBase64Encoding, &errors); + if ( errors ) + { + CFError::throwMe(); + } + + SecTransformSetAttribute(sha1, kSecTransformInputAttributeName, data, &errors); + if ( errors ) + { + CFError::throwMe(); + } + + SecTransformConnectTransforms(sha1, kSecTransformOutputAttributeName, b64, kSecTransformInputAttributeName, group, &errors); + if ( errors ) + { + CFError::throwMe(); + } + + CFRef keyData = (CFDataRef)SecTransformExecute(group, &errors); + if ( errors ) + { + CFError::throwMe(); + } + + return makeCFString(keyData); +} #pragma mark - EvaluationTask @@ -56,15 +99,21 @@ public: private: EvaluationTask(PolicyEngine *engine, CFURLRef path, AuthorityType type); virtual ~EvaluationTask(); + + // Tasks cannot be copied. + EvaluationTask(EvaluationTask const&) = delete; + EvaluationTask& operator=(EvaluationTask const&) = delete; + void performEvaluation(SecAssessmentFlags flags, CFDictionaryRef context); void waitForCompletion(SecAssessmentFlags flags, CFMutableDictionaryRef result); + void kick(); PolicyEngine *mPolicyEngine; AuthorityType mType; dispatch_queue_t mWorkQueue; dispatch_queue_t mFeedbackQueue; dispatch_semaphore_t mAssessmentLock; - __block dispatch_once_t mAssessmentKicked; + dispatch_once_t mAssessmentKicked; int32_t mReferenceCount; int32_t mEvalCount; // This whole thing is a pre-existing crutch and must be fixed soon. @@ -147,7 +196,8 @@ void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef 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, ^{ + dispatch_block_t assessmentBlock = + 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); @@ -183,13 +233,16 @@ void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef break; default: MacOSError::throwMe(errSecCSInvalidAttributeValues); - break; } } catch(...) { mExceptionToRethrow = std::current_exception(); } - - })); + + }); + assert(assessmentBlock != NULL); + + dispatch_async(mWorkQueue, assessmentBlock); + Block_release(assessmentBlock); // wait for the assessment to start dispatch_semaphore_wait(startLock, DISPATCH_TIME_FOREVER); @@ -199,16 +252,14 @@ void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef // 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. + + // Convert the evaluation path and type to a base64 encoded hash to use as a key + // Use that to generate an xpc_activity identifier. This identifier should be smaller + // than 128 characters due to rdar://problem/20094806 - // 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); + CFCopyRef cfKey(EvaluationTaskCreateKey(mPath, mType)); + string key = cfStringRelease(cfKey); + snprintf(mXpcActivityName, UNOFFICIAL_MAX_XPC_ID_LENGTH, "com.apple.security.assess/%s", key.c_str()); // schedule the assessment to be permitted to run (beyond start) -- this // will either happen once we're no longer on battery power, or @@ -222,9 +273,9 @@ void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef 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); - }); + // We use the Evaluation Manager to get the task, as the task may be gone already + // (and with it, its mAssessmentKicked member). + EvaluationManager::globalManager()->kickTask(cfKey); }); xpc_release(criteria); } @@ -234,13 +285,15 @@ void EvaluationTask::performEvaluation(SecAssessmentFlags flags, CFDictionaryRef // with an existing task has been requested in the foreground, kick it // immediately. if (!lowPriority) { - dispatch_once(&mAssessmentKicked, ^{ - dispatch_semaphore_signal(mAssessmentLock); - }); + kick(); } } - +void EvaluationTask::kick() { + dispatch_once(&mAssessmentKicked, ^{ + dispatch_semaphore_signal(mAssessmentLock); + }); +} void EvaluationTask::waitForCompletion(SecAssessmentFlags flags, CFMutableDictionaryRef result) { @@ -252,14 +305,19 @@ void EvaluationTask::waitForCompletion(SecAssessmentFlags flags, CFMutableDictio // 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); + dispatch_block_t wait_block = 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); + }); + }); + assert(wait_block != NULL); + dispatch_sync(mWorkQueue, wait_block); + Block_release(wait_block); } @@ -321,15 +379,16 @@ EvaluationTask *EvaluationManager::evaluationTask(PolicyEngine *engine, CFURLRef __block EvaluationTask *evalTask = NULL; dispatch_sync(mListLockQueue, ^{ + CFRef key = EvaluationTaskCreateKey(path, type); // is path already being evaluated? if (!(flags & kSecAssessmentFlagIgnoreActiveAssessments)) - evalTask = (EvaluationTask *)CFDictionaryGetValue(mCurrentEvaluations.get(), path); + evalTask = (EvaluationTask *)CFDictionaryGetValue(mCurrentEvaluations.get(), key.get()); 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); + CFDictionaryAddValue(mCurrentEvaluations.get(), key.get(), evalTask); } evalTask->mReferenceCount++; }); @@ -341,25 +400,41 @@ EvaluationTask *EvaluationManager::evaluationTask(PolicyEngine *engine, CFURLRef } -void EvaluationManager::waitForCompletion(EvaluationTask *task, SecAssessmentFlags flags, CFMutableDictionaryRef result) +void EvaluationManager::finalizeTask(EvaluationTask *task, SecAssessmentFlags flags, CFMutableDictionaryRef result) { task->waitForCompletion(flags, result); + + std::exception_ptr pendingException = task->mExceptionToRethrow; + + removeTask(task); + + if (pendingException) std::rethrow_exception(pendingException); } void EvaluationManager::removeTask(EvaluationTask *task) { dispatch_sync(mListLockQueue, ^{ + CFRef key = EvaluationTaskCreateKey(task->path(), task->type()); // 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()); + CFDictionaryRemoveValue(mCurrentEvaluations.get(), key.get()); delete task; } }); } - +void EvaluationManager::kickTask(CFStringRef key) +{ + dispatch_sync(mListLockQueue, ^{ + EvaluationTask *evalTask = (EvaluationTask*)CFDictionaryGetValue(mCurrentEvaluations.get(), + key); + if (evalTask != NULL) { + evalTask->kick(); + } + }); +} } // end namespace CodeSigning } // end namespace Security