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 <bsm/libbsm.h>
41 #include <security_utilities/cfmunge.h>
42 #include <sys/param.h> // MAXPATHLEN
45 namespace CodeSigning
{
49 // The running-kernel singletons
51 ModuleNexus
<KernelCode::Globals
> KernelCode::globals
;
53 KernelCode::Globals::Globals()
55 code
= new KernelCode
;
56 staticCode
= new KernelStaticCode
;
59 KernelCode::KernelCode()
64 KernelStaticCode::KernelStaticCode()
65 : SecStaticCode(new KernelDiskRep())
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.
77 SecCode
*KernelCode::locateGuest(CFDictionaryRef attributes
)
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
);
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
90 if (auditData
&& CFDataGetLength(auditData
) != sizeof(audit_token_t
))
91 MacOSError::throwMe(errSecCSInvalidAttributeValues
);
93 audit_token_t
* audit
= NULL
;
95 pid
= cfNumber
<pid_t
>(pidNumber
);
97 audit
= (audit_token_t
*)CFDataGetBytePtr(auditData
);
98 if (audit
&& pid
== 0)
99 pid
= audit_token_to_pid(*audit
);
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
);
109 diskRep
= new PidDiskRep(pid
, audit
, infoPlist
);
113 return (new ProcessCode(pid
, audit
, diskRep
))->retain();
115 MacOSError::throwMe(errSecCSUnimplemented
);
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).
125 SecStaticCode
*KernelCode::identifyGuest(SecCode
*iguest
, CFDataRef
*cdhash
)
127 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
)) {
129 if (guest
->pidBased()) {
131 SecPointer
<SecStaticCode
> code
= new ProcessDynamicCode(guest
);
132 guest
->pidBased()->setCredentials(code
->codeDirectory());
134 SHA1::Digest kernelHash
;
135 MacOSError::check(guest
->csops(CS_OPS_CDHASH
, kernelHash
, sizeof(kernelHash
)));
136 *cdhash
= makeCFData(kernelHash
, sizeof(kernelHash
));
141 char path
[2 * MAXPATHLEN
]; // reasonable upper limit
142 if (::proc_pidpath(guest
->pid(), path
, sizeof(path
))) {
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
);
148 SHA1::Digest kernelHash
;
149 if (guest
->csops(CS_OPS_CDHASH
, kernelHash
, sizeof(kernelHash
)) == -1)
151 case EBADEXEC
: // means "no CodeDirectory hash for this program"
155 MacOSError::throwMe(errSecCSNoSuchCode
);
157 UnixError::throwMe();
160 *cdhash
= makeCFData(kernelHash
, sizeof(kernelHash
));
161 CODESIGN_GUEST_CDHASH_PROCESS(guest
, kernelHash
, sizeof(kernelHash
));
165 UnixError::throwMe();
167 MacOSError::throwMe(errSecCSNoSuchCode
);
172 // We obtain the guest's status by asking the kernel
174 SecCodeStatus
KernelCode::getGuestStatus(SecCode
*iguest
)
176 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
)) {
178 csops(guest
, CS_OPS_STATUS
, &pFlags
);
179 secinfo("kcode", "guest %p(%d) kernel status 0x%x", guest
, guest
->pid(), pFlags
);
182 MacOSError::throwMe(errSecCSNoSuchCode
);
187 // We tell the kernel to make status changes
189 void KernelCode::changeGuestStatus(SecCode
*iguest
, SecCodeStatusOperation operation
, CFDictionaryRef arguments
)
191 if (ProcessCode
*guest
= dynamic_cast<ProcessCode
*>(iguest
))
193 case kSecCodeOperationNull
:
195 case kSecCodeOperationInvalidate
:
196 csops(guest
, CS_OPS_MARKINVALID
);
198 case kSecCodeOperationSetHard
:
199 csops(guest
, CS_OPS_MARKHARD
);
201 case kSecCodeOperationSetKill
:
202 csops(guest
, CS_OPS_MARKKILL
);
205 MacOSError::throwMe(errSecCSUnimplemented
);
208 MacOSError::throwMe(errSecCSNoSuchCode
);
213 // The StaticCode for the running kernel is explicit.
214 // We can't ask our own host for it, naturally.
216 void KernelCode::identify()
218 mStaticCode
.take(globals().staticCode
->retain());
219 // the kernel isn't currently signed, so we don't get a cdHash for it
224 // Interface to kernel csops() system call.
226 void KernelCode::csops(ProcessCode
*proc
, unsigned int op
, void *addr
, size_t length
)
228 if (proc
->csops(op
, addr
, length
) == -1) {
231 MacOSError::throwMe(errSecCSNoSuchCode
);
233 UnixError::throwMe();