]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2006-2007 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 | ||
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() throw() | |
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 | SecStaticCode *hostDisk = this->host()->staticCode(); | |
205 | ||
206 | // check my static state | |
207 | myDisk->validateDirectory(); | |
208 | ||
209 | // check my own dynamic state | |
210 | if (!(this->host()->getGuestStatus(this) & kSecCodeStatusValid)) | |
211 | MacOSError::throwMe(errSecCSGuestInvalid); | |
212 | ||
213 | // check that static and dynamic views are consistent | |
214 | if (this->cdHash() && !CFEqual(this->cdHash(), myDisk->cdHash())) | |
215 | MacOSError::throwMe(errSecCSStaticCodeChanged); | |
216 | ||
217 | // check host/guest constraints | |
218 | if (!this->host()->isRoot()) { // not hosted by root of trust | |
219 | myDisk->validateRequirements(kSecHostRequirementType, hostDisk, errSecCSHostReject); | |
220 | hostDisk->validateRequirements(kSecGuestRequirementType, myDisk); | |
221 | } | |
222 | } | |
223 | ||
224 | ||
225 | // | |
226 | // By default, we track no validity for guests (we don't have any) | |
227 | // | |
228 | uint32_t SecCode::getGuestStatus(SecCode *guest) | |
229 | { | |
230 | MacOSError::throwMe(errSecCSNoSuchCode); | |
231 | } | |
232 | ||
233 | void SecCode::changeGuestStatus(SecCode *guest, SecCodeStatusOperation operation, CFDictionaryRef arguments) | |
234 | { | |
235 | MacOSError::throwMe(errSecCSNoSuchCode); | |
236 | } | |
237 | ||
238 | ||
239 | // | |
240 | // Given a bag of attribute values, automagically come up with a SecCode | |
241 | // without any other information. | |
242 | // This is meant to be the "just do what makes sense" generic call, for callers | |
243 | // who don't want to engage in the fascinating dance of manual guest enumeration. | |
244 | // | |
245 | // Note that we expect the logic embedded here to change over time (in backward | |
246 | // compatible fashion, one hopes), and that it's all right to use heuristics here | |
247 | // as long as it's done sensibly. | |
248 | // | |
249 | // Be warned that the present logic is quite a bit ad-hoc, and will likely not | |
250 | // handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated | |
251 | // hosting all that well. | |
252 | // | |
253 | SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags) | |
254 | { | |
255 | // special case: with no attributes at all, return the root of trust | |
256 | if (CFDictionaryGetCount(attributes) == 0) | |
257 | return KernelCode::active()->retain(); | |
258 | ||
259 | // main logic: we need a pid, and we'll take a canonical guest id as an option | |
260 | int pid = 0; | |
261 | if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid)) | |
262 | CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes); | |
263 | if (SecCode *process = | |
427c49bc | 264 | KernelCode::active()->locateGuest(attributes)) { |
b1ab9ed8 A |
265 | SecPointer<SecCode> code; |
266 | code.take(process); // locateGuest gave us a retained object | |
267 | if (code->staticCode()->flag(kSecCodeSignatureHost)) { | |
268 | // might be a code host. Let's find out | |
269 | CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes); | |
270 | CFDictionaryRemoveValue(rest, kSecGuestAttributePid); | |
271 | if (SecCode *guest = code->locateGuest(rest)) | |
272 | return guest; | |
273 | } | |
274 | if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) { | |
275 | // only "soft" attributes, and no hosting is happening. Return the (non-)host itself | |
276 | return code.yield(); | |
277 | } | |
278 | } | |
279 | MacOSError::throwMe(errSecCSNoSuchCode); | |
280 | } | |
281 | ||
282 | ||
283 | } // end namespace CodeSigning | |
284 | } // end namespace Security |