]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_codesigning/lib/machorep.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / Security / libsecurity_codesigning / lib / machorep.cpp
diff --git a/Security/libsecurity_codesigning/lib/machorep.cpp b/Security/libsecurity_codesigning/lib/machorep.cpp
deleted file mode 100644 (file)
index b2f7cae..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (c) 2006,2011-2012,2014 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@
- */
-
-//
-// machorep - DiskRep mix-in for handling Mach-O main executables
-//
-#include "machorep.h"
-#include "StaticCode.h"
-#include "reqmaker.h"
-
-
-namespace Security {
-namespace CodeSigning {
-
-using namespace UnixPlusPlus;
-
-
-//
-// Object management.
-// We open the main executable lazily, so nothing much happens on construction.
-// If the context specifies a file offset, we directly pick that Mach-O binary (only).
-// if it specifies an architecture, we try to pick that. Otherwise, we deliver the whole
-// Universal object (which will usually deliver the "native" architecture later).
-//
-MachORep::MachORep(const char *path, const Context *ctx)
-       : SingleDiskRep(path), mSigningData(NULL)
-{
-       if (ctx)
-               if (ctx->offset)
-                       mExecutable = new Universal(fd(), (size_t)ctx->offset, ctx->size);
-               else if (ctx->arch) {
-                       auto_ptr<Universal> full(new Universal(fd()));
-                       mExecutable = new Universal(fd(), full->archOffset(ctx->arch), full->archLength(ctx->arch));
-               } else
-                       mExecutable = new Universal(fd());
-       else
-               mExecutable = new Universal(fd());
-
-       assert(mExecutable);
-       CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx);
-}
-
-MachORep::~MachORep()
-{
-       delete mExecutable;
-       ::free(mSigningData);
-}
-
-
-//
-// Sniffer function for "plausible Mach-O binary"
-//
-bool MachORep::candidate(FileDesc &fd)
-{
-       switch (Universal::typeOf(fd)) {
-       case MH_EXECUTE:
-       case MH_DYLIB:
-       case MH_DYLINKER:
-       case MH_BUNDLE:
-       case MH_KEXT_BUNDLE:
-       case MH_PRELOAD:
-               return true;            // dynamic image; supported
-       case MH_OBJECT:
-               return false;           // maybe later...
-       default:
-               return false;           // not Mach-O (or too exotic)
-       }
-}
-
-
-
-//
-// Nowadays, the main executable object is created upon construction.
-//
-Universal *MachORep::mainExecutableImage()
-{
-       return mExecutable;
-}
-
-
-//
-// Signing base is the start of the Mach-O architecture we're using
-//
-size_t MachORep::signingBase()
-{
-       return mainExecutableImage()->archOffset();
-}
-
-
-//
-// We choose the binary identifier for a Mach-O binary as follows:
-//     - If the Mach-O headers have a UUID command, use the UUID.
-//     - Otherwise, use the SHA-1 hash of the (entire) load commands.
-//
-CFDataRef MachORep::identification()
-{
-       std::auto_ptr<MachO> macho(mainExecutableImage()->architecture());
-       return identificationFor(macho.get());
-}
-
-CFDataRef MachORep::identificationFor(MachO *macho)
-{
-       // if there is a LC_UUID load command, use the UUID contained therein
-       if (const load_command *cmd = macho->findCommand(LC_UUID)) {
-               const uuid_command *uuidc = reinterpret_cast<const uuid_command *>(cmd);
-               // uuidc->cmdsize should be sizeof(uuid_command), so if it is not,
-               // something is wrong. Fail out.
-               if (macho->flip(uuidc->cmdsize) != sizeof(uuid_command))
-                       MacOSError::throwMe(errSecCSSignatureInvalid);
-               char result[4 + sizeof(uuidc->uuid)];
-               memcpy(result, "UUID", 4);
-               memcpy(result+4, uuidc->uuid, sizeof(uuidc->uuid));
-               return makeCFData(result, sizeof(result));
-       }
-       
-       // otherwise, use the SHA-1 hash of the entire load command area
-       SHA1 hash;
-       hash(&macho->header(), sizeof(mach_header));
-       hash(macho->loadCommands(), macho->commandLength());
-       SHA1::Digest digest;
-       hash.finish(digest);
-       return makeCFData(digest, sizeof(digest));
-}
-
-
-//
-// Retrieve a component from the executable.
-// This reads the entire signing SuperBlob when first called for an executable,
-// and then caches it for further use.
-// Note that we could read individual components directly off disk and only cache
-// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
-// to cache the pieces anyway.
-//
-CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot)
-{
-       switch (slot) {
-       case cdInfoSlot:
-               return infoPlist();
-       default:
-               return embeddedComponent(slot);
-       }
-}
-
-
-// Retrieve a component from the embedded signature SuperBlob (if present).
-// This reads the entire signing SuperBlob when first called for an executable,
-// and then caches it for further use.
-// Note that we could read individual components directly off disk and only cache
-// the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected
-// to cache the pieces anyway. But it's not clear that the resulting multiple I/O
-// calls wouldn't be slower in the end.
-//
-CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot)
-{
-       if (!mSigningData) {            // fetch and cache
-               auto_ptr<MachO> macho(mainExecutableImage()->architecture());
-               if (macho.get())
-                       if (const linkedit_data_command *cs = macho->findCodeSignature()) {
-                               size_t offset = macho->flip(cs->dataoff);
-                               size_t length = macho->flip(cs->datasize);
-                               if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) {
-                                       secdebug("machorep", "%zd signing bytes in %d blob(s) from %s(%s)",
-                                               mSigningData->length(), mSigningData->count(),
-                                               mainExecutablePath().c_str(), macho->architecture().name());
-                               } else {
-                                       secdebug("machorep", "failed to read signing bytes from %s(%s)",
-                                               mainExecutablePath().c_str(), macho->architecture().name());
-                                       MacOSError::throwMe(errSecCSSignatureInvalid);
-                               }
-                       }
-       }
-       if (mSigningData)
-               return mSigningData->component(slot);
-       
-       // not found
-       return NULL;
-}
-
-
-//
-// Extract an embedded Info.plist from the file.
-// Returns NULL if none is found.
-//
-CFDataRef MachORep::infoPlist()
-{
-       CFRef<CFDataRef> info;
-       try {
-               auto_ptr<MachO> macho(mainExecutableImage()->architecture());
-               if (const section *sect = macho->findSection("__TEXT", "__info_plist")) {
-                       if (macho->is64()) {
-                               const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect);
-                               info.take(macho->dataAt(macho->flip(sect64->offset), (size_t)macho->flip(sect64->size)));
-                       } else {
-                               info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size)));
-                       }
-               }
-       } catch (...) {
-               secdebug("machorep", "exception reading embedded Info.plist");
-       }
-       return info.yield();
-}
-
-
-//
-// Provide a (vaguely) human readable characterization of this code
-//
-string MachORep::format()
-{
-       if (Universal *fat = mainExecutableImage()) {
-               Universal::Architectures archs;
-               fat->architectures(archs);
-               if (fat->isUniversal()) {
-                       string s = "Mach-O universal (";
-                       for (Universal::Architectures::const_iterator it = archs.begin();
-                                       it != archs.end(); ++it) {
-                               if (it != archs.begin())
-                                       s += " ";
-                               s += it->displayName();
-                       }
-                       return s + ")";
-               } else {
-                       assert(archs.size() == 1);
-                       return string("Mach-O thin (") + archs.begin()->displayName() + ")";
-               }
-       } else
-               return "Mach-O (unrecognized format)";
-}
-
-
-//
-// Flush cached data
-//
-void MachORep::flush()
-{
-       size_t offset = mExecutable->offset();
-       size_t length = mExecutable->length();
-       delete mExecutable;
-       mExecutable = NULL;
-       ::free(mSigningData);
-       mSigningData = NULL;
-       SingleDiskRep::flush();
-       mExecutable = new Universal(fd(), offset, length);
-}
-
-
-//
-// Return a recommended unique identifier.
-// If our file has an embedded Info.plist, use the CFBundleIdentifier from that.
-// Otherwise, use the default.
-//
-string MachORep::recommendedIdentifier(const SigningContext &ctx)
-{
-       if (CFDataRef info = infoPlist()) {
-               if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) {
-                       CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey));
-                       if (code && CFGetTypeID(code) != CFStringGetTypeID())
-                               MacOSError::throwMe(errSecCSBadDictionaryFormat);
-                       if (code)
-                               return cfString(code);
-               } else
-                       MacOSError::throwMe(errSecCSBadDictionaryFormat);
-       }
-       
-       // ah well. Use the default
-       return SingleDiskRep::recommendedIdentifier(ctx);
-}
-
-
-//
-// The default suggested requirements for Mach-O binaries are as follows:
-// Library requirement: Composed from dynamic load commands.
-//
-const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx)
-{
-       assert(arch);           // enforced by signing infrastructure
-       Requirements::Maker maker;
-               
-       // add library requirements from DYLIB commands (if any)
-       if (Requirement *libreq = libraryRequirements(arch, ctx))
-               maker.add(kSecLibraryRequirementType, libreq);  // takes ownership
-
-       // that's all
-       return maker.make();
-}
-
-Requirement *MachORep::libraryRequirements(const Architecture *arch, const SigningContext &ctx)
-{
-       auto_ptr<MachO> macho(mainExecutableImage()->architecture(*arch));
-       Requirement::Maker maker;
-       Requirement::Maker::Chain chain(maker, opOr);
-
-       if (macho.get())
-               if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) {
-                       size_t offset = macho->flip(ldep->dataoff);
-                       size_t length = macho->flip(ldep->datasize);
-                       if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) {
-                               try {
-                                       secdebug("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)",
-                                               deplist->length(), deplist->count(),
-                                               mainExecutablePath().c_str(), macho->architecture().name());
-                                       unsigned count = deplist->count();
-                                       // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far
-                                       for (unsigned n = 0; n < count; n++) {
-                                               const Requirement *req = NULL;
-                                               if (const BlobCore *dep = deplist->blob(n)) {
-                                                       if ((req = Requirement::specific(dep))) {
-                                                               // binary code requirement; good to go
-                                                       } else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) {
-                                                               // blob-wrapped text form - convert to binary requirement
-                                                               std::string reqString = std::string((const char *)wrap->data(), wrap->length());
-                                                               CFRef<SecRequirementRef> areq;
-                                                               MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref()));
-                                                               CFRef<CFDataRef> reqData;
-                                                               MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref()));
-                                                               req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData));
-                                                       } else {
-                                                               secdebug("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n);
-                                                               continue;
-                                                       }
-                                                       chain.add();
-                                                       maker.copy(req);
-                                               } else
-                                                       secdebug("machorep", "missing DR info for library index %d", n);
-                                       }
-                                       ::free(deplist);
-                               } catch (...) {
-                                       ::free(deplist);
-                                       throw;
-                               }
-                       }
-               }
-       if (chain.empty())
-               return NULL;
-       else
-               return maker.make();
-}
-
-
-//
-// Default to system page size for segmented (paged) signatures
-//
-size_t MachORep::pageSize(const SigningContext &)
-{
-       return segmentedPageSize;
-}
-
-
-//
-// Strict validation
-//
-void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated)
-{
-       // if the constructor found suspicious issues, fail a struct validation now
-       if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end())
-               MacOSError::throwMe(errSecCSBadMainExecutable);
-       
-       // the signature's code extent must be what we would have picked (no funny hand editing)
-       if (cd) {
-               auto_ptr<MachO> macho(mExecutable->architecture());
-               if (cd->codeLimit != macho->signingExtent())
-                       MacOSError::throwMe(errSecCSSignatureInvalid);
-       }
-}
-
-
-//
-// FileDiskRep::Writers
-//
-DiskRep::Writer *MachORep::writer()
-{
-       return new Writer(this);
-}
-
-
-//
-// Write a component.
-// MachORep::Writers don't write to components directly; the signing code uses special
-// knowledge of the Mach-O format to build embedded signatures and blasts them directly
-// to disk. Thus this implementation will never be called (and, if called, will simply fail).
-//
-void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
-{
-       assert(false);
-       MacOSError::throwMe(errSecCSInternalError);
-}
-
-
-} // end namespace CodeSigning
-} // end namespace Security