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