X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/libsecurity_codesigning/lib/machorep.cpp diff --git a/libsecurity_codesigning/lib/machorep.cpp b/libsecurity_codesigning/lib/machorep.cpp deleted file mode 100644 index 7d9f5d02..00000000 --- a/libsecurity_codesigning/lib/machorep.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (c) 2006 Apple Computer, 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 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(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(cmd); - 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(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 info; - try { - auto_ptr macho(mainExecutableImage()->architecture()); - if (const section *sect = macho->findSection("__TEXT", "__info_plist")) { - if (macho->is64()) { - const section_64 *sect64 = reinterpret_cast(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 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(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 areq; - MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref())); - CFRef 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 ToleratedErrors& tolerated) -{ - if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end()) - MacOSError::throwMe(errSecCSBadMainExecutable); -} - - -// -// 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