2 * Copyright (c) 2006-2007,2011-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // cskernel - Kernel implementation of the Code Signing Host Interface.
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.
31 // The kernel host could represent non-process guests one day. One candidate
32 // are Kernel Extensions.
35 #include "csprocess.h"
36 #include "kerneldiskrep.h"
39 #include <sys/codesign.h>
40 #include <sys/param.h> // MAXPATHLEN
43 namespace CodeSigning
{
47 // The running-kernel singletons
49 ModuleNexus
<KernelCode::Globals
> KernelCode::globals
;
51 KernelCode::Globals::Globals()
53 code
= new KernelCode
;
54 staticCode
= new KernelStaticCode
;
57 KernelCode::KernelCode()
62 KernelStaticCode::KernelStaticCode()
63 : SecStaticCode(new KernelDiskRep())
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.
75 SecCode
*KernelCode::locateGuest(CFDictionaryRef attributes
)
77 if (CFTypeRef attr
= CFDictionaryGetValue(attributes
, kSecGuestAttributePid
)) {
78 RefPointer
<PidDiskRep
> diskRep
= NULL
;
80 if (CFGetTypeID(attr
) != CFNumberGetTypeID())
81 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
83 pid_t pid
= cfNumber
<pid_t
>(CFNumberRef(attr
));
85 if (CFDictionaryGetValue(attributes
, kSecGuestAttributeDynamicCode
) != NULL
) {
86 CFDataRef infoPlist
= (CFDataRef
)CFDictionaryGetValue(attributes
, kSecGuestAttributeDynamicCodeInfoPlist
);
87 if (infoPlist
&& CFGetTypeID(infoPlist
) != CFDataGetTypeID())
88 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
91 diskRep
= new PidDiskRep(pid
, infoPlist
);
94 return (new ProcessCode(cfNumber
<pid_t
>(CFNumberRef(attr
)), diskRep
))->retain();
96 MacOSError::throwMe(errSecCSUnsupportedGuestAttributes
);
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).
105 SecStaticCode
*KernelCode::identifyGuest(SecCode
*iguest
, CFDataRef
*cdhash
)
107 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
)) {
109 if (guest
->pidBased()) {
111 SecPointer
<SecStaticCode
> code
= new ProcessDynamicCode(guest
);
113 SHA1::Digest kernelHash
;
114 MacOSError::check(::csops(guest
->pid(), CS_OPS_CDHASH
, kernelHash
, sizeof(kernelHash
)));
115 *cdhash
= makeCFData(kernelHash
, sizeof(kernelHash
));
120 char path
[2 * MAXPATHLEN
]; // reasonable upper limit
121 if (::proc_pidpath(guest
->pid(), path
, sizeof(path
))) {
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
);
127 SHA1::Digest kernelHash
;
128 if (::csops(guest
->pid(), CS_OPS_CDHASH
, kernelHash
, sizeof(kernelHash
)) == -1)
130 case EBADEXEC
: // means "no CodeDirectory hash for this program"
134 MacOSError::throwMe(errSecCSNoSuchCode
);
136 UnixError::throwMe();
139 *cdhash
= makeCFData(kernelHash
, sizeof(kernelHash
));
140 CODESIGN_GUEST_CDHASH_PROCESS(guest
, kernelHash
, sizeof(kernelHash
));
144 UnixError::throwMe();
146 MacOSError::throwMe(errSecCSNoSuchCode
);
151 // We obtain the guest's status by asking the kernel
153 SecCodeStatus
KernelCode::getGuestStatus(SecCode
*iguest
)
155 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
)) {
157 csops(guest
, CS_OPS_STATUS
, &pFlags
);
158 secdebug("kcode", "guest %p(%d) kernel status 0x%x", guest
, guest
->pid(), pFlags
);
161 MacOSError::throwMe(errSecCSNoSuchCode
);
166 // We tell the kernel to make status changes
168 void KernelCode::changeGuestStatus(SecCode
*iguest
, SecCodeStatusOperation operation
, CFDictionaryRef arguments
)
170 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
))
172 case kSecCodeOperationNull
:
174 case kSecCodeOperationInvalidate
:
175 csops(guest
, CS_OPS_MARKINVALID
);
177 case kSecCodeOperationSetHard
:
178 csops(guest
, CS_OPS_MARKHARD
);
180 case kSecCodeOperationSetKill
:
181 csops(guest
, CS_OPS_MARKKILL
);
184 MacOSError::throwMe(errSecCSUnimplemented
);
187 MacOSError::throwMe(errSecCSNoSuchCode
);
192 // The StaticCode for the running kernel is explicit.
193 // We can't ask our own host for it, naturally.
195 void KernelCode::identify()
197 mStaticCode
.take(globals().staticCode
->retain());
198 // the kernel isn't currently signed, so we don't get a cdHash for it
203 // Interface to kernel csops() system call.
205 void KernelCode::csops(ProcessCode
*proc
, unsigned int op
, void *addr
, size_t length
)
207 if (::csops(proc
->pid(), op
, addr
, length
) == -1) {
210 MacOSError::throwMe(errSecCSNoSuchCode
);
212 UnixError::throwMe();