]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/cdbuilder.cpp
Security-58286.251.4.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / 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