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