]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/machorep.cpp
Security-57337.50.23.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / machorep.cpp
1 /*
2 * Copyright (c) 2006,2011-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 // machorep - DiskRep mix-in for handling Mach-O main executables
26 //
27 #include "machorep.h"
28 #include "StaticCode.h"
29 #include "reqmaker.h"
30
31
32 namespace Security {
33 namespace CodeSigning {
34
35 using namespace UnixPlusPlus;
36
37
38 //
39 // Object management.
40 // We open the main executable lazily, so nothing much happens on construction.
41 // If the context specifies a file offset, we directly pick that Mach-O binary (only).
42 // if it specifies an architecture, we try to pick that. Otherwise, we deliver the whole
43 // Universal object (which will usually deliver the "native" architecture later).
44 //
45 MachORep::MachORep(const char *path, const Context *ctx)
46 : SingleDiskRep(path), mSigningData(NULL)
47 {
48 if (ctx)
49 if (ctx->offset)
50 mExecutable = new Universal(fd(), (size_t)ctx->offset, ctx->size);
51 else if (ctx->arch) {
52 auto_ptr<Universal> full(new Universal(fd()));
53 mExecutable = new Universal(fd(), full->archOffset(ctx->arch), full->archLength(ctx->arch));
54 } else
55 mExecutable = new Universal(fd());
56 else
57 mExecutable = new Universal(fd());
58
59 assert(mExecutable);
60 CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx);
61 }
62
63 MachORep::~MachORep()
64 {
65 delete mExecutable;
66 ::free(mSigningData);
67 }
68
69
70 //
71 // Sniffer function for "plausible Mach-O binary"
72 //
73 bool MachORep::candidate(FileDesc &fd)
74 {
75 switch (Universal::typeOf(fd)) {
76 case MH_EXECUTE:
77 case MH_DYLIB:
78 case MH_DYLINKER:
79 case MH_BUNDLE:
80 case MH_KEXT_BUNDLE:
81 case MH_PRELOAD:
82 return true; // dynamic image; supported
83 case MH_OBJECT:
84 return false; // maybe later...
85 default:
86 return false; // not Mach-O (or too exotic)
87 }
88 }
89
90
91
92 //
93 // Nowadays, the main executable object is created upon construction.
94 //
95 Universal *MachORep::mainExecutableImage()
96 {
97 return mExecutable;
98 }
99
100
101 //
102 // Explicitly default to SHA256 (only) digests if the minimum deployment
103 // target is young enough.
104 //
105 void MachORep::prepareForSigning(SigningContext &context)
106 {
107 if (context.digestAlgorithms().empty()) {
108 MachO *macho = mainExecutableImage()->architecture();
109 if (const version_min_command *version = macho->findMinVersion()) {
110 uint32_t limit = 0;
111 switch (macho->flip(version->cmd)) {
112 case LC_VERSION_MIN_MACOSX:
113 limit = (10 << 16 | 11 << 8 | 4 << 0);
114 break;
115 #if 0 /* need updated libMIS before we can do this switch */
116 case LC_VERSION_MIN_IPHONEOS:
117 limit = (9 << 16 | 3 << 8);
118 break;
119 case LC_VERSION_MIN_WATCHOS:
120 limit = (2 << 16 | 2 << 8);
121 break;
122 case LC_VERSION_MIN_TVOS:
123 limit = (9 << 16 | 2 << 8);
124 break;
125 default:
126 break;
127 #else
128 case LC_VERSION_MIN_IPHONEOS:
129 case LC_VERSION_MIN_WATCHOS:
130 case LC_VERSION_MIN_TVOS:
131 return;
132 default:
133 break;
134 #endif
135 }
136 if (macho->flip(version->version) >= limit) {
137 // young enough not to need SHA-1 legacy support
138 context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
139 }
140 }
141 }
142 }
143
144
145 //
146 // Signing base is the start of the Mach-O architecture we're using
147 //
148 size_t MachORep::signingBase()
149 {
150 return mainExecutableImage()->archOffset();
151 }
152
153
154 //
155 // We choose the binary identifier for a Mach-O binary as follows:
156 // - If the Mach-O headers have a UUID command, use the UUID.
157 // - Otherwise, use the SHA-1 hash of the (entire) load commands.
158 //
159 CFDataRef MachORep::identification()
160 {
161 std::auto_ptr<MachO> macho(mainExecutableImage()->architecture());
162 return identificationFor(macho.get());
163 }
164
165 CFDataRef MachORep::identificationFor(MachO *macho)
166 {
167 // if there is a LC_UUID load command, use the UUID contained therein
168 if (const load_command *cmd = macho->findCommand(LC_UUID)) {
169 const uuid_command *uuidc = reinterpret_cast<const uuid_command *>(cmd);
170 // uuidc->cmdsize should be sizeof(uuid_command), so if it is not,
171 // something is wrong. Fail out.
172 if (macho->flip(uuidc->cmdsize) != sizeof(uuid_command))
173 MacOSError::throwMe(errSecCSSignatureInvalid);
174 char result[4 + sizeof(uuidc->uuid)];
175 memcpy(result, "UUID", 4);
176 memcpy(result+4, uuidc->uuid, sizeof(uuidc->uuid));
177 return makeCFData(result, sizeof(result));
178 }
179
180 // otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete)
181 SHA1 hash;
182 hash(&macho->header(), sizeof(mach_header));
183 hash(macho->loadCommands(), macho->commandLength());
184 SHA1::Digest digest;
185 hash.finish(digest);
186 return makeCFData(digest, sizeof(digest));
187 }
188
189
190 //
191 // Retrieve a component from the executable.
192 // This reads the entire signing SuperBlob when first called for an executable,
193 // and then caches it for further use.
194 // Note that we could read individual components directly off disk and only cache
195 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
196 // to cache the pieces anyway.
197 //
198 CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
199 {
200 switch (slot) {
201 case cdInfoSlot:
202 return infoPlist();
203 default:
204 return embeddedComponent(slot);
205 }
206 }
207
208
209 // Retrieve a component from the embedded signature SuperBlob (if present).
210 // This reads the entire signing SuperBlob when first called for an executable,
211 // and then caches it for further use.
212 // Note that we could read individual components directly off disk and only cache
213 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
214 // to cache the pieces anyway. But it's not clear that the resulting multiple I/O
215 // calls wouldn't be slower in the end.
216 //
217 CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
218 {
219 if (!mSigningData) { // fetch and cache
220 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
221 if (macho.get())
222 if (const linkedit_data_command *cs = macho->findCodeSignature()) {
223 size_t offset = macho->flip(cs->dataoff);
224 size_t length = macho->flip(cs->datasize);
225 if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) {
226 secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
227 mSigningData->length(), mSigningData->count(),
228 mainExecutablePath().c_str(), macho->architecture().name());
229 } else {
230 secdebug("machorep", "failed to read signing bytes from %s(%s)",
231 mainExecutablePath().c_str(), macho->architecture().name());
232 MacOSError::throwMe(errSecCSSignatureInvalid);
233 }
234 }
235 }
236 if (mSigningData)
237 return mSigningData->component(slot);
238
239 // not found
240 return NULL;
241 }
242
243
244 //
245 // Extract an embedded Info.plist from the file.
246 // Returns NULL if none is found.
247 //
248 CFDataRef MachORep::infoPlist()
249 {
250 CFRef<CFDataRef> info;
251 try {
252 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
253 if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
254 if (macho->is64()) {
255 const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
256 info.take(macho->dataAt(macho->flip(sect64->offset), (size_t)macho->flip(sect64->size)));
257 } else {
258 info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)));
259 }
260 }
261 } catch (...) {
262 secdebug("machorep", "exception reading embedded Info.plist");
263 }
264 return info.yield();
265 }
266
267
268 //
269 // Provide a (vaguely) human readable characterization of this code
270 //
271 string MachORep::format()
272 {
273 if (Universal *fat = mainExecutableImage()) {
274 Universal::Architectures archs;
275 fat->architectures(archs);
276 if (fat->isUniversal()) {
277 string s = "Mach-O universal (";
278 for (Universal::Architectures::const_iterator it = archs.begin();
279 it != archs.end(); ++it) {
280 if (it != archs.begin())
281 s += " ";
282 s += it->displayName();
283 }
284 return s + ")";
285 } else {
286 assert(archs.size() == 1);
287 return string("Mach-O thin (") + archs.begin()->displayName() + ")";
288 }
289 } else
290 return "Mach-O (unrecognized format)";
291 }
292
293
294 //
295 // Flush cached data
296 //
297 void MachORep::flush()
298 {
299 size_t offset = mExecutable->offset();
300 size_t length = mExecutable->length();
301 delete mExecutable;
302 mExecutable = NULL;
303 ::free(mSigningData);
304 mSigningData = NULL;
305 SingleDiskRep::flush();
306 mExecutable = new Universal(fd(), offset, length);
307 }
308
309
310 //
311 // Return a recommended unique identifier.
312 // If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
313 // Otherwise, use the default.
314 //
315 string MachORep::recommendedIdentifier(const SigningContext &ctx)
316 {
317 if (CFDataRef info = infoPlist()) {
318 if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) {
319 CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey));
320 if (code && CFGetTypeID(code) != CFStringGetTypeID())
321 MacOSError::throwMe(errSecCSBadDictionaryFormat);
322 if (code)
323 return cfString(code);
324 } else
325 MacOSError::throwMe(errSecCSBadDictionaryFormat);
326 }
327
328 // ah well. Use the default
329 return SingleDiskRep::recommendedIdentifier(ctx);
330 }
331
332
333 //
334 // The default suggested requirements for Mach-O binaries are as follows:
335 // Library requirement: Composed from dynamic load commands.
336 //
337 const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
338 {
339 assert(arch); // enforced by signing infrastructure
340 Requirements::Maker maker;
341
342 // add library requirements from DYLIB commands (if any)
343 if (Requirement *libreq = libraryRequirements(arch, ctx))
344 maker.add(kSecLibraryRequirementType, libreq); // takes ownership
345
346 // that's all
347 return maker.make();
348 }
349
350 Requirement *MachORep::libraryRequirements(const Architecture *arch, const SigningContext &ctx)
351 {
352 auto_ptr<MachO> macho(mainExecutableImage()->architecture(*arch));
353 Requirement::Maker maker;
354 Requirement::Maker::Chain chain(maker, opOr);
355
356 if (macho.get())
357 if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) {
358 size_t offset = macho->flip(ldep->dataoff);
359 size_t length = macho->flip(ldep->datasize);
360 if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) {
361 try {
362 secdebug("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
363 deplist->length(), deplist->count(),
364 mainExecutablePath().c_str(), macho->architecture().name());
365 unsigned count = deplist->count();
366 // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far
367 for (unsigned n = 0; n < count; n++) {
368 const Requirement *req = NULL;
369 if (const BlobCore *dep = deplist->blob(n)) {
370 if ((req = Requirement::specific(dep))) {
371 // binary code requirement; good to go
372 } else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) {
373 // blob-wrapped text form - convert to binary requirement
374 std::string reqString = std::string((const char *)wrap->data(), wrap->length());
375 CFRef<SecRequirementRef> areq;
376 MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref()));
377 CFRef<CFDataRef> reqData;
378 MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref()));
379 req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData));
380 } else {
381 secdebug("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
382 continue;
383 }
384 chain.add();
385 maker.copy(req);
386 } else
387 secdebug("machorep", "missing DR info for library index %d", n);
388 }
389 ::free(deplist);
390 } catch (...) {
391 ::free(deplist);
392 throw;
393 }
394 }
395 }
396 if (chain.empty())
397 return NULL;
398 else
399 return maker.make();
400 }
401
402
403 //
404 // Default to system page size for segmented (paged) signatures
405 //
406 size_t MachORep::pageSize(const SigningContext &)
407 {
408 return segmentedPageSize;
409 }
410
411
412 //
413 // Strict validation
414 //
415 void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
416 {
417 DiskRep::strictValidate(cd, tolerated, flags);
418
419 // if the constructor found suspicious issues, fail a struct validation now
420 if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end())
421 MacOSError::throwMe(errSecCSBadMainExecutable);
422
423 // the signature's code extent must be what we would have picked (no funny hand editing)
424 if (cd) {
425 auto_ptr<MachO> macho(mExecutable->architecture());
426 if (cd->signingLimit() != macho->signingExtent())
427 MacOSError::throwMe(errSecCSSignatureInvalid);
428 }
429 }
430
431
432 //
433 // FileDiskRep::Writers
434 //
435 DiskRep::Writer *MachORep::writer()
436 {
437 return new Writer(this);
438 }
439
440
441 //
442 // Write a component.
443 // MachORep::Writers don't write to components directly; the signing code uses special
444 // knowledge of the Mach-O format to build embedded signatures and blasts them directly
445 // to disk. Thus this implementation will never be called (and, if called, will simply fail).
446 //
447 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
448 {
449 assert(false);
450 MacOSError::throwMe(errSecCSInternalError);
451 }
452
453
454 } // end namespace CodeSigning
455 } // end namespace Security