size_t numNonHiddenSections() const;
uint64_t fileOff = 0;
+ uint64_t fileSize = 0;
StringRef name;
uint32_t maxProt = 0;
uint32_t initProt = 0;
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/SHA256.h"
+
+#if defined(__APPLE__)
+#include <sys/mman.h>
+#endif
using namespace llvm;
using namespace llvm::support;
off += str.size() + 1; // account for null terminator
}
}
+
+CodeSignatureSection::CodeSignatureSection()
+ : LinkEditSection(segment_names::linkEdit, section_names::codeSignature) {
+ align = 16; // required by libstuff
+ fileName = config->outputFile;
+ size_t slashIndex = fileName.rfind("/");
+ if (slashIndex != std::string::npos)
+ fileName = fileName.drop_front(slashIndex + 1);
+ allHeadersSize = alignTo<16>(fixedHeadersSize + fileName.size() + 1);
+ fileNamePad = allHeadersSize - fixedHeadersSize - fileName.size();
+}
+
+uint32_t CodeSignatureSection::getBlockCount() const {
+ return (fileOff + blockSize - 1) / blockSize;
+}
+
+uint64_t CodeSignatureSection::getRawSize() const {
+ return allHeadersSize + getBlockCount() * hashSize;
+}
+
+void CodeSignatureSection::writeHashes(uint8_t *buf) const {
+ uint8_t *code = buf;
+ uint8_t *codeEnd = buf + fileOff;
+ uint8_t *hashes = codeEnd + allHeadersSize;
+ while (code < codeEnd) {
+ StringRef block(reinterpret_cast<char *>(code),
+ std::min(codeEnd - code, static_cast<ssize_t>(blockSize)));
+ SHA256 hasher;
+ hasher.update(block);
+ StringRef hash = hasher.final();
+ assert(hash.size() == hashSize);
+ memcpy(hashes, hash.data(), hashSize);
+ code += blockSize;
+ hashes += hashSize;
+ }
+#if defined(__APPLE__)
+ // This is macOS-specific work-around and makes no sense for any
+ // other host OS. See https://openradar.appspot.com/FB8914231
+ //
+ // The macOS kernel maintains a signature-verification cache to
+ // quickly validate applications at time of execve(2). The trouble
+ // is that for the kernel creates the cache entry at the time of the
+ // mmap(2) call, before we have a chance to write either the code to
+ // sign or the signature header+hashes. The fix is to invalidate
+ // all cached data associated with the output file, thus discarding
+ // the bogus prematurely-cached signature.
+ msync(buf, fileOff + getSize(), MS_INVALIDATE);
+#endif
+}
+
+void CodeSignatureSection::writeTo(uint8_t *buf) const {
+ using namespace llvm::MachO;
+ uint32_t signatureSize = static_cast<uint32_t>(getSize());
+ auto *superBlob = reinterpret_cast<CS_SuperBlob *>(buf);
+ write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE);
+ write32be(&superBlob->length, signatureSize);
+ write32be(&superBlob->count, 1);
+ auto *blobIndex = reinterpret_cast<CS_BlobIndex *>(&superBlob[1]);
+ write32be(&blobIndex->type, CSSLOT_CODEDIRECTORY);
+ write32be(&blobIndex->offset, blobHeadersSize);
+ auto *codeDirectory =
+ reinterpret_cast<CS_CodeDirectory *>(buf + blobHeadersSize);
+ write32be(&codeDirectory->magic, CSMAGIC_CODEDIRECTORY);
+ write32be(&codeDirectory->length, signatureSize - blobHeadersSize);
+ write32be(&codeDirectory->version, CS_SUPPORTSEXECSEG);
+ write32be(&codeDirectory->flags, CS_ADHOC | CS_LINKER_SIGNED);
+ write32be(&codeDirectory->hashOffset,
+ sizeof(CS_CodeDirectory) + fileName.size() + fileNamePad);
+ write32be(&codeDirectory->identOffset, sizeof(CS_CodeDirectory));
+ codeDirectory->nSpecialSlots = 0;
+ write32be(&codeDirectory->nCodeSlots, getBlockCount());
+ write32be(&codeDirectory->codeLimit, fileOff);
+ codeDirectory->hashSize = static_cast<uint8_t>(hashSize);
+ codeDirectory->hashType = kSecCodeSignatureHashSHA256;
+ codeDirectory->platform = 0;
+ codeDirectory->pageSize = blockSizeShift;
+ codeDirectory->spare2 = 0;
+ codeDirectory->scatterOffset = 0;
+ codeDirectory->teamOffset = 0;
+ codeDirectory->spare3 = 0;
+ codeDirectory->codeLimit64 = 0;
+ OutputSegment *textSeg = getOrCreateOutputSegment(segment_names::text);
+ write64be(&codeDirectory->execSegBase, textSeg->fileOff);
+ write64be(&codeDirectory->execSegLimit, textSeg->fileSize);
+ write64be(&codeDirectory->execSegFlags,
+ config->outputType == MH_EXECUTE ? CS_EXECSEG_MAIN_BINARY : 0);
+ auto *id = reinterpret_cast<char *>(&codeDirectory[1]);
+ memcpy(id, fileName.begin(), fileName.size());
+ memset(id + fileName.size(), 0, fileNamePad);
+}
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
constexpr const char symbolTable[] = "__symbol_table";
constexpr const char indirectSymbolTable[] = "__ind_sym_tab";
constexpr const char stringTable[] = "__string_table";
+constexpr const char codeSignature[] = "__code_signature";
constexpr const char got[] = "__got";
constexpr const char threadPtrs[] = "__thread_ptrs";
constexpr const char unwindInfo[] = "__unwind_info";
// NOTE: This assumes that the extra bytes required for alignment can be
// zero-valued bytes.
uint64_t getSize() const override final {
- return llvm::alignTo(getRawSize(), WordSize);
+ return llvm::alignTo(getRawSize(), align);
}
};
void writeTo(uint8_t *buf) const override;
};
+// The code signature comes at the very end of the linked output file.
+class CodeSignatureSection : public LinkEditSection {
+public:
+ static constexpr uint8_t blockSizeShift = 12;
+ static constexpr size_t blockSize = (1 << blockSizeShift); // 4 KiB
+ static constexpr size_t hashSize = 256 / 8;
+ static constexpr size_t blobHeadersSize = llvm::alignTo<8>(
+ sizeof(llvm::MachO::CS_SuperBlob) + sizeof(llvm::MachO::CS_BlobIndex));
+ static constexpr uint32_t fixedHeadersSize =
+ blobHeadersSize + sizeof(llvm::MachO::CS_CodeDirectory);
+
+ uint32_t fileNamePad = 0;
+ uint32_t allHeadersSize = 0;
+ StringRef fileName;
+
+ CodeSignatureSection();
+ uint64_t getRawSize() const override;
+ bool isNeeded() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+ uint32_t getBlockCount() const;
+ void writeHashes(uint8_t *buf) const;
+};
+
+static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, "");
+static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, "");
+
struct InStruct {
MachHeaderSection *header = nullptr;
RebaseSection *rebase = nullptr;
void openFile();
void writeSections();
void writeUuid();
+ void writeCodeSignature();
void run();
StringTableSection *stringTableSection = nullptr;
SymtabSection *symtabSection = nullptr;
IndirectSymtabSection *indirectSymtabSection = nullptr;
+ CodeSignatureSection *codeSignatureSection = nullptr;
UnwindInfoSection *unwindInfoSection = nullptr;
LCUuid *uuidCommand = nullptr;
};
mutable uint8_t *uuidBuf;
};
+class LCCodeSignature : public LoadCommand {
+public:
+ LCCodeSignature(CodeSignatureSection *section) : section(section) {}
+
+ uint32_t getSize() const override { return sizeof(linkedit_data_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<linkedit_data_command *>(buf);
+ c->cmd = LC_CODE_SIGNATURE;
+ c->cmdsize = getSize();
+ c->dataoff = static_cast<uint32_t>(section->fileOff);
+ c->datasize = section->getSize();
+ }
+
+ CodeSignatureSection *section;
+};
+
} // namespace
static void prepareSymbolRelocation(lld::macho::Symbol *sym,
}
}
+ if (codeSignatureSection)
+ in.header->addLoadCommand(make<LCCodeSignature>(codeSignatureSection));
+
const uint32_t MACOS_MAXPATHLEN = 1024;
config->headerPad = std::max(
config->headerPad, (config->headerPadMaxInstallNames
.Case(section_names::symbolTable, -3)
.Case(section_names::indirectSymbolTable, -2)
.Case(section_names::stringTable, -1)
+ .Case(section_names::codeSignature, std::numeric_limits<int>::max())
.Default(0);
}
// ZeroFill sections must always be the at the end of their segments,
unwindInfoSection = make<UnwindInfoSection>(); // TODO(gkm): only when no -r
symtabSection = make<SymtabSection>(*stringTableSection);
indirectSymtabSection = make<IndirectSymtabSection>();
+ if (config->outputType == MH_EXECUTE &&
+ (config->arch == AK_arm64 || config->arch == AK_arm64e))
+ codeSignatureSection = make<CodeSignatureSection>();
switch (config->outputType) {
case MH_EXECUTE:
addr += osec->getSize();
fileOff += osec->getFileSize();
}
+ seg->fileSize = fileOff - seg->fileOff;
}
void Writer::openFile() {
uuidCommand->writeUuid(digest);
}
+void Writer::writeCodeSignature() {
+ if (codeSignatureSection)
+ codeSignatureSection->writeHashes(buffer->getBufferStart());
+}
+
void Writer::run() {
// dyld requires __LINKEDIT segment to always exist (even if empty).
OutputSegment *linkEditSegment =
writeSections();
writeUuid();
+ writeCodeSignature();
if (auto e = buffer->commit())
error("failed to write to the output file: " + toString(std::move(e)));
};
LLVM_PACKED_END
+/* code signing attributes of a process */
+
+enum CodeSignAttrs {
+ CS_VALID = 0x00000001, /* dynamically valid */
+ CS_ADHOC = 0x00000002, /* ad hoc signed */
+ CS_GET_TASK_ALLOW = 0x00000004, /* has get-task-allow entitlement */
+ CS_INSTALLER = 0x00000008, /* has installer entitlement */
+
+ CS_FORCED_LV =
+ 0x00000010, /* Library Validation required by Hardened System Policy */
+ CS_INVALID_ALLOWED = 0x00000020, /* (macOS Only) Page invalidation allowed by
+ task port policy */
+
+ CS_HARD = 0x00000100, /* don't load invalid pages */
+ CS_KILL = 0x00000200, /* kill process if it becomes invalid */
+ CS_CHECK_EXPIRATION = 0x00000400, /* force expiration checking */
+ CS_RESTRICT = 0x00000800, /* tell dyld to treat restricted */
+
+ CS_ENFORCEMENT = 0x00001000, /* require enforcement */
+ CS_REQUIRE_LV = 0x00002000, /* require library validation */
+ CS_ENTITLEMENTS_VALIDATED =
+ 0x00004000, /* code signature permits restricted entitlements */
+ CS_NVRAM_UNRESTRICTED =
+ 0x00008000, /* has com.apple.rootless.restricted-nvram-variables.heritable
+ entitlement */
+
+ CS_RUNTIME = 0x00010000, /* Apply hardened runtime policies */
+ CS_LINKER_SIGNED = 0x00020000, /* Automatically signed by the linker */
+
+ CS_ALLOWED_MACHO =
+ (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | CS_RESTRICT |
+ CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED),
+
+ CS_EXEC_SET_HARD = 0x00100000, /* set CS_HARD on any exec'ed process */
+ CS_EXEC_SET_KILL = 0x00200000, /* set CS_KILL on any exec'ed process */
+ CS_EXEC_SET_ENFORCEMENT =
+ 0x00400000, /* set CS_ENFORCEMENT on any exec'ed process */
+ CS_EXEC_INHERIT_SIP =
+ 0x00800000, /* set CS_INSTALLER on any exec'ed process */
+
+ CS_KILLED = 0x01000000, /* was killed by kernel for invalidity */
+ CS_DYLD_PLATFORM =
+ 0x02000000, /* dyld used to load this is a platform binary */
+ CS_PLATFORM_BINARY = 0x04000000, /* this is a platform binary */
+ CS_PLATFORM_PATH =
+ 0x08000000, /* platform binary by the fact of path (osx only) */
+
+ CS_DEBUGGED = 0x10000000, /* process is currently or has previously been
+ debugged and allowed to run with invalid pages */
+ CS_SIGNED = 0x20000000, /* process has a signature (may have gone invalid) */
+ CS_DEV_CODE =
+ 0x40000000, /* code is dev signed, cannot be loaded into prod signed code
+ (will go away with rdar://problem/28322552) */
+ CS_DATAVAULT_CONTROLLER =
+ 0x80000000, /* has Data Vault controller entitlement */
+
+ CS_ENTITLEMENT_FLAGS = (CS_GET_TASK_ALLOW | CS_INSTALLER |
+ CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED),
+};
+
+/* executable segment flags */
+
+enum CodeSignExecSegFlags {
+
+ CS_EXECSEG_MAIN_BINARY = 0x1, /* executable segment denotes main binary */
+ CS_EXECSEG_ALLOW_UNSIGNED = 0x10, /* allow unsigned pages (for debugging) */
+ CS_EXECSEG_DEBUGGER = 0x20, /* main binary is debugger */
+ CS_EXECSEG_JIT = 0x40, /* JIT enabled */
+ CS_EXECSEG_SKIP_LV = 0x80, /* OBSOLETE: skip library validation */
+ CS_EXECSEG_CAN_LOAD_CDHASH = 0x100, /* can bless cdhash for execution */
+ CS_EXECSEG_CAN_EXEC_CDHASH = 0x200, /* can execute blessed cdhash */
+
+};
+
+/* Magic numbers used by Code Signing */
+
+enum CodeSignMagic {
+ CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */
+ CSMAGIC_REQUIREMENTS =
+ 0xfade0c01, /* Requirements vector (internal requirements) */
+ CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */
+ CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */
+ CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */
+ CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */
+ CSMAGIC_DETACHED_SIGNATURE =
+ 0xfade0cc1, /* multi-arch collection of embedded signatures */
+ CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */
+
+ CS_SUPPORTSSCATTER = 0x20100,
+ CS_SUPPORTSTEAMID = 0x20200,
+ CS_SUPPORTSCODELIMIT64 = 0x20300,
+ CS_SUPPORTSEXECSEG = 0x20400,
+ CS_SUPPORTSRUNTIME = 0x20500,
+ CS_SUPPORTSLINKAGE = 0x20600,
+
+ CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */
+ CSSLOT_INFOSLOT = 1,
+ CSSLOT_REQUIREMENTS = 2,
+ CSSLOT_RESOURCEDIR = 3,
+ CSSLOT_APPLICATION = 4,
+ CSSLOT_ENTITLEMENTS = 5,
+
+ CSSLOT_ALTERNATE_CODEDIRECTORIES =
+ 0x1000, /* first alternate CodeDirectory, if any */
+ CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */
+ CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT =
+ CSSLOT_ALTERNATE_CODEDIRECTORIES +
+ CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */
+
+ CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */
+ CSSLOT_IDENTIFICATIONSLOT = 0x10001,
+ CSSLOT_TICKETSLOT = 0x10002,
+
+ CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */
+ CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */
+
+ CS_HASHTYPE_SHA1 = 1,
+ CS_HASHTYPE_SHA256 = 2,
+ CS_HASHTYPE_SHA256_TRUNCATED = 3,
+ CS_HASHTYPE_SHA384 = 4,
+
+ CS_SHA1_LEN = 20,
+ CS_SHA256_LEN = 32,
+ CS_SHA256_TRUNCATED_LEN = 20,
+
+ CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */
+ CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */
+
+ /*
+ * Currently only to support Legacy VPN plugins, and Mac App Store
+ * but intended to replace all the various platform code, dev code etc. bits.
+ */
+ CS_SIGNER_TYPE_UNKNOWN = 0,
+ CS_SIGNER_TYPE_LEGACYVPN = 5,
+ CS_SIGNER_TYPE_MAC_APP_STORE = 6,
+
+ CS_SUPPL_SIGNER_TYPE_UNKNOWN = 0,
+ CS_SUPPL_SIGNER_TYPE_TRUSTCACHE = 7,
+ CS_SUPPL_SIGNER_TYPE_LOCAL = 8,
+};
+
+struct CS_CodeDirectory {
+ uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
+ uint32_t length; /* total length of CodeDirectory blob */
+ uint32_t version; /* compatibility version */
+ uint32_t flags; /* setup and mode flags */
+ uint32_t hashOffset; /* offset of hash slot element at index zero */
+ uint32_t identOffset; /* offset of identifier string */
+ uint32_t nSpecialSlots; /* number of special hash slots */
+ uint32_t nCodeSlots; /* number of ordinary (code) hash slots */
+ uint32_t codeLimit; /* limit to main image signature range */
+ uint8_t hashSize; /* size of each hash in bytes */
+ uint8_t hashType; /* type of hash (cdHashType* constants) */
+ uint8_t platform; /* platform identifier; zero if not platform binary */
+ uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
+ uint32_t spare2; /* unused (must be zero) */
+
+ /* Version 0x20100 */
+ uint32_t scatterOffset; /* offset of optional scatter vector */
+
+ /* Version 0x20200 */
+ uint32_t teamOffset; /* offset of optional team identifier */
+
+ /* Version 0x20300 */
+ uint32_t spare3; /* unused (must be zero) */
+ uint64_t codeLimit64; /* limit to main image signature range, 64 bits */
+
+ /* Version 0x20400 */
+ uint64_t execSegBase; /* offset of executable segment */
+ uint64_t execSegLimit; /* limit of executable segment */
+ uint64_t execSegFlags; /* executable segment flags */
+};
+
+static_assert(sizeof(CS_CodeDirectory) == 88, "");
+
+struct CS_BlobIndex {
+ uint32_t type; /* type of entry */
+ uint32_t offset; /* offset of entry */
+};
+
+struct CS_SuperBlob {
+ uint32_t magic; /* magic number */
+ uint32_t length; /* total length of SuperBlob */
+ uint32_t count; /* number of index entries following */
+ /* followed by Blobs in no particular order as indicated by index offsets */
+};
+
+enum SecCSDigestAlgorithm {
+ kSecCodeSignatureNoHash = 0, /* null value */
+ kSecCodeSignatureHashSHA1 = 1, /* SHA-1 */
+ kSecCodeSignatureHashSHA256 = 2, /* SHA-256 */
+ kSecCodeSignatureHashSHA256Truncated =
+ 3, /* SHA-256 truncated to first 20 bytes */
+ kSecCodeSignatureHashSHA384 = 4, /* SHA-384 */
+ kSecCodeSignatureHashSHA512 = 5, /* SHA-512 */
+};
+
} // end namespace MachO
} // end namespace llvm