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