]> git.saurik.com Git - apple/libsecurity_codesigning.git/blame - lib/machorep.cpp
libsecurity_codesigning-32953.tar.gz
[apple/libsecurity_codesigning.git] / lib / machorep.cpp
CommitLineData
7d31e928
A
1/*
2 * Copyright (c) 2006 Apple Computer, 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
29
30namespace Security {
31namespace CodeSigning {
32
33using namespace UnixPlusPlus;
34
35
36//
37// Object management.
38// We open the main executable lazily, so nothing much happens on construction.
39//
40MachORep::MachORep(const char *path)
41 : SingleDiskRep(path), mSigningData(NULL)
42{
43 mExecutable = new Universal(fd());
44}
45
46MachORep::~MachORep()
47{
48 delete mExecutable;
49 ::free(mSigningData);
50}
51
52
53//
54// Sniffer function for "plausible Mach-O binary"
55//
56bool MachORep::candidiate(FileDesc &fd)
57{
58 switch (Universal::typeOf(fd)) {
59 case MH_EXECUTE:
60 case MH_DYLIB:
61 case MH_DYLINKER:
62 case MH_BUNDLE:
63 case MH_PRELOAD:
64 return true; // dynamic image; supported
65 case MH_OBJECT:
66 return false; // maybe later...
67 default:
68 return false; // not Mach-O (or too exotic)
69 }
70}
71
72
73//
74// For Mach-O binaries that are of PowerPC architecture, we recommend
75// allowing the Rosetta translator as a host. Otherwise, no suggestions.
76//
77static 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,
83};
84
85const Requirements *MachORep::defaultRequirements(const Architecture *arch)
86{
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
90 else
91 return NULL;
92}
93
94
95//
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.
99//
100Universal *MachORep::mainExecutableImage()
101{
102 if (!mExecutable)
103 mExecutable = new Universal(fd());
104 return mExecutable;
105}
106
107
108//
109// Default to system page size for segmented (paged) signatures
110//
111size_t MachORep::pageSize()
112{
113 return segmentedPageSize;
114}
115
116
117//
118// Signing base is the start of the Mach-O architecture we're using
119//
120size_t MachORep::signingBase()
121{
122 return mainExecutableImage()->archOffset();
123}
124
125
126//
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.
133//
134CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
135{
136 switch (slot) {
137 case cdInfoSlot:
138 return infoPlist();
139 default:
140 return embeddedComponent(slot);
141 }
142}
143
144
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.
152//
153CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
154{
155 if (!mSigningData) // fetch and cache
156 try {
157 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
158 if (macho.get())
159 if (size_t offset = macho->signingOffset()) {
160 macho->seek(offset);
161 mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd());
162 if (mSigningData)
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());
166 else
167 secdebug("machorep", "failed to read signing bytes from %s(%s)",
168 mainExecutablePath().c_str(), macho->architecture().name());
169 }
170 } catch (...) {
171 secdebug("machorep", "exception reading Mach-O from universal");
172 }
173 if (mSigningData)
174 return mSigningData->component(slot);
175
176 // not found
177 return NULL;
178}
179
180
181//
182// Extract an embedded Info.plist from the file.
183// Returns NULL if none is found.
184//
185CFDataRef MachORep::infoPlist()
186{
187 CFRef<CFDataRef> info;
188 try {
189 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
190 if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
191 if (macho->is64()) {
192 const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
193 info = macho->dataAt(macho->flip(sect64->offset), macho->flip(sect64->size));
194 } else {
195 info = macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size));
196 }
197 }
198 } catch (...) {
199 secdebug("machorep", "exception reading embedded Info.plist");
200 }
201 return info.yield();
202}
203
204
205//
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.
209//
210string MachORep::recommendedIdentifier()
211{
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);
217 if (code)
218 return cfString(code);
219 } else
220 MacOSError::throwMe(errSecCSBadDictionaryFormat);
221 }
222
223 // ah well. Use the default
224 return SingleDiskRep::recommendedIdentifier();
225}
226
227
228//
229// Provide a (vaguely) human readable characterization of this code
230//
231string MachORep::format()
232{
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())
241 s += " ";
242 s += it->name();
243 }
244 return s + ")";
245 } else {
246 assert(archs.size() == 1);
247 return string("Mach-O thin (") + archs.begin()->name() + ")";
248 }
249 } else
250 return "not Mach-O"; // (you don't usually show that one to the user)
251}
252
253
254//
255// Flush cached data
256//
257void MachORep::flush()
258{
259 delete mExecutable;
260 mExecutable = NULL;
261 ::free(mSigningData);
262 mSigningData = NULL;
263 SingleDiskRep::flush();
264}
265
266
267//
268// FileDiskRep::Writers
269//
270DiskRep::Writer *MachORep::writer()
271{
272 return new Writer(this);
273}
274
275
276//
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).
281//
282void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
283{
284 MacOSError::throwMe(errSecCSInternalError);
285}
286
287
288} // end namespace CodeSigning
289} // end namespace Security