cskernel.cpp
1 /* 2 * Copyright (c) 2006-2007,2011-2013 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 <bsm/libbsm.h> 41 #include <security_utilities/cfmunge.h> 42 #include <sys/param.h> // MAXPATHLEN 43 44 namespace Security { 45 namespace CodeSigning { 46 47 48 // 49 // The running-kernel singletons 50 // 51 ModuleNexus<KernelCode::Globals> KernelCode::globals; 52 53 KernelCode::Globals::Globals() 54 { 55 code = new KernelCode; 56 staticCode = new KernelStaticCode; 57 } 58 59 KernelCode::KernelCode() 60 : SecCode(NULL) 61 { 62 } 63 64 KernelStaticCode::KernelStaticCode() 65 : SecStaticCode(new KernelDiskRep()) 66 { 67 } 68 69 70 // 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. 76 // 77 SecCode *KernelCode::locateGuest(CFDictionaryRef attributes) 78 { 79 #if TARGET_OS_OSX 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); 86 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 89 // don't agree. 90 if (auditData && CFDataGetLength(auditData) != sizeof(audit_token_t)) 91 MacOSError::throwMe(errSecCSInvalidAttributeValues); 92 pid_t pid = 0; 93 audit_token_t* audit = NULL; 94 if (pidNumber) 95 pid = cfNumber<pid_t>(pidNumber); 96 if (auditData) 97 audit = (audit_token_t*)CFDataGetBytePtr(auditData); 98 if (audit && pid == 0) 99 pid = audit_token_to_pid(*audit); 100 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); 107 108 try { 109 diskRep = new PidDiskRep(pid, audit, infoPlist); 110 } catch (...) { } 111 } 112 113 return (new ProcessCode(pid, audit, diskRep))->retain(); 114 #else 115 MacOSError::throwMe(errSecCSUnimplemented); 116 #endif 117 } 118 119 120 // 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). 124 // 125 SecStaticCode *KernelCode::identifyGuest(SecCode *iguest, CFDataRef *cdhash) 126 { 127 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { 128 129 if (guest->pidBased()) { 130 131 SecPointer<SecStaticCode> code = new ProcessDynamicCode(guest); 132 guest->pidBased()->setCredentials(code->codeDirectory()); 133 134 #ifndef DARLING 135 SHA1::Digest kernelHash; 136 MacOSError::check(guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash))); 137 *cdhash = makeCFData(kernelHash, sizeof(kernelHash)); 138 #endif 139 140 return code.yield(); 141 } 142 143 char path[2 * MAXPATHLEN]; // reasonable upper limit 144 if (::proc_pidpath(guest->pid(), path, sizeof(path))) { 145 #ifndef DARLING 146 off_t offset; 147 csops(guest, CS_OPS_PIDOFFSET, &offset, sizeof(offset)); 148 SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path, (size_t)offset)); 149 CODESIGN_GUEST_IDENTIFY_PROCESS(guest, guest->pid(), code); 150 if (cdhash) { 151 SHA1::Digest kernelHash; 152 if (guest->csops(CS_OPS_CDHASH, kernelHash, sizeof(kernelHash)) == -1) 153 switch (errno) { 154 case EBADEXEC: // means "no CodeDirectory hash for this program" 155 *cdhash = NULL; 156 break; 157 case ESRCH: 158 MacOSError::throwMe(errSecCSNoSuchCode); 159 default: 160 UnixError::throwMe(); 161 } 162 else // succeeded 163 *cdhash = makeCFData(kernelHash, sizeof(kernelHash)); 164 CODESIGN_GUEST_CDHASH_PROCESS(guest, kernelHash, sizeof(kernelHash)); 165 } 166 #else 167 SecPointer<SecStaticCode> code = new ProcessStaticCode(DiskRep::bestGuess(path)); 168 #endif 169 return code.yield(); 170 } else 171 UnixError::throwMe(); 172 } 173 MacOSError::throwMe(errSecCSNoSuchCode); 174 } 175 176 177 // 178 // We obtain the guest's status by asking the kernel 179 // 180 SecCodeStatus KernelCode::getGuestStatus(SecCode *iguest) 181 { 182 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) { 183 uint32_t pFlags; 184 csops(guest, CS_OPS_STATUS, &pFlags, sizeof(pFlags)); 185 secinfo("kcode", "guest %p(%d) kernel status 0x%x", guest, guest->pid(), pFlags); 186 return pFlags; 187 } else 188 MacOSError::throwMe(errSecCSNoSuchCode); 189 } 190 191 192 // 193 // We tell the kernel to make status changes 194 // 195 void KernelCode::changeGuestStatus(SecCode *iguest, SecCodeStatusOperation operation, CFDictionaryRef arguments) 196 { 197 if (ProcessCode *guest = dynamic_cast<ProcessCode *>(iguest)) 198 switch (operation) { 199 case kSecCodeOperationNull: 200 break; 201 case kSecCodeOperationInvalidate: 202 csops(guest, CS_OPS_MARKINVALID); 203 break; 204 case kSecCodeOperationSetHard: 205 csops(guest, CS_OPS_MARKHARD); 206 break; 207 case kSecCodeOperationSetKill: 208 csops(guest, CS_OPS_MARKKILL); 209 break; 210 default: 211 MacOSError::throwMe(errSecCSUnimplemented); 212 } 213 else 214 MacOSError::throwMe(errSecCSNoSuchCode); 215 } 216 217 218 // 219 // The StaticCode for the running kernel is explicit. 220 // We can't ask our own host for it, naturally. 221 // 222 void KernelCode::identify() 223 { 224 mStaticCode.take(globals().staticCode->retain()); 225 // the kernel isn't currently signed, so we don't get a cdHash for it 226 } 227 228 229 // 230 // Interface to kernel csops() system call. 231 // 232 void KernelCode::csops(ProcessCode *proc, unsigned int op, void *addr, size_t length) 233 { 234 #ifndef DARLING 235 if (proc->csops(op, addr, length) == -1) { 236 switch (errno) { 237 case ESRCH: 238 MacOSError::throwMe(errSecCSNoSuchCode); 239 default: 240 UnixError::throwMe(); 241 } 242 } 243 #endif 244 } 245 246 247 } // CodeSigning 248 } // Security