/ OSX / libsecurity_codesigning / lib / cdbuilder.cpp
cdbuilder.cpp
  1  /*
  2   * Copyright (c) 2006-2012,2014 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  // cdbuilder - constructor for CodeDirectories
 26  //
 27  #include "cdbuilder.h"
 28  #include <security_utilities/memutils.h>
 29  #include <cmath>
 30  
 31  using namespace UnixPlusPlus;
 32  using LowLevelMemoryUtilities::alignUp;
 33  
 34  
 35  namespace Security {
 36  namespace CodeSigning {
 37  
 38  
 39  //
 40  // Create an (empty) builder
 41  //
 42  CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm)
 43  	: mFlags(0),
 44  	  mHashType(digestAlgorithm),
 45  	  mPlatform(0),
 46  	  mSpecialSlots(0),
 47  	  mCodeSlots(0),
 48  	  mScatter(NULL),
 49  	  mScatterSize(0),
 50  	  mExecSegOffset(0),
 51  	  mExecSegLimit(0),
 52  	  mExecSegFlags(0),
 53  	  mGeneratePreEncryptHashes(false),
 54  	  mRuntimeVersion(0),
 55  	  mDir(NULL)
 56  {
 57  	mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength();
 58  	mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength);
 59  }
 60  
 61  CodeDirectory::Builder::~Builder()
 62  {
 63  	::free(mSpecial);
 64  	::free(mScatter);
 65  }
 66  
 67  
 68  //
 69  // Set the source of the main executable (i.e. the code pages)
 70  //
 71  void CodeDirectory::Builder::executable(string path,
 72  	size_t pagesize, size_t offset, size_t length)
 73  {
 74  	mExec.close();			// any previously opened one
 75  	mExec.open(path);
 76  	mPageSize = pagesize;
 77  	mExecOffset = offset;
 78  	mExecLength = length;
 79  }
 80  
 81  void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length)
 82  {
 83  	assert(opened());					// already called executable()
 84  	mExec.close();
 85  	mExec.open(path);
 86  	mExecOffset = offset;
 87  	mExecLength = length;
 88  }
 89  
 90  bool CodeDirectory::Builder::opened()
 91  {
 92  	return bool(mExec);
 93  }
 94  
 95  
 96  //
 97  // Set the source for one special slot
 98  //
 99  void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data)
100  {
101  	assert(slot <= cdSlotMax);
102  	MakeHash<Builder> hash(this);
103  	hash->update(CFDataGetBytePtr(data), CFDataGetLength(data));
104  	hash->finish(specialSlot(slot));
105  	mFilledSpecialSlots.insert(slot);
106  	if (slot >= mSpecialSlots)
107  		mSpecialSlots = slot;
108  }
109  
110  
111  //
112  // Allocate a Scatter vector
113  //
114  CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count)
115  {
116  	mScatterSize = (count + 1) * sizeof(Scatter);
117  	if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize)))
118  		UnixError::throwMe(ENOMEM);
119  	::memset(mScatter, 0, mScatterSize);
120  	return mScatter;
121  }
122  
123  //
124  // Keep the allocated size of the (static) CodeDirectory consistent with
125  // the version chosen. We dynamically picked the least-needed version
126  // to provide stability of virtual signatures.
127  //
128  size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
129  {
130  	size_t cdSize = sizeof(CodeDirectory);
131  	if (version < supportsPreEncrypt)
132  		cdSize -= sizeof(mDir->runtime) + sizeof(mDir->preEncryptOffset);
133  	if (version < supportsExecSegment)
134  		cdSize -= sizeof(mDir->execSegBase) + sizeof(mDir->execSegLimit) + sizeof(mDir->execSegFlags);
135  	if (version < supportsCodeLimit64)
136  		cdSize -= sizeof(mDir->spare3) + sizeof(mDir->codeLimit64);
137  	if (version < supportsTeamID)
138  		cdSize -= sizeof(mDir->teamIDOffset);
139  
140  	return cdSize;
141  }
142  
143  //
144  // Calculate the size we'll need for the CodeDirectory as described so far
145  //
146  size_t CodeDirectory::Builder::size(const uint32_t version)
147  {
148  	assert(mExec);			// must have called executable()
149  	if (mExecLength == 0)
150  		mExecLength = mExec.fileSize() - mExecOffset;
151  
152  	// how many code pages?
153  	if (mExecLength <= 0) {	// no code, no slots
154  		mCodeSlots = 0;
155  	} else if (mPageSize == 0) {	// indefinite - one page
156  		mCodeSlots = 1;
157  	} else {				// finite - calculate from file size
158  		mCodeSlots = (mExecLength - 1) / mPageSize + 1;
159  	}
160  		
161  	size_t offset = fixedSize(version);
162  	size_t offset0 = offset;
163  	
164  	offset += mScatterSize;				// scatter vector
165  	offset += mIdentifier.size() + 1;	// size of identifier (with null byte)
166  	if (mTeamID.size())
167  		offset += mTeamID.size() + 1;	// size of teamID (with null byte)
168  	offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector
169  
170  	if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
171  		offset += mCodeSlots * mDigestLength;
172  	}
173  
174  	if (offset <= offset0)
175  		UnixError::throwMe(ENOEXEC);
176  
177  	return offset;
178  }
179  
180  
181  //
182  // Take everything added to date and wrap it up in a shiny new CodeDirectory.
183  //
184  // Note that this only constructs a CodeDirectory; it does not touch any subsidiary
185  // structures (resource tables, etc.), nor does it create any signature to secure
186  // the CodeDirectory.
187  // The returned CodeDirectory object is yours, and you may modify it as desired.
188  // But the memory layout is set here, so the various sizes and counts should be good
189  // when you call build().
190  // It's up to us to order the dynamic fields as we wish; but note that we currently
191  // don't pad them, and so they should be allocated in non-increasing order of required
192  // alignment. Make sure to keep the code here in sync with the size-calculating code above.
193  //
194  CodeDirectory *CodeDirectory::Builder::build()
195  {
196  	assert(mExec);			// must have (successfully) called executable()
197  	uint32_t version;
198  	
199  	// size and allocate
200  	size_t identLength = mIdentifier.size() + 1;
201  	size_t teamIDLength = mTeamID.size() + 1;
202  	
203  	// Determine the version
204  	if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty() || mRuntimeVersion) {
205  		version = currentVersion;
206  	} else if (mExecSegLimit > 0) {
207  		version = supportsExecSegment;
208  	} else if (mExecLength > UINT32_MAX) {
209  		version = supportsCodeLimit64;
210  	} else if (mTeamID.size()) {
211  		version = supportsTeamID;
212  	} else {
213  		version = supportsScatter;
214  	}
215  	
216  	if (mCodeSlots > UINT32_MAX)	// (still limited to 32 bits)
217  		MacOSError::throwMe(errSecCSTooBig);
218  	
219  	size_t total = size(version);
220  	if (!(mDir = (CodeDirectory *)calloc(1, total)))	// initialize to zero
221  		UnixError::throwMe(ENOMEM);
222  
223  	// fill header
224  	mDir->initialize(total);
225  	mDir->version = version;
226  	mDir->flags = mFlags;
227  	mDir->nSpecialSlots = (uint32_t)mSpecialSlots;
228  	mDir->nCodeSlots = (uint32_t)mCodeSlots;
229  	if (mExecLength > UINT32_MAX) {
230  		mDir->codeLimit = UINT32_MAX;
231  		mDir->codeLimit64 = mExecLength;
232  	} else {
233  		mDir->codeLimit = uint32_t(mExecLength);
234  	}
235  	mDir->hashType = mHashType;
236  	mDir->platform = mPlatform;
237  	mDir->hashSize = mDigestLength;
238  	if (mPageSize) {
239  		int pglog;
240  		assert(frexp(mPageSize, &pglog) == 0.5); // must be power of 2
241  		frexp(mPageSize, &pglog);
242  		assert(pglog < 256);
243  		mDir->pageSize = pglog - 1;
244  	} else
245  		mDir->pageSize = 0;	// means infinite page size
246  
247  	mDir->execSegBase = mExecSegOffset;
248  	mDir->execSegLimit = mExecSegLimit;
249  	mDir->execSegFlags = mExecSegFlags;
250  	mDir->runtime = mRuntimeVersion;
251  
252  	// locate and fill flex fields
253  	size_t offset = fixedSize(mDir->version);
254  	
255  	if (mScatter) {
256  		mDir->scatterOffset = (uint32_t)offset;
257  		memcpy(mDir->scatterVector(), mScatter, mScatterSize);
258  		offset += mScatterSize;
259  	}
260  
261  	mDir->identOffset = (uint32_t)offset;
262  	memcpy(mDir->identifier(), mIdentifier.c_str(), identLength);
263  	offset += identLength;
264  	
265  	if (mTeamID.size()) {
266  		mDir->teamIDOffset = (uint32_t)offset;
267  		memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength);
268  		offset += teamIDLength;
269  	}
270  
271  	// (add new flexibly-allocated fields here)
272  
273  	/* Pre-encrypt hashes come before normal hashes, so that the kernel can free
274  	 * the normal, potentially post-encrypt hashes away easily. */
275  	if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
276  		mDir->preEncryptOffset = (uint32_t)offset;
277  		offset += mCodeSlots * mDigestLength;
278  	}
279  
280  	mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength);
281  	offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
282  
283  	assert(offset == total);	// matches allocated size
284  
285  	(void)offset;
286  	
287  	// fill special slots
288  	memset(mDir->getSlotMutable((int)-mSpecialSlots, false), 0, mDigestLength * mSpecialSlots);
289  	for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
290  		memcpy(mDir->getSlotMutable((int)-slot, false), specialSlot((SpecialSlot)slot), mDigestLength);
291  	
292  	// fill code slots
293  	mExec.seek(mExecOffset);
294  	size_t remaining = mExecLength;
295  	for (unsigned int slot = 0; slot < mCodeSlots; ++slot) {
296  		size_t thisPage = remaining;
297  		if (mPageSize)
298  			thisPage = min(thisPage, mPageSize);
299  		MakeHash<Builder> hasher(this);
300  		generateHash(hasher, mExec, mDir->getSlotMutable(slot, false), thisPage);
301  		if (mGeneratePreEncryptHashes && mPreservedPreEncryptHashMap.empty()) {
302  			memcpy(mDir->getSlotMutable(slot, true), mDir->getSlot(slot, false),
303  				   mDir->hashSize);
304  		}
305  		remaining -= thisPage;
306  	}
307  	assert(remaining == 0);
308  
309  	PreEncryptHashMap::iterator preEncrypt =
310  		mPreservedPreEncryptHashMap.find(mHashType);
311  	if (preEncrypt != mPreservedPreEncryptHashMap.end()) {
312  		memcpy(mDir->getSlotMutable(0, true),
313  			   CFDataGetBytePtr(preEncrypt->second),
314  			   mCodeSlots * mDigestLength);
315  		mPreservedPreEncryptHashMap.erase(preEncrypt->first); // Releases the CFData memory.
316  	}
317  	
318  	// all done. Pass ownership to caller
319  	return mDir;
320  }
321  
322  
323  }	// CodeSigning
324  }	// Security