]>
Commit | Line | Data |
---|---|---|
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 | ||
30 | namespace Security { | |
31 | namespace CodeSigning { | |
32 | ||
33 | using namespace UnixPlusPlus; | |
34 | ||
35 | ||
36 | // | |
37 | // Object management. | |
38 | // We open the main executable lazily, so nothing much happens on construction. | |
39 | // | |
40 | MachORep::MachORep(const char *path) | |
41 | : SingleDiskRep(path), mSigningData(NULL) | |
42 | { | |
43 | mExecutable = new Universal(fd()); | |
44 | } | |
45 | ||
46 | MachORep::~MachORep() | |
47 | { | |
48 | delete mExecutable; | |
49 | ::free(mSigningData); | |
50 | } | |
51 | ||
52 | ||
53 | // | |
54 | // Sniffer function for "plausible Mach-O binary" | |
55 | // | |
56 | bool 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 | // | |
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, | |
83 | }; | |
84 | ||
85 | const 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 | // | |
100 | Universal *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 | // | |
111 | size_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 | // | |
120 | size_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 | // | |
134 | CFDataRef 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 | // | |
153 | CFDataRef 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 | // | |
185 | CFDataRef 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 | // | |
210 | string 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 | // | |
231 | string 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 | // | |
257 | void 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 | // | |
270 | DiskRep::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 | // | |
282 | void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) | |
283 | { | |
284 | MacOSError::throwMe(errSecCSInternalError); | |
285 | } | |
286 | ||
287 | ||
288 | } // end namespace CodeSigning | |
289 | } // end namespace Security |