]> git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/bundlediskrep.cpp
ad4d8195f42f29d3bcea4c55ce160ad21c7ee4f4
[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_codesigning/cfmunge.h>
27
28
29 namespace Security {
30 namespace CodeSigning {
31
32 using namespace UnixPlusPlus;
33
34
35 //
36 // We make a CFBundleRef immediately, but everything else is lazy
37 //
38 BundleDiskRep::BundleDiskRep(const char *path)
39 : mBundle(_CFBundleCreateIfLooksLikeBundle(NULL, CFTempURL(path)))
40 {
41 if (!mBundle)
42 MacOSError::throwMe(errSecCSBadObjectFormat);
43 mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath());
44 }
45
46 BundleDiskRep::BundleDiskRep(CFBundleRef ref)
47 {
48 mBundle = ref; // retains
49 mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath());
50 }
51
52
53 //
54 // Create a path to a bundle signing resource, by name.
55 // Note that these are stored in the bundle's Content directory,
56 // not its Resources directory.
57 //
58 string BundleDiskRep::resourcePath(const char *name)
59 {
60 if (mResourcePath.empty())
61 mResourcePath = cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true);
62 return mResourcePath + "/" + name;
63 }
64
65
66 //
67 // Load the data for a signing resource, by URL.
68 //
69 CFDataRef BundleDiskRep::resourceData(CFURLRef url)
70 {
71 CFDataRef data;
72 SInt32 error;
73 if (CFURLCreateDataAndPropertiesFromResource(NULL, url,
74 &data, NULL, NULL, &error)) {
75 return data;
76 } else {
77 secdebug("bundlerep", "failed to fetch %s error=%d",
78 cfString(url).c_str(), int(error));
79 return NULL;
80 }
81 }
82
83
84 //
85 // Load and return a component, by slot number.
86 // Info.plist components come from the bundle, always (we don't look
87 // for Mach-O embedded versions).
88 // Everything else comes from the embedded blobs of a Mach-O image, or from
89 // files located in the Contents directory of the bundle.
90 //
91 CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot)
92 {
93 switch (slot) {
94 // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there
95 case cdInfoSlot:
96 if (CFRef<CFURLRef> info = _CFBundleCopyInfoPlistURL(mBundle))
97 return resourceData(info);
98 else
99 return NULL;
100 // by default, we take components from the executable image or files
101 default:
102 if (CFDataRef data = mExecRep->component(slot))
103 return data;
104 // falling through
105 // but the following always come from files
106 case cdResourceDirSlot:
107 if (const char *name = CodeDirectory::canonicalSlotName(slot))
108 return resourceData(name);
109 else
110 return NULL;
111 }
112 }
113
114
115 //
116 // Various aspects of our DiskRep personality.
117 //
118 CFURLRef BundleDiskRep::canonicalPath()
119 {
120 return CFBundleCopyBundleURL(mBundle);
121 }
122
123 string BundleDiskRep::recommendedIdentifier()
124 {
125 if (CFStringRef identifier = CFBundleGetIdentifier(mBundle))
126 return cfString(identifier);
127 if (CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle))
128 if (CFStringRef identifier = CFStringRef(CFDictionaryGetValue(infoDict, kCFBundleNameKey)))
129 return cfString(identifier);
130
131 // fall back to using the $(basename) of the canonical path. Drop any .app suffix
132 string path = cfString(this->canonicalPath());
133 if (path.substr(path.size() - 4) == ".app")
134 path = path.substr(0, path.size() - 4);
135 string::size_type p = path.rfind('/');
136 if (p == string::npos)
137 return path;
138 else
139 return path.substr(p+1);
140 }
141
142 string BundleDiskRep::mainExecutablePath()
143 {
144 if (CFURLRef exec = CFBundleCopyExecutableURL(mBundle))
145 return cfString(exec, true);
146 else
147 MacOSError::throwMe(errSecCSBadObjectFormat);
148 }
149
150 string BundleDiskRep::resourcesRootPath()
151 {
152 return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle), true);
153 }
154
155 CFDictionaryRef BundleDiskRep::defaultResourceRules()
156 {
157 return cfmake<CFDictionaryRef>("{rules={"
158 "'^version.plist$' = #T"
159 "'^Resources/' = #T"
160 "'^Resources/.*\\.lproj/' = {optional=#T, weight=1000}"
161 "'^Resources/.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}"
162 "}}");
163 }
164
165 const Requirements *BundleDiskRep::defaultRequirements(const Architecture *arch)
166 {
167 return mExecRep->defaultRequirements(arch);
168 }
169
170
171 Universal *BundleDiskRep::mainExecutableImage()
172 {
173 return mExecRep->mainExecutableImage();
174 }
175
176 size_t BundleDiskRep::pageSize()
177 {
178 return mExecRep->pageSize();
179 }
180
181 size_t BundleDiskRep::signingBase()
182 {
183 return mExecRep->signingBase();
184 }
185
186 size_t BundleDiskRep::signingLimit()
187 {
188 return mExecRep->signingLimit();
189 }
190
191 string BundleDiskRep::format()
192 {
193 return string("bundle with ") + mExecRep->format();
194 }
195
196 CFArrayRef BundleDiskRep::modifiedFiles()
197 {
198 CFMutableArrayRef files = CFArrayCreateMutableCopy(NULL, 0, mExecRep->modifiedFiles());
199 checkModifiedFile(files, cdCodeDirectorySlot);
200 checkModifiedFile(files, cdSignatureSlot);
201 checkModifiedFile(files, cdResourceDirSlot);
202 return files;
203 }
204
205 void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot)
206 {
207 if (CFDataRef data = mExecRep->component(slot)) // provided by executable file
208 CFRelease(data);
209 else if (const char *resourceName = CodeDirectory::canonicalSlotName(slot)) // bundle file
210 CFArrayAppendValue(files, CFTempURL(resourcePath(resourceName)));
211 else
212 /* we don't have that one */;
213 }
214
215 FileDesc &BundleDiskRep::fd()
216 {
217 return mExecRep->fd();
218 }
219
220 void BundleDiskRep::flush()
221 {
222 mExecRep->flush();
223 }
224
225
226 //
227 // Writers
228 //
229 DiskRep::Writer *BundleDiskRep::writer()
230 {
231 return new Writer(this);
232 }
233
234 BundleDiskRep::Writer::Writer(BundleDiskRep *r)
235 : rep(r)
236 {
237 execWriter = rep->mExecRep->writer();
238 }
239
240
241 //
242 // Write a component.
243 // Note that this isn't concerned with Mach-O writing; this is handled at
244 // a much higher level. If we're called, we write to a file in the Bundle's contents directory.
245 //
246 void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
247 {
248 switch (slot) {
249 default:
250 if (!execWriter->attribute(writerLastResort)) // willing to take the data...
251 return execWriter->component(slot, data); // ... so hand it through
252 // execWriter doesn't want the data; store it as a resource file (below)
253 case cdResourceDirSlot:
254 case cdRequirementsSlot:
255 // the resource directory always goes into a bundle file
256 if (const char *name = CodeDirectory::canonicalSlotName(slot)) {
257 AutoFileDesc fd(rep->resourcePath(name), O_WRONLY | O_CREAT | O_TRUNC);
258 fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data));
259 } else
260 MacOSError::throwMe(errSecCSBadObjectFormat);
261 }
262 }
263
264
265 void BundleDiskRep::Writer::flush()
266 {
267 execWriter->flush();
268 }
269
270
271 } // end namespace CodeSigning
272 } // end namespace Security