/ OSX / libsecurity_codesigning / lib / cskernel.cpp
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