]> git.saurik.com Git - apple/libsecurity_codesigning.git/blame - lib/machorep.cpp
libsecurity_codesigning-55037.15.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"
d1c1ab47
A
28#include "StaticCode.h"
29#include "reqmaker.h"
7d31e928
A
30
31
32namespace Security {
33namespace CodeSigning {
34
35using namespace UnixPlusPlus;
36
37
38//
39// Object management.
40// We open the main executable lazily, so nothing much happens on construction.
d1c1ab47
A
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).
7d31e928 44//
d1c1ab47 45MachORep::MachORep(const char *path, const Context *ctx)
7d31e928
A
46 : SingleDiskRep(path), mSigningData(NULL)
47{
d1c1ab47
A
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);
7d31e928
A
60}
61
62MachORep::~MachORep()
63{
64 delete mExecutable;
65 ::free(mSigningData);
66}
67
68
69//
70// Sniffer function for "plausible Mach-O binary"
71//
d1c1ab47 72bool MachORep::candidate(FileDesc &fd)
7d31e928
A
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
7d31e928
A
89
90//
f60086fc 91// Nowadays, the main executable object is created upon construction.
7d31e928
A
92//
93Universal *MachORep::mainExecutableImage()
94{
7d31e928
A
95 return mExecutable;
96}
97
98
99//
d1c1ab47 100// Signing base is the start of the Mach-O architecture we're using
7d31e928 101//
d1c1ab47 102size_t MachORep::signingBase()
7d31e928 103{
d1c1ab47 104 return mainExecutableImage()->archOffset();
7d31e928
A
105}
106
107
108//
d1c1ab47
A
109// We choose the binary identifier for a Mach-O binary as follows:
110// - If the Mach-O headers have a UUID command, use the UUID.
111// - Otherwise, use the SHA-1 hash of the (entire) load commands.
7d31e928 112//
d1c1ab47 113CFDataRef MachORep::identification()
7d31e928 114{
d1c1ab47
A
115 std::auto_ptr<MachO> macho(mainExecutableImage()->architecture());
116 return identificationFor(macho.get());
117}
118
119CFDataRef MachORep::identificationFor(MachO *macho)
120{
121 // if there is a LC_UUID load command, use the UUID contained therein
122 if (const load_command *cmd = macho->findCommand(LC_UUID)) {
123 const uuid_command *uuidc = reinterpret_cast<const uuid_command *>(cmd);
124 char result[4 + sizeof(uuidc->uuid)];
125 memcpy(result, "UUID", 4);
126 memcpy(result+4, uuidc->uuid, sizeof(uuidc->uuid));
127 return makeCFData(result, sizeof(result));
128 }
129
130 // otherwise, use the SHA-1 hash of the entire load command area
131 SHA1 hash;
132 hash(&macho->header(), sizeof(mach_header));
133 hash(macho->loadCommands(), macho->commandLength());
134 SHA1::Digest digest;
135 hash.finish(digest);
136 return makeCFData(digest, sizeof(digest));
7d31e928
A
137}
138
139
140//
141// Retrieve a component from the executable.
142// This reads the entire signing SuperBlob when first called for an executable,
143// and then caches it for further use.
144// Note that we could read individual components directly off disk and only cache
145// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
146// to cache the pieces anyway.
147//
148CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
149{
150 switch (slot) {
151 case cdInfoSlot:
152 return infoPlist();
153 default:
154 return embeddedComponent(slot);
155 }
156}
157
158
159// Retrieve a component from the embedded signature SuperBlob (if present).
160// This reads the entire signing SuperBlob when first called for an executable,
161// and then caches it for further use.
162// Note that we could read individual components directly off disk and only cache
163// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
164// to cache the pieces anyway. But it's not clear that the resulting multiple I/O
165// calls wouldn't be slower in the end.
166//
167CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
168{
d1c1ab47
A
169 if (!mSigningData) { // fetch and cache
170 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
171 if (macho.get())
172 if (const linkedit_data_command *cs = macho->findCodeSignature()) {
173 size_t offset = macho->flip(cs->dataoff);
174 size_t length = macho->flip(cs->datasize);
62e4ed3d 175 if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) {
d1c1ab47
A
176 secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
177 mSigningData->length(), mSigningData->count(),
178 mainExecutablePath().c_str(), macho->architecture().name());
179 } else {
180 secdebug("machorep", "failed to read signing bytes from %s(%s)",
181 mainExecutablePath().c_str(), macho->architecture().name());
182 MacOSError::throwMe(errSecCSSignatureInvalid);
7d31e928 183 }
d1c1ab47
A
184 }
185 }
7d31e928
A
186 if (mSigningData)
187 return mSigningData->component(slot);
188
189 // not found
190 return NULL;
191}
192
193
194//
195// Extract an embedded Info.plist from the file.
196// Returns NULL if none is found.
197//
198CFDataRef MachORep::infoPlist()
199{
200 CFRef<CFDataRef> info;
201 try {
202 auto_ptr<MachO> macho(mainExecutableImage()->architecture());
203 if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
204 if (macho->is64()) {
205 const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
d1c1ab47 206 info.take(macho->dataAt(macho->flip(sect64->offset), macho->flip(sect64->size)));
7d31e928 207 } else {
d1c1ab47 208 info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)));
7d31e928
A
209 }
210 }
211 } catch (...) {
212 secdebug("machorep", "exception reading embedded Info.plist");
213 }
214 return info.yield();
215}
216
217
7d31e928
A
218//
219// Provide a (vaguely) human readable characterization of this code
220//
221string MachORep::format()
222{
223 if (Universal *fat = mainExecutableImage()) {
224 Universal::Architectures archs;
225 fat->architectures(archs);
226 if (fat->isUniversal()) {
227 string s = "Mach-O universal (";
228 for (Universal::Architectures::const_iterator it = archs.begin();
229 it != archs.end(); ++it) {
230 if (it != archs.begin())
231 s += " ";
d1c1ab47 232 s += it->displayName();
7d31e928
A
233 }
234 return s + ")";
235 } else {
236 assert(archs.size() == 1);
d1c1ab47 237 return string("Mach-O thin (") + archs.begin()->displayName() + ")";
7d31e928
A
238 }
239 } else
d1c1ab47 240 return "Mach-O (unrecognized format)";
7d31e928
A
241}
242
243
244//
245// Flush cached data
246//
247void MachORep::flush()
248{
249 delete mExecutable;
250 mExecutable = NULL;
251 ::free(mSigningData);
252 mSigningData = NULL;
253 SingleDiskRep::flush();
f60086fc
A
254 mExecutable = new Universal(fd());
255}
256
257
258//
259// Return a recommended unique identifier.
260// If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
261// Otherwise, use the default.
262//
263string MachORep::recommendedIdentifier(const SigningContext &ctx)
264{
265 if (CFDataRef info = infoPlist()) {
62e4ed3d 266 if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) {
f60086fc
A
267 CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey));
268 if (code && CFGetTypeID(code) != CFStringGetTypeID())
269 MacOSError::throwMe(errSecCSBadDictionaryFormat);
270 if (code)
271 return cfString(code);
272 } else
273 MacOSError::throwMe(errSecCSBadDictionaryFormat);
274 }
275
276 // ah well. Use the default
277 return SingleDiskRep::recommendedIdentifier(ctx);
278}
279
280
281//
282// The default suggested requirements for Mach-O binaries are as follows:
f60086fc
A
283// Library requirement: Composed from dynamic load commands.
284//
f60086fc
A
285const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
286{
287 assert(arch); // enforced by signing infrastructure
288 Requirements::Maker maker;
f60086fc
A
289
290 // add library requirements from DYLIB commands (if any)
291 if (Requirement *libreq = libraryRequirements(arch, ctx))
292 maker.add(kSecLibraryRequirementType, libreq); // takes ownership
293
294 // that's all
295 return maker.make();
296}
297
298Requirement *MachORep::libraryRequirements(const Architecture *arch, const SigningContext &ctx)
299{
300 auto_ptr<MachO> macho(mainExecutableImage()->architecture(*arch));
301 Requirement::Maker maker;
302 Requirement::Maker::Chain chain(maker, opOr);
62e4ed3d
A
303
304 if (macho.get())
305 if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) {
306 size_t offset = macho->flip(ldep->dataoff);
307 size_t length = macho->flip(ldep->datasize);
308 if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) {
309 try {
310 secdebug("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
311 deplist->length(), deplist->count(),
312 mainExecutablePath().c_str(), macho->architecture().name());
313 unsigned count = deplist->count();
314 // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far
315 for (unsigned n = 0; n < count; n++) {
316 const Requirement *req = NULL;
317 if (const BlobCore *dep = deplist->blob(n)) {
318 if ((req = Requirement::specific(dep))) {
319 // binary code requirement; good to go
320 } else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) {
321 // blob-wrapped text form - convert to binary requirement
322 std::string reqString = std::string((const char *)wrap->data(), wrap->length());
323 CFRef<SecRequirementRef> areq;
324 MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref()));
325 CFRef<CFDataRef> reqData;
326 MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref()));
327 req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData));
328 } else {
329 secdebug("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
330 continue;
331 }
332 chain.add();
333 maker.copy(req);
334 } else
335 secdebug("machorep", "missing DR info for library index %d", n);
f60086fc 336 }
62e4ed3d
A
337 ::free(deplist);
338 } catch (...) {
339 ::free(deplist);
340 throw;
341 }
f60086fc
A
342 }
343 }
f60086fc
A
344 if (chain.empty())
345 return NULL;
346 else
347 return maker.make();
348}
349
350
351//
352// Default to system page size for segmented (paged) signatures
353//
354size_t MachORep::pageSize(const SigningContext &)
355{
356 return segmentedPageSize;
7d31e928
A
357}
358
359
360//
361// FileDiskRep::Writers
362//
363DiskRep::Writer *MachORep::writer()
364{
365 return new Writer(this);
366}
367
368
369//
370// Write a component.
371// MachORep::Writers don't write to components directly; the signing code uses special
372// knowledge of the Mach-O format to build embedded signatures and blasts them directly
373// to disk. Thus this implementation will never be called (and, if called, will simply fail).
374//
375void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
376{
d1c1ab47 377 assert(false);
7d31e928
A
378 MacOSError::throwMe(errSecCSInternalError);
379}
380
381
382} // end namespace CodeSigning
383} // end namespace Security