]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/machorep.cpp
620802bd8cb96e669585311a189035989f9116c6
[apple/libsecurity_codesigning.git] / lib / machorep.cpp
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 #include "StaticCode.h"
29 #include "reqmaker.h"
30
31
32 namespace Security {
33 namespace CodeSigning {
34
35 using namespace UnixPlusPlus;
36
37
38 //
39 // Object management.
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).
44 //
45 MachORep::MachORep(const char *path, const Context *ctx)
46 : SingleDiskRep(path), mSigningData(NULL)
47 {
48 if (ctx)
49 if (ctx->offset)
50 mExecutable = new Universal(fd(), ctx->offset);
51 else if (ctx->arch) {
52 auto_ptr<Universal> full(new Universal(fd()));
53 mExecutable = new Universal(fd(), full->archOffset(ctx->arch));
54 } else
55 mExecutable = new Universal(fd());
56 else
57 mExecutable = new Universal(fd());
58 assert(mExecutable);
59 CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx);
60 }
61
62 MachORep::~MachORep()
63 {
64 delete mExecutable;
65 ::free(mSigningData);
66 }
67
68
69 //
70 // Sniffer function for "plausible Mach-O binary"
71 //
72 bool MachORep::candidate(FileDesc &fd)
73 {
74 switch (Universal::typeOf(fd)) {
75 case MH_EXECUTE:
76 case MH_DYLIB:
77 case MH_DYLINKER:
78 case MH_BUNDLE:
79 case MH_PRELOAD:
80 return true; // dynamic image; supported
81 case MH_OBJECT:
82 return false; // maybe later...
83 default:
84 return false; // not Mach-O (or too exotic)
85 }
86 }
87
88
89 //
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.
93 //
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,
98 };
99
100 const Requirements *MachORep::defaultRequirements(const Architecture *arch)
101 {
102 assert(arch); // enforced by signing infrastructure
103 Requirements::Maker maker;
104
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());
108
109 // add library requirements from DYLIB commands (if any)
110 if (Requirement *libreq = libraryRequirements(arch))
111 maker.add(kSecLibraryRequirementType, libreq); // takes ownership
112
113 // that's all
114 return maker.make();
115 }
116
117 Requirement *MachORep::libraryRequirements(const Architecture *arch)
118 {
119 auto_ptr<MachO> macho(mainExecutableImage()->architecture(*arch));
120 Requirement::Maker maker;
121 Requirement::Maker::Chain chain(maker, opOr);
122 if (macho.get()) {
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))
127 try {
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);
134 chain.add();
135 chain.maker.copy(req);
136 }
137 } catch (...) {
138 secdebug("machorep", "exception getting library requirement (ignored)");
139 }
140 else
141 secdebug("machorep", "no string for DYLIB command (ignored)");
142 }
143 }
144 }
145 if (chain.empty())
146 return NULL;
147 else
148 return maker.make();
149 }
150
151
152
153 //
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.
157 //
158 Universal *MachORep::mainExecutableImage()
159 {
160 if (!mExecutable)
161 mExecutable = new Universal(fd());
162 return mExecutable;
163 }
164
165
166 //
167 // Default to system page size for segmented (paged) signatures
168 //
169 size_t MachORep::pageSize()
170 {
171 return segmentedPageSize;
172 }
173
174
175 //
176 // Signing base is the start of the Mach-O architecture we're using
177 //
178 size_t MachORep::signingBase()
179 {
180 return mainExecutableImage()->archOffset();
181 }
182
183
184 //
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.
188 //
189 CFDataRef MachORep::identification()
190 {
191 std::auto_ptr<MachO> macho(mainExecutableImage()->architecture());
192 return identificationFor(macho.get());
193 }
194
195 CFDataRef MachORep::identificationFor(MachO *macho)
196 {
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));
204 }
205
206 // otherwise, use the SHA-1 hash of the entire load command area
207 SHA1 hash;
208 hash(&macho->header(), sizeof(mach_header));
209 hash(macho->loadCommands(), macho->commandLength());
210 SHA1::Digest digest;
211 hash.finish(digest);
212 return makeCFData(digest, sizeof(digest));
213 }
214
215
216 //
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.
223 //
224 CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
225 {
226 switch (slot) {
227 case cdInfoSlot:
228 return infoPlist();
229 default:
230 return embeddedComponent(slot);
231 }
232 }
233
234
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.
242 //
243 CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
244 {
245 if (!mSigningData) { // fetch and cache
246 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
247 if (macho.get())
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());
255 } else {
256 secdebug("machorep", "failed to read signing bytes from %s(%s)",
257 mainExecutablePath().c_str(), macho->architecture().name());
258 MacOSError::throwMe(errSecCSSignatureInvalid);
259 }
260 }
261 }
262 if (mSigningData)
263 return mSigningData->component(slot);
264
265 // not found
266 return NULL;
267 }
268
269
270 //
271 // Extract an embedded Info.plist from the file.
272 // Returns NULL if none is found.
273 //
274 CFDataRef MachORep::infoPlist()
275 {
276 CFRef<CFDataRef> info;
277 try {
278 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
279 if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
280 if (macho->is64()) {
281 const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
282 info.take(macho->dataAt(macho->flip(sect64->offset), macho->flip(sect64->size)));
283 } else {
284 info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)));
285 }
286 }
287 } catch (...) {
288 secdebug("machorep", "exception reading embedded Info.plist");
289 }
290 return info.yield();
291 }
292
293
294 //
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.
298 //
299 string MachORep::recommendedIdentifier()
300 {
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);
306 if (code)
307 return cfString(code);
308 } else
309 MacOSError::throwMe(errSecCSBadDictionaryFormat);
310 }
311
312 // ah well. Use the default
313 return SingleDiskRep::recommendedIdentifier();
314 }
315
316
317 //
318 // Provide a (vaguely) human readable characterization of this code
319 //
320 string MachORep::format()
321 {
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())
330 s += " ";
331 s += it->name();
332 }
333 return s + ")";
334 } else {
335 assert(archs.size() == 1);
336 return string("Mach-O thin (") + archs.begin()->name() + ")";
337 }
338 } else
339 return "Mach-O (unrecognized format)";
340 }
341
342
343 //
344 // Flush cached data
345 //
346 void MachORep::flush()
347 {
348 delete mExecutable;
349 mExecutable = NULL;
350 ::free(mSigningData);
351 mSigningData = NULL;
352 SingleDiskRep::flush();
353 }
354
355
356 //
357 // FileDiskRep::Writers
358 //
359 DiskRep::Writer *MachORep::writer()
360 {
361 return new Writer(this);
362 }
363
364
365 //
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).
370 //
371 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
372 {
373 assert(false);
374 MacOSError::throwMe(errSecCSInternalError);
375 }
376
377
378 } // end namespace CodeSigning
379 } // end namespace Security