#include "bundlediskrep.h"
#include "filediskrep.h"
#include "dirscanner.h"
+#include "notarization.h"
#include <CoreFoundation/CFBundlePriv.h>
#include <CoreFoundation/CFURLAccess.h>
#include <CoreFoundation/CFBundlePriv.h>
// We make a CFBundleRef immediately, but everything else is lazy
//
BundleDiskRep::BundleDiskRep(const char *path, const Context *ctx)
- : mBundle(CFBundleCreate(NULL, CFTempURL(path)))
+ : mBundle(_CFBundleCreateUnique(NULL, CFTempURL(path))), forcePlatform(false)
{
if (!mBundle)
MacOSError::throwMe(errSecCSBadBundleFormat);
setup(ctx);
+ forcePlatform = mExecRep->appleInternalForcePlatform();
CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path, (void*)ctx, mExecRep);
}
{
mBundle = ref; // retains
setup(ctx);
+ forcePlatform = mExecRep->appleInternalForcePlatform();
CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref, (void*)ctx, mExecRep);
}
// to their "Current" version binary in the main bundle
if (realpath(cfString(oldPath).c_str(), cOld) == NULL ||
realpath(cfString(newPath).c_str(), cNew) == NULL)
- MacOSError::throwMe(errSecCSInternalError);
+ MacOSError::throwMe(errSecCSAmbiguousBundleFormat);
if (strcmp(cOld, cNew) != 0)
recordStrictError(errSecCSAmbiguousBundleFormat);
// common construction code
void BundleDiskRep::setup(const Context *ctx)
{
+ mComponentsFromExecValid = false; // not yet known
mInstallerPackage = false; // default
mAppLike = false; // pessimism first
bool appDisqualified = false; // found reason to disqualify as app
// validate the bundle root; fish around for the desired framework version
string root = cfStringRelease(copyCanonicalPath());
+ if (filehasExtendedAttribute(root, XATTR_FINDERINFO_NAME))
+ recordStrictError(errSecCSInvalidAssociatedFileData);
string contents = root + "/Contents";
string supportFiles = root + "/Support Files";
string version = root + "/Versions/"
// treat like a shallow bundle; do not allow Versions arbitration
appDisqualified = true;
} else if (::access(version.c_str(), F_OK) == 0) { // versioned bundle
- if (CFBundleRef versionBundle = CFBundleCreate(NULL, CFTempURL(version)))
+ if (CFBundleRef versionBundle = _CFBundleCreateUnique(NULL, CFTempURL(version)))
mBundle.take(versionBundle); // replace top bundle ref
else
MacOSError::throwMe(errSecCSStaticCodeNotFound);
mMainExecutableURL = mainExec;
mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx);
- if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
- recordStrictError(errSecCSRegularFile);
+ checkPlainFile(mExecRep->fd(), this->mainExecutablePath());
CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle);
bool isAppBundle = false;
if (infoDict)
if (!mMainExecutableURL)
MacOSError::throwMe(errSecCSBadBundleFormat);
mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
- if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
- recordStrictError(errSecCSRegularFile);
+ checkPlainFile(mExecRep->fd(), this->mainExecutablePath());
mFormat = "widget bundle";
mAppLike = true;
return;
// focus on the Info.plist (which we know exists) as the nominal "main executable" file
mMainExecutableURL = infoURL;
mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
- if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
- recordStrictError(errSecCSRegularFile);
+ checkPlainFile(mExecRep->fd(), this->mainExecutablePath());
if (packageVersion) {
mInstallerPackage = true;
mFormat = "installer package bundle";
if (!distFile.empty()) {
mMainExecutableURL = makeCFURL(distFile);
mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
- if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
- recordStrictError(errSecCSRegularFile);
+ checkPlainFile(mExecRep->fd(), this->mainExecutablePath());
mInstallerPackage = true;
mFormat = "installer package bundle";
return;
}
-//
-// Create a path to a bundle signing resource, by name.
-// If the BUNDLEDISKREP_DIRECTORY directory exists in the bundle's support directory, files
-// will be read and written there. Otherwise, they go directly into the support directory.
-//
-string BundleDiskRep::metaPath(const char *name)
-{
- if (mMetaPath.empty()) {
- string support = cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle));
- mMetaPath = support + "/" BUNDLEDISKREP_DIRECTORY;
- if (::access(mMetaPath.c_str(), F_OK) == 0) {
- mMetaExists = true;
- } else {
- mMetaPath = support;
- mMetaExists = false;
- }
- }
- return mMetaPath + "/" + name;
-}
-
-
//
// Try to create the meta-file directory in our bundle.
// Does nothing if the directory already exists.
//
void BundleDiskRep::createMeta()
{
- string meta = metaPath(BUNDLEDISKREP_DIRECTORY);
+ string meta = metaPath(NULL);
if (!mMetaExists) {
if (::mkdir(meta.c_str(), 0755) == 0) {
copyfile(cfStringRelease(copyCanonicalPath()).c_str(), meta.c_str(), NULL, COPYFILE_SECURITY);
UnixError::throwMe();
}
}
+
+//
+// Create a path to a bundle signing resource, by name.
+// This is in the BUNDLEDISKREP_DIRECTORY directory in the bundle's support directory.
+//
+string BundleDiskRep::metaPath(const char *name)
+{
+ if (mMetaPath.empty()) {
+ string support = cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle));
+ mMetaPath = support + "/" BUNDLEDISKREP_DIRECTORY;
+ mMetaExists = ::access(mMetaPath.c_str(), F_OK) == 0;
+ }
+ if (name)
+ return mMetaPath + "/" + name;
+ else
+ return mMetaPath;
+}
+
+CFDataRef BundleDiskRep::metaData(const char *name)
+{
+ if (CFRef<CFURLRef> url = makeCFURL(metaPath(name))) {
+ return cfLoadFile(url);
+ } else {
+ secnotice("bundlediskrep", "no metapath for %s", name);
+ return NULL;
+ }
+}
+
+CFDataRef BundleDiskRep::metaData(CodeDirectory::SpecialSlot slot)
+{
+ if (const char *name = CodeDirectory::canonicalSlotName(slot))
+ return metaData(name);
+ else
+ return NULL;
+}
+
+
+
//
// Load's a CFURL and makes sure that it is a regular file and not a symlink (or fifo, etc.)
//
AutoFileDesc fd(path);
- if (!fd.isPlainFile(path))
- recordStrictError(errSecCSRegularFile);
+ checkPlainFile(fd, path);
data = cfLoadFile(fd, fd.fileSize());
if (!data) {
- secdebug(__PRETTY_FUNCTION__, "failed to load %s", cfString(url).c_str());
- MacOSError::throwMe(errSecCSInternalError);
+ secinfo("bundlediskrep", "failed to load %s", cfString(url).c_str());
+ MacOSError::throwMe(errSecCSInvalidSymlink);
}
return data;
// Load and return a component, by slot number.
// Info.plist components come from the bundle, always (we don't look
// for Mach-O embedded versions).
+// ResourceDirectory always comes from bundle files.
// Everything else comes from the embedded blobs of a Mach-O image, or from
-// files located in the Contents directory of the bundle.
+// files located in the Contents directory of the bundle; but we must be consistent
+// (no half-and-half situations).
//
CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot)
{
return loadRegularFile(info);
else
return NULL;
- // by default, we take components from the executable image or files
- default:
- if (CFDataRef data = mExecRep->component(slot))
- return data;
- // falling through
- // but the following always come from files
case cdResourceDirSlot:
- if (const char *name = CodeDirectory::canonicalSlotName(slot))
- return metaData(name);
- else
- return NULL;
+ mUsedComponents.insert(slot);
+ return metaData(slot);
+ // by default, we take components from the executable image or files (but not both)
+ default:
+ if (CFRef<CFDataRef> data = mExecRep->component(slot)) {
+ componentFromExec(true);
+ return data.yield();
+ }
+ if (CFRef<CFDataRef> data = metaData(slot)) {
+ componentFromExec(false);
+ mUsedComponents.insert(slot);
+ return data.yield();
+ }
+ return NULL;
+ }
+}
+
+BundleDiskRep::RawComponentMap BundleDiskRep::createRawComponents()
+{
+ RawComponentMap map;
+
+ /* Those are the slots known to BundleDiskReps.
+ * Unlike e.g. MachOReps, we cannot handle unknown slots,
+ * as we won't know their slot <-> filename mapping.
+ */
+ int const slots[] = {
+ cdCodeDirectorySlot, cdSignatureSlot, cdResourceDirSlot,
+ cdTopDirectorySlot, cdEntitlementSlot, cdEntitlementDERSlot,
+ cdRepSpecificSlot};
+
+ for (int slot = 0; slot < (int)(sizeof(slots)/sizeof(slots[0])); ++slot) {
+ /* Here, we only handle metaData slots, i.e. slots that
+ * are explicit files in the _CodeSignature directory.
+ * Main executable slots (if the main executable is a
+ * EditableDiskRep) are handled when editing the
+ * main executable's rep explicitly.
+ * There is also an Info.plist slot, which is not a
+ * real part of the code signature.
+ */
+ CFRef<CFDataRef> data = metaData(slot);
+
+ if (data) {
+ map[slot] = data;
+ }
+ }
+
+ for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot) {
+ CFRef<CFDataRef> data = metaData(slot);
+
+ if (data) {
+ map[slot] = data;
+ }
+ }
+
+ return map;
+}
+
+// Check that all components of this BundleDiskRep come from either the main
+// executable or the _CodeSignature directory (not mix-and-match).
+void BundleDiskRep::componentFromExec(bool fromExec)
+{
+ if (!mComponentsFromExecValid) {
+ // first use; set latch
+ mComponentsFromExecValid = true;
+ mComponentsFromExec = fromExec;
+ } else if (mComponentsFromExec != fromExec) {
+ // subsequent use: check latch
+ MacOSError::throwMe(errSecCSSignatureFailed);
}
}
return mExecRep->signingLimit();
}
+size_t BundleDiskRep::execSegBase(const Architecture *arch)
+{
+ return mExecRep->execSegBase(arch);
+}
+
+size_t BundleDiskRep::execSegLimit(const Architecture *arch)
+{
+ return mExecRep->execSegLimit(arch);
+}
+
string BundleDiskRep::format()
{
return mFormat;
CFArrayRef BundleDiskRep::modifiedFiles()
{
- CFMutableArrayRef files = CFArrayCreateMutableCopy(NULL, 0, mExecRep->modifiedFiles());
+ CFRef<CFArrayRef> execFiles = mExecRep->modifiedFiles();
+ CFRef<CFMutableArrayRef> files = CFArrayCreateMutableCopy(NULL, 0, execFiles);
checkModifiedFile(files, cdCodeDirectorySlot);
checkModifiedFile(files, cdSignatureSlot);
checkModifiedFile(files, cdResourceDirSlot);
checkModifiedFile(files, cdTopDirectorySlot);
checkModifiedFile(files, cdEntitlementSlot);
+ checkModifiedFile(files, cdEntitlementDERSlot);
checkModifiedFile(files, cdRepSpecificSlot);
for (CodeDirectory::Slot slot = cdAlternateCodeDirectorySlots; slot < cdAlternateCodeDirectoryLimit; ++slot)
checkModifiedFile(files, slot);
- return files;
+ return files.yield();
}
void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot)
mExecRep->flush();
}
+CFDictionaryRef BundleDiskRep::diskRepInformation()
+{
+ return mExecRep->diskRepInformation();
+}
//
// Defaults for signing operations
"'^.*' = #T" // everything is a resource
"'^Info\\.plist$' = {omit=#T,weight=10}" // explicitly exclude this for backward compatibility
"}}");
-
- // new (V2) executable bundle rules
- return cfmake<CFDictionaryRef>("{" // *** the new (V2) world ***
- "rules={" // old (V1; legacy) version
- "'^version.plist$' = #T" // include version.plist
- "%s = #T" // include Resources
- "%s = {optional=#T, weight=1000}" // make localizations optional
- "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files
- "},rules2={"
- "'^.*' = #T" // include everything as a resource, with the following exceptions
- "'^[^/]+$' = {nested=#T, weight=10}" // files directly in Contents
- "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories
- "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code)
- "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files
- "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told
- "'^version\\.plist$' = {weight=20}" // include version.plist as resource
- "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource
- "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included
- "%s = {weight=20}" // Resources override default nested (widgets)
- "%s = {optional=#T, weight=1000}" // make localizations optional
- "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files
- "}}",
-
- (string("^") + resources).c_str(),
- (string("^") + resources + ".*\\.lproj/").c_str(),
- (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(),
-
- (string("^") + resources).c_str(),
- (string("^") + resources + ".*\\.lproj/").c_str(),
- (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str()
- );
+
+ // new (V2) executable bundle rules
+ if (!resources.empty()) {
+ return cfmake<CFDictionaryRef>("{" // *** the new (V2) world ***
+ "rules={" // old (V1; legacy) version
+ "'^version.plist$' = #T" // include version.plist
+ "%s = #T" // include Resources
+ "%s = {optional=#T, weight=1000}" // make localizations optional
+ "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all
+ "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files
+ "},rules2={"
+ "'^.*' = #T" // include everything as a resource, with the following exceptions
+ "'^[^/]+$' = {nested=#T, weight=10}" // files directly in Contents
+ "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=10}" // dynamic repositories
+ "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code)
+ "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files
+ "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told
+ "'^version\\.plist$' = {weight=20}" // include version.plist as resource
+ "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource
+ "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included
+ "%s = {weight=20}" // Resources override default nested (widgets)
+ "%s = {optional=#T, weight=1000}" // make localizations optional
+ "%s = {weight=1010}" // ... except for Base.lproj which really isn't optional at all
+ "%s = {omit=#T, weight=1100}" // exclude all locversion.plist files
+ "}}",
+
+ (string("^") + resources).c_str(),
+ (string("^") + resources + ".*\\.lproj/").c_str(),
+ (string("^") + resources + "Base\\.lproj/").c_str(),
+ (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str(),
+
+ (string("^") + resources).c_str(),
+ (string("^") + resources + ".*\\.lproj/").c_str(),
+ (string("^") + resources + "Base\\.lproj/").c_str(),
+ (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str()
+ );
+ } else {
+ /* This is a bundle format without a Resources directory, which means we need to omit
+ * Resources-directory specific rules that would create conflicts. */
+
+ /* We also declare that flat bundles do not use any nested code rules.
+ * Embedded, where flat bundles are used, currently does not support them,
+ * and we have no plans for allowing the replacement of nested code there.
+ * Should anyone actually intend to use nested code rules in a flat
+ * bundle, they are free to create their own rules. */
+
+ return cfmake<CFDictionaryRef>("{" // *** the new (V2) world ***
+ "rules={" // old (V1; legacy) version
+ "'^version.plist$' = #T" // include version.plist
+ "'^.*' = #T" // include Resources
+ "'^.*\\.lproj/' = {optional=#T, weight=1000}" // make localizations optional
+ "'^Base\\.lproj/' = {weight=1010}" // ... except for Base.lproj which really isn't optional at all
+ "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}" // exclude all locversion.plist files
+ "},rules2={"
+ "'^.*' = #T" // include everything as a resource, with the following exceptions
+ "'.*\\.dSYM($|/)' = {weight=11}" // but allow dSYM directories in code locations (parallel to their code)
+ "'^(.*/)?\\.DS_Store$' = {omit=#T,weight=2000}" // ignore .DS_Store files
+ "'^Info\\.plist$' = {omit=#T, weight=20}" // excluded automatically now, but old systems need to be told
+ "'^version\\.plist$' = {weight=20}" // include version.plist as resource
+ "'^embedded\\.provisionprofile$' = {weight=20}" // include embedded.provisionprofile as resource
+ "'^PkgInfo$' = {omit=#T, weight=20}" // traditionally not included
+ "'^.*\\.lproj/' = {optional=#T, weight=1000}" // make localizations optional
+ "'^Base\\.lproj/' = {weight=1010}" // ... except for Base.lproj which really isn't optional at all
+ "'^.*\\.lproj/locversion.plist$' = {omit=#T, weight=1100}" // exclude all locversion.plist files
+ "}}"
+ );
+ }
}
//
void BundleDiskRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
{
+ strictValidateStructure(cd, tolerated, flags);
+
+ // now strict-check the main executable (which won't be an app-like object)
+ mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike);
+}
+
+void BundleDiskRep::strictValidateStructure(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
+{
+ // scan our metadirectory (_CodeSignature) for unwanted guests
+ if (!(flags & kSecCSQuickCheck))
+ validateMetaDirectory(cd);
+
+ // check accumulated strict errors and report them
+ if (!(flags & kSecCSRestrictSidebandData)) // tolerate resource forks etc.
+ mStrictErrors.erase(errSecCSInvalidAssociatedFileData);
+
std::vector<OSStatus> fatalErrors;
set_difference(mStrictErrors.begin(), mStrictErrors.end(), tolerated.begin(), tolerated.end(), back_inserter(fatalErrors));
if (!fatalErrors.empty())
if (!mAppLike)
if (tolerated.find(kSecCSRestrictToAppLike) == tolerated.end())
MacOSError::throwMe(errSecCSNotAppLike);
-
- // now strict-check the main executable (which won't be an app-like object)
- mExecRep->strictValidate(cd, tolerated, flags & ~kSecCSRestrictToAppLike);
}
void BundleDiskRep::recordStrictError(OSStatus error)
}
+void BundleDiskRep::validateMetaDirectory(const CodeDirectory* cd)
+{
+ // we know the resource directory will be checked after this call, so we'll give it a pass here
+ if (cd->slotIsPresent(-cdResourceDirSlot))
+ mUsedComponents.insert(cdResourceDirSlot);
+
+ // make a set of allowed (regular) filenames in this directory
+ std::set<std::string> allowedFiles;
+ for (auto it = mUsedComponents.begin(); it != mUsedComponents.end(); ++it) {
+ switch (*it) {
+ case cdInfoSlot:
+ break; // always from Info.plist, not from here
+ default:
+ if (const char *name = CodeDirectory::canonicalSlotName(*it)) {
+ allowedFiles.insert(name);
+ }
+ break;
+ }
+ }
+ DirScanner scan(mMetaPath);
+ if (scan.initialized()) {
+ while (struct dirent* ent = scan.getNext()) {
+ if (!scan.isRegularFile(ent))
+ MacOSError::throwMe(errSecCSUnsealedAppRoot); // only regular files allowed
+ if (allowedFiles.find(ent->d_name) == allowedFiles.end()) { // not in expected set of files
+ if (strcmp(ent->d_name, kSecCS_SIGNATUREFILE) == 0) {
+ // special case - might be empty and unused (adhoc signature)
+ AutoFileDesc fd(metaPath(kSecCS_SIGNATUREFILE));
+ if (fd.fileSize() == 0)
+ continue; // that's okay, then
+ }
+ // not on list of needed files; it's a freeloading rogue!
+ recordStrictError(errSecCSUnsealedAppRoot); // funnel through strict set so GKOpaque can override it
+ }
+ }
+ }
+}
+
+
//
// Check framework root for unsafe symlinks and unsealed content.
//
}
}
+
+//
+// Check a file descriptor for harmlessness. This is a strict check (only).
+//
+void BundleDiskRep::checkPlainFile(FileDesc fd, const std::string& path)
+{
+ if (!fd.isPlainFile(path))
+ recordStrictError(errSecCSRegularFile);
+ checkForks(fd);
+}
+
+void BundleDiskRep::checkForks(FileDesc fd)
+{
+ if (fd.hasExtendedAttribute(XATTR_RESOURCEFORK_NAME) || fd.hasExtendedAttribute(XATTR_FINDERINFO_NAME))
+ recordStrictError(errSecCSInvalidAssociatedFileData);
+}
+
//
// Writers
if (const char *name = CodeDirectory::canonicalSlotName(slot)) {
rep->createMeta();
string path = rep->metaPath(name);
+
+#if TARGET_OS_OSX
+ // determine AFSC status if we are told to preserve compression
+ bool conductCompression = false;
+ cmpInfo cInfo;
+ if (this->getPreserveAFSC()) {
+ struct stat statBuffer;
+ if (stat(path.c_str(), &statBuffer) == 0) {
+ if (queryCompressionInfo(path.c_str(), &cInfo) == 0) {
+ if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) {
+ conductCompression = true;
+ }
+ }
+ }
+ }
+#endif
+
AutoFileDesc fd(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data));
+ fd.close();
+
+#if TARGET_OS_OSX
+ // if the original file was compressed, compress the new file after move
+ if (conductCompression) {
+ CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType);
+ CFDictionarySetValue(options, kAFSCCompressionTypes, val);
+ CFRelease(val);
+
+ CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options);
+
+ if (!CompressFile(compressionQueue, path.c_str(), NULL)) {
+ secinfo("bundlediskrep", "%p Failed to queue compression of file %s", this, path.c_str());
+ MacOSError::throwMe(errSecCSInternalError);
+ }
+
+ FinishCompressionAndCleanUp(compressionQueue);
+ compressionQueue = NULL;
+ CFRelease(options);
+ }
+#endif
+
+ mWrittenFiles.insert(name);
} else
MacOSError::throwMe(errSecCSBadBundleFormat);
}
void BundleDiskRep::Writer::flush()
{
execWriter->flush();
+ purgeMetaDirectory();
}
+// purge _CodeSignature of all left-over files from any previous signature
+void BundleDiskRep::Writer::purgeMetaDirectory()
+{
+ DirScanner scan(rep->mMetaPath);
+ if (scan.initialized()) {
+ while (struct dirent* ent = scan.getNext()) {
+ if (!scan.isRegularFile(ent))
+ MacOSError::throwMe(errSecCSUnsealedAppRoot); // only regular files allowed
+ if (mWrittenFiles.find(ent->d_name) == mWrittenFiles.end()) { // we didn't write this!
+ scan.unlink(ent, 0);
+ }
+ }
+ }
+
+}
+
+void BundleDiskRep::registerStapledTicket()
+{
+ string root = cfStringRelease(copyCanonicalPath());
+ registerStapledTicketInBundle(root);
+}
} // end namespace CodeSigning
} // end namespace Security