#include <sys/stat.h>
#include <sys/types.h>
+#ifndef LDID_NOSMIME
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
+#endif
#ifdef __APPLE__
#include <CommonCrypto/CommonDigest.h>
#define LDID_SHA1_Final SHA1_Final
#endif
+#ifndef LDID_NOPLIST
#include <plist/plist.h>
+#endif
#include "ldid.hpp"
#define _assert__(line) \
_assert___(line)
+#ifdef __EXCEPTIONS
#define _assert_(expr, format, ...) \
do if (!(expr)) { \
fprintf(stderr, "%s(%u): _assert(): " format "\n", __FILE__, __LINE__, ## __VA_ARGS__); \
throw __FILE__ "(" _assert__(__LINE__) "): _assert(" #expr ")"; \
} while (false)
+#else
+// XXX: this is not acceptable
+#define _assert_(expr, format, ...) \
+ do if (!(expr)) { \
+ fprintf(stderr, "%s(%u): _assert(): " format "\n", __FILE__, __LINE__, ## __VA_ARGS__); \
+ exit(-1); \
+ } while (false)
+#endif
#define _assert(expr) \
_assert_(expr, "%s", #expr)
#define _scope(function) \
_scope_(__COUNTER__, function)
+#define CPU_ARCH_MASK uint32_t(0xff000000)
+#define CPU_ARCH_ABI64 uint32_t(0x01000000)
+
+#define CPU_TYPE_ANY uint32_t(-1)
+#define CPU_TYPE_VAX uint32_t( 1)
+#define CPU_TYPE_MC680x0 uint32_t( 6)
+#define CPU_TYPE_X86 uint32_t( 7)
+#define CPU_TYPE_MC98000 uint32_t(10)
+#define CPU_TYPE_HPPA uint32_t(11)
+#define CPU_TYPE_ARM uint32_t(12)
+#define CPU_TYPE_MC88000 uint32_t(13)
+#define CPU_TYPE_SPARC uint32_t(14)
+#define CPU_TYPE_I860 uint32_t(15)
+#define CPU_TYPE_POWERPC uint32_t(18)
+
+#define CPU_TYPE_I386 CPU_TYPE_X86
+
+#define CPU_TYPE_ARM64 (CPU_ARCH_ABI64 | CPU_TYPE_ARM)
+#define CPU_TYPE_POWERPC64 (CPU_ARCH_ABI64 | CPU_TYPE_POWERPC)
+#define CPU_TYPE_X86_64 (CPU_ARCH_ABI64 | CPU_TYPE_X86)
+
struct fat_header {
uint32_t magic;
uint32_t nfat_arch;
#define LC_DYLD_INFO_ONLY uint32_t(0x22 | LC_REQ_DYLD)
#define LC_ENCRYPTION_INFO_64 uint32_t(0x2c)
+union Version {
+ struct {
+ uint8_t patch;
+ uint8_t minor;
+ uint16_t major;
+ } _packed;
+
+ uint32_t value;
+};
+
struct dylib {
uint32_t name;
uint32_t timestamp;
uint32_t flags;
uint32_t reserved1;
uint32_t reserved2;
+ uint32_t reserved3;
} _packed;
struct linkedit_data_command {
return Swap(static_cast<uint64_t>(value));
}
-template <typename Target_>
-class Pointer;
-
class Swapped {
protected:
bool swapped_;
return load_commands;
}
- std::vector<segment_command *> GetSegments(const char *segment_name) const {
- std::vector<struct segment_command *> segment_commands;
-
- _foreach (load_command, GetLoadCommands()) {
- if (Swap(load_command->cmd) == LC_SEGMENT) {
- segment_command *segment_command = reinterpret_cast<struct segment_command *>(load_command);
- if (strncmp(segment_command->segname, segment_name, 16) == 0)
- segment_commands.push_back(segment_command);
- }
- }
-
- return segment_commands;
- }
-
- std::vector<segment_command_64 *> GetSegments64(const char *segment_name) const {
- std::vector<struct segment_command_64 *> segment_commands;
-
- _foreach (load_command, GetLoadCommands()) {
- if (Swap(load_command->cmd) == LC_SEGMENT_64) {
- segment_command_64 *segment_command = reinterpret_cast<struct segment_command_64 *>(load_command);
- if (strncmp(segment_command->segname, segment_name, 16) == 0)
- segment_commands.push_back(segment_command);
- }
- }
-
- return segment_commands;
- }
-
- std::vector<section *> GetSections(const char *segment_name, const char *section_name) const {
- std::vector<section *> sections;
-
- _foreach (segment, GetSegments(segment_name)) {
- section *section = (struct section *) (segment + 1);
-
- uint32_t sect;
- for (sect = 0; sect != Swap(segment->nsects); ++sect) {
- if (strncmp(section->sectname, section_name, 16) == 0)
- sections.push_back(section);
- ++section;
- }
- }
-
- return sections;
- }
+ void ForSection(const ldid::Functor<void (const char *, const char *, void *, size_t)> &code) const {
+ _foreach (load_command, GetLoadCommands())
+ switch (Swap(load_command->cmd)) {
+ case LC_SEGMENT: {
+ auto segment(reinterpret_cast<struct segment_command *>(load_command));
+ code(segment->segname, NULL, GetOffset<void>(segment->fileoff), segment->filesize);
+ auto section(reinterpret_cast<struct section *>(segment + 1));
+ for (uint32_t i(0), e(Swap(segment->nsects)); i != e; ++i, ++section)
+ code(segment->segname, section->sectname, GetOffset<void>(segment->fileoff + section->offset), section->size);
+ } break;
- template <typename Target_>
- Pointer<Target_> GetPointer(uint32_t address, const char *segment_name = NULL) const {
- load_command *load_command = (struct load_command *) (mach_header_ + 1);
- uint32_t cmd;
-
- for (cmd = 0; cmd != Swap(mach_header_->ncmds); ++cmd) {
- if (Swap(load_command->cmd) == LC_SEGMENT) {
- segment_command *segment_command = (struct segment_command *) load_command;
- if (segment_name != NULL && strncmp(segment_command->segname, segment_name, 16) != 0)
- goto next_command;
-
- section *sections = (struct section *) (segment_command + 1);
-
- uint32_t sect;
- for (sect = 0; sect != Swap(segment_command->nsects); ++sect) {
- section *section = §ions[sect];
- //printf("%s %u %p %p %u\n", segment_command->segname, sect, address, section->addr, section->size);
- if (address >= Swap(section->addr) && address < Swap(section->addr) + Swap(section->size)) {
- //printf("0x%.8x %s\n", address, segment_command->segname);
- return Pointer<Target_>(this, reinterpret_cast<Target_ *>(address - Swap(section->addr) + Swap(section->offset) + (char *) mach_header_));
- }
- }
+ case LC_SEGMENT_64: {
+ auto segment(reinterpret_cast<struct segment_command_64 *>(load_command));
+ code(segment->segname, NULL, GetOffset<void>(segment->fileoff), segment->filesize);
+ auto section(reinterpret_cast<struct section_64 *>(segment + 1));
+ for (uint32_t i(0), e(Swap(segment->nsects)); i != e; ++i, ++section)
+ code(segment->segname, section->sectname, GetOffset<void>(segment->fileoff + section->offset), section->size);
+ } break;
}
-
- next_command:
- load_command = (struct load_command *) ((char *) load_command + Swap(load_command->cmdsize));
- }
-
- return Pointer<Target_>(this);
}
template <typename Target_>
- Pointer<Target_> GetOffset(uint32_t offset) {
- return Pointer<Target_>(this, reinterpret_cast<Target_ *>(offset + (uint8_t *) mach_header_));
+ Target_ *GetOffset(uint32_t offset) const {
+ return reinterpret_cast<Target_ *>(offset + (uint8_t *) mach_header_);
}
};
}
};
-template <typename Target_>
-class Pointer {
- private:
- const MachHeader *framework_;
- const Target_ *pointer_;
-
- public:
- Pointer(const MachHeader *framework = NULL, const Target_ *pointer = NULL) :
- framework_(framework),
- pointer_(pointer)
- {
- }
-
- operator const Target_ *() const {
- return pointer_;
- }
-
- const Target_ *operator ->() const {
- return pointer_;
- }
-
- Pointer<Target_> &operator ++() {
- ++pointer_;
- return *this;
- }
-
- template <typename Value_>
- Value_ Swap(Value_ value) {
- return framework_->Swap(value);
- }
-};
-
#define CSMAGIC_REQUIREMENT uint32_t(0xfade0c00)
#define CSMAGIC_REQUIREMENTS uint32_t(0xfade0c01)
#define CSMAGIC_CODEDIRECTORY uint32_t(0xfade0c02)
uint32_t spare2;
} _packed;
+#ifndef LDID_NOFLAGT
extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
+#endif
static void sha1(uint8_t *hash, const void *data, size_t size) {
LDID_SHA1(static_cast<const uint8_t *>(data), size, hash);
}
+static void sha1(std::vector<char> &hash, const void *data, size_t size) {
+ hash.resize(LDID_SHA1_DIGEST_LENGTH);
+ sha1(reinterpret_cast<uint8_t *>(hash.data()), data, size);
+}
+
struct CodesignAllocation {
FatMachHeader mach_header_;
uint32_t offset_;
}
};
+#ifndef LDID_NOTOOLS
class File {
private:
int file_;
return std::string(static_cast<char *>(data_), size_);
}
};
+#endif
namespace ldid {
-static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor<size_t (size_t)> &allocate, const Functor<size_t (std::streambuf &output, size_t, const std::string &, const char *)> &save) {
+static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor<size_t (const MachHeader &, size_t)> &allocate, const Functor<size_t (const MachHeader &, std::streambuf &output, size_t, const std::string &, const char *)> &save) {
FatHeader source(const_cast<void *>(idata), isize);
size_t offset(0);
size = end;
}
- size_t alloc(allocate(size));
+ size_t alloc(allocate(mach_header, size));
auto *fat_arch(mach_header.GetFatArch());
- uint32_t align(fat_arch == NULL ? 0 : source.Swap(fat_arch->align));
+ uint32_t align;
+
+ if (fat_arch != NULL)
+ align = source.Swap(fat_arch->align);
+ else switch (mach_header.GetCPUType()) {
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ case CPU_TYPE_X86:
+ case CPU_TYPE_X86_64:
+ align = 0xc;
+ break;
+ case CPU_TYPE_ARM:
+ case CPU_TYPE_ARM64:
+ align = 0xe;
+ break;
+ default:
+ align = 0x0;
+ break;
+ }
+
offset = Align(offset, 1 << align);
uint32_t limit(size);
break;
size_t size(mach_header.Swap(allocation.limit_ + allocation.alloc_ - mach_header.Swap(segment_command->fileoff)));
segment_command->filesize = size;
- segment_command->vmsize = Align(size, PageSize_);
+ segment_command->vmsize = Align(size, 1 << allocation.align_);
} break;
case LC_SEGMENT_64: {
break;
size_t size(mach_header.Swap(allocation.limit_ + allocation.alloc_ - mach_header.Swap(segment_command->fileoff)));
segment_command->filesize = size;
- segment_command->vmsize = Align(size, PageSize_);
+ segment_command->vmsize = Align(size, 1 << allocation.align_);
} break;
}
pad(output, allocation.limit_ - allocation.size_);
position += allocation.limit_ - allocation.size_;
- size_t saved(save(output, allocation.limit_, overlap, top));
+ size_t saved(save(mach_header, output, allocation.limit_, overlap, top));
if (allocation.alloc_ > saved)
pad(output, allocation.alloc_ - saved);
position += allocation.alloc_;
return offset;
}
+#ifndef LDID_NOSMIME
class Buffer {
private:
BIO *bio_;
return value_;
}
};
+#endif
class NullBuffer :
public std::streambuf
}
};
+#ifndef LDID_NOTOOLS
static bool Starts(const std::string &lhs, const std::string &rhs) {
return lhs.size() >= rhs.size() && lhs.compare(0, rhs.size(), rhs) == 0;
}
_syscall(rename(temp.c_str(), path.c_str()));
}
+#endif
namespace ldid {
void Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, const std::string &key, const Slots &slots) {
- Allocate(idata, isize, output, fun([&](size_t size) -> size_t {
+ Allocate(idata, isize, output, fun([&](const MachHeader &mach_header, size_t size) -> size_t {
size_t alloc(sizeof(struct SuperBlob));
uint32_t special(0);
_foreach (slot, slots)
special = std::max(special, slot.first);
+ mach_header.ForSection(fun([&](const char *segment, const char *section, void *data, size_t size) {
+ if (strcmp(segment, "__TEXT") == 0 && section != NULL && strcmp(section, "__info_plist") == 0)
+ special = std::max(special, CSSLOT_INFOSLOT);
+ }));
+
uint32_t normal((size + PageSize_ - 1) / PageSize_);
alloc = Align(alloc + (special + normal) * LDID_SHA1_DIGEST_LENGTH, 16);
return alloc;
- }), fun([&](std::streambuf &output, size_t limit, const std::string &overlap, const char *top) -> size_t {
+ }), fun([&](const MachHeader &mach_header, std::streambuf &output, size_t limit, const std::string &overlap, const char *top) -> size_t {
Blobs blobs;
if (true) {
if (true) {
std::stringbuf data;
+ Slots posts(slots);
+
+ mach_header.ForSection(fun([&](const char *segment, const char *section, void *data, size_t size) {
+ if (strcmp(segment, "__TEXT") == 0 && section != NULL && strcmp(section, "__info_plist") == 0)
+ sha1(posts[CSSLOT_INFOSLOT], data, size);
+ }));
+
uint32_t special(0);
_foreach (blob, blobs)
special = std::max(special, blob.first);
- _foreach (slot, slots)
+ _foreach (slot, posts)
special = std::max(special, slot.first);
uint32_t normal((limit + PageSize_ - 1) / PageSize_);
sha1((uint8_t *) (hashes - blob.first), local, Swap(local->length));
}
- _foreach (slot, slots) {
+ _foreach (slot, posts) {
_assert(sizeof(*hashes) == slot.second.size());
memcpy(hashes - slot.first, slot.second.data(), slot.second.size());
}
insert(blobs, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY, data);
}
+#ifndef LDID_NOSMIME
if (!key.empty()) {
std::stringbuf data;
const std::string &sign(blobs[CSSLOT_CODEDIRECTORY]);
insert(blobs, CSSLOT_SIGNATURESLOT, CSMAGIC_BLOBWRAPPER, data);
}
+#endif
return put(output, CSMAGIC_EMBEDDED_SIGNATURE, blobs);
}));
}
+#ifndef LDID_NOTOOLS
static void Unsign(void *idata, size_t isize, std::streambuf &output) {
- Allocate(idata, isize, output, fun([](size_t size) -> size_t {
+ Allocate(idata, isize, output, fun([](const MachHeader &mach_header, size_t size) -> size_t {
return 0;
- }), fun([](std::streambuf &output, size_t limit, const std::string &overlap, const char *top) -> size_t {
+ }), fun([](const MachHeader &mach_header, std::streambuf &output, size_t limit, const std::string &overlap, const char *top) -> size_t {
return 0;
}));
}
void DiskFolder::Find(const std::string &path, const Functor<void (const std::string &, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &)>&code) {
Find(path, "", code);
}
+#endif
SubFolder::SubFolder(Folder &parent, const std::string &path) :
parent_(parent),
void UnionFolder::Find(const std::string &path, const Functor<void (const std::string &, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &)> &code) {
parent_.Find(path, fun([&](const std::string &name, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &save) {
- if (files_.find(name) == files_.end())
+ if (files_.find(path + name) == files_.end())
code(name, save);
}));
for (auto &file : files_)
- code(file.first, fun([&](const Functor<void (std::streambuf &, std::streambuf &)> &code) {
- parent_.Save(file.first, fun([&](std::streambuf &save) {
- file.second.pubseekpos(0, std::ios::in);
- code(file.second, save);
+ if (file.first.size() >= path.size() && file.first.substr(0, path.size()) == path)
+ code(file.first.substr(path.size()), fun([&](const Functor<void (std::streambuf &, std::streambuf &)> &code) {
+ parent_.Save(file.first, fun([&](std::streambuf &save) {
+ file.second.pubseekpos(0, std::ios::in);
+ code(file.second, save);
+ }));
}));
- }));
}
+#ifndef LDID_NOTOOLS
static size_t copy(std::streambuf &source, std::streambuf &target) {
size_t total(0);
for (;;) {
return total;
}
+#ifndef LDID_NOPLIST
static plist_t plist(const std::string &data) {
plist_t plist(NULL);
if (Starts(data, "bplist00"))
_scope({ free(data); });
return data;
}
+#endif
enum Mode {
NoMode,
}
};
+#ifndef LDID_NOPLIST
std::string Bundle(const std::string &root, Folder &folder, const std::string &key, std::map<std::string, std::vector<char>> &remote, const std::string &entitlements) {
std::string executable;
std::string identifier;
return executable;
}
+#endif
+#endif
}
+#ifndef LDID_NOTOOLS
int main(int argc, char *argv[]) {
+#ifndef LDID_NOSMIME
OpenSSL_add_all_algorithms();
+#endif
union {
uint16_t word;
bool flag_r(false);
bool flag_e(false);
+#ifndef LDID_NOFLAGT
bool flag_T(false);
+#endif
bool flag_S(false);
bool flag_s(false);
bool flag_A(false);
bool flag_a(false);
+ bool flag_u(false);
+
uint32_t flag_CPUType(_not(uint32_t));
uint32_t flag_CPUSubtype(_not(uint32_t));
const char *flag_I(NULL);
+#ifndef LDID_NOFLAGT
bool timeh(false);
uint32_t timev(0);
+#endif
Map entitlements;
Map key;
char *arge;
unsigned number(strtoul(slot, &arge, 0));
_assert(arge == colon);
- std::vector<char> &hash(slots[number]);
- hash.resize(LDID_SHA1_DIGEST_LENGTH);
- sha1(reinterpret_cast<uint8_t *>(hash.data()), file.data(), file.size());
+ sha1(slots[number], file.data(), file.size());
} break;
case 'D': flag_D = true; break;
break;
case 'K':
- key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE);
+ if (argv[argi][2] != '\0')
+ key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE);
break;
+#ifndef LDID_NOFLAGT
case 'T': {
flag_T = true;
if (argv[argi][2] == '-')
_assert(arge == argv[argi] + strlen(argv[argi]));
}
} break;
+#endif
+
+ case 'u': {
+ flag_u = true;
+ } break;
case 'I': {
flag_I = argv[argi] + 2;
_syscall(stat(path.c_str(), &info));
if (S_ISDIR(info.st_mode)) {
+#ifndef LDID_NOPLIST
_assert(!flag_r);
ldid::DiskFolder folder(path);
std::map<std::string, std::vector<char>> hashes;
path += "/" + Bundle("", folder, key, hashes, entitlements);
+#else
+ _assert(false);
+#endif
} else if (flag_S || flag_r) {
Map input(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
Commit(path, temp);
}
- Map mapping(path, flag_T || flag_s);
+ bool modify(false);
+#ifndef LDID_NOFLAGT
+ if (flag_T)
+ modify = true;
+#endif
+ if (flag_s)
+ modify = true;
+
+ Map mapping(path, modify);
FatHeader fat_header(mapping.data(), mapping.size());
_foreach (mach_header, fat_header.GetMachHeaders()) {
signature = reinterpret_cast<struct linkedit_data_command *>(load_command);
else if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64)
encryption = reinterpret_cast<struct encryption_info_command *>(load_command);
+ else if (cmd == LC_LOAD_DYLIB) {
+ volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(load_command));
+ const char *name(reinterpret_cast<const char *>(load_command) + mach_header.Swap(dylib_command->dylib.name));
+
+ if (strcmp(name, "/System/Library/Frameworks/UIKit.framework/UIKit") == 0) {
+ if (flag_u) {
+ Version version;
+ version.value = mach_header.Swap(dylib_command->dylib.current_version);
+ printf("uikit=%u.%u.%u\n", version.major, version.minor, version.patch);
+ }
+ }
+ }
+#ifndef LDID_NOFLAGT
else if (cmd == LC_ID_DYLIB) {
volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(load_command));
dylib_command->dylib.timestamp = mach_header.Swap(timed);
}
}
+#endif
}
if (flag_D) {
return filee;
}
+#endif