]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/cdbuilder.cpp
Security-57336.1.9.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 mDir(NULL)
51 {
52 mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength();
53 mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength);
54 }
55
56 CodeDirectory::Builder::~Builder()
57 {
58 ::free(mSpecial);
59 ::free(mScatter);
60 }
61
62
63 //
64 // Set the source of the main executable (i.e. the code pages)
65 //
66 void CodeDirectory::Builder::executable(string path,
67 size_t pagesize, size_t offset, size_t length)
68 {
69 mExec.close(); // any previously opened one
70 mExec.open(path);
71 mPageSize = pagesize;
72 mExecOffset = offset;
73 mExecLength = length;
74 }
75
76 void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length)
77 {
78 assert(mExec); // already called executable()
79 mExec.close();
80 mExec.open(path);
81 mExecOffset = offset;
82 mExecLength = length;
83 }
84
85
86 //
87 // Set the source for one special slot
88 //
89 void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data)
90 {
91 assert(slot <= cdSlotMax);
92 MakeHash<Builder> hash(this);
93 hash->update(CFDataGetBytePtr(data), CFDataGetLength(data));
94 hash->finish(specialSlot(slot));
95 if (slot >= mSpecialSlots)
96 mSpecialSlots = slot;
97 }
98
99
100 //
101 // Allocate a Scatter vector
102 //
103 CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count)
104 {
105 mScatterSize = (count + 1) * sizeof(Scatter);
106 if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize)))
107 UnixError::throwMe(ENOMEM);
108 ::memset(mScatter, 0, mScatterSize);
109 return mScatter;
110 }
111
112 // This calculates the fixed size of the code directory
113 // Because of <rdar://problem/16102695>, if the team ID
114 // field is not used, we leave out the team ID offset
115 // as well, to keep cd hashes consistent between
116 // versions.
117 const size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
118 {
119 size_t cdSize = sizeof(CodeDirectory);
120 if (version < supportsTeamID)
121 cdSize -= sizeof(mDir->teamIDOffset);
122
123 return cdSize;
124 }
125
126 //
127 // Calculate the size we'll need for the CodeDirectory as described so far
128 //
129 size_t CodeDirectory::Builder::size(const uint32_t version)
130 {
131 assert(mExec); // must have called executable()
132 if (mExecLength == 0)
133 mExecLength = mExec.fileSize() - mExecOffset;
134
135 // how many code pages?
136 if (mExecLength <= 0) { // no code, no slots
137 mCodeSlots = 0;
138 } else if (mPageSize == 0) { // indefinite - one page
139 mCodeSlots = 1;
140 } else { // finite - calculate from file size
141 mCodeSlots = (mExecLength - 1) / mPageSize + 1;
142 }
143
144 size_t offset = fixedSize(version);
145 size_t offset0 = offset;
146
147 offset += mScatterSize; // scatter vector
148 offset += mIdentifier.size() + 1; // size of identifier (with null byte)
149 if (mTeamID.size())
150 offset += mTeamID.size() + 1; // size of teamID (with null byte)
151 offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector
152 if (offset <= offset0)
153 UnixError::throwMe(ENOEXEC);
154
155 return offset;
156 }
157
158
159 //
160 // Take everything added to date and wrap it up in a shiny new CodeDirectory.
161 //
162 // Note that this only constructs a CodeDirectory; it does not touch any subsidiary
163 // structures (resource tables, etc.), nor does it create any signature to secure
164 // the CodeDirectory.
165 // The returned CodeDirectory object is yours, and you may modify it as desired.
166 // But the memory layout is set here, so the various sizes and counts should be good
167 // when you call build().
168 // It's up to us to order the dynamic fields as we wish; but note that we currently
169 // don't pad them, and so they should be allocated in non-increasing order of required
170 // alignment. Make sure to keep the code here in sync with the size-calculating code above.
171 //
172 CodeDirectory *CodeDirectory::Builder::build()
173 {
174 assert(mExec); // must have (successfully) called executable()
175 uint32_t version;
176
177 // size and allocate
178 size_t identLength = mIdentifier.size() + 1;
179 size_t teamIDLength = mTeamID.size() + 1;
180
181 // Determine the version
182 if (mTeamID.size()) {
183 version = currentVersion;
184 } else {
185 version = supportsScatter;
186 }
187
188 size_t total = size(version);
189 if (!(mDir = (CodeDirectory *)calloc(1, total))) // initialize to zero
190 UnixError::throwMe(ENOMEM);
191
192 if (mExecLength > UINT32_MAX)
193 MacOSError::throwMe(errSecCSTooBig);
194
195 // fill header
196 mDir->initialize(total);
197 mDir->version = version;
198 mDir->flags = mFlags;
199 mDir->nSpecialSlots = (uint32_t)mSpecialSlots;
200 mDir->nCodeSlots = (uint32_t)mCodeSlots;
201 mDir->codeLimit = (uint32_t)mExecLength;
202 mDir->hashType = mHashType;
203 mDir->platform = mPlatform;
204 mDir->hashSize = mDigestLength;
205 if (mPageSize) {
206 int pglog;
207 assert(frexp(mPageSize, &pglog) == 0.5); // must be power of 2
208 frexp(mPageSize, &pglog);
209 assert(pglog < 256);
210 mDir->pageSize = pglog - 1;
211 } else
212 mDir->pageSize = 0; // means infinite page size
213
214 // locate and fill flex fields
215 size_t offset = fixedSize(mDir->version);
216
217 if (mScatter) {
218 mDir->scatterOffset = (uint32_t)offset;
219 memcpy(mDir->scatterVector(), mScatter, mScatterSize);
220 offset += mScatterSize;
221 }
222
223 mDir->identOffset = (uint32_t)offset;
224 memcpy(mDir->identifier(), mIdentifier.c_str(), identLength);
225 offset += identLength;
226
227 if (mTeamID.size()) {
228 mDir->teamIDOffset = (uint32_t)offset;
229 memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength);
230 offset += teamIDLength;
231 }
232 // (add new flexibly-allocated fields here)
233
234 mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength);
235 offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
236 assert(offset == total); // matches allocated size
237
238 // fill special slots
239 memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots);
240 for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
241 memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength);
242
243 // fill code slots
244 mExec.seek(mExecOffset);
245 size_t remaining = mExecLength;
246 for (unsigned int slot = 0; slot < mCodeSlots; ++slot) {
247 size_t thisPage = min(mPageSize, remaining);
248 MakeHash<Builder> hasher(this);
249 generateHash(hasher, mExec, (*mDir)[slot], thisPage);
250 remaining -= thisPage;
251 }
252
253 // all done. Pass ownership to caller
254 return mDir;
255 }
256
257
258 } // CodeSigning
259 } // Security