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