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