/* * Copyright (c) 2006-2007 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 <Security/SecCodeHost.h> #include "cskernel.h" #include <security_utilities/cfmunge.h> #include <security_utilities/debugging.h> 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<SecCode *>(&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(); 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(CFTemp<CFDictionaryRef>("{%O=%d}", kSecGuestAttributePid, pid))) { SecPointer<SecCode> 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<CFMutableDictionaryRef> 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