]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/cskernel.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / cskernel.cpp
diff --git a/libsecurity_codesigning/lib/cskernel.cpp b/libsecurity_codesigning/lib/cskernel.cpp
new file mode 100644 (file)
index 0000000..9821465
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+//
+// 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
+
+namespace Security {
+namespace CodeSigning {
+
+
+//
+// The running-kernel singletons
+//
+ModuleNexus<KernelCode::Globals> KernelCode::globals;
+
+KernelCode::Globals::Globals()
+{
+       code = new KernelCode;
+       staticCode = new KernelStaticCode;
+}
+
+KernelCode::KernelCode()
+       : SecCode(NULL)
+{
+}
+
+KernelStaticCode::KernelStaticCode()
+       : SecStaticCode(new KernelDiskRep())
+{
+}
+
+
+//
+// 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)
+{
+       if (CFTypeRef attr = CFDictionaryGetValue(attributes, kSecGuestAttributePid)) {
+               if (CFDictionaryGetCount(attributes) != 1)
+                       MacOSError::throwMe(errSecCSUnsupportedGuestAttributes); // had more
+               if (CFGetTypeID(attr) == CFNumberGetTypeID())
+                       return (new ProcessCode(cfNumber<pid_t>(CFNumberRef(attr))))->retain();
+               MacOSError::throwMe(errSecCSInvalidAttributeValues);
+       } else
+               MacOSError::throwMe(errSecCSUnsupportedGuestAttributes);
+}
+
+
+//
+// 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::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))) {
+                       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
+//
+SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest)
+{
+       if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) {
+               uint32_t pFlags;
+               csops(guest, CS_OPS_STATUS, &pFlags);
+               secdebug("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags);
+               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.
+//
+void KernelCode::identify()
+{
+       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();
+               }
+       }
+}
+
+
+} // CodeSigning
+} // Security