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