]>
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 | // 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 <sys/param.h> // MAXPATHLEN | |
41 | ||
42 | namespace Security { | |
43 | namespace CodeSigning { | |
44 | ||
45 | ||
46 | // | |
47 | // The running-kernel singletons | |
48 | // | |
49 | ModuleNexus<KernelCode::Globals> KernelCode::globals; | |
50 | ||
51 | KernelCode::Globals::Globals() | |
52 | { | |
53 | code = new KernelCode; | |
54 | staticCode = new KernelStaticCode; | |
55 | } | |
56 | ||
57 | KernelCode::KernelCode() | |
58 | : SecCode(NULL) | |
59 | { | |
60 | } | |
61 | ||
62 | KernelStaticCode::KernelStaticCode() | |
63 | : SecStaticCode(new KernelDiskRep()) | |
64 | { | |
65 | } | |
66 | ||
67 | ||
68 | // | |
69 | // Identify our guests (UNIX processes) by attribute. | |
70 | // The only supported lookup attribute is currently the pid. (We could support | |
71 | // task ports, but those can easily be mapped to pids.) | |
72 | // Note that we don't actually validate the pid here; if it's invalid, we'll notice | |
73 | // when we try to ask the kernel about it later. | |
74 | // | |
75 | SecCode *KernelCode::locateGuest(CFDictionaryRef attributes) | |
76 | { | |
77 | if (CFTypeRef attr = CFDictionaryGetValue(attributes, kSecGuestAttributePid)) { | |
78 | if (CFDictionaryGetCount(attributes) != 1) | |
79 | MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); // had more | |
80 | if (CFGetTypeID(attr) == CFNumberGetTypeID()) | |
81 | return (new ProcessCode(cfNumber<pid_t>(CFNumberRef(attr))))->retain(); | |
82 | MacOSError::throwMe(errSecCSInvalidAttributeValues); | |
83 | } else | |
84 | MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); | |
85 | } | |
86 | ||
87 | ||
88 | // | |
89 | // We map guests to disk by calling a kernel service. | |
90 | // It is here that we verify that our user-space concept of the code identity | |
91 | // matches the kernel's idea (to defeat just-in-time switching attacks). | |
92 | // | |
93 | SecStaticCode *KernelCode::identifyGuest(SecCode *iguest, CFDataRef *cdhash) | |
94 | { | |
95 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { | |
96 | char path[2 * MAXPATHLEN]; // reasonable upper limit | |
97 | if (::proc_pidpath(guest->pid(), path, sizeof(path))) { | |
98 | off_t offset; | |
99 | csops(guest, CS_OPS_PIDOFFSET, &offset, sizeof(offset)); | |
100 | SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, offset)); | |
101 | CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code); | |
102 | if (cdhash) { | |
103 | SHA1::Digest kernelHash; | |
104 | if (::csops(guest->pid(), CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1) | |
105 | switch (errno) { | |
106 | case EBADEXEC: // means "no CodeDirectory hash for this program" | |
107 | *cdhash = NULL; | |
108 | break; | |
109 | case ESRCH: | |
110 | MacOSError::throwMe(errSecCSNoSuchCode); | |
111 | default: | |
112 | UnixError::throwMe(); | |
113 | } | |
114 | else // succeeded | |
115 | *cdhash = makeCFData(kernelHash, sizeof(kernelHash)); | |
116 | CODESIGN_GUEST_CDHASH_PROCESS(guest, kernelHash, sizeof(kernelHash)); | |
117 | } | |
118 | return code.yield(); | |
119 | } else | |
120 | UnixError::throwMe(); | |
121 | } | |
122 | MacOSError::throwMe(errSecCSNoSuchCode); | |
123 | } | |
124 | ||
125 | ||
126 | // | |
127 | // We obtain the guest's status by asking the kernel | |
128 | // | |
129 | SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest) | |
130 | { | |
131 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { | |
132 | uint32_t pFlags; | |
133 | csops(guest, CS_OPS_STATUS, &pFlags); | |
134 | secdebug("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags); | |
135 | return pFlags; | |
136 | } else | |
137 | MacOSError::throwMe(errSecCSNoSuchCode); | |
138 | } | |
139 | ||
140 | ||
141 | // | |
142 | // We tell the kernel to make status changes | |
143 | // | |
144 | void KernelCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments) | |
145 | { | |
146 | if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) | |
147 | switch (operation) { | |
148 | case kSecCodeOperationNull: | |
149 | break; | |
150 | case kSecCodeOperationInvalidate: | |
151 | csops(guest, CS_OPS_MARKINVALID); | |
152 | break; | |
153 | case kSecCodeOperationSetHard: | |
154 | csops(guest, CS_OPS_MARKHARD); | |
155 | break; | |
156 | case kSecCodeOperationSetKill: | |
157 | csops(guest, CS_OPS_MARKKILL); | |
158 | break; | |
159 | default: | |
160 | MacOSError::throwMe(errSecCSUnimplemented); | |
161 | } | |
162 | else | |
163 | MacOSError::throwMe(errSecCSNoSuchCode); | |
164 | } | |
165 | ||
166 | ||
167 | // | |
168 | // The StaticCode for the running kernel is explicit. | |
169 | // We can't ask our own host for it, naturally. | |
170 | // | |
171 | void KernelCode::identify() | |
172 | { | |
173 | mStaticCode.take(globals().staticCode->retain()); | |
174 | // the kernel isn't currently signed, so we don't get a cdHash for it | |
175 | } | |
176 | ||
177 | ||
178 | // | |
179 | // Interface to kernel csops() system call. | |
180 | // | |
181 | void KernelCode::csops(ProcessCode *proc, unsigned int op, void *addr, size_t length) | |
182 | { | |
183 | if (::csops(proc->pid(), op, addr, length) == -1) { | |
184 | switch (errno) { | |
185 | case ESRCH: | |
186 | MacOSError::throwMe(errSecCSNoSuchCode); | |
187 | default: | |
188 | UnixError::throwMe(); | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | ||
194 | } // CodeSigning | |
195 | } // Security |