2 * Copyright (c) 2006 Apple Computer, 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 "StaticCode.h"
33 namespace CodeSigning
{
35 using namespace UnixPlusPlus
;
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).
45 MachORep::MachORep(const char *path
, const Context
*ctx
)
46 : SingleDiskRep(path
), mSigningData(NULL
)
50 mExecutable
= new Universal(fd(), ctx
->offset
);
52 auto_ptr
<Universal
> full(new Universal(fd()));
53 mExecutable
= new Universal(fd(), full
->archOffset(ctx
->arch
));
55 mExecutable
= new Universal(fd());
57 mExecutable
= new Universal(fd());
59 CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path
, (void*)ctx
);
70 // Sniffer function for "plausible Mach-O binary"
72 bool MachORep::candidate(FileDesc
&fd
)
74 switch (Universal::typeOf(fd
)) {
80 return true; // dynamic image; supported
82 return false; // maybe later...
84 return false; // not Mach-O (or too exotic)
90 // The default suggested requirements for Mach-O binaries are as follows:
91 // Hosting requirement: Rosetta if it's PPC, none otherwise.
92 // Library requirement: Composed from dynamic load commands.
94 static const uint8_t ppc_host_ireq
[] = { // anchor apple and identifier com.apple.translate
95 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
96 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x63, 0x6f, 0x6d, 0x2e,
97 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x00,
100 const Requirements
*MachORep::defaultRequirements(const Architecture
*arch
)
102 assert(arch
); // enforced by signing infrastructure
103 Requirements::Maker maker
;
105 // if ppc architecture, add hosting requirement for Rosetta's translate tool
106 if (arch
->cpuType() == CPU_TYPE_POWERPC
)
107 maker
.add(kSecHostRequirementType
, ((const Requirement
*)ppc_host_ireq
)->clone());
109 // add library requirements from DYLIB commands (if any)
110 if (Requirement
*libreq
= libraryRequirements(arch
))
111 maker
.add(kSecLibraryRequirementType
, libreq
); // takes ownership
117 Requirement
*MachORep::libraryRequirements(const Architecture
*arch
)
119 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture(*arch
));
120 Requirement::Maker maker
;
121 Requirement::Maker::Chain
chain(maker
, opOr
);
123 for (const load_command
*command
= macho
->loadCommands(); command
; command
= macho
->nextCommand(command
)) {
124 if (macho
->flip(command
->cmd
) == LC_LOAD_DYLIB
) {
125 const dylib_command
*dycmd
= (const dylib_command
*)command
;
126 if (const char *name
= macho
->string(command
, dycmd
->dylib
.name
))
128 secdebug("machorep", "examining DYLIB %s", name
);
129 // find path on disk, get designated requirement (if signed)
130 if (RefPointer
<DiskRep
> rep
= DiskRep::bestFileGuess(name
))
131 if (SecPointer
<SecStaticCode
> code
= new SecStaticCode(rep
))
132 if (const Requirement
*req
= code
->designatedRequirement()) {
133 secdebug("machorep", "adding library requirement for %s", name
);
135 chain
.maker
.copy(req
);
138 secdebug("machorep", "exception getting library requirement (ignored)");
141 secdebug("machorep", "no string for DYLIB command (ignored)");
154 // Obtain, cache, and return a Universal reference to the main executable,
155 // IF the main executable is a Mach-O binary (or fat version thereof).
156 // Returns NULL if the main executable can't be opened as such.
158 Universal
*MachORep::mainExecutableImage()
161 mExecutable
= new Universal(fd());
167 // Default to system page size for segmented (paged) signatures
169 size_t MachORep::pageSize()
171 return segmentedPageSize
;
176 // Signing base is the start of the Mach-O architecture we're using
178 size_t MachORep::signingBase()
180 return mainExecutableImage()->archOffset();
185 // We choose the binary identifier for a Mach-O binary as follows:
186 // - If the Mach-O headers have a UUID command, use the UUID.
187 // - Otherwise, use the SHA-1 hash of the (entire) load commands.
189 CFDataRef
MachORep::identification()
191 std::auto_ptr
<MachO
> macho(mainExecutableImage()->architecture());
192 return identificationFor(macho
.get());
195 CFDataRef
MachORep::identificationFor(MachO
*macho
)
197 // if there is a LC_UUID load command, use the UUID contained therein
198 if (const load_command
*cmd
= macho
->findCommand(LC_UUID
)) {
199 const uuid_command
*uuidc
= reinterpret_cast<const uuid_command
*>(cmd
);
200 char result
[4 + sizeof(uuidc
->uuid
)];
201 memcpy(result
, "UUID", 4);
202 memcpy(result
+4, uuidc
->uuid
, sizeof(uuidc
->uuid
));
203 return makeCFData(result
, sizeof(result
));
206 // otherwise, use the SHA-1 hash of the entire load command area
208 hash(&macho
->header(), sizeof(mach_header
));
209 hash(macho
->loadCommands(), macho
->commandLength());
212 return makeCFData(digest
, sizeof(digest
));
217 // Retrieve a component from the executable.
218 // This reads the entire signing SuperBlob when first called for an executable,
219 // and then caches it for further use.
220 // Note that we could read individual components directly off disk and only cache
221 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
222 // to cache the pieces anyway.
224 CFDataRef
MachORep::component(CodeDirectory::SpecialSlot slot
)
230 return embeddedComponent(slot
);
235 // Retrieve a component from the embedded signature SuperBlob (if present).
236 // This reads the entire signing SuperBlob when first called for an executable,
237 // and then caches it for further use.
238 // Note that we could read individual components directly off disk and only cache
239 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
240 // to cache the pieces anyway. But it's not clear that the resulting multiple I/O
241 // calls wouldn't be slower in the end.
243 CFDataRef
MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot
)
245 if (!mSigningData
) { // fetch and cache
246 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture());
248 if (const linkedit_data_command
*cs
= macho
->findCodeSignature()) {
249 size_t offset
= macho
->flip(cs
->dataoff
);
250 size_t length
= macho
->flip(cs
->datasize
);
251 if (mSigningData
= EmbeddedSignatureBlob::readBlob(macho
->fd(), macho
->offset() + offset
, length
)) {
252 secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
253 mSigningData
->length(), mSigningData
->count(),
254 mainExecutablePath().c_str(), macho
->architecture().name());
256 secdebug("machorep", "failed to read signing bytes from %s(%s)",
257 mainExecutablePath().c_str(), macho
->architecture().name());
258 MacOSError::throwMe(errSecCSSignatureInvalid
);
263 return mSigningData
->component(slot
);
271 // Extract an embedded Info.plist from the file.
272 // Returns NULL if none is found.
274 CFDataRef
MachORep::infoPlist()
276 CFRef
<CFDataRef
> info
;
278 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture());
279 if (const section
*sect
= macho
->findSection("__TEXT", "__info_plist")) {
281 const section_64
*sect64
= reinterpret_cast<const section_64
*>(sect
);
282 info
.take(macho
->dataAt(macho
->flip(sect64
->offset
), macho
->flip(sect64
->size
)));
284 info
.take(macho
->dataAt(macho
->flip(sect
->offset
), macho
->flip(sect
->size
)));
288 secdebug("machorep", "exception reading embedded Info.plist");
295 // Return a recommended unique identifier.
296 // If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
297 // Otherwise, use the default.
299 string
MachORep::recommendedIdentifier()
301 if (CFDataRef info
= infoPlist()) {
302 if (CFDictionaryRef dict
= makeCFDictionaryFrom(info
)) {
303 CFStringRef code
= CFStringRef(CFDictionaryGetValue(dict
, kCFBundleIdentifierKey
));
304 if (code
&& CFGetTypeID(code
) != CFStringGetTypeID())
305 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
307 return cfString(code
);
309 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
312 // ah well. Use the default
313 return SingleDiskRep::recommendedIdentifier();
318 // Provide a (vaguely) human readable characterization of this code
320 string
MachORep::format()
322 if (Universal
*fat
= mainExecutableImage()) {
323 Universal::Architectures archs
;
324 fat
->architectures(archs
);
325 if (fat
->isUniversal()) {
326 string s
= "Mach-O universal (";
327 for (Universal::Architectures::const_iterator it
= archs
.begin();
328 it
!= archs
.end(); ++it
) {
329 if (it
!= archs
.begin())
335 assert(archs
.size() == 1);
336 return string("Mach-O thin (") + archs
.begin()->name() + ")";
339 return "Mach-O (unrecognized format)";
346 void MachORep::flush()
350 ::free(mSigningData
);
352 SingleDiskRep::flush();
357 // FileDiskRep::Writers
359 DiskRep::Writer
*MachORep::writer()
361 return new Writer(this);
366 // Write a component.
367 // MachORep::Writers don't write to components directly; the signing code uses special
368 // knowledge of the Mach-O format to build embedded signatures and blasts them directly
369 // to disk. Thus this implementation will never be called (and, if called, will simply fail).
371 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
374 MacOSError::throwMe(errSecCSInternalError
);
378 } // end namespace CodeSigning
379 } // end namespace Security