]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_codesigning/lib/bundlediskrep.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / bundlediskrep.cpp
diff --git a/libsecurity_codesigning/lib/bundlediskrep.cpp b/libsecurity_codesigning/lib/bundlediskrep.cpp
deleted file mode 100644 (file)
index 636e213..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (c) 2006-2011 Apple Inc. All Rights Reserved.
- * 
- * @APPLE_LICENSE_HEADER_START@
- * 
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- * 
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- * 
- * @APPLE_LICENSE_HEADER_END@
- */
-#include "bundlediskrep.h"
-#include "filediskrep.h"
-#include "dirscanner.h"
-#include <CoreFoundation/CFURLAccess.h>
-#include <CoreFoundation/CFBundlePriv.h>
-#include <security_utilities/cfmunge.h>
-#include <copyfile.h>
-#include <fts.h>
-
-
-namespace Security {
-namespace CodeSigning {
-
-using namespace UnixPlusPlus;
-
-
-//
-// Local helpers
-//
-static std::string findDistFile(const std::string &directory);
-
-
-//
-// We make a CFBundleRef immediately, but everything else is lazy
-//
-BundleDiskRep::BundleDiskRep(const char *path, const Context *ctx)
-       : mBundle(CFBundleCreate(NULL, CFTempURL(path)))
-{
-       if (!mBundle)
-               MacOSError::throwMe(errSecCSBadBundleFormat);
-       setup(ctx);
-       CODESIGN_DISKREP_CREATE_BUNDLE_PATH(this, (char*)path, (void*)ctx, mExecRep);
-}
-
-BundleDiskRep::BundleDiskRep(CFBundleRef ref, const Context *ctx)
-{
-       mBundle = ref;          // retains
-       setup(ctx);
-       CODESIGN_DISKREP_CREATE_BUNDLE_REF(this, ref, (void*)ctx, mExecRep);
-}
-
-BundleDiskRep::~BundleDiskRep()
-{
-}
-
-// common construction code
-void BundleDiskRep::setup(const Context *ctx)
-{
-       mInstallerPackage = false;      // default
-
-       // capture the path of the main executable before descending into a specific version
-       CFRef<CFURLRef> mainExecBefore = CFBundleCopyExecutableURL(mBundle);
-
-       // validate the bundle root; fish around for the desired framework version
-       string root = cfStringRelease(copyCanonicalPath());
-       string contents = root + "/Contents";
-       string version = root + "/Versions/"
-               + ((ctx && ctx->version) ? ctx->version : "Current")
-               + "/.";
-       if (::access(contents.c_str(), F_OK) == 0) {    // not shallow
-               DirValidator val;
-               val.require("^Contents$", DirValidator::directory);      // duh
-               val.allow("^(\\.LSOverride|\\.DS_Store|Icon\r|\\.SoftwareDepot\\.tracking)$", DirValidator::file | DirValidator::noexec);
-               try {
-                       val.validate(root, errSecCSUnsealedAppRoot);
-               } catch (const MacOSError &err) {
-                       recordStrictError(err.error);
-               }
-       } else if (::access(version.c_str(), F_OK) == 0) {      // versioned bundle
-               if (CFBundleRef versionBundle = CFBundleCreate(NULL, CFTempURL(version)))
-                       mBundle.take(versionBundle);    // replace top bundle ref
-               else
-                       MacOSError::throwMe(errSecCSStaticCodeNotFound);
-               validateFrameworkRoot(root);
-       } else {
-               if (ctx && ctx->version)        // explicitly specified
-                       MacOSError::throwMe(errSecCSStaticCodeNotFound);
-       }
-
-       CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle);
-       assert(infoDict);       // CFBundle will always make one up for us
-       CFTypeRef mainHTML = CFDictionaryGetValue(infoDict, CFSTR("MainHTML"));
-       CFTypeRef packageVersion = CFDictionaryGetValue(infoDict, CFSTR("IFMajorVersion"));
-
-       // conventional executable bundle: CFBundle identifies an executable for us
-       if (CFRef<CFURLRef> mainExec = CFBundleCopyExecutableURL(mBundle))              // if CFBundle claims an executable...
-               if (mainHTML == NULL) {                                                                                         // ... and it's not a widget
-
-                       // Note that this check is skipped if there is a specific framework version checked.
-                       // That's because you know what you are doing if you are looking at a specific version.
-                       // This check is designed to stop someone who did a verification on an app root, from mistakenly
-                       // verifying a framework
-                       if (mainExecBefore && (!ctx || !ctx->version)) {
-                               char main_exec_before[PATH_MAX];
-                               char main_exec[PATH_MAX];
-                               // The realpath call is important because alot of Framework bundles have a symlink
-                               // to their "Current" version binary in the main bundle
-                               if (realpath(cfString(mainExecBefore).c_str(), main_exec_before) == NULL ||
-                                       realpath(cfString(mainExec).c_str(), main_exec) == NULL)
-                                       MacOSError::throwMe(errSecCSInternalError);
-
-                               if (strcmp(main_exec_before, main_exec) != 0)
-                                       recordStrictError(errSecCSAmbiguousBundleFormat);
-                       }
-
-                       mMainExecutableURL = mainExec;
-                       mExecRep = DiskRep::bestFileGuess(this->mainExecutablePath(), ctx);
-                       if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
-                               recordStrictError(errSecCSRegularFile);
-                       mFormat = "bundle with " + mExecRep->format();
-                       return;
-               }
-       
-       // widget
-       if (mainHTML) {
-               if (CFGetTypeID(mainHTML) != CFStringGetTypeID())
-                       MacOSError::throwMe(errSecCSBadBundleFormat);
-               mMainExecutableURL.take(makeCFURL(cfString(CFStringRef(mainHTML)), false,
-                       CFRef<CFURLRef>(CFBundleCopySupportFilesDirectoryURL(mBundle))));
-               if (!mMainExecutableURL)
-                       MacOSError::throwMe(errSecCSBadBundleFormat);
-               mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
-               if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
-                       recordStrictError(errSecCSRegularFile);
-               mFormat = "widget bundle";
-               return;
-       }
-       
-       // do we have a real Info.plist here?
-       if (CFRef<CFURLRef> infoURL = _CFBundleCopyInfoPlistURL(mBundle)) {
-               // 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);
-               if (packageVersion) {
-                       mInstallerPackage = true;
-                       mFormat = "installer package bundle";
-               } else {
-                       mFormat = "bundle";
-               }
-               return;
-       }
-
-       // we're getting desperate here. Perhaps an oldish-style installer package? Look for a *.dist file
-       std::string distFile = findDistFile(this->resourcesRootPath());
-       if (!distFile.empty()) {
-               mMainExecutableURL = makeCFURL(distFile);
-               mExecRep = new FileDiskRep(this->mainExecutablePath().c_str());
-               if (!mExecRep->fd().isPlainFile(this->mainExecutablePath()))
-                       recordStrictError(errSecCSRegularFile);
-               mInstallerPackage = true;
-               mFormat = "installer package bundle";
-               return;
-       }
-       
-       // this bundle cannot be signed
-       MacOSError::throwMe(errSecCSBadBundleFormat);
-}
-
-
-//
-// Return the full path to the one-and-only file named something.dist in a directory.
-// Return empty string if none; throw an exception if multiple. Do not descend into subdirectories.
-//
-static std::string findDistFile(const std::string &directory)
-{
-       std::string found;
-       char *paths[] = {(char *)directory.c_str(), NULL};
-       FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
-       bool root = true;
-       while (FTSENT *ent = fts_read(fts)) {
-               switch (ent->fts_info) {
-               case FTS_F:
-               case FTS_NSOK:
-                       if (!strcmp(ent->fts_path + ent->fts_pathlen - 5, ".dist")) {   // found plain file foo.dist
-                               if (found.empty())      // first found
-                                       found = ent->fts_path;
-                               else                            // multiple *.dist files (bad)
-                                       MacOSError::throwMe(errSecCSBadBundleFormat);
-                       }
-                       break;
-               case FTS_D:
-                       if (!root)
-                               fts_set(fts, ent, FTS_SKIP);    // don't descend
-                       root = false;
-                       break;
-               default:
-                       break;
-               }
-       }
-       fts_close(fts);
-       return found;
-}
-
-
-//
-// 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.
-// Throws if an error occurs.
-//
-void BundleDiskRep::createMeta()
-{
-       string meta = metaPath(BUNDLEDISKREP_DIRECTORY);
-       if (!mMetaExists) {
-               if (::mkdir(meta.c_str(), 0755) == 0) {
-                       copyfile(cfStringRelease(copyCanonicalPath()).c_str(), meta.c_str(), NULL, COPYFILE_SECURITY);
-                       mMetaPath = meta;
-                       mMetaExists = true;
-               } else if (errno != EEXIST)
-                       UnixError::throwMe();
-       }
-}
-
-//
-// Load's a CFURL and makes sure that it is a regular file and not a symlink (or fifo, etc.)
-//
-CFDataRef BundleDiskRep::loadRegularFile(CFURLRef url)
-{
-       assert(url);
-
-       CFDataRef data = NULL;
-
-       std::string path(cfString(url));
-
-       AutoFileDesc fd(path);
-
-       if (!fd.isPlainFile(path))
-               recordStrictError(errSecCSRegularFile);
-
-       data = cfLoadFile(fd, fd.fileSize());
-
-       if (!data) {
-               secdebug(__PRETTY_FUNCTION__, "failed to load %s", cfString(url).c_str());
-               MacOSError::throwMe(errSecCSInternalError);
-       }
-
-       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).
-// Everything else comes from the embedded blobs of a Mach-O image, or from
-// files located in the Contents directory of the bundle.
-//
-CFDataRef BundleDiskRep::component(CodeDirectory::SpecialSlot slot)
-{
-       switch (slot) {
-       // the Info.plist comes from the magic CFBundle-indicated place and ONLY from there
-       case cdInfoSlot:
-               if (CFRef<CFURLRef> info = _CFBundleCopyInfoPlistURL(mBundle))
-                       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;
-       }
-}
-
-
-//
-// The binary identifier is taken directly from the main executable.
-//
-CFDataRef BundleDiskRep::identification()
-{
-       return mExecRep->identification();
-}
-
-
-//
-// Various aspects of our DiskRep personality.
-//
-CFURLRef BundleDiskRep::copyCanonicalPath()
-{
-       if (CFURLRef url = CFBundleCopyBundleURL(mBundle))
-               return url;
-       CFError::throwMe();
-}
-
-string BundleDiskRep::mainExecutablePath()
-{
-       return cfString(mMainExecutableURL);
-}
-
-string BundleDiskRep::resourcesRootPath()
-{
-       return cfStringRelease(CFBundleCopySupportFilesDirectoryURL(mBundle));
-}
-
-void BundleDiskRep::adjustResources(ResourceBuilder &builder)
-{
-       // exclude entire contents of meta directory
-       builder.addExclusion("^" BUNDLEDISKREP_DIRECTORY "$");
-       builder.addExclusion("^" CODERESOURCES_LINK "$");       // ancient-ish symlink into it
-
-       // exclude the store manifest directory
-       builder.addExclusion("^" STORE_RECEIPT_DIRECTORY "$");
-       
-       // exclude the main executable file
-       string resources = resourcesRootPath();
-       if (resources.compare(resources.size() - 2, 2, "/.") == 0)      // chop trailing /.
-               resources = resources.substr(0, resources.size()-2);
-       string executable = mainExecutablePath();
-       if (!executable.compare(0, resources.length(), resources, 0, resources.length())
-               && executable[resources.length()] == '/')       // is proper directory prefix
-               builder.addExclusion(string("^")
-                       + ResourceBuilder::escapeRE(executable.substr(resources.length()+1)) + "$");
-}
-
-
-
-Universal *BundleDiskRep::mainExecutableImage()
-{
-       return mExecRep->mainExecutableImage();
-}
-
-size_t BundleDiskRep::signingBase()
-{
-       return mExecRep->signingBase();
-}
-
-size_t BundleDiskRep::signingLimit()
-{
-       return mExecRep->signingLimit();
-}
-
-string BundleDiskRep::format()
-{
-       return mFormat;
-}
-
-CFArrayRef BundleDiskRep::modifiedFiles()
-{
-       CFMutableArrayRef files = CFArrayCreateMutableCopy(NULL, 0, mExecRep->modifiedFiles());
-       checkModifiedFile(files, cdCodeDirectorySlot);
-       checkModifiedFile(files, cdSignatureSlot);
-       checkModifiedFile(files, cdResourceDirSlot);
-       checkModifiedFile(files, cdEntitlementSlot);
-       return files;
-}
-
-void BundleDiskRep::checkModifiedFile(CFMutableArrayRef files, CodeDirectory::SpecialSlot slot)
-{
-       if (CFDataRef data = mExecRep->component(slot)) // provided by executable file
-               CFRelease(data);
-       else if (const char *resourceName = CodeDirectory::canonicalSlotName(slot)) {
-               string file = metaPath(resourceName);
-               if (::access(file.c_str(), F_OK) == 0)
-                       CFArrayAppendValue(files, CFTempURL(file));
-       }
-}
-
-FileDesc &BundleDiskRep::fd()
-{
-       return mExecRep->fd();
-}
-
-void BundleDiskRep::flush()
-{
-       mExecRep->flush();
-}
-
-
-//
-// Defaults for signing operations
-//
-string BundleDiskRep::recommendedIdentifier(const SigningContext &)
-{
-       if (CFStringRef identifier = CFBundleGetIdentifier(mBundle))
-               return cfString(identifier);
-       if (CFDictionaryRef infoDict = CFBundleGetInfoDictionary(mBundle))
-               if (CFStringRef identifier = CFStringRef(CFDictionaryGetValue(infoDict, kCFBundleNameKey)))
-                       return cfString(identifier);
-       
-       // fall back to using the canonical path
-       return canonicalIdentifier(cfStringRelease(this->copyCanonicalPath()));
-}
-
-string BundleDiskRep::resourcesRelativePath()
-{
-       // figure out the resource directory base. Clean up some gunk inserted by CFBundle in frameworks
-       string rbase = this->resourcesRootPath();
-       size_t pos = rbase.find("/./"); // gratuitously inserted by CFBundle in some frameworks
-       while (pos != std::string::npos) {
-               rbase = rbase.replace(pos, 2, "", 0);
-               pos = rbase.find("/./");
-       }
-       if (rbase.substr(rbase.length()-2, 2) == "/.")  // produced by versioned bundle implicit "Current" case
-               rbase = rbase.substr(0, rbase.length()-2);      // ... so take it off for this
-       
-       // find the resources directory relative to the resource base
-       string resources = cfStringRelease(CFBundleCopyResourcesDirectoryURL(mBundle));
-       if (resources == rbase)
-               resources = "";
-       else if (resources.compare(0, rbase.length(), rbase, 0, rbase.length()) != 0)   // Resources not in resource root
-               MacOSError::throwMe(errSecCSBadBundleFormat);
-       else
-               resources = resources.substr(rbase.length() + 1) + "/"; // differential path segment
-
-       return resources;
-}
-
-CFDictionaryRef BundleDiskRep::defaultResourceRules(const SigningContext &ctx)
-{
-       string resources = this->resourcesRelativePath();
-
-       // installer package rules
-       if (mInstallerPackage)
-               return cfmake<CFDictionaryRef>("{rules={"
-                       "'^.*' = #T"                                                    // include everything, but...
-                       "%s = {optional=#T, weight=1000}"               // make localizations optional
-                       "'^.*/.*\\.pkg/' = {omit=#T, weight=10000}" // and exclude all nested packages (by name)
-                       "}}",
-                       (string("^") + resources + ".*\\.lproj/").c_str()
-               );
-       
-       // old (V1) executable bundle rules - compatible with before
-       if (ctx.signingFlags() & kSecCSSignV1)                          // *** must be exactly the same as before ***
-               return cfmake<CFDictionaryRef>("{rules={"
-                       "'^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
-                       "}}",
-                       (string("^") + resources).c_str(),
-                       (string("^") + resources + ".*\\.lproj/").c_str(),
-                       (string("^") + resources + ".*\\.lproj/locversion.plist$").c_str()
-               );
-       
-       // FMJ (everything is a resource) rules
-       if (ctx.signingFlags() & kSecCSSignOpaque)                      // Full Metal Jacket - everything is a resource file
-               return cfmake<CFDictionaryRef>("{rules={"
-                       "'^.*' = #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()
-       );
-}
-
-
-CFArrayRef BundleDiskRep::allowedResourceOmissions()
-{
-       return cfmake<CFArrayRef>("["
-               "'^(.*/)?\\.DS_Store$'"
-               "'^Info\\.plist$'"
-               "'^PkgInfo$'"
-               "%s"
-               "]",
-               (string("^") + this->resourcesRelativePath() + ".*\\.lproj/locversion.plist$").c_str()
-       );
-}
-
-
-const Requirements *BundleDiskRep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
-{
-       return mExecRep->defaultRequirements(arch, ctx);
-}
-
-size_t BundleDiskRep::pageSize(const SigningContext &ctx)
-{
-       return mExecRep->pageSize(ctx);
-}
-
-
-//
-// Strict validation.
-// Takes an array of CFNumbers of errors to tolerate.
-//
-void BundleDiskRep::strictValidate(const ToleratedErrors& tolerated)
-{
-       std::vector<OSStatus> fatalErrors;
-       set_difference(mStrictErrors.begin(), mStrictErrors.end(), tolerated.begin(), tolerated.end(), back_inserter(fatalErrors));
-       if (!fatalErrors.empty())
-               MacOSError::throwMe(fatalErrors[0]);
-       mExecRep->strictValidate(tolerated);
-}
-
-void BundleDiskRep::recordStrictError(OSStatus error)
-{
-       mStrictErrors.insert(error);
-}
-
-
-//
-// Check framework root for unsafe symlinks and unsealed content.
-//
-void BundleDiskRep::validateFrameworkRoot(string root)
-{
-       // build regex element that matches either the "Current" symlink, or the name of the current version
-       string current = "Current";
-       char currentVersion[PATH_MAX];
-       ssize_t len = ::readlink((root + "/Versions/Current").c_str(), currentVersion, sizeof(currentVersion)-1);
-       if (len > 0) {
-               currentVersion[len] = '\0';
-               current = string("(Current|") + ResourceBuilder::escapeRE(currentVersion) + ")";
-       }
-
-       DirValidator val;
-       val.require("^Versions$", DirValidator::directory | DirValidator::descend);     // descend into Versions directory
-       val.require("^Versions/[^/]+$", DirValidator::directory);                                       // require at least one version
-       val.require("^Versions/Current$", DirValidator::symlink,                                        // require Current symlink...
-               "^(\\./)?(\\.\\.[^/]+|\\.?[^\\./][^/]*)$");                                                             // ...must point to a version
-       val.allow("^(Versions/)?\\.DS_Store$", DirValidator::file | DirValidator::noexec); // allow .DS_Store files
-       val.allow("^[^/]+$", DirValidator::symlink, ^ string (const string &name, const string &target) {
-               // top-level symlinks must point to namesake in current version
-               return string("^(\\./)?Versions/") + current + "/" + ResourceBuilder::escapeRE(name) + "$";
-       });
-       // module.map must be regular non-executable file, or symlink to module.map in current version
-       val.allow("^module\\.map$", DirValidator::file | DirValidator::noexec | DirValidator::symlink,
-               string("^(\\./)?Versions/") + current + "/module\\.map$");
-
-       try {
-               val.validate(root, errSecCSUnsealedFrameworkRoot);
-       } catch (const MacOSError &err) {
-               recordStrictError(err.error);
-       }
-}
-
-
-//
-// Writers
-//
-DiskRep::Writer *BundleDiskRep::writer()
-{
-       return new Writer(this);
-}
-
-BundleDiskRep::Writer::Writer(BundleDiskRep *r)
-       : rep(r), mMadeMetaDirectory(false)
-{
-       execWriter = rep->mExecRep->writer();
-}
-
-
-//
-// Write a component.
-// Note that this isn't concerned with Mach-O writing; this is handled at
-// a much higher level. If we're called, we write to a file in the Bundle's meta directory.
-//
-void BundleDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
-{
-       switch (slot) {
-       default:
-               if (!execWriter->attribute(writerLastResort))   // willing to take the data...
-                       return execWriter->component(slot, data);       // ... so hand it through
-               // execWriter doesn't want the data; store it as a resource file (below)
-       case cdResourceDirSlot:
-               // the resource directory always goes into a bundle file
-               if (const char *name = CodeDirectory::canonicalSlotName(slot)) {
-                       rep->createMeta();
-                       string path = rep->metaPath(name);
-                       AutoFileDesc fd(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-                       fd.writeAll(CFDataGetBytePtr(data), CFDataGetLength(data));
-               } else
-                       MacOSError::throwMe(errSecCSBadBundleFormat);
-       }
-}
-
-
-//
-// Remove all signature data
-//
-void BundleDiskRep::Writer::remove()
-{
-       // remove signature from the executable
-       execWriter->remove();
-       
-       // remove signature files from bundle
-       for (CodeDirectory::SpecialSlot slot = 0; slot < cdSlotCount; slot++)
-               remove(slot);
-       remove(cdSignatureSlot);
-}
-
-void BundleDiskRep::Writer::remove(CodeDirectory::SpecialSlot slot)
-{
-       if (const char *name = CodeDirectory::canonicalSlotName(slot))
-               if (::unlink(rep->metaPath(name).c_str()))
-                       switch (errno) {
-                       case ENOENT:            // not found - that's okay
-                               break;
-                       default:
-                               UnixError::throwMe();
-                       }
-}
-
-
-void BundleDiskRep::Writer::flush()
-{
-       execWriter->flush();
-}
-
-
-} // end namespace CodeSigning
-} // end namespace Security