#include "machorep.h"
#include "StaticCode.h"
#include "reqmaker.h"
+#include <security_utilities/logging.h>
+#include <security_utilities/cfmunge.h>
+#include <security_utilities/casts.h>
+
namespace Security {
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> 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
{
return mainExecutableImage()->archOffset();
}
+
+size_t MachORep::signingLimit()
+{
+ auto_ptr<MachO> 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> 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<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->fileoff);
+ } else {
+ off = text_cmd->fileoff;
+ }
+
+ return off;
+}
+
+size_t MachORep::execSegLimit(const Architecture *arch)
+{
+ auto_ptr<MachO> 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<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->filesize);
+ } else {
+ size = text_cmd->filesize;
+ }
+
+ return size;
+}
//
}
}
+//
+// 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,
// 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> macho(mainExecutableImage()->architecture());
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;
}
}
}
} catch (...) {
- secdebug("machorep", "exception reading embedded Info.plist");
+ secinfo("machorep", "exception reading embedded Info.plist");
}
return info.yield();
}
mExecutable = new Universal(fd(), offset, length);
}
+CFDictionaryRef MachORep::diskRepInformation()
+{
+ auto_ptr<MachO> macho (mainExecutableImage()->architecture());
+ CFRef<CFDictionaryRef> 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<CFMutableDictionaryRef>("{%O = %d,%O = %d,%O = %d}",
+ kSecCodeInfoDiskRepVersionPlatform, platform,
+ kSecCodeInfoDiskRepVersionMin, minVersion,
+ kSecCodeInfoDiskRepVersionSDK, sdkVersion));
+
+ if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8))
+ {
+ info.take(cfmake<CFMutableDictionaryRef>("{+%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.
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();
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 (...) {
//
// 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> macho(mExecutable->architecture());
- if (cd->codeLimit != macho->signingExtent())
- MacOSError::throwMe(errSecCSSignatureInvalid);
- }
}
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);
}