2 * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // machorep - DiskRep mix-in for handling Mach-O main executables
28 #include "notarization.h"
29 #include "StaticCode.h"
31 #include <security_utilities/logging.h>
32 #include <security_utilities/cfmunge.h>
33 #include <security_utilities/casts.h>
38 namespace CodeSigning
{
40 using namespace UnixPlusPlus
;
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).
50 MachORep::MachORep(const char *path
, const Context
*ctx
)
51 : SingleDiskRep(path
), mSigningData(NULL
)
55 mExecutable
= new Universal(fd(), (size_t)ctx
->offset
, ctx
->size
);
57 unique_ptr
<Universal
> full(new Universal(fd()));
58 mExecutable
= new Universal(fd(), full
->archOffset(ctx
->arch
), full
->archLength(ctx
->arch
));
60 mExecutable
= new Universal(fd());
62 mExecutable
= new Universal(fd());
65 CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path
, (void*)ctx
);
76 // Sniffer function for "plausible Mach-O binary"
78 bool MachORep::candidate(FileDesc
&fd
)
80 switch (Universal::typeOf(fd
)) {
87 return true; // dynamic image; supported
89 return false; // maybe later...
91 return false; // not Mach-O (or too exotic)
98 // Nowadays, the main executable object is created upon construction.
100 Universal
*MachORep::mainExecutableImage()
107 // Explicitly default to SHA256 (only) digests if the minimum deployment
108 // target is young enough.
110 void MachORep::prepareForSigning(SigningContext
&context
)
112 if (context
.digestAlgorithms().empty()) {
113 unique_ptr
<MachO
> macho(mainExecutableImage()->architecture());
116 switch (macho
->platform()) {
118 // If we don't know the platform, we stay agile.
121 // 10.11.4 had first proper sha256 support.
122 limit
= (10 << 16 | 11 << 8 | 4 << 0);
126 // iOS 11 and tvOS 11 had first proper sha256 support.
127 limit
= (11 << 16 | 0 << 8 | 0 << 0);
129 case PLATFORM_WATCHOS
:
130 // We stay agile on the watch right now.
133 // All other platforms are assumed to be new and support SHA256.
136 if (macho
->minVersion() >= limit
) {
137 // young enough not to need SHA-1 legacy support
138 context
.setDigestAlgorithm(kSecCodeSignatureHashSHA256
);
145 // Signing base is the start of the Mach-O architecture we're using
147 size_t MachORep::signingBase()
149 return mainExecutableImage()->archOffset();
152 size_t MachORep::signingLimit()
154 unique_ptr
<MachO
> macho(mExecutable
->architecture());
155 return macho
->signingExtent();
158 bool MachORep::needsExecSeg(const MachO
& macho
) {
159 uint32_t platform
= macho
.platform();
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;
167 size_t MachORep::execSegBase(const Architecture
*arch
)
169 unique_ptr
<MachO
> macho(arch
? mExecutable
->architecture(*arch
) : mExecutable
->architecture());
171 if (!needsExecSeg(*macho
)) {
175 segment_command
const * const text_cmd
= macho
->findSegment("__TEXT");
177 if (text_cmd
== NULL
) {
184 off
= int_cast
<uint64_t,size_t>(reinterpret_cast<segment_command_64
const * const>(text_cmd
)->fileoff
);
186 off
= text_cmd
->fileoff
;
192 size_t MachORep::execSegLimit(const Architecture
*arch
)
194 unique_ptr
<MachO
> macho(arch
? mExecutable
->architecture(*arch
) : mExecutable
->architecture());
196 if (!needsExecSeg(*macho
)) {
200 segment_command
const * const text_cmd
= macho
->findSegment("__TEXT");
202 if (text_cmd
== NULL
) {
209 size
= int_cast
<uint64_t,size_t>(reinterpret_cast<segment_command_64
const * const>(text_cmd
)->filesize
);
211 size
= text_cmd
->filesize
;
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.
223 CFDataRef
MachORep::identification()
225 std::unique_ptr
<MachO
> macho(mainExecutableImage()->architecture());
226 return identificationFor(macho
.get());
229 CFDataRef
MachORep::identificationFor(MachO
*macho
)
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
));
244 // otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete)
246 hash(&macho
->header(), sizeof(mach_header
));
247 hash(macho
->loadCommands(), macho
->commandLength());
250 return makeCFData(digest
, sizeof(digest
));
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.
262 CFDataRef
MachORep::component(CodeDirectory::SpecialSlot slot
)
268 return embeddedComponent(slot
);
273 // Retrieve all components, used for signature editing.
275 EditableDiskRep::RawComponentMap
MachORep::createRawComponents()
277 EditableDiskRep::RawComponentMap blobMap
;
279 // First call to signingData() caches the result, so this
280 // _should_ not cause performance issues.
281 if (NULL
== signingData()) {
282 MacOSError::throwMe(errSecCSUnsigned
);
284 const EmbeddedSignatureBlob
&blobs
= *signingData();
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
);
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.
302 CFDataRef
MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot
)
305 return signingData()->component(slot
);
314 EmbeddedSignatureBlob
*MachORep::signingData()
316 if (!mSigningData
) { // fetch and cache
317 unique_ptr
<MachO
> macho(mainExecutableImage()->architecture());
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());
327 secinfo("machorep", "failed to read signing bytes from %s(%s)",
328 mainExecutablePath().c_str(), macho
->architecture().name());
329 MacOSError::throwMe(errSecCSSignatureInvalid
);
338 // Extract an embedded Info.plist from the file.
339 // Returns NULL if none is found.
341 CFDataRef
MachORep::infoPlist()
343 CFRef
<CFDataRef
> info
;
345 unique_ptr
<MachO
> macho(mainExecutableImage()->architecture());
346 if (const section
*sect
= macho
->findSection("__TEXT", "__info_plist")) {
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
)));
351 info
.take(macho
->dataAt(macho
->flip(sect
->offset
), macho
->flip(sect
->size
)));
355 secinfo("machorep", "exception reading embedded Info.plist");
362 // Provide a (vaguely) human readable characterization of this code
364 string
MachORep::format()
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())
375 s
+= it
->displayName();
379 assert(archs
.size() == 1);
380 return string("Mach-O thin (") + archs
.begin()->displayName() + ")";
383 return "Mach-O (unrecognized format)";
390 void MachORep::flush()
392 size_t offset
= mExecutable
->offset();
393 size_t length
= mExecutable
->length();
396 ::free(mSigningData
);
398 SingleDiskRep::flush();
399 mExecutable
= new Universal(fd(), offset
, length
);
402 CFDictionaryRef
MachORep::diskRepInformation()
404 unique_ptr
<MachO
> macho (mainExecutableImage()->architecture());
405 CFRef
<CFDictionaryRef
> info
;
407 uint32_t platform
= 0;
408 uint32_t minVersion
= 0;
409 uint32_t sdkVersion
= 0;
411 if (macho
->version(&platform
, &minVersion
, &sdkVersion
)) {
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.
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.
423 info
.take(cfmake
<CFMutableDictionaryRef
>("{%O = %d,%O = %d,%O = %d}",
424 kSecCodeInfoDiskRepVersionPlatform
, platform
,
425 kSecCodeInfoDiskRepVersionMin
, minVersion
,
426 kSecCodeInfoDiskRepVersionSDK
, sdkVersion
));
428 if (platform
== PLATFORM_MACOS
&& sdkVersion
< (10 << 16 | 9 << 8))
430 info
.take(cfmake
<CFMutableDictionaryRef
>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}",
432 kSecCodeInfoDiskRepNoLibraryValidation
));
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.
445 string
MachORep::recommendedIdentifier(const SigningContext
&ctx
)
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
);
453 return cfString(code
);
455 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
458 // ah well. Use the default
459 return SingleDiskRep::recommendedIdentifier(ctx
);
464 // The default suggested requirements for Mach-O binaries are as follows:
465 // Library requirement: Composed from dynamic load commands.
467 const Requirements
*MachORep::defaultRequirements(const Architecture
*arch
, const SigningContext
&ctx
)
469 assert(arch
); // enforced by signing infrastructure
470 Requirements::Maker maker
;
472 // add library requirements from DYLIB commands (if any)
473 if (Requirement
*libreq
= libraryRequirements(arch
, ctx
))
474 maker
.add(kSecLibraryRequirementType
, libreq
); // takes ownership
480 Requirement
*MachORep::libraryRequirements(const Architecture
*arch
, const SigningContext
&ctx
)
482 unique_ptr
<MachO
> macho(mainExecutableImage()->architecture(*arch
));
483 Requirement::Maker maker
;
484 Requirement::Maker::Chain
chain(maker
, opOr
);
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
)) {
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
));
511 secinfo("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep
->magic(), n
);
517 secinfo("machorep", "missing DR info for library index %d", n
);
534 // Default to system page size for segmented (paged) signatures
536 size_t MachORep::pageSize(const SigningContext
&)
538 return segmentedPageSize
;
545 void MachORep::strictValidate(const CodeDirectory
* cd
, const ToleratedErrors
& tolerated
, SecCSFlags flags
)
547 SingleDiskRep::strictValidate(cd
, tolerated
, flags
);
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
);
556 // FileDiskRep::Writers
558 DiskRep::Writer
*MachORep::writer()
560 return new Writer(this);
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).
570 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
573 Syslog::notice("code signing internal error: trying to write Mach-O component directly");
574 MacOSError::throwMe(errSecCSInternalError
);
577 void MachORep::registerStapledTicket()
579 CFRef
<CFDataRef
> data
= NULL
;
581 data
.take(mSigningData
->component(cdTicketSlot
));
582 registerStapledTicketInMachO(data
);
586 } // end namespace CodeSigning
587 } // end namespace Security