]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/bundlediskrep.cpp
b7d3315d03c7c134912cdfd0e69cc1d0b2dcdb8d
[apple/libsecurity_codesigning.git] / lib / bundlediskrep.cpp
1 /*
2 * Copyright (c) 2006-2007 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 #include "bundlediskrep.h"
24 #include <CoreFoundation/CFURLAccess.h>
25 #include <CoreFoundation/CFBundlePriv.h>
26 #include <security_utilities/cfmunge.h>
27 #include <copyfile.h>
28
29
30 namespace Security {
31 namespace CodeSigning {
32
33 using namespace UnixPlusPlus;
34
35
36 //
37 // We make a CFBundleRef immediately, but everything else is lazy
38 //
39 BundleDiskRep::BundleDiskRep(const char *path, const Context *ctx)
40 : mBundle(_CFBundleCreateIfMightBeBundle(NULL, CFTempURL(path)))
41 {
42 if (!mBundle)
43 MacOSError::throwMe(errSecCSBadObjectFormat);
44 setup(ctx);
45 CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path, (void*)ctx, mExecRep);
46 }
47
48 BundleDiskRep::BundleDiskRep(CFBundleRef ref, const Context *ctx)
49 {
50 mBundle = ref; // retains
51 setup(ctx);
52 CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref, (void*)ctx, mExecRep);
53 }
54
55 void BundleDiskRep::setup(const Context *ctx)
56 {
57 string version = resourcesRootPath()
58 + "/Versions/"
59 + ((ctx && ctx->version) ? ctx->version : "Current")
60 + "/.";
61 if (::access(version.c_str(), F_OK) == 0) { // versioned bundle
62 if (CFBundleRef versionBundle = CFBundleCreate(NULL, CFTempURL(version)))
63 mBundle.take(versionBundle); // replace top bundle ref
64 else
65 MacOSError::throwMe(errSecCSStaticCodeNotFound);
66 } else {
67 if (ctx && ctx->version) // explicitly specified
68 MacOSError::throwMe(errSecCSStaticCodeNotFound);
69 }
70 mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx);
71 }
72
73
74 //
75 // Create a path to a bundle signing resource, by name.
76 // If the BUNDLEDISKREP_DIRECTORY directory exists in the bundle's support directory, files
77 // will be read and written there. Otherwise, they go directly into the support directory.
78 //
79 string BundleDiskRep::metaPath(const char *name)
80 {
81 if (mMetaPath.empty()) {
82 string support = cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true);
83 mMetaPath = support + "/" BUNDLEDISKREP_DIRECTORY;
84 if (::access(mMetaPath.c_str(), F_OK) == 0) {
85 mMetaExists = true;
86 } else {
87 mMetaPath = support;
88 mMetaExists = false;
89 }
90 }
91 return mMetaPath + "/" + name;
92 }
93
94
95 //
96 // Try to create the meta-file directory in our bundle.
97 // Does nothing if the directory already exists.
98 // Throws if an error occurs.
99 //
100 void BundleDiskRep::createMeta()
101 {
102 string meta = metaPath(BUNDLEDISKREP_DIRECTORY);
103 if (!mMetaExists) {
104 if (::mkdir(meta.c_str(), 0755) == 0) {
105 copyfile(cfString(canonicalPath(), true).c_str(), meta.c_str(), NULL, COPYFILE_SECURITY);
106 mMetaPath = meta;
107 mMetaExists = true;
108 } else if (errno != EEXIST)
109 UnixError::throwMe();
110 }
111 }
112
113
114 //
115 // Load and return a component, by slot number.
116 // Info.plist components come from the bundle, always (we don't look
117 // for Mach-O embedded versions).
118 // Everything else comes from the embedded blobs of a Mach-O image, or from
119 // files located in the Contents directory of the bundle.
120 //
121 CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot)
122 {
123 switch (slot) {
124 // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there
125 case cdInfoSlot:
126 if (CFRef<CFURLRef> info = _CFBundleCopyInfoPlistURL(mBundle))
127 return cfLoadFile(info);
128 else
129 return NULL;
130 // by default, we take components from the executable image or files
131 default:
132 if (CFDataRef data = mExecRep->component(slot))
133 return data;
134 // falling through
135 // but the following always come from files
136 case cdResourceDirSlot:
137 if (const char *name = CodeDirectory::canonicalSlotName(slot))
138 return metaData(name);
139 else
140 return NULL;
141 }
142 }
143
144
145 //
146 // The binary identifier is taken directly from the main executable.
147 //
148 CFDataRef BundleDiskRep::identification()
149 {
150 return mExecRep->identification();
151 }
152
153
154 //
155 // Various aspects of our DiskRep personality.
156 //
157 CFURLRef BundleDiskRep::canonicalPath()
158 {
159 return CFBundleCopyBundleURL(mBundle);
160 }
161
162 string BundleDiskRep::mainExecutablePath()
163 {
164 if (CFURLRef exec = CFBundleCopyExecutableURL(mBundle))
165 return cfString(exec, true);
166 else
167 MacOSError::throwMe(errSecCSNoMainExecutable);
168 }
169
170 string BundleDiskRep::resourcesRootPath()
171 {
172 return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true);
173 }
174
175 void BundleDiskRep::adjustResources(ResourceBuilder &builder)
176 {
177 // exclude entire contents of meta directory
178 builder.addExclusion("^" BUNDLEDISKREP_DIRECTORY "/");
179
180 // exclude the main executable file
181 string resources = resourcesRootPath();
182 string executable = mainExecutablePath();
183 if (!executable.compare(0, resources.length(), resources, 0, resources.length())) // is prefix
184 builder.addExclusion(string("^")
185 + ResourceBuilder::escapeRE(executable.substr(resources.length() + 1)) + "$");
186 }
187
188
189
190 Universal *BundleDiskRep::mainExecutableImage()
191 {
192 return mExecRep->mainExecutableImage();
193 }
194
195 size_t BundleDiskRep::signingBase()
196 {
197 return mExecRep->signingBase();
198 }
199
200 size_t BundleDiskRep::signingLimit()
201 {
202 return mExecRep->signingLimit();
203 }
204
205 string BundleDiskRep::format()
206 {
207 return string("bundle with ") + mExecRep->format();
208 }
209
210 CFArrayRef BundleDiskRep::modifiedFiles()
211 {
212 CFMutableArrayRef files = CFArrayCreateMutableCopy(NULL, 0, mExecRep->modifiedFiles());
213 checkModifiedFile(files, cdCodeDirectorySlot);
214 checkModifiedFile(files, cdSignatureSlot);
215 checkModifiedFile(files, cdResourceDirSlot);
216 checkModifiedFile(files, cdEntitlementSlot);
217 return files;
218 }
219
220 void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot)
221 {
222 if (CFDataRef data = mExecRep->component(slot)) // provided by executable file
223 CFRelease(data);
224 else if (const char *resourceName = CodeDirectory::canonicalSlotName(slot)) {
225 string file = metaPath(resourceName);
226 if (::access(file.c_str(), F_OK) == 0)
227 CFArrayAppendValue(files, CFTempURL(file));
228 }
229 }
230
231 FileDesc &BundleDiskRep::fd()
232 {
233 return mExecRep->fd();
234 }
235
236 void BundleDiskRep::flush()
237 {
238 mExecRep->flush();
239 }
240
241
242 //
243 // Defaults for signing operations
244 //
245 string BundleDiskRep::recommendedIdentifier(const SigningContext &)
246 {
247 if (CFStringRef identifier = CFBundleGetIdentifier(mBundle))
248 return cfString(identifier);
249 if (CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle))
250 if (CFStringRef identifier = CFStringRef(CFDictionaryGetValue(infoDict, kCFBundleNameKey)))
251 return cfString(identifier);
252
253 // fall back to using the canonical path
254 return canonicalIdentifier(cfString(this->canonicalPath()));
255 }
256
257 CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &)
258 {
259 return cfmake<CFDictionaryRef>("{rules={"
260 "'^version.plist$' = #T"
261 "'^Resources/' = #T"
262 "'^Resources/.*\\.lproj/' = {optional=#T, weight=1000}"
263 "'^Resources/.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}"
264 "}}");
265 }
266
267 const Requirements *BundleDiskRep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
268 {
269 return mExecRep->defaultRequirements(arch, ctx);
270 }
271
272 size_t BundleDiskRep::pageSize(const SigningContext &ctx)
273 {
274 return mExecRep->pageSize(ctx);
275 }
276
277
278 //
279 // Writers
280 //
281 DiskRep::Writer *BundleDiskRep::writer()
282 {
283 return new Writer(this);
284 }
285
286 BundleDiskRep::Writer::Writer(BundleDiskRep *r)
287 : rep(r), mMadeMetaDirectory(false)
288 {
289 execWriter = rep->mExecRep->writer();
290 }
291
292
293 //
294 // Write a component.
295 // Note that this isn't concerned with Mach-O writing; this is handled at
296 // a much higher level. If we're called, we write to a file in the Bundle's meta directory.
297 //
298 void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
299 {
300 switch (slot) {
301 default:
302 if (!execWriter->attribute(writerLastResort)) // willing to take the data...
303 return execWriter->component(slot, data); // ... so hand it through
304 // execWriter doesn't want the data; store it as a resource file (below)
305 case cdResourceDirSlot:
306 // the resource directory always goes into a bundle file
307 if (const char *name = CodeDirectory::canonicalSlotName(slot)) {
308 rep->createMeta();
309 string path = rep->metaPath(name);
310 AutoFileDesc fd(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
311 fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data));
312 } else
313 MacOSError::throwMe(errSecCSBadObjectFormat);
314 }
315 }
316
317
318 //
319 // Remove all signature data
320 //
321 void BundleDiskRep::Writer::remove()
322 {
323 // remove signature from the executable
324 execWriter->remove();
325
326 // remove signature files from bundle
327 for (CodeDirectory::SpecialSlot slot = 0; slot < cdSlotCount; slot++)
328 remove(slot);
329 remove(cdSignatureSlot);
330 }
331
332 void BundleDiskRep::Writer::remove(CodeDirectory::SpecialSlot slot)
333 {
334 if (const char *name = CodeDirectory::canonicalSlotName(slot))
335 if (::unlink(rep->metaPath(name).c_str()))
336 switch (errno) {
337 case ENOENT: // not found - that's okay
338 break;
339 default:
340 UnixError::throwMe();
341 }
342 }
343
344
345 void BundleDiskRep::Writer::flush()
346 {
347 execWriter->flush();
348 }
349
350
351 } // end namespace CodeSigning
352 } // end namespace Security