]>
git.saurik.com Git - apple/libsecurity_codesigning.git/blob - lib/bundlediskrep.cpp
d8785d1335811c693488f51f968354a97b79db9e
2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 #include "bundlediskrep.h"
24 #include <CoreFoundation/CFURLAccess.h>
25 #include <CoreFoundation/CFBundlePriv.h>
26 #include <security_utilities/cfmunge.h>
31 namespace CodeSigning
{
33 using namespace UnixPlusPlus
;
37 // We make a CFBundleRef immediately, but everything else is lazy
39 BundleDiskRep::BundleDiskRep(const char *path
, const Context
*ctx
)
40 : mBundle(_CFBundleCreateIfMightBeBundle(NULL
, CFTempURL(path
)))
43 MacOSError::throwMe(errSecCSBadObjectFormat
);
44 mExecRep
= DiskRep::bestFileGuess(this->mainExecutablePath(), ctx
);
45 CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path
, (void*)ctx
, mExecRep
);
48 BundleDiskRep::BundleDiskRep(CFBundleRef ref
, const Context
*ctx
)
50 mBundle
= ref
; // retains
51 mExecRep
= DiskRep::bestFileGuess(this->mainExecutablePath(), ctx
);
52 CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref
, (void*)ctx
, mExecRep
);
57 // Create a path to a bundle signing resource, by name.
58 // If the BUNDLEDISKREP_DIRECTORY directory exists in the bundle's support directory, files
59 // will be read and written there. Otherwise, they go directly into the support directory.
61 string
BundleDiskRep::metaPath(const char *name
)
63 if (mMetaPath
.empty()) {
64 string support
= cfString(CFBundleCopySupportFilesDirectoryURL(mBundle
), true);
65 mMetaPath
= support
+ "/" BUNDLEDISKREP_DIRECTORY
;
66 if (::access(mMetaPath
.c_str(), F_OK
) == 0) {
73 return mMetaPath
+ "/" + name
;
78 // Try to create the meta-file directory in our bundle.
79 // Does nothing if the directory already exists.
80 // Throws if an error occurs.
82 void BundleDiskRep::createMeta()
84 string meta
= metaPath(BUNDLEDISKREP_DIRECTORY
);
86 if (::mkdir(meta
.c_str(), 0755) == 0) {
87 copyfile(cfString(canonicalPath(), true).c_str(), meta
.c_str(), NULL
, COPYFILE_SECURITY
);
90 } else if (errno
!= EEXIST
)
97 // Load and return a component, by slot number.
98 // Info.plist components come from the bundle, always (we don't look
99 // for Mach-O embedded versions).
100 // Everything else comes from the embedded blobs of a Mach-O image, or from
101 // files located in the Contents directory of the bundle.
103 CFDataRef
BundleDiskRep::component(CodeDirectory::SpecialSlot slot
)
106 // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there
108 if (CFRef
<CFURLRef
> info
= _CFBundleCopyInfoPlistURL(mBundle
))
109 return cfLoadFile(info
);
112 // by default, we take components from the executable image or files
114 if (CFDataRef data
= mExecRep
->component(slot
))
117 // but the following always come from files
118 case cdResourceDirSlot
:
119 if (const char *name
= CodeDirectory::canonicalSlotName(slot
))
120 return metaData(name
);
128 // The binary identifier is taken directly from the main executable.
130 CFDataRef
BundleDiskRep::identification()
132 return mExecRep
->identification();
137 // Various aspects of our DiskRep personality.
139 CFURLRef
BundleDiskRep::canonicalPath()
141 return CFBundleCopyBundleURL(mBundle
);
144 string
BundleDiskRep::recommendedIdentifier()
146 if (CFStringRef identifier
= CFBundleGetIdentifier(mBundle
))
147 return cfString(identifier
);
148 if (CFDictionaryRef infoDict
= CFBundleGetInfoDictionary(mBundle
))
149 if (CFStringRef identifier
= CFStringRef(CFDictionaryGetValue(infoDict
, kCFBundleNameKey
)))
150 return cfString(identifier
);
152 // fall back to using the $(basename) of the canonical path. Drop any .app suffix
153 string path
= cfString(this->canonicalPath(), true);
154 if (path
.substr(path
.size() - 4) == ".app")
155 path
= path
.substr(0, path
.size() - 4);
156 string::size_type p
= path
.rfind('/');
157 if (p
== string::npos
)
160 return path
.substr(p
+1);
163 string
BundleDiskRep::mainExecutablePath()
165 if (CFURLRef exec
= CFBundleCopyExecutableURL(mBundle
))
166 return cfString(exec
, true);
168 MacOSError::throwMe(errSecCSBadObjectFormat
);
171 string
BundleDiskRep::resourcesRootPath()
173 return cfString(CFBundleCopySupportFilesDirectoryURL(mBundle
), true);
176 CFDictionaryRef
BundleDiskRep::defaultResourceRules()
178 return cfmake
<CFDictionaryRef
>("{rules={"
179 "'^version.plist$' = #T"
181 "'^Resources/.*\\.lproj/' = {optional=#T, weight=1000}"
182 "'^Resources/.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}"
186 void BundleDiskRep::adjustResources(ResourceBuilder
&builder
)
188 // exclude entire contents of meta directory
189 builder
.addExclusion("^" BUNDLEDISKREP_DIRECTORY
"/");
191 // exclude the main executable file
192 string resources
= resourcesRootPath();
193 string executable
= mainExecutablePath();
194 if (!executable
.compare(0, resources
.length(), resources
, 0, resources
.length())) // is prefix
195 builder
.addExclusion(string("^")
196 + ResourceBuilder::escapeRE(executable
.substr(resources
.length() + 1)) + "$");
200 const Requirements
*BundleDiskRep::defaultRequirements(const Architecture
*arch
)
202 return mExecRep
->defaultRequirements(arch
);
206 Universal
*BundleDiskRep::mainExecutableImage()
208 return mExecRep
->mainExecutableImage();
211 size_t BundleDiskRep::pageSize()
213 return mExecRep
->pageSize();
216 size_t BundleDiskRep::signingBase()
218 return mExecRep
->signingBase();
221 size_t BundleDiskRep::signingLimit()
223 return mExecRep
->signingLimit();
226 string
BundleDiskRep::format()
228 return string("bundle with ") + mExecRep
->format();
231 CFArrayRef
BundleDiskRep::modifiedFiles()
233 CFMutableArrayRef files
= CFArrayCreateMutableCopy(NULL
, 0, mExecRep
->modifiedFiles());
234 checkModifiedFile(files
, cdCodeDirectorySlot
);
235 checkModifiedFile(files
, cdSignatureSlot
);
236 checkModifiedFile(files
, cdResourceDirSlot
);
237 checkModifiedFile(files
, cdEntitlementSlot
);
241 void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files
, CodeDirectory::SpecialSlot slot
)
243 if (CFDataRef data
= mExecRep
->component(slot
)) // provided by executable file
245 else if (const char *resourceName
= CodeDirectory::canonicalSlotName(slot
)) {
246 string file
= metaPath(resourceName
);
247 if (::access(file
.c_str(), F_OK
) == 0)
248 CFArrayAppendValue(files
, CFTempURL(file
));
252 FileDesc
&BundleDiskRep::fd()
254 return mExecRep
->fd();
257 void BundleDiskRep::flush()
266 DiskRep::Writer
*BundleDiskRep::writer()
268 return new Writer(this);
271 BundleDiskRep::Writer::Writer(BundleDiskRep
*r
)
272 : rep(r
), mMadeMetaDirectory(false)
274 execWriter
= rep
->mExecRep
->writer();
279 // Write a component.
280 // Note that this isn't concerned with Mach-O writing; this is handled at
281 // a much higher level. If we're called, we write to a file in the Bundle's meta directory.
283 void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot
, CFDataRef data
)
287 if (!execWriter
->attribute(writerLastResort
)) // willing to take the data...
288 return execWriter
->component(slot
, data
); // ... so hand it through
289 // execWriter doesn't want the data; store it as a resource file (below)
290 case cdResourceDirSlot
:
291 // the resource directory always goes into a bundle file
292 if (const char *name
= CodeDirectory::canonicalSlotName(slot
)) {
294 string path
= rep
->metaPath(name
);
295 AutoFileDesc
fd(path
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
296 fd
.writeAll(CFDataGetBytePtr(data
), CFDataGetLength(data
));
297 if (rep
->mMetaExists
) {
298 // leave a symlink in the support directory for pre-10.5.3 compatibility (but ignore errors)
299 string legacy
= cfString(CFBundleCopySupportFilesDirectoryURL(rep
->mBundle
), true) + "/" + name
;
300 # if FORCE_REPLACE_SYMLINK /* replace any existing file with legacy symlink */
301 ::unlink(legacy
.c_str()); // force-replace
303 ::symlink((string(BUNDLEDISKREP_DIRECTORY
"/") + name
).c_str(), legacy
.c_str());
306 MacOSError::throwMe(errSecCSBadObjectFormat
);
312 // Remove all signature data
314 void BundleDiskRep::Writer::remove()
316 // remove signature from the executable
317 execWriter
->remove();
319 // remove signature files from bundle
320 for (CodeDirectory::SpecialSlot slot
= 0; slot
< cdSlotCount
; slot
++)
322 remove(cdSignatureSlot
);
325 void BundleDiskRep::Writer::remove(CodeDirectory::SpecialSlot slot
)
327 if (const char *name
= CodeDirectory::canonicalSlotName(slot
))
328 if (::unlink(rep
->metaPath(name
).c_str()))
330 case ENOENT
: // not found - that's okay
333 UnixError::throwMe();
338 void BundleDiskRep::Writer::flush()
344 } // end namespace CodeSigning
345 } // end namespace Security