*/
//
-// cskernel - Kernel implementation of the Code Signing Host Interface
+// cskernel - Kernel implementation of the Code Signing Host Interface.
+//
+// The kernel host currently supports only UNIX processes as guests.
+// It tracks then by their pid. Perhaps one day we'll get a more stable
+// means of tracking processes that doesn't involve reusing identifiers.
+//
+// The kernel host could represent non-process guests one day. One candidate
+// are Kernel Extensions.
//
#include "cskernel.h"
#include "csprocess.h"
#include "kerneldiskrep.h"
+#include "machorep.h"
#include <libproc.h>
#include <sys/codesign.h>
#include <sys/param.h> // MAXPATHLEN
//
-// We locate a guest (process) by invoking a kernel service.
-// The only attributes supported are ("pid", pid_t).
-// (We could also support task ports if we liked, but those can be translated
-// to pids by the caller without trouble.)
+// Identify our guests (UNIX processes) by attribute.
+// The only supported lookup attribute is currently the pid. (We could support
+// task ports, but those can easily be mapped to pids.)
+// Note that we don't actually validate the pid here; if it's invalid, we'll notice
+// when we try to ask the kernel about it later.
//
SecCode *KernelCode::locateGuest(CFDictionaryRef attributes)
{
//
// We map guests to disk by calling a kernel service.
+// It is here that we verify that our user-space concept of the code identity
+// matches the kernel's idea (to defeat just-in-time switching attacks).
//
-SecStaticCode *KernelCode::mapGuestToStatic(SecCode *iguest)
+SecStaticCode *KernelCode::identifyGuest(SecCode *iguest, CFDataRef *cdhash)
{
if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) {
char path[2 * MAXPATHLEN]; // reasonable upper limit
- if (::proc_pidpath(guest->pid(), path, sizeof(path)))
- return (new ProcessStaticCode(DiskRep::bestGuess(path)))->retain();
- else
+ if (::proc_pidpath(guest->pid(), path, sizeof(path))) {
+ off_t offset;
+ csops(guest, CS_OPS_PIDOFFSET, &offset, sizeof(offset));
+ SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, offset));
+ CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code);
+ if (cdhash) {
+ SHA1::Digest kernelHash;
+ if (::csops(guest->pid(), CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1)
+ switch (errno) {
+ case EBADEXEC: // means "no CodeDirectory hash for this program"
+ *cdhash = NULL;
+ break;
+ case ESRCH:
+ MacOSError::throwMe(errSecCSNoSuchCode);
+ default:
+ UnixError::throwMe();
+ }
+ else // succeeded
+ *cdhash = makeCFData(kernelHash, sizeof(kernelHash));
+ CODESIGN_GUEST_CDHASH_PROCESS(guest, kernelHash, sizeof(kernelHash));
+ }
+ return code.yield();
+ } else
UnixError::throwMe();
}
MacOSError::throwMe(errSecCSNoSuchCode);
//
// We obtain the guest's status by asking the kernel
//
-uint32_t KernelCode::getGuestStatus(SecCode *iguest)
+SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest)
{
if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) {
uint32_t pFlags;
- if (::csops(guest->pid(), CS_OPS_STATUS, &pFlags, 0) == -1) {
- secdebug("kcode", "cannot get guest status of %p(%d) errno=%d",
- guest, guest->pid(), errno);
- switch (errno) {
- case ESRCH:
- MacOSError::throwMe(errSecCSNoSuchCode);
- default:
- UnixError::throwMe();
- }
- }
+ csops(guest, CS_OPS_STATUS, &pFlags);
secdebug("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags);
-
-#if defined(USERSPACE_VALIDATION)
- // Former static substitute for dynamic kernel validation of executable pages.
- // This is now done in the kernel's page-in path.
- guest->staticCode()->validateExecutable();
-#endif //USERSPACE_VALIDATION
-
return pFlags;
} else
MacOSError::throwMe(errSecCSNoSuchCode);
}
+//
+// We tell the kernel to make status changes
+//
+void KernelCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments)
+{
+ if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest))
+ switch (operation) {
+ case kSecCodeOperationNull:
+ break;
+ case kSecCodeOperationInvalidate:
+ csops(guest, CS_OPS_MARKINVALID);
+ break;
+ case kSecCodeOperationSetHard:
+ csops(guest, CS_OPS_MARKHARD);
+ break;
+ case kSecCodeOperationSetKill:
+ csops(guest, CS_OPS_MARKKILL);
+ break;
+ default:
+ MacOSError::throwMe(errSecCSUnimplemented);
+ }
+ else
+ MacOSError::throwMe(errSecCSNoSuchCode);
+}
+
+
//
// The StaticCode for the running kernel is explicit.
// We can't ask our own host for it, naturally.
//
-SecStaticCode *KernelCode::getStaticCode()
+void KernelCode::identify()
{
- return globals().staticCode->retain();
+ mStaticCode.take(globals().staticCode->retain());
+ // the kernel isn't currently signed, so we don't get a cdHash for it
+}
+
+
+//
+// Interface to kernel csops() system call.
+//
+void KernelCode::csops(ProcessCode *proc, unsigned int op, void *addr, size_t length)
+{
+ if (::csops(proc->pid(), op, addr, length) == -1) {
+ switch (errno) {
+ case ESRCH:
+ MacOSError::throwMe(errSecCSNoSuchCode);
+ default:
+ UnixError::throwMe();
+ }
+ }
}