X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_codesigning/lib/diskrep.cpp diff --git a/libsecurity_codesigning/lib/diskrep.cpp b/libsecurity_codesigning/lib/diskrep.cpp new file mode 100644 index 00000000..395402c6 --- /dev/null +++ b/libsecurity_codesigning/lib/diskrep.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2006-2007 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@ + */ + +// +// diskrep - disk representations of code +// +#include "diskrep.h" +#include +#include + +// specific disk representations created by the bestGuess() function +#include "filediskrep.h" +#include "bundlediskrep.h" +#include "cfmdiskrep.h" +#include "slcrep.h" + + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Abstract features +// +DiskRep::DiskRep() +{ +} + +DiskRep::~DiskRep() +{ + CODESIGN_DISKREP_DESTROY(this); +} + + +// +// Normal DiskReps are their own base. +// +DiskRep *DiskRep::base() +{ + return this; +} + + +// +// By default, DiskReps are read-only. +// +DiskRep::Writer *DiskRep::writer() +{ + MacOSError::throwMe(errSecCSUnimplemented); +} + + +void DiskRep::Writer::addDiscretionary(CodeDirectory::Builder &) +{ + // do nothing +} + + +// +// Given a file system path, come up with the most likely correct +// disk representation for what's there. +// This is, strictly speaking, a heuristic that could be fooled - there's +// no fool-proof rule for figuring this out. But we'd expect this to work +// fine in ordinary use. If you happen to know what you're looking at +// (say, a bundle), then just create the suitable subclass of DiskRep directly. +// That's quite legal. +// The optional context argument can provide additional information that guides the guess. +// +DiskRep *DiskRep::bestGuess(const char *path, const Context *ctx) +{ + try { + if (!(ctx && ctx->fileOnly)) { + struct stat st; + if (::stat(path, &st)) + UnixError::throwMe(); + + // if it's a directory, assume it's a bundle + if ((st.st_mode & S_IFMT) == S_IFDIR) // directory - assume bundle + return new BundleDiskRep(path, ctx); + + // see if it's the main executable of a recognized bundle + if (CFRef pathURL = makeCFURL(path)) + if (CFRef bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) + return new BundleDiskRep(bundle, ctx); + } + + // try the various single-file representations + AutoFileDesc fd(path, O_RDONLY); + if (MachORep::candidate(fd)) + return new MachORep(path, ctx); + if (CFMDiskRep::candidate(fd)) + return new CFMDiskRep(path); + if (DYLDCacheRep::candidate(fd)) + return new DYLDCacheRep(path); + + // ultimate fallback - the generic file representation + return new FileDiskRep(path); + + } catch (const CommonError &error) { + switch (error.unixError()) { + case ENOENT: + MacOSError::throwMe(errSecCSStaticCodeNotFound); + default: + throw; + } + } +} + + +DiskRep *DiskRep::bestFileGuess(const char *path, const Context *ctx) +{ + Context dctx; + if (ctx) + dctx = *ctx; + dctx.fileOnly = true; + return bestGuess(path, &dctx); +} + + +// +// Given a main executable known to be a Mach-O binary, and an offset into +// the file of the actual architecture desired (of a Universal file), +// produce a suitable MachORep. +// This function does not consider non-MachO binaries. It does however handle +// bundles with Mach-O main executables correctly. +// +DiskRep *DiskRep::bestGuess(const char *path, size_t archOffset) +{ + try { + // is it the main executable of a bundle? + if (CFRef pathURL = makeCFURL(path)) + if (CFRef bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) { + Context ctx; ctx.offset = archOffset; + return new BundleDiskRep(bundle, &ctx); // ask bundle to make bundle-with-MachO-at-offset + } + // else, must be a Mach-O binary + Context ctx; ctx.offset = archOffset; + return new MachORep(path, &ctx); + } catch (const CommonError &error) { + switch (error.unixError()) { + case ENOENT: + MacOSError::throwMe(errSecCSStaticCodeNotFound); + default: + throw; + } + } +} + + +// +// Default behaviors of DiskRep +// +string DiskRep::resourcesRootPath() +{ + return ""; // has no resources directory +} + +void DiskRep::adjustResources(ResourceBuilder &builder) +{ + // do nothing +} + +Universal *DiskRep::mainExecutableImage() +{ + return NULL; // no Mach-O executable +} + +size_t DiskRep::signingBase() +{ + return 0; // whole file (start at beginning) +} + +CFArrayRef DiskRep::modifiedFiles() +{ + // by default, claim (just) the main executable modified + CFRef mainURL = makeCFURL(mainExecutablePath()); + return makeCFArray(1, mainURL.get()); +} + +void DiskRep::flush() +{ + // nothing cached +} + + +CFDictionaryRef DiskRep::defaultResourceRules(const SigningContext &) +{ + return NULL; // none +} + +const Requirements *DiskRep::defaultRequirements(const Architecture *, const SigningContext &) +{ + return NULL; // none +} + +size_t DiskRep::pageSize(const SigningContext &) +{ + return monolithicPageSize; // unpaged (monolithic) +} + + +// +// Given some string (usually a pathname), derive a suggested signing identifier +// in a canonical way (so there's some consistency). +// +// This is a heuristic. First we lop off any leading directories and final (non-numeric) +// extension. Then we walk backwards, eliminating numeric extensions except the first one. +// Thus, libfrotz7.3.5.dylib becomes libfrotz7, mumble.77.plugin becomes mumble.77, +// and rumble.rb becomes rumble. This isn't perfect, but it ought to handle 98%+ of +// the common varieties out there. Specify an explicit identifier for the oddballs. +// +// This is called by the various recommendedIdentifier() methods, who are +// free to modify or override it. +// +// Note: We use strchr("...") instead of is*() here because we do not +// wish to be influenced by locale settings. +// +std::string DiskRep::canonicalIdentifier(const std::string &name) +{ + string s = name; + string::size_type p; + + // lop off any directory prefixes + if ((p = s.rfind('/')) != string::npos) + s = s.substr(p+1); + + // remove any final extension (last dot) unless it's numeric + if ((p = s.rfind('.')) != string::npos && !strchr("0123456789", s[p+1])) + s = s.substr(0, p); + + // eat numeric suffixes except the first one; roughly: + // foo.2.3.4 => foo.2, foo2.3 => foo2, foo.9 => foo.9, foo => foo + if (strchr("0123456789.", s[0])) // starts with digit or . + return s; // ... so don't mess with it + p = s.size()-1; + // foo3.5^, foo.3.5^, foo3^, foo.3^, foo^ + while (strchr("0123456789.", s[p])) + p--; + // fo^o3.5, fo^o.3.5, fo^o3, fo^o.3, fo^o + p++; + // foo^3.5, foo^.3.5, foo^3, foo^.3, foo^ + if (s[p] == '.') + p++; + // foo^3.5, foo.^3.5, foo^3, foo.^3, foo^ + while (p < s.size() && strchr("0123456789", s[p])) + p++; + // foo3^.5, foo.3^.5, foo3^, foo.3^, foo^ + return s.substr(0, p); +} + + +// +// Writers +// +DiskRep::Writer::Writer(uint32_t attrs) + : mArch(CPU_TYPE_ANY), mAttributes(attrs) +{ +} + +DiskRep::Writer::~Writer() +{ /* virtual */ } + +uint32_t DiskRep::Writer::attributes() const +{ return mAttributes; } + +void DiskRep::Writer::flush() +{ /* do nothing */ } + +void DiskRep::Writer::remove() +{ + MacOSError::throwMe(errSecCSNotSupported); +} + + +} // end namespace CodeSigning +} // end namespace Security