]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/cskernel.cpp
Security-57740.51.3.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 CFNumberRef pidNumber = NULL;
80 CFDataRef auditData = NULL;
81 cfscan(attributes, "{%O=%NO}", kSecGuestAttributePid, &pidNumber);
82 cfscan(attributes, "{%O=%XO}", kSecGuestAttributeAudit, &auditData);
83 if (pidNumber == NULL && auditData == NULL)
84 MacOSError::throwMe(errSecCSUnsupportedGuestAttributes);
85
86 // Extract information from pid and audit token as presented. We need at least one.
87 // If both are specified, we pass them both to the kernel, which will fail if they
88 // don't agree.
89 if (auditData && CFDataGetLength(auditData) != sizeof(audit_token_t))
90 MacOSError::throwMe(errSecCSInvalidAttributeValues);
91 pid_t pid = 0;
92 audit_token_t* audit = NULL;
93 if (pidNumber)
94 pid = cfNumber<pid_t>(pidNumber);
95 if (auditData)
96 audit = (audit_token_t*)CFDataGetBytePtr(auditData);
97 if (audit && pid == 0)
98 pid = audit_token_to_pid(*audit);
99
100 // handle requests for server-based validation
101 RefPointer<PidDiskRep> diskRep = NULL;
102 if (CFDictionaryGetValue(attributes, kSecGuestAttributeDynamicCode) != NULL) {
103 CFDataRef infoPlist = (CFDataRef)CFDictionaryGetValue(attributes, kSecGuestAttributeDynamicCodeInfoPlist);
104 if (infoPlist && CFGetTypeID(infoPlist) != CFDataGetTypeID())
105 MacOSError::throwMe(errSecCSInvalidAttributeValues);
106
107 try {
108 diskRep = new PidDiskRep(pid, infoPlist);
109 } catch (...) { }
110 }
111
112 return (new ProcessCode(pid, audit, diskRep))->retain();
113 }
114
115
116 //
117 // We map guests to disk by calling a kernel service.
118 // It is here that we verify that our user-space concept of the code identity
119 // matches the kernel's idea (to defeat just-in-time switching attacks).
120 //
121 SecStaticCode *KernelCode::identifyGuest(SecCode *iguest, CFDataRef *cdhash)
122 {
123 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) {
124
125 if (guest->pidBased()) {
126
127 SecPointer<SecStaticCode> code = new ProcessDynamicCode(guest);
128 guest->pidBased()->setCredentials(code->codeDirectory());
129
130 SHA1::Digest kernelHash;
131 MacOSError::check(guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)));
132 *cdhash = makeCFData(kernelHash, sizeof(kernelHash));
133
134 return code.yield();
135 }
136
137 char path[2 * MAXPATHLEN]; // reasonable upper limit
138 if (::proc_pidpath(guest->pid(), path, sizeof(path))) {
139 off_t offset;
140 csops(guest, CS_OPS_PIDOFFSET, &offset, sizeof(offset));
141 SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, (size_t)offset));
142 CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code);
143 if (cdhash) {
144 SHA1::Digest kernelHash;
145 if (guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1)
146 switch (errno) {
147 case EBADEXEC: // means "no CodeDirectory hash for this program"
148 *cdhash = NULL;
149 break;
150 case ESRCH:
151 MacOSError::throwMe(errSecCSNoSuchCode);
152 default:
153 UnixError::throwMe();
154 }
155 else // succeeded
156 *cdhash = makeCFData(kernelHash, sizeof(kernelHash));
157 CODESIGN_GUEST_CDHASH_PROCESS(guest, kernelHash, sizeof(kernelHash));
158 }
159 return code.yield();
160 } else
161 UnixError::throwMe();
162 }
163 MacOSError::throwMe(errSecCSNoSuchCode);
164 }
165
166
167 //
168 // We obtain the guest's status by asking the kernel
169 //
170 SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest)
171 {
172 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) {
173 uint32_t pFlags;
174 csops(guest, CS_OPS_STATUS, &pFlags);
175 secinfo("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags);
176 return pFlags;
177 } else
178 MacOSError::throwMe(errSecCSNoSuchCode);
179 }
180
181
182 //
183 // We tell the kernel to make status changes
184 //
185 void KernelCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments)
186 {
187 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest))
188 switch (operation) {
189 case kSecCodeOperationNull:
190 break;
191 case kSecCodeOperationInvalidate:
192 csops(guest, CS_OPS_MARKINVALID);
193 break;
194 case kSecCodeOperationSetHard:
195 csops(guest, CS_OPS_MARKHARD);
196 break;
197 case kSecCodeOperationSetKill:
198 csops(guest, CS_OPS_MARKKILL);
199 break;
200 default:
201 MacOSError::throwMe(errSecCSUnimplemented);
202 }
203 else
204 MacOSError::throwMe(errSecCSNoSuchCode);
205 }
206
207
208 //
209 // The StaticCode for the running kernel is explicit.
210 // We can't ask our own host for it, naturally.
211 //
212 void KernelCode::identify()
213 {
214 mStaticCode.take(globals().staticCode->retain());
215 // the kernel isn't currently signed, so we don't get a cdHash for it
216 }
217
218
219 //
220 // Interface to kernel csops() system call.
221 //
222 void KernelCode::csops(ProcessCode *proc, unsigned int op, void *addr, size_t length)
223 {
224 if (proc->csops(op, addr, length) == -1) {
225 switch (errno) {
226 case ESRCH:
227 MacOSError::throwMe(errSecCSNoSuchCode);
228 default:
229 UnixError::throwMe();
230 }
231 }
232 }
233
234
235 } // CodeSigning
236 } // Security