]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/cskernel.cpp
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / cskernel.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 // 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>
40 #include <bsm/libbsm.h>
41 #include <security_utilities/cfmunge.h>
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.
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.
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 {
79 #if TARGET_OS_OSX
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)
85 MacOSError::throwMe(errSecCSUnsupportedGuestAttributes);
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, audit, infoPlist);
110 } catch (...) { }
111 }
112
113 return (new ProcessCode(pid, audit, diskRep))->retain();
114 #else
115 MacOSError::throwMe(errSecCSUnimplemented);
116 #endif
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)) {
128
129 if (guest->pidBased()) {
130
131 SecPointer<SecStaticCode> code = new ProcessDynamicCode(guest);
132 guest->pidBased()->setCredentials(code->codeDirectory());
133
134 SHA1::Digest kernelHash;
135 MacOSError::check(guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)));
136 *cdhash = makeCFData(kernelHash, sizeof(kernelHash));
137
138 return code.yield();
139 }
140
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));
145 SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, (size_t)offset));
146 CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code);
147 if (cdhash) {
148 SHA1::Digest kernelHash;
149 if (guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1)
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);
179 secinfo("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags);
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 {
228 if (proc->csops(op, addr, length) == -1) {
229 switch (errno) {
230 case ESRCH:
231 MacOSError::throwMe(errSecCSNoSuchCode);
232 default:
233 UnixError::throwMe();
234 }
235 }
236 }
237
238
239 } // CodeSigning
240 } // Security