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