Code.cpp
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 "cskernel.h" 30 #include <security_utilities/cfmunge.h> 31 #include <security_utilities/debugging.h> 32 #include "SecInternalReleasePriv.h" 33 34 namespace Security { 35 namespace CodeSigning { 36 37 38 // 39 // Construction 40 // 41 SecCode::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 // 51 SecCode::~SecCode() _NOEXCEPT 52 try { 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 // 62 bool 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 73 CFHashCode 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 // 85 SecCode *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 // 96 SecStaticCode *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 // 112 CFDataRef 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 // 125 SecCodeStatus 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 133 void 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 // 145 SecCode *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 // 155 void 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 // 164 SecStaticCode *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 // 181 void 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->validateNonResourceComponents(); // also validates the CodeDirectory 209 if (flags & kSecCSStrictValidate) { 210 myDisk->diskRep()->strictValidate(myDisk->codeDirectory(), DiskRep::ToleratedErrors(), flags); 211 } else if (flags & kSecCSStrictValidateStructure) { 212 myDisk->diskRep()->strictValidateStructure(myDisk->codeDirectory(), DiskRep::ToleratedErrors(), flags); 213 } 214 215 // check my own dynamic state 216 SecCodeStatus dynamic_status = this->host()->getGuestStatus(this); 217 bool isValid = (dynamic_status & kSecCodeStatusValid) != 0; 218 if (!isValid) { 219 bool isDebugged = (dynamic_status & kSecCodeStatusDebugged) != 0; 220 bool isPlatform = (dynamic_status & kSecCodeStatusPlatform) != 0; 221 bool isInternal = SecIsInternalRelease(); 222 223 if (!isDebugged || (isPlatform && !isInternal)) { 224 // fatal if the code is invalid and not being debugged, but 225 // never let platform code be debugged except on internal systems. 226 MacOSError::throwMe(errSecCSGuestInvalid); 227 } 228 } 229 230 // check that static and dynamic views are consistent 231 if (this->cdHash() && !CFEqual(this->cdHash(), myDisk->cdHash())) 232 MacOSError::throwMe(errSecCSStaticCodeChanged); 233 234 // check host/guest constraints 235 if (!this->host()->isRoot()) { // not hosted by root of trust 236 myDisk->validateRequirements(kSecHostRequirementType, hostDisk, errSecCSHostReject); 237 hostDisk->validateRequirements(kSecGuestRequirementType, myDisk); 238 } 239 } 240 241 242 // 243 // By default, we track no validity for guests (we don't have any) 244 // 245 uint32_t SecCode::getGuestStatus(SecCode *guest) 246 { 247 MacOSError::throwMe(errSecCSNoSuchCode); 248 } 249 250 void SecCode::changeGuestStatus(SecCode *guest, SecCodeStatusOperation operation, CFDictionaryRef arguments) 251 { 252 MacOSError::throwMe(errSecCSNoSuchCode); 253 } 254 255 256 // 257 // Given a bag of attribute values, automagically come up with a SecCode 258 // without any other information. 259 // This is meant to be the "just do what makes sense" generic call, for callers 260 // who don't want to engage in the fascinating dance of manual guest enumeration. 261 // 262 // Note that we expect the logic embedded here to change over time (in backward 263 // compatible fashion, one hopes), and that it's all right to use heuristics here 264 // as long as it's done sensibly. 265 // 266 // Be warned that the present logic is quite a bit ad-hoc, and will likely not 267 // handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated 268 // hosting all that well. 269 // 270 SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) 271 { 272 #if TARGET_OS_OSX 273 // special case: with no attributes at all, return the root of trust 274 if (CFDictionaryGetCount(attributes) == 0) 275 return KernelCode::active()->retain(); 276 277 // main logic: we need a pid or audit trailer; everything else goes to the guests 278 if (CFDictionaryGetValue(attributes, kSecGuestAttributePid) == NULL 279 && CFDictionaryGetValue(attributes, kSecGuestAttributeAudit) == NULL) 280 CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); 281 if (SecCode *process = 282 KernelCode::active()->locateGuest(attributes)) { 283 SecPointer<SecCode> code; 284 code.take(process); // locateGuest gave us a retained object 285 if (code->staticCode()->flag(kSecCodeSignatureHost)) { 286 // might be a code host. Let's find out 287 CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes); 288 CFDictionaryRemoveValue(rest, kSecGuestAttributePid); 289 CFDictionaryRemoveValue(rest, kSecGuestAttributeAudit); 290 if (SecCode *guest = code->locateGuest(rest)) 291 return guest; 292 } 293 if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { 294 // only "soft" attributes, and no hosting is happening. Return the (non-)host itself 295 return code.yield(); 296 } 297 } 298 #endif // TARGET_OS_OSX 299 MacOSError::throwMe(errSecCSNoSuchCode); 300 } 301 302 303 } // end namespace CodeSigning 304 } // end namespace Security