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