X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..b54c578e17e9bcbd74aa30ea75e25e955b9a6205:/OSX/libsecurity_codesigning/lib/machorep.cpp diff --git a/OSX/libsecurity_codesigning/lib/machorep.cpp b/OSX/libsecurity_codesigning/lib/machorep.cpp index d362b5ab..02e7faa8 100644 --- a/OSX/libsecurity_codesigning/lib/machorep.cpp +++ b/OSX/libsecurity_codesigning/lib/machorep.cpp @@ -27,6 +27,10 @@ #include "machorep.h" #include "StaticCode.h" #include "reqmaker.h" +#include +#include +#include + namespace Security { @@ -97,6 +101,44 @@ Universal *MachORep::mainExecutableImage() return mExecutable; } + +// +// Explicitly default to SHA256 (only) digests if the minimum deployment +// target is young enough. +// +void MachORep::prepareForSigning(SigningContext &context) +{ + if (context.digestAlgorithms().empty()) { + auto_ptr macho(mainExecutableImage()->architecture()); + + uint32_t limit = 0; + switch (macho->platform()) { + case 0: + // If we don't know the platform, we stay agile. + return; + case PLATFORM_MACOS: + // 10.11.4 had first proper sha256 support. + limit = (10 << 16 | 11 << 8 | 4 << 0); + break; + case PLATFORM_TVOS: + case PLATFORM_IOS: + // iOS 11 and tvOS 11 had first proper sha256 support. + limit = (11 << 16 | 0 << 8 | 0 << 0); + break; + case PLATFORM_WATCHOS: + // We stay agile on the watch right now. + return; + default: + // All other platforms are assumed to be new and support SHA256. + break; + } + if (macho->minVersion() >= limit) { + // young enough not to need SHA-1 legacy support + context.setDigestAlgorithm(kSecCodeSignatureHashSHA256); + } + } +} + // // Signing base is the start of the Mach-O architecture we're using @@ -105,6 +147,68 @@ size_t MachORep::signingBase() { return mainExecutableImage()->archOffset(); } + +size_t MachORep::signingLimit() +{ + auto_ptr macho(mExecutable->architecture()); + return macho->signingExtent(); +} + +bool MachORep::needsExecSeg(const MachO& macho) { + uint32_t platform = macho.platform(); + // Everything embedded gets an exec segment. + return platform != 0 && platform != PLATFORM_MACOS; +} + +size_t MachORep::execSegBase(const Architecture *arch) +{ + auto_ptr macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture()); + + if (!needsExecSeg(*macho)) { + return 0; + } + + segment_command const * const text_cmd = macho->findSegment("__TEXT"); + + if (text_cmd == NULL) { + return 0; + } + + size_t off = 0; + + if (macho->is64()) { + off = int_cast(reinterpret_cast(text_cmd)->fileoff); + } else { + off = text_cmd->fileoff; + } + + return off; +} + +size_t MachORep::execSegLimit(const Architecture *arch) +{ + auto_ptr macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture()); + + if (!needsExecSeg(*macho)) { + return 0; + } + + segment_command const * const text_cmd = macho->findSegment("__TEXT"); + + if (text_cmd == NULL) { + return 0; + } + + size_t size = 0; + + if (macho->is64()) { + size = int_cast(reinterpret_cast(text_cmd)->filesize); + } else { + size = text_cmd->filesize; + } + + return size; +} // @@ -161,6 +265,21 @@ CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot) } } +// +// Retrieve all components, used for signature editing. +// +EditableDiskRep::RawComponentMap MachORep::createRawComponents() +{ + EditableDiskRep::RawComponentMap blobMap; + const EmbeddedSignatureBlob &blobs = *signingData(); + + for (unsigned int i = 0; i < blobs.count(); ++i) { + CodeDirectory::Slot slot = blobs.type(i); + const BlobCore *blob = blobs.blob(i); + blobMap[slot] = blobs.blobData(slot, blob); + } + return blobMap; +} // Retrieve a component from the embedded signature SuperBlob (if present). // This reads the entire signing SuperBlob when first called for an executable, @@ -171,6 +290,18 @@ CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot) // calls wouldn't be slower in the end. // CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot) +{ + if (signingData()) { + return signingData()->component(slot); + } + + // not found + return NULL; +} + + + +EmbeddedSignatureBlob *MachORep::signingData() { if (!mSigningData) { // fetch and cache auto_ptr macho(mainExecutableImage()->architecture()); @@ -179,21 +310,17 @@ CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot) 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()); + secinfo("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()); + secinfo("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; + return mSigningData; } @@ -215,7 +342,7 @@ CFDataRef MachORep::infoPlist() } } } catch (...) { - secdebug("machorep", "exception reading embedded Info.plist"); + secinfo("machorep", "exception reading embedded Info.plist"); } return info.yield(); } @@ -262,6 +389,43 @@ void MachORep::flush() mExecutable = new Universal(fd(), offset, length); } +CFDictionaryRef MachORep::diskRepInformation() +{ + auto_ptr macho (mainExecutableImage()->architecture()); + CFRef info; + + uint32_t platform = 0; + uint32_t minVersion = 0; + uint32_t sdkVersion = 0; + + if (macho->version(&platform, &minVersion, &sdkVersion)) { + + /* These keys replace the old kSecCodeInfoDiskRepOSPlatform, kSecCodeInfoDiskRepOSVersionMin + * and kSecCodeInfoDiskRepOSSDKVersion. The keys were renamed because we changed what value + * "platform" represents: For the old key, the actual load command (e.g. LC_VERSION_MIN_MACOSX) + * was returned; for the new key, we return one of the PLATFORM_* values used by LC_BUILD_VERSION. + * + * The keys are private and undocumented, and maintaining a translation table between the old and + * new domain would provide little value at high cost, but we do remove the old keys to make + * the change obvious. + */ + + info.take(cfmake("{%O = %d,%O = %d,%O = %d}", + kSecCodeInfoDiskRepVersionPlatform, platform, + kSecCodeInfoDiskRepVersionMin, minVersion, + kSecCodeInfoDiskRepVersionSDK, sdkVersion)); + + if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8)) + { + info.take(cfmake("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}", + info.get(), + kSecCodeInfoDiskRepNoLibraryValidation)); + } + } + + return info.yield(); +} + // // Return a recommended unique identifier. @@ -315,7 +479,7 @@ Requirement *MachORep::libraryRequirements(const Architecture *arch, const Signi 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)", + secinfo("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(); @@ -334,13 +498,13 @@ Requirement *MachORep::libraryRequirements(const Architecture *arch, const Signi 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); + secinfo("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); + secinfo("machorep", "missing DR info for library index %d", n); } ::free(deplist); } catch (...) { @@ -368,18 +532,13 @@ size_t MachORep::pageSize(const SigningContext &) // // Strict validation // -void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated) +void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) { + SingleDiskRep::strictValidate(cd, tolerated, flags); + // 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(mExecutable->architecture()); - if (cd->codeLimit != macho->signingExtent()) - MacOSError::throwMe(errSecCSSignatureInvalid); - } } @@ -401,6 +560,7 @@ DiskRep::Writer *MachORep::writer() void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) { assert(false); + Syslog::notice("code signing internal error: trying to write Mach-O component directly"); MacOSError::throwMe(errSecCSInternalError); }