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