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
31 namespace CodeSigning
{
33 using namespace UnixPlusPlus
;
38 // We open the main executable lazily, so nothing much happens on construction.
40 MachORep::MachORep(const char *path
)
41 : SingleDiskRep(path
), mSigningData(NULL
)
43 mExecutable
= new Universal(fd());
54 // Sniffer function for "plausible Mach-O binary"
56 bool MachORep::candidiate(FileDesc
&fd
)
58 switch (Universal::typeOf(fd
)) {
64 return true; // dynamic image; supported
66 return false; // maybe later...
68 return false; // not Mach-O (or too exotic)
74 // For Mach-O binaries that are of PowerPC architecture, we recommend
75 // allowing the Rosetta translator as a host. Otherwise, no suggestions.
77 static const uint8_t ppc_ireqs
[] = { // host => anchor apple and identifier com.apple.translate
78 0xfa, 0xde, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
79 0x00, 0x00, 0x00, 0x14, 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01,
80 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13,
81 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c,
82 0x61, 0x74, 0x65, 0x00,
85 const Requirements
*MachORep::defaultRequirements(const Architecture
*arch
)
87 assert(arch
); // enforced by signing infrastructure
88 if (arch
->cpuType() == CPU_TYPE_POWERPC
)
89 return ((const Requirements
*)ppc_ireqs
)->clone(); // need to pass ownership
96 // Obtain, cache, and return a Universal reference to the main executable,
97 // IF the main executable is a Mach-O binary (or fat version thereof).
98 // Returns NULL if the main executable can't be opened as such.
100 Universal
*MachORep::mainExecutableImage()
103 mExecutable
= new Universal(fd());
109 // Default to system page size for segmented (paged) signatures
111 size_t MachORep::pageSize()
113 return segmentedPageSize
;
118 // Signing base is the start of the Mach-O architecture we're using
120 size_t MachORep::signingBase()
122 return mainExecutableImage()->archOffset();
127 // Retrieve a component from the executable.
128 // This reads the entire signing SuperBlob when first called for an executable,
129 // and then caches it for further use.
130 // Note that we could read individual components directly off disk and only cache
131 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
132 // to cache the pieces anyway.
134 CFDataRef
MachORep::component(CodeDirectory::SpecialSlot slot
)
140 return embeddedComponent(slot
);
145 // Retrieve a component from the embedded signature SuperBlob (if present).
146 // This reads the entire signing SuperBlob when first called for an executable,
147 // and then caches it for further use.
148 // Note that we could read individual components directly off disk and only cache
149 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
150 // to cache the pieces anyway. But it's not clear that the resulting multiple I/O
151 // calls wouldn't be slower in the end.
153 CFDataRef
MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot
)
155 if (!mSigningData
) // fetch and cache
157 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture());
159 if (size_t offset
= macho
->signingOffset()) {
161 mSigningData
= EmbeddedSignatureBlob::readBlob(macho
->fd());
163 secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
164 mSigningData
->length(), mSigningData
->count(),
165 mainExecutablePath().c_str(), macho
->architecture().name());
167 secdebug("machorep", "failed to read signing bytes from %s(%s)",
168 mainExecutablePath().c_str(), macho
->architecture().name());
171 secdebug("machorep", "exception reading Mach-O from universal");
174 return mSigningData
->component(slot
);
182 // Extract an embedded Info.plist from the file.
183 // Returns NULL if none is found.
185 CFDataRef
MachORep::infoPlist()
187 CFRef
<CFDataRef
> info
;
189 auto_ptr
<MachO
> macho(mainExecutableImage()->architecture());
190 if (const section
*sect
= macho
->findSection("__TEXT", "__info_plist")) {
192 const section_64
*sect64
= reinterpret_cast<const section_64
*>(sect
);
193 info
= macho
->dataAt(macho
->flip(sect64
->offset
), macho
->flip(sect64
->size
));
195 info
= macho
->dataAt(macho
->flip(sect
->offset
), macho
->flip(sect
->size
));
199 secdebug("machorep", "exception reading embedded Info.plist");
206 // Return a recommended unique identifier.
207 // If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
208 // Otherwise, use the default.
210 string
MachORep::recommendedIdentifier()
212 if (CFDataRef info
= infoPlist()) {
213 if (CFDictionaryRef dict
= makeCFDictionaryFrom(info
)) {
214 CFStringRef code
= CFStringRef(CFDictionaryGetValue(dict
, kCFBundleIdentifierKey
));
215 if (code
&& CFGetTypeID(code
) != CFStringGetTypeID())
216 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
218 return cfString(code
);
220 MacOSError::throwMe(errSecCSBadDictionaryFormat
);
223 // ah well. Use the default
224 return SingleDiskRep::recommendedIdentifier();
229 // Provide a (vaguely) human readable characterization of this code
231 string
MachORep::format()
233 if (Universal
*fat
= mainExecutableImage()) {
234 Universal::Architectures archs
;
235 fat
->architectures(archs
);
236 if (fat
->isUniversal()) {
237 string s
= "Mach-O universal (";
238 for (Universal::Architectures::const_iterator it
= archs
.begin();
239 it
!= archs
.end(); ++it
) {
240 if (it
!= archs
.begin())
246 assert(archs
.size() == 1);
247 return string("Mach-O thin (") + archs
.begin()->name() + ")";
250 return "not Mach-O"; // (you don't usually show that one to the user)
257 void MachORep::flush()
261 ::free(mSigningData
);
263 SingleDiskRep::flush();
268 // FileDiskRep::Writers
270 DiskRep::Writer
*MachORep::writer()
272 return new Writer(this);
277 // Write a component.
278 // MachORep::Writers don't write to components directly; the signing code uses special
279 // knowledge of the Mach-O format to build embedded signatures and blasts them directly
280 // to disk. Thus this implementation will never be called (and, if called, will simply fail).
282 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
284 MacOSError::throwMe(errSecCSInternalError
);
288 } // end namespace CodeSigning
289 } // end namespace Security