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