]>
Commit | Line | Data |
---|---|---|
e3d460c9 A |
1 | /* |
2 | * Copyright (c) 2015 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 | // diskimagerep - DiskRep representing a single read-only compressed disk image file | |
26 | // | |
27 | #include "diskimagerep.h" | |
28 | #include "sigblob.h" | |
29 | #include "CodeSigner.h" | |
30 | #include <security_utilities/endian.h> | |
31 | #include <algorithm> | |
32 | ||
33 | ||
34 | namespace Security { | |
35 | namespace CodeSigning { | |
36 | ||
37 | using Security::n2h; | |
38 | using Security::h2n; | |
39 | using namespace UnixPlusPlus; | |
40 | ||
41 | ||
42 | static const int32_t udifVersion = 4; // supported image file version | |
43 | ||
44 | ||
45 | // | |
46 | // Temporary hack to imply a fUDIFCryptosigFieldsset at the start of the "reserved" area of an UDIF header | |
47 | // | |
48 | struct UDIFSigning { | |
49 | uint64_t fCodeSignatureOffset; | |
50 | uint64_t fCodeSignatureLength; | |
51 | }; | |
52 | ||
53 | UDIFSigning& sigFields(UDIFFileHeader& header); | |
54 | const UDIFSigning& sigFields(const UDIFFileHeader& header); | |
55 | ||
56 | UDIFSigning& sigFields(UDIFFileHeader& header) | |
57 | { | |
58 | return *(UDIFSigning*)&header.fReserved; | |
59 | } | |
60 | ||
61 | const UDIFSigning& sigFields(const UDIFFileHeader& header) | |
62 | { | |
63 | return *(UDIFSigning*)&header.fReserved; | |
64 | } | |
65 | ||
66 | bool DiskImageRep::readHeader(FileDesc& fd, UDIFFileHeader& header) | |
67 | { | |
68 | // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment | |
69 | static const size_t headerLength = sizeof(header); | |
70 | size_t length = fd.fileSize(); | |
71 | if (length < sizeof(UDIFFileHeader) + sizeof(BlobCore)) | |
72 | return false; | |
73 | size_t headerOffset = length - sizeof(UDIFFileHeader); | |
74 | if (fd.read(&header, headerLength, headerOffset) != headerLength) | |
75 | return false; | |
76 | if (n2h(header.fUDIFSignature) != kUDIFSignature) | |
77 | return false; | |
78 | if (n2h(header.fUDIFVersion) != udifVersion) // current as of this writing | |
79 | return false; | |
80 | ||
81 | return true; | |
82 | } | |
83 | ||
84 | ||
85 | // | |
86 | // Object management. | |
87 | // | |
88 | DiskImageRep::DiskImageRep(const char *path) | |
89 | : SingleDiskRep(path) | |
90 | { | |
91 | this->setup(); | |
92 | } | |
93 | ||
94 | void DiskImageRep::setup() | |
95 | { | |
96 | mSigningData = NULL; | |
97 | ||
98 | // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment | |
99 | if (!readHeader(fd(), this->mHeader)) | |
100 | UnixError::throwMe(errSecCSBadDiskImageFormat); | |
101 | ||
102 | mHeaderOffset = fd().fileSize() - sizeof(UDIFFileHeader); | |
103 | size_t signatureOffset = size_t(n2h(sigFields(this->mHeader).fCodeSignatureOffset)); | |
104 | size_t signatureLength = size_t(n2h(sigFields(this->mHeader).fCodeSignatureLength)); | |
105 | sigFields(this->mHeader).fCodeSignatureLength = 0; // blind length (signature covers header) | |
106 | if (signatureOffset == 0) { | |
107 | mEndOfDataOffset = mHeaderOffset; | |
108 | sigFields(mHeader).fCodeSignatureOffset = h2n(mHeaderOffset); | |
109 | return; // unsigned, header prepared for possible signing | |
110 | } else { | |
111 | mEndOfDataOffset = signatureOffset; | |
112 | } | |
113 | ||
114 | // read the signature superblob | |
115 | const size_t frameLength = mHeaderOffset - signatureOffset; // room to following header | |
116 | if (EmbeddedSignatureBlob* blob = EmbeddedSignatureBlob::readBlob(fd(), signatureOffset, frameLength)) { | |
117 | if (blob->length() != frameLength || frameLength != signatureLength) { | |
118 | free(blob); | |
119 | MacOSError::throwMe(errSecCSBadDiskImageFormat); | |
120 | } | |
121 | mSigningData = blob; | |
122 | } | |
123 | } | |
124 | ||
125 | ||
126 | // | |
127 | // The default binary identification of a SingleDiskRep is the (SHA-1) hash | |
128 | // of the entire file itself. | |
129 | // | |
130 | CFDataRef DiskImageRep::identification() | |
131 | { | |
132 | SHA1 hash; // not security sensitive | |
133 | hash(&mHeader, sizeof(mHeader)); | |
134 | SHA1::Digest digest; | |
135 | hash.finish(digest); | |
136 | return makeCFData(digest, sizeof(digest)); | |
137 | } | |
138 | ||
139 | ||
140 | // | |
141 | // Sniffer function for UDIF disk image files. | |
142 | // This just looks for the trailing "header" and its magic number. | |
143 | // | |
144 | bool DiskImageRep::candidate(FileDesc &fd) | |
145 | { | |
146 | UDIFFileHeader header; | |
147 | return readHeader(fd, header) == true; | |
148 | } | |
149 | ||
150 | ||
151 | // | |
152 | // Signing limit is the start of the (trailing) signature | |
153 | // | |
154 | size_t DiskImageRep::signingLimit() | |
155 | { | |
156 | return mEndOfDataOffset; | |
157 | } | |
158 | ||
159 | void DiskImageRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) | |
160 | { | |
161 | DiskRep::strictValidate(cd, tolerated, flags); | |
162 | ||
163 | if (cd) { | |
164 | size_t cd_limit = cd->signingLimit(); | |
165 | size_t dr_limit = signingLimit(); | |
166 | if (cd_limit != dr_limit && // must cover exactly the entire data | |
167 | cd_limit != fd().fileSize()) // or, for legacy detached sigs, the entire file | |
168 | MacOSError::throwMe(errSecCSSignatureInvalid); | |
169 | } | |
170 | } | |
171 | ||
172 | ||
173 | // | |
174 | // Retrieve a component from the executable. | |
175 | // Our mCache has mapped the entire file, so we just fish the contents out of | |
176 | // the mapped area as needed. | |
177 | // | |
178 | CFDataRef DiskImageRep::component(CodeDirectory::SpecialSlot slot) | |
179 | { | |
180 | switch (slot) { | |
181 | case cdRepSpecificSlot: | |
182 | return makeCFData(&mHeader, sizeof(mHeader)); | |
183 | default: | |
184 | return mSigningData ? mSigningData->component(slot) : NULL; | |
185 | } | |
186 | } | |
187 | ||
188 | ||
189 | // | |
190 | // Provide a (vaguely) human readable characterization of this code | |
191 | // | |
192 | string DiskImageRep::format() | |
193 | { | |
194 | return "disk image"; | |
195 | } | |
196 | ||
197 | void DiskImageRep::prepareForSigning(SigningContext& context) | |
198 | { | |
199 | // default to SHA256 unconditionally - we have no legacy issues to worry about | |
200 | if (context.digestAlgorithms().empty()) | |
201 | context.setDigestAlgorithm(kSecCodeSignatureHashSHA256); | |
202 | } | |
203 | ||
204 | ||
205 | // | |
206 | // DiskImageRep::Writers | |
207 | // | |
208 | DiskRep::Writer *DiskImageRep::writer() | |
209 | { | |
210 | return new Writer(this); | |
211 | } | |
212 | ||
213 | ||
214 | // | |
215 | // Write a component. | |
216 | // | |
217 | void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) | |
218 | { | |
219 | assert(slot != cdRepSpecificSlot); | |
220 | EmbeddedSignatureBlob::Maker::component(slot, data); | |
221 | } | |
222 | ||
223 | ||
224 | // | |
225 | // Append the superblob we built to the cache file. | |
226 | // | |
227 | void DiskImageRep::Writer::flush() | |
228 | { | |
229 | delete mSigningData; // ditch previous blob just in case | |
230 | mSigningData = Maker::make(); // assemble new signature SuperBlob | |
231 | ||
232 | // write signature superblob | |
233 | size_t location = rep->mEndOfDataOffset; | |
234 | assert(location); | |
235 | fd().seek(location); | |
236 | fd().writeAll(*mSigningData); // write signature | |
237 | ||
238 | // now (re)write disk image header after it | |
239 | UDIFFileHeader fullHeader = rep->mHeader; | |
240 | sigFields(fullHeader).fCodeSignatureOffset = h2n(location); | |
241 | sigFields(fullHeader).fCodeSignatureLength = h2n(mSigningData->length()); | |
242 | fd().writeAll(&fullHeader, sizeof(rep->mHeader)); | |
243 | } | |
244 | ||
245 | ||
246 | // | |
247 | // Discretionary manipulations | |
248 | // | |
249 | void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder &builder) | |
250 | { | |
251 | } | |
252 | ||
253 | ||
254 | } // end namespace CodeSigning | |
255 | } // end namespace Security |