1/* 2 * Copyright (c) 2006-2007,2011,2013 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24// 25// Code - SecCode API objects 26// 27#include "Code.h" 28#include "StaticCode.h" 29#include <Security/SecCodeHost.h> 30#include "cskernel.h" 31#include <security_utilities/cfmunge.h> 32#include <security_utilities/debugging.h> 33 34namespace Security { 35namespace CodeSigning { 36 37 38// 39// Construction 40// 41SecCode::SecCode(SecCode *host) 42 : mHost(host), mIdentified(false) 43{ 44 CODESIGN_DYNAMIC_CREATE(this, host); 45} 46 47 48// 49// Clean up a SecCode object 50// 51SecCode::~SecCode() throw() 52try { 53} catch (...) { 54 return; 55} 56 57 58// 59// CF-level comparison of SecStaticCode objects compares CodeDirectory hashes if signed, 60// and falls back on comparing canonical paths if (both are) not. 61// 62bool SecCode::equal(SecCFObject &secOther) 63{ 64 SecCode *other = static_cast<SecCode *>(&secOther); 65 CFDataRef mine = this->cdHash(); 66 CFDataRef his = other->cdHash(); 67 if (mine || his) 68 return mine && his && CFEqual(mine, his); 69 else 70 return this->staticCode()->equal(*other->staticCode()); 71} 72 73CFHashCode SecCode::hash() 74{ 75 if (CFDataRef h = this->cdHash()) 76 return CFHash(h); 77 else 78 return this->staticCode()->hash(); 79} 80 81 82// 83// Yield the host Code 84// 85SecCode *SecCode::host() const 86{ 87 return mHost; 88} 89 90 91// 92// Yield the static code. This is cached. 93// The caller does not own the object returned; it lives (at least) as long 94// as the SecCode it was derived from. 95// 96SecStaticCode *SecCode::staticCode() 97{ 98 if (!mIdentified) { 99 this->identify(); 100 mIdentified = true; 101 } 102 assert(mStaticCode); 103 return mStaticCode; 104} 105 106 107// 108// Yield the CodeDirectory hash as presented by our host. 109// This usually is the same as the hash of staticCode().codeDirectory(), but might not 110// if files are changing on disk while code is running. 111// 112CFDataRef SecCode::cdHash() 113{ 114 if (!mIdentified) { 115 this->identify(); 116 mIdentified = true; 117 } 118 return mCDHash; // can be NULL (host has no dynamic identity for guest) 119} 120 121 122// 123// Retrieve current dynamic status. 124// 125SecCodeStatus SecCode::status() 126{ 127 if (this->isRoot()) 128 return kSecCodeStatusValid; // root of trust, presumed valid 129 else 130 return this->host()->getGuestStatus(this); 131} 132 133void SecCode::status(SecCodeStatusOperation operation, CFDictionaryRef arguments) 134{ 135 if (this->isRoot()) 136 MacOSError::throwMe(errSecCSHostProtocolStateError); 137 else 138 this->host()->changeGuestStatus(this, operation, arguments); 139} 140 141 142// 143// By default, we have no guests 144// 145SecCode *SecCode::locateGuest(CFDictionaryRef) 146{ 147 return NULL; 148} 149 150 151// 152// By default, we self-identify by asking our host to identify us. 153// (This is currently only overridden in the root-of-trust (kernel) implementation.) 154// 155void SecCode::identify() 156{ 157 mStaticCode.take(host()->identifyGuest(this, &mCDHash.aref())); 158} 159 160 161// 162// The default implementation cannot map guests to disk 163// 164SecStaticCode *SecCode::identifyGuest(SecCode *, CFDataRef *) 165{ 166 MacOSError::throwMe(errSecCSNoSuchCode); 167} 168 169 170// 171// Master validation function. 172// 173// This is the most important function in all of Code Signing. It performs 174// dynamic validation on running code. Despite its simple structure, it does 175// everything that's needed to establish whether a Code is currently valid... 176// with a little help from StaticCode, format drivers, type drivers, and so on. 177// 178// This function validates internal requirements in the hosting chain. It does 179// not validate external requirements - the caller needs to do that with a separate call. 180// 181void SecCode::checkValidity(SecCSFlags flags) 182{ 183 if (this->isRoot()) { 184 // the root-of-trust is valid by definition 185 CODESIGN_EVAL_DYNAMIC_ROOT(this); 186 return; 187 } 188 DTRACK(CODESIGN_EVAL_DYNAMIC, this, (char*)this->staticCode()->mainExecutablePath().c_str()); 189 190 // 191 // Do not reorder the operations below without thorough cogitation. There are 192 // interesting dependencies and significant performance issues. There is also 193 // client code that relies on errors being noticed in a particular order. 194 // 195 // For the most part, failure of (reliable) identity will cause exceptions to be 196 // thrown, and success is indicated by survival. If you make it to the end, 197 // you have won the validity race. (Good rat.) 198 // 199 200 // check my host first, recursively 201 this->host()->checkValidity(flags); 202 203 SecStaticCode *myDisk = this->staticCode(); 204 myDisk->setValidationFlags(flags); 205 SecStaticCode *hostDisk = this->host()->staticCode(); 206 207 // check my static state 208 myDisk->validateDirectory(); 209 210 // check my own dynamic state 211 if (!(this->host()->getGuestStatus(this) & kSecCodeStatusValid)) 212 MacOSError::throwMe(errSecCSGuestInvalid); 213 214 // check that static and dynamic views are consistent 215 if (this->cdHash() && !CFEqual(this->cdHash(), myDisk->cdHash())) 216 MacOSError::throwMe(errSecCSStaticCodeChanged); 217 218 // check host/guest constraints 219 if (!this->host()->isRoot()) { // not hosted by root of trust 220 myDisk->validateRequirements(kSecHostRequirementType, hostDisk, errSecCSHostReject); 221 hostDisk->validateRequirements(kSecGuestRequirementType, myDisk); 222 } 223} 224 225 226// 227// By default, we track no validity for guests (we don't have any) 228// 229uint32_t SecCode::getGuestStatus(SecCode *guest) 230{ 231 MacOSError::throwMe(errSecCSNoSuchCode); 232} 233 234void SecCode::changeGuestStatus(SecCode *guest, SecCodeStatusOperation operation, CFDictionaryRef arguments) 235{ 236 MacOSError::throwMe(errSecCSNoSuchCode); 237} 238 239 240// 241// Given a bag of attribute values, automagically come up with a SecCode 242// without any other information. 243// This is meant to be the "just do what makes sense" generic call, for callers 244// who don't want to engage in the fascinating dance of manual guest enumeration. 245// 246// Note that we expect the logic embedded here to change over time (in backward 247// compatible fashion, one hopes), and that it's all right to use heuristics here 248// as long as it's done sensibly. 249// 250// Be warned that the present logic is quite a bit ad-hoc, and will likely not 251// handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated 252// hosting all that well. 253// 254SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) 255{ 256 // special case: with no attributes at all, return the root of trust 257 if (CFDictionaryGetCount(attributes) == 0) 258 return KernelCode::active()->retain(); 259 260 // main logic: we need a pid, and we'll take a canonical guest id as an option 261 int pid = 0; 262 if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid)) 263 CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); 264 if (SecCode *process = 265 KernelCode::active()->locateGuest(attributes)) { 266 SecPointer<SecCode> code; 267 code.take(process); // locateGuest gave us a retained object 268 if (code->staticCode()->flag(kSecCodeSignatureHost)) { 269 // might be a code host. Let's find out 270 CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes); 271 CFDictionaryRemoveValue(rest, kSecGuestAttributePid); 272 if (SecCode *guest = code->locateGuest(rest)) 273 return guest; 274 } 275 if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { 276 // only "soft" attributes, and no hosting is happening. Return the (non-)host itself 277 return code.yield(); 278 } 279 } 280 MacOSError::throwMe(errSecCSNoSuchCode); 281} 282 283 284} // end namespace CodeSigning 285} // end namespace Security 286