2 * Copyright (c) 2006-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // cdbuilder - constructor for CodeDirectories
27 #include "cdbuilder.h"
28 #include <security_utilities/memutils.h>
31 using namespace UnixPlusPlus
;
32 using LowLevelMemoryUtilities::alignUp
;
36 namespace CodeSigning
{
40 // Create an (empty) builder
42 CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm
)
44 mHashType(digestAlgorithm
),
53 mGeneratePreEncryptHashes(false),
57 mDigestLength
= (uint32_t)MakeHash
<Builder
>(this)->digestLength();
58 mSpecial
= (unsigned char *)calloc(cdSlotMax
, mDigestLength
);
61 CodeDirectory::Builder::~Builder()
69 // Set the source of the main executable (i.e. the code pages)
71 void CodeDirectory::Builder::executable(string path
,
72 size_t pagesize
, size_t offset
, size_t length
)
74 mExec
.close(); // any previously opened one
81 void CodeDirectory::Builder::reopen(string path
, size_t offset
, size_t length
)
83 assert(opened()); // already called executable()
90 bool CodeDirectory::Builder::opened()
97 // Set the source for one special slot
99 void CodeDirectory::Builder::specialSlot(SpecialSlot slot
, CFDataRef data
)
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
;
112 // Allocate a Scatter vector
114 CodeDirectory::Scatter
*CodeDirectory::Builder::scatter(unsigned count
)
116 mScatterSize
= (count
+ 1) * sizeof(Scatter
);
117 if (!(mScatter
= (Scatter
*)::realloc(mScatter
, mScatterSize
)))
118 UnixError::throwMe(ENOMEM
);
119 ::memset(mScatter
, 0, mScatterSize
);
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.
128 size_t CodeDirectory::Builder::fixedSize(const uint32_t version
)
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
);
144 // Calculate the size we'll need for the CodeDirectory as described so far
146 size_t CodeDirectory::Builder::size(const uint32_t version
)
148 assert(mExec
); // must have called executable()
149 if (mExecLength
== 0)
150 mExecLength
= mExec
.fileSize() - mExecOffset
;
152 // how many code pages?
153 if (mExecLength
<= 0) { // no code, no slots
155 } else if (mPageSize
== 0) { // indefinite - one page
157 } else { // finite - calculate from file size
158 mCodeSlots
= (mExecLength
- 1) / mPageSize
+ 1;
161 size_t offset
= fixedSize(version
);
162 size_t offset0
= offset
;
164 offset
+= mScatterSize
; // scatter vector
165 offset
+= mIdentifier
.size() + 1; // size of identifier (with null byte)
167 offset
+= mTeamID
.size() + 1; // size of teamID (with null byte)
168 offset
+= (mCodeSlots
+ mSpecialSlots
) * mDigestLength
; // hash vector
170 if (mGeneratePreEncryptHashes
|| !mPreservedPreEncryptHashMap
.empty()) {
171 offset
+= mCodeSlots
* mDigestLength
;
174 if (offset
<= offset0
)
175 UnixError::throwMe(ENOEXEC
);
182 // Take everything added to date and wrap it up in a shiny new CodeDirectory.
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.
194 CodeDirectory
*CodeDirectory::Builder::build()
196 assert(mExec
); // must have (successfully) called executable()
200 size_t identLength
= mIdentifier
.size() + 1;
201 size_t teamIDLength
= mTeamID
.size() + 1;
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
;
213 version
= supportsScatter
;
216 if (mCodeSlots
> UINT32_MAX
) // (still limited to 32 bits)
217 MacOSError::throwMe(errSecCSTooBig
);
219 size_t total
= size(version
);
220 if (!(mDir
= (CodeDirectory
*)calloc(1, total
))) // initialize to zero
221 UnixError::throwMe(ENOMEM
);
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
;
233 mDir
->codeLimit
= uint32_t(mExecLength
);
235 mDir
->hashType
= mHashType
;
236 mDir
->platform
= mPlatform
;
237 mDir
->hashSize
= mDigestLength
;
240 assert(frexp(mPageSize
, &pglog
) == 0.5); // must be power of 2
241 frexp(mPageSize
, &pglog
);
243 mDir
->pageSize
= pglog
- 1;
245 mDir
->pageSize
= 0; // means infinite page size
247 mDir
->execSegBase
= mExecSegOffset
;
248 mDir
->execSegLimit
= mExecSegLimit
;
249 mDir
->execSegFlags
= mExecSegFlags
;
250 mDir
->runtime
= mRuntimeVersion
;
252 // locate and fill flex fields
253 size_t offset
= fixedSize(mDir
->version
);
256 mDir
->scatterOffset
= (uint32_t)offset
;
257 memcpy(mDir
->scatterVector(), mScatter
, mScatterSize
);
258 offset
+= mScatterSize
;
261 mDir
->identOffset
= (uint32_t)offset
;
262 memcpy(mDir
->identifier(), mIdentifier
.c_str(), identLength
);
263 offset
+= identLength
;
265 if (mTeamID
.size()) {
266 mDir
->teamIDOffset
= (uint32_t)offset
;
267 memcpy(mDir
->teamID(), mTeamID
.c_str(), teamIDLength
);
268 offset
+= teamIDLength
;
271 // (add new flexibly-allocated fields here)
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
;
280 mDir
->hashOffset
= (uint32_t)(offset
+ mSpecialSlots
* mDigestLength
);
281 offset
+= (mSpecialSlots
+ mCodeSlots
) * mDigestLength
;
283 assert(offset
== total
); // matches allocated size
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
);
293 mExec
.seek(mExecOffset
);
294 size_t remaining
= mExecLength
;
295 for (unsigned int slot
= 0; slot
< mCodeSlots
; ++slot
) {
296 size_t thisPage
= remaining
;
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),
305 remaining
-= thisPage
;
307 assert(remaining
== 0);
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.
318 // all done. Pass ownership to caller