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