]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006-2007,2011-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 | // cskernel - Kernel implementation of the Code Signing Host Interface. | |
26 | // | |
27 | // The kernel host currently supports only UNIX processes as guests. | |
28 | // It tracks then by their pid. Perhaps one day we'll get a more stable | |
29 | // means of tracking processes that doesn't involve reusing identifiers. | |
30 | // | |
31 | // The kernel host could represent non-process guests one day. One candidate | |
32 | // are Kernel Extensions. | |
33 | // | |
34 | #include "cskernel.h" | |
35 | #include "csprocess.h" | |
36 | #include "kerneldiskrep.h" | |
37 | #include "machorep.h" | |
38 | #include <libproc.h> | |
39 | #include <sys/codesign.h> | |
fa7225c8 A |
40 | #include <bsm/libbsm.h> |
41 | #include <security_utilities/cfmunge.h> | |
b1ab9ed8 A |
42 | #include <sys/param.h> // MAXPATHLEN |
43 | ||
44 | namespace Security { | |
45 | namespace CodeSigning { | |
46 | ||
47 | ||
48 | // | |
49 | // The running-kernel singletons | |
50 | // | |
51 | ModuleNexus<KernelCode::Globals> KernelCode::globals; | |
52 | ||
53 | KernelCode::Globals::Globals() | |
54 | { | |
55 | code = new KernelCode; | |
56 | staticCode = new KernelStaticCode; | |
57 | } | |
58 | ||
59 | KernelCode::KernelCode() | |
60 | : SecCode(NULL) | |
61 | { | |
62 | } | |
63 | ||
64 | KernelStaticCode::KernelStaticCode() | |
65 | : SecStaticCode(new KernelDiskRep()) | |
66 | { | |
67 | } | |
68 | ||
69 | ||
70 | // | |
71 | // Identify our guests (UNIX processes) by attribute. | |
fa7225c8 A |
72 | // We support either pid or audit token (which contains the pid). If we get both, |
73 | // we record them both and let the kernel sort them out. | |
b1ab9ed8 A |
74 | // Note that we don't actually validate the pid here; if it's invalid, we'll notice |
75 | // when we try to ask the kernel about it later. | |
76 | // | |
77 | SecCode *KernelCode::locateGuest(CFDictionaryRef attributes) | |
78 | { | |
866f8763 | 79 | #if TARGET_OS_OSX |
fa7225c8 A |
80 | CFNumberRef pidNumber = NULL; |
81 | CFDataRef auditData = NULL; | |
82 | cfscan(attributes, "{%O=%NO}", kSecGuestAttributePid, &pidNumber); | |
83 | cfscan(attributes, "{%O=%XO}", kSecGuestAttributeAudit, &auditData); | |
84 | if (pidNumber == NULL && auditData == NULL) | |
b1ab9ed8 | 85 | MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); |
fa7225c8 A |
86 | |
87 | // Extract information from pid and audit token as presented. We need at least one. | |
88 | // If both are specified, we pass them both to the kernel, which will fail if they | |
89 | // don't agree. | |
90 | if (auditData && CFDataGetLength(auditData) != sizeof(audit_token_t)) | |
91 | MacOSError::throwMe(errSecCSInvalidAttributeValues); | |
92 | pid_t pid = 0; | |
93 | audit_token_t* audit = NULL; | |
94 | if (pidNumber) | |
95 | pid = cfNumber<pid_t>(pidNumber); | |
96 | if (auditData) | |
97 | audit = (audit_token_t*)CFDataGetBytePtr(auditData); | |
98 | if (audit && pid == 0) | |
99 | pid = audit_token_to_pid(*audit); | |
100 | ||
101 | // handle requests for server-based validation | |
102 | RefPointer<PidDiskRep> diskRep = NULL; | |
103 | if (CFDictionaryGetValue(attributes, kSecGuestAttributeDynamicCode) != NULL) { | |
104 | CFDataRef infoPlist = (CFDataRef)CFDictionaryGetValue(attributes, kSecGuestAttributeDynamicCodeInfoPlist); | |
105 | if (infoPlist && CFGetTypeID(infoPlist) != CFDataGetTypeID()) | |
106 | MacOSError::throwMe(errSecCSInvalidAttributeValues); | |
107 | ||
108 | try { | |
109 | diskRep = new PidDiskRep(pid, infoPlist); | |
110 | } catch (...) { } | |
111 | } | |
112 | ||
113 | return (new ProcessCode(pid, audit, diskRep))->retain(); | |
866f8763 A |
114 | #else |
115 | MacOSError::throwMe(errSecCSUnimplemented); | |
116 | #endif | |
b1ab9ed8 A |
117 | } |
118 | ||
119 | ||
120 | // | |
121 | // We map guests to disk by calling a kernel service. | |
122 | // It is here that we verify that our user-space concept of the code identity | |
123 | // matches the kernel's idea (to defeat just-in-time switching attacks). | |
124 | // | |
125 | SecStaticCode *KernelCode::identifyGuest(SecCode *iguest, CFDataRef *cdhash) | |
126 | { | |
127 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { | |
427c49bc A |
128 | |
129 | if (guest->pidBased()) { | |
130 | ||
131 | SecPointer<SecStaticCode> code = new ProcessDynamicCode(guest); | |
fa7225c8 | 132 | guest->pidBased()->setCredentials(code->codeDirectory()); |
427c49bc A |
133 | |
134 | SHA1::Digest kernelHash; | |
fa7225c8 | 135 | MacOSError::check(guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash))); |
427c49bc A |
136 | *cdhash = makeCFData(kernelHash, sizeof(kernelHash)); |
137 | ||
138 | return code.yield(); | |
139 | } | |
140 | ||
b1ab9ed8 A |
141 | char path[2 * MAXPATHLEN]; // reasonable upper limit |
142 | if (::proc_pidpath(guest->pid(), path, sizeof(path))) { | |
143 | off_t offset; | |
144 | csops(guest, CS_OPS_PIDOFFSET, &offset, sizeof(offset)); | |
427c49bc | 145 | SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, (size_t)offset)); |
b1ab9ed8 A |
146 | CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code); |
147 | if (cdhash) { | |
148 | SHA1::Digest kernelHash; | |
fa7225c8 | 149 | if (guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1) |
b1ab9ed8 A |
150 | switch (errno) { |
151 | case EBADEXEC: // means "no CodeDirectory hash for this program" | |
152 | *cdhash = NULL; | |
153 | break; | |
154 | case ESRCH: | |
155 | MacOSError::throwMe(errSecCSNoSuchCode); | |
156 | default: | |
157 | UnixError::throwMe(); | |
158 | } | |
159 | else // succeeded | |
160 | *cdhash = makeCFData(kernelHash, sizeof(kernelHash)); | |
161 | CODESIGN_GUEST_CDHASH_PROCESS(guest, kernelHash, sizeof(kernelHash)); | |
162 | } | |
163 | return code.yield(); | |
164 | } else | |
165 | UnixError::throwMe(); | |
166 | } | |
167 | MacOSError::throwMe(errSecCSNoSuchCode); | |
168 | } | |
169 | ||
170 | ||
171 | // | |
172 | // We obtain the guest's status by asking the kernel | |
173 | // | |
174 | SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest) | |
175 | { | |
176 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { | |
177 | uint32_t pFlags; | |
178 | csops(guest, CS_OPS_STATUS, &pFlags); | |
fa7225c8 | 179 | secinfo("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags); |
b1ab9ed8 A |
180 | return pFlags; |
181 | } else | |
182 | MacOSError::throwMe(errSecCSNoSuchCode); | |
183 | } | |
184 | ||
185 | ||
186 | // | |
187 | // We tell the kernel to make status changes | |
188 | // | |
189 | void KernelCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments) | |
190 | { | |
191 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) | |
192 | switch (operation) { | |
193 | case kSecCodeOperationNull: | |
194 | break; | |
195 | case kSecCodeOperationInvalidate: | |
196 | csops(guest, CS_OPS_MARKINVALID); | |
197 | break; | |
198 | case kSecCodeOperationSetHard: | |
199 | csops(guest, CS_OPS_MARKHARD); | |
200 | break; | |
201 | case kSecCodeOperationSetKill: | |
202 | csops(guest, CS_OPS_MARKKILL); | |
203 | break; | |
204 | default: | |
205 | MacOSError::throwMe(errSecCSUnimplemented); | |
206 | } | |
207 | else | |
208 | MacOSError::throwMe(errSecCSNoSuchCode); | |
209 | } | |
210 | ||
211 | ||
212 | // | |
213 | // The StaticCode for the running kernel is explicit. | |
214 | // We can't ask our own host for it, naturally. | |
215 | // | |
216 | void KernelCode::identify() | |
217 | { | |
218 | mStaticCode.take(globals().staticCode->retain()); | |
219 | // the kernel isn't currently signed, so we don't get a cdHash for it | |
220 | } | |
221 | ||
222 | ||
223 | // | |
224 | // Interface to kernel csops() system call. | |
225 | // | |
226 | void KernelCode::csops(ProcessCode *proc, unsigned int op, void *addr, size_t length) | |
227 | { | |
fa7225c8 | 228 | if (proc->csops(op, addr, length) == -1) { |
b1ab9ed8 A |
229 | switch (errno) { |
230 | case ESRCH: | |
231 | MacOSError::throwMe(errSecCSNoSuchCode); | |
232 | default: | |
233 | UnixError::throwMe(); | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | ||
239 | } // CodeSigning | |
240 | } // Security |