X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_codesigning/lib/Code.cpp?ds=sidebyside diff --git a/Security/libsecurity_codesigning/lib/Code.cpp b/Security/libsecurity_codesigning/lib/Code.cpp new file mode 100644 index 00000000..643c4f58 --- /dev/null +++ b/Security/libsecurity_codesigning/lib/Code.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2006-2007,2011,2013 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@ + */ + +// +// Code - SecCode API objects +// +#include "Code.h" +#include "StaticCode.h" +#include +#include "cskernel.h" +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// Construction +// +SecCode::SecCode(SecCode *host) + : mHost(host), mIdentified(false) +{ + CODESIGN_DYNAMIC_CREATE(this, host); +} + + +// +// Clean up a SecCode object +// +SecCode::~SecCode() throw() +try { +} catch (...) { + return; +} + + +// +// CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed, +// and falls back on comparing canonical paths if (both are) not. +// +bool SecCode::equal(SecCFObject &secOther) +{ + SecCode *other = static_cast(&secOther); + CFDataRef mine = this->cdHash(); + CFDataRef his = other->cdHash(); + if (mine || his) + return mine && his && CFEqual(mine, his); + else + return this->staticCode()->equal(*other->staticCode()); +} + +CFHashCode SecCode::hash() +{ + if (CFDataRef h = this->cdHash()) + return CFHash(h); + else + return this->staticCode()->hash(); +} + + +// +// Yield the host Code +// +SecCode *SecCode::host() const +{ + return mHost; +} + + +// +// Yield the static code. This is cached. +// The caller does not own the object returned; it lives (at least) as long +// as the SecCode it was derived from. +// +SecStaticCode *SecCode::staticCode() +{ + if (!mIdentified) { + this->identify(); + mIdentified = true; + } + assert(mStaticCode); + return mStaticCode; +} + + +// +// Yield the CodeDirectory hash as presented by our host. +// This usually is the same as the hash of staticCode().codeDirectory(), but might not +// if files are changing on disk while code is running. +// +CFDataRef SecCode::cdHash() +{ + if (!mIdentified) { + this->identify(); + mIdentified = true; + } + return mCDHash; // can be NULL (host has no dynamic identity for guest) +} + + +// +// Retrieve current dynamic status. +// +SecCodeStatus SecCode::status() +{ + if (this->isRoot()) + return kSecCodeStatusValid; // root of trust, presumed valid + else + return this->host()->getGuestStatus(this); +} + +void SecCode::status(SecCodeStatusOperation operation, CFDictionaryRef arguments) +{ + if (this->isRoot()) + MacOSError::throwMe(errSecCSHostProtocolStateError); + else + this->host()->changeGuestStatus(this, operation, arguments); +} + + +// +// By default, we have no guests +// +SecCode *SecCode::locateGuest(CFDictionaryRef) +{ + return NULL; +} + + +// +// By default, we self-identify by asking our host to identify us. +// (This is currently only overridden in the root-of-trust (kernel) implementation.) +// +void SecCode::identify() +{ + mStaticCode.take(host()->identifyGuest(this, &mCDHash.aref())); +} + + +// +// The default implementation cannot map guests to disk +// +SecStaticCode *SecCode::identifyGuest(SecCode *, CFDataRef *) +{ + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// Master validation function. +// +// This is the most important function in all of Code Signing. It performs +// dynamic validation on running code. Despite its simple structure, it does +// everything that's needed to establish whether a Code is currently valid... +// with a little help from StaticCode, format drivers, type drivers, and so on. +// +// This function validates internal requirements in the hosting chain. It does +// not validate external requirements - the caller needs to do that with a separate call. +// +void SecCode::checkValidity(SecCSFlags flags) +{ + if (this->isRoot()) { + // the root-of-trust is valid by definition + CODESIGN_EVAL_DYNAMIC_ROOT(this); + return; + } + DTRACK(CODESIGN_EVAL_DYNAMIC, this, (char*)this->staticCode()->mainExecutablePath().c_str()); + + // + // Do not reorder the operations below without thorough cogitation. There are + // interesting dependencies and significant performance issues. There is also + // client code that relies on errors being noticed in a particular order. + // + // For the most part, failure of (reliable) identity will cause exceptions to be + // thrown, and success is indicated by survival. If you make it to the end, + // you have won the validity race. (Good rat.) + // + + // check my host first, recursively + this->host()->checkValidity(flags); + + SecStaticCode *myDisk = this->staticCode(); + myDisk->setValidationFlags(flags); + SecStaticCode *hostDisk = this->host()->staticCode(); + + // check my static state + myDisk->validateDirectory(); + + // check my own dynamic state + if (!(this->host()->getGuestStatus(this) & kSecCodeStatusValid)) + MacOSError::throwMe(errSecCSGuestInvalid); + + // check that static and dynamic views are consistent + if (this->cdHash() && !CFEqual(this->cdHash(), myDisk->cdHash())) + MacOSError::throwMe(errSecCSStaticCodeChanged); + + // check host/guest constraints + if (!this->host()->isRoot()) { // not hosted by root of trust + myDisk->validateRequirements(kSecHostRequirementType, hostDisk, errSecCSHostReject); + hostDisk->validateRequirements(kSecGuestRequirementType, myDisk); + } +} + + +// +// By default, we track no validity for guests (we don't have any) +// +uint32_t SecCode::getGuestStatus(SecCode *guest) +{ + MacOSError::throwMe(errSecCSNoSuchCode); +} + +void SecCode::changeGuestStatus(SecCode *guest, SecCodeStatusOperation operation, CFDictionaryRef arguments) +{ + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +// +// Given a bag of attribute values, automagically come up with a SecCode +// without any other information. +// This is meant to be the "just do what makes sense" generic call, for callers +// who don't want to engage in the fascinating dance of manual guest enumeration. +// +// Note that we expect the logic embedded here to change over time (in backward +// compatible fashion, one hopes), and that it's all right to use heuristics here +// as long as it's done sensibly. +// +// Be warned that the present logic is quite a bit ad-hoc, and will likely not +// handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated +// hosting all that well. +// +SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) +{ + // special case: with no attributes at all, return the root of trust + if (CFDictionaryGetCount(attributes) == 0) + return KernelCode::active()->retain(); + + // main logic: we need a pid, and we'll take a canonical guest id as an option + int pid = 0; + if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid)) + CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); + if (SecCode *process = + KernelCode::active()->locateGuest(attributes)) { + SecPointer code; + code.take(process); // locateGuest gave us a retained object + if (code->staticCode()->flag(kSecCodeSignatureHost)) { + // might be a code host. Let's find out + CFRef rest = makeCFMutableDictionary(attributes); + CFDictionaryRemoveValue(rest, kSecGuestAttributePid); + if (SecCode *guest = code->locateGuest(rest)) + return guest; + } + if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { + // only "soft" attributes, and no hosting is happening. Return the (non-)host itself + return code.yield(); + } + } + MacOSError::throwMe(errSecCSNoSuchCode); +} + + +} // end namespace CodeSigning +} // end namespace Security