X-Git-Url: https://git.saurik.com/ldid.git/blobdiff_plain/296fd38eaec86c17bf93de6343b4c1c98b630ec5..f4f5892d577b1c746b037fe79ba78eca949eb5a8:/ldid.cpp diff --git a/ldid.cpp b/ldid.cpp index 6a16aa8..7154e10 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -245,6 +245,7 @@ struct mach_header { #define MH_OBJECT 0x1 #define MH_EXECUTE 0x2 #define MH_DYLIB 0x6 +#define MH_DYLINKER 0x7 #define MH_BUNDLE 0x8 #define MH_DYLIB_STUB 0x9 @@ -701,6 +702,7 @@ class MachHeader : _assert( Swap(mach_header_->filetype) == MH_EXECUTE || Swap(mach_header_->filetype) == MH_DYLIB || + Swap(mach_header_->filetype) == MH_DYLINKER || Swap(mach_header_->filetype) == MH_BUNDLE ); } @@ -1035,19 +1037,28 @@ struct AlgorithmSHA256 : } }; +static bool do_sha1(true); +static bool do_sha256(true); + static const std::vector &GetAlgorithms() { static AlgorithmSHA1 sha1; static AlgorithmSHA256 sha256; - static Algorithm *array[] = { - &sha1, - &sha256, - }; + static std::vector algorithms; + if (algorithms.empty()) { + if (do_sha1) + algorithms.push_back(&sha1); + if (do_sha256) + algorithms.push_back(&sha256); + } - static std::vector algorithms(array, array + sizeof(array) / sizeof(array[0])); return algorithms; } +struct Baton { + std::string entitlements_; +}; + struct CodesignAllocation { FatMachHeader mach_header_; uint32_t offset_; @@ -1056,15 +1067,17 @@ struct CodesignAllocation { uint32_t alloc_; uint32_t align_; const char *arch_; + Baton baton_; - CodesignAllocation(FatMachHeader mach_header, size_t offset, size_t size, size_t limit, size_t alloc, size_t align, const char *arch) : + CodesignAllocation(FatMachHeader mach_header, size_t offset, size_t size, size_t limit, size_t alloc, size_t align, const char *arch, const Baton &baton) : mach_header_(mach_header), offset_(offset), size_(size), limit_(limit), alloc_(alloc), align_(align), - arch_(arch) + arch_(arch), + baton_(baton) { } }; @@ -1172,35 +1185,44 @@ class Map { namespace ldid { +#ifndef LDID_NOPLIST +static plist_t plist(const std::string &data); +#endif + +void Analyze(const MachHeader &mach_header, const Functor &entitle) { + _foreach (load_command, mach_header.GetLoadCommands()) + if (mach_header.Swap(load_command->cmd) == LC_CODE_SIGNATURE) { + auto signature(reinterpret_cast(load_command)); + auto offset(mach_header.Swap(signature->dataoff)); + auto pointer(reinterpret_cast(mach_header.GetBase()) + offset); + auto super(reinterpret_cast(pointer)); + + for (size_t index(0); index != Swap(super->count); ++index) + if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) { + auto begin(Swap(super->index[index].offset)); + auto blob(reinterpret_cast(pointer + begin)); + auto writ(Swap(blob->length) - sizeof(*blob)); + entitle(reinterpret_cast(blob + 1), writ); + } + } +} + std::string Analyze(const void *data, size_t size) { std::string entitlements; FatHeader fat_header(const_cast(data), size); _foreach (mach_header, fat_header.GetMachHeaders()) - _foreach (load_command, mach_header.GetLoadCommands()) - if (mach_header.Swap(load_command->cmd) == LC_CODE_SIGNATURE) { - auto signature(reinterpret_cast(load_command)); - auto offset(mach_header.Swap(signature->dataoff)); - auto pointer(reinterpret_cast(mach_header.GetBase()) + offset); - auto super(reinterpret_cast(pointer)); - - for (size_t index(0); index != Swap(super->count); ++index) - if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) { - auto begin(Swap(super->index[index].offset)); - auto blob(reinterpret_cast(pointer + begin)); - auto writ(Swap(blob->length) - sizeof(*blob)); - - if (entitlements.empty()) - entitlements.assign(reinterpret_cast(blob + 1), writ); - else - _assert(entitlements.compare(0, entitlements.size(), reinterpret_cast(blob + 1), writ) == 0); - } - } + Analyze(mach_header, fun([&](const char *data, size_t size) { + if (entitlements.empty()) + entitlements.assign(data, size); + else + _assert(entitlements.compare(0, entitlements.size(), data, size) == 0); + })); return entitlements; } -static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor &allocate, const Functor &save, const Progress &progress) { +static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor &allocate, const Functor &save, const Progress &progress) { FatHeader source(const_cast(idata), isize); size_t offset(0); @@ -1238,7 +1260,8 @@ static void Allocate(const void *idata, size_t isize, std::streambuf &output, co } } - size_t alloc(allocate(mach_header, size)); + Baton baton; + size_t alloc(allocate(mach_header, baton, size)); auto *fat_arch(mach_header.GetFatArch()); uint32_t align; @@ -1289,7 +1312,7 @@ static void Allocate(const void *idata, size_t isize, std::streambuf &output, co if (alloc != 0) limit = Align(limit, 0x10); - allocations.push_back(CodesignAllocation(mach_header, offset, size, limit, alloc, align, arch)); + allocations.push_back(CodesignAllocation(mach_header, offset, size, limit, alloc, align, arch, baton)); offset += size + alloc; offset = Align(offset, 0x10); } @@ -1411,7 +1434,7 @@ static void Allocate(const void *idata, size_t isize, std::streambuf &output, co pad(output, allocation.limit_ - allocation.size_); position += allocation.limit_ - allocation.size_; - size_t saved(save(mach_header, output, allocation.limit_, overlap, top, progress)); + size_t saved(save(mach_header, allocation.baton_, output, allocation.limit_, overlap, top, progress)); if (allocation.alloc_ > saved) pad(output, allocation.alloc_ - saved); else @@ -1583,8 +1606,12 @@ class Signature { for (unsigned i(0), e(sk_X509_num(certs)); i != e; i++) _assert(PKCS7_add_certificate(value_, sk_X509_value(certs, e - i - 1))); - auto info(PKCS7_sign_add_signer(value_, stuff, stuff, NULL, PKCS7_NOSMIMECAP)); + // XXX: this is the same as PKCS7_sign_add_signer(value_, stuff, stuff, NULL, PKCS7_NOSMIMECAP) + _assert(X509_check_private_key(stuff, stuff)); + auto info(PKCS7_add_signature(value_, stuff, stuff, EVP_sha1())); _assert(info != NULL); + _assert(PKCS7_add_certificate(value_, stuff)); + _assert(PKCS7_add_signed_attribute(info, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data))); PKCS7_set_detached(value_, 1); @@ -1600,7 +1627,13 @@ class Signature { throw; } - _assert(PKCS7_final(value_, data, PKCS7_BINARY)); + // XXX: this is the same as PKCS7_final(value_, data, PKCS7_BINARY) + BIO *bio(PKCS7_dataInit(value_, NULL)); + _assert(bio != NULL); + _scope({ BIO_free_all(bio); }); + SMIME_crlf_copy(data, bio, PKCS7_BINARY); + BIO_flush(bio); + _assert(PKCS7_dataFinal(value_, bio)); } ~Signature() { @@ -1780,7 +1813,7 @@ static void req(std::streambuf &buffer, uint8_t (&&data)[Size_]) { put(buffer, zeros, 3 - (Size_ + 3) % 4); } -Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, const std::string &requirements, const std::string &key, const Slots &slots, uint32_t flags, bool platform, const Progress &progress) { +Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, uint32_t flags, bool platform, const Progress &progress) { Hash hash; @@ -1831,7 +1864,7 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st // XXX: this is just a "sufficiently large number" size_t certificate(0x3000); - Allocate(idata, isize, output, fun([&](const MachHeader &mach_header, size_t size) -> size_t { + Allocate(idata, isize, output, fun([&](const MachHeader &mach_header, Baton &baton, size_t size) -> size_t { size_t alloc(sizeof(struct SuperBlob)); uint32_t normal((size + PageSize_ - 1) / PageSize_); @@ -1850,11 +1883,56 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st alloc += sizeof(struct BlobIndex); alloc += backing.str().size(); - if (!entitlements.empty()) { + if (!merge) + baton.entitlements_ = entitlements; + else { +#ifndef LDID_NOPLIST + Analyze(mach_header, fun([&](const char *data, size_t size) { + baton.entitlements_.assign(data, size); + })); + + if (baton.entitlements_.empty()) + baton.entitlements_ = entitlements; + else if (!entitlements.empty()) { + auto combined(plist(baton.entitlements_)); + _scope({ plist_free(combined); }); + _assert(plist_get_node_type(combined) == PLIST_DICT); + + auto merging(plist(entitlements)); + _scope({ plist_free(merging); }); + _assert(plist_get_node_type(merging) == PLIST_DICT); + + plist_dict_iter iterator(NULL); + plist_dict_new_iter(merging, &iterator); + _scope({ free(iterator); }); + + for (;;) { + char *key(NULL); + plist_t value(NULL); + plist_dict_next_item(merging, iterator, &key, &value); + if (key == NULL) + break; + _scope({ free(key); }); + plist_dict_set_item(combined, key, plist_copy(value)); + } + + char *xml(NULL); + uint32_t size; + plist_to_xml(combined, &xml, &size); + _scope({ free(xml); }); + + baton.entitlements_.assign(xml, size); + } +#else + _assert(false); +#endif + } + + if (!baton.entitlements_.empty()) { special = std::max(special, CSSLOT_ENTITLEMENTS); alloc += sizeof(struct BlobIndex); alloc += sizeof(struct Blob); - alloc += entitlements.size(); + alloc += baton.entitlements_.size(); } size_t directory(0); @@ -1879,16 +1957,16 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st #endif return alloc; - }), fun([&](const MachHeader &mach_header, std::streambuf &output, size_t limit, const std::string &overlap, const char *top, const Progress &progress) -> size_t { + }), fun([&](const MachHeader &mach_header, const Baton &baton, std::streambuf &output, size_t limit, const std::string &overlap, const char *top, const Progress &progress) -> size_t { Blobs blobs; if (true) { insert(blobs, CSSLOT_REQUIREMENTS, backing); } - if (!entitlements.empty()) { + if (!baton.entitlements_.empty()) { std::stringbuf data; - put(data, entitlements.data(), entitlements.size()); + put(data, baton.entitlements_.data(), baton.entitlements_.size()); insert(blobs, CSSLOT_ENTITLEMENTS, CSMAGIC_EMBEDDED_ENTITLEMENTS, data); } @@ -2058,9 +2136,9 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st #ifndef LDID_NOTOOLS static void Unsign(void *idata, size_t isize, std::streambuf &output, const Progress &progress) { - Allocate(idata, isize, output, fun([](const MachHeader &mach_header, size_t size) -> size_t { + Allocate(idata, isize, output, fun([](const MachHeader &mach_header, Baton &baton, size_t size) -> size_t { return 0; - }), fun([](const MachHeader &mach_header, std::streambuf &output, size_t limit, const std::string &overlap, const char *top, const Progress &progress) -> size_t { + }), fun([](const MachHeader &mach_header, const Baton &baton, std::streambuf &output, size_t limit, const std::string &overlap, const char *top, const Progress &progress) -> size_t { return 0; }), progress); } @@ -2395,7 +2473,7 @@ struct RuleCode { }; #ifndef LDID_NOPLIST -static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Hash &hash, std::streambuf &save, const std::string &identifier, const std::string &entitlements, const std::string &requirements, const std::string &key, const Slots &slots, size_t length, uint32_t flags, bool platform, const Progress &progress) { +static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Hash &hash, std::streambuf &save, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, size_t length, uint32_t flags, bool platform, const Progress &progress) { // XXX: this is a miserable fail std::stringbuf temp; put(temp, prefix, size); @@ -2405,7 +2483,7 @@ static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Has auto data(temp.str()); HashProxy proxy(hash, save); - return Sign(data.data(), data.size(), proxy, identifier, entitlements, requirements, key, slots, flags, platform, percent); + return Sign(data.data(), data.size(), proxy, identifier, entitlements, merge, requirements, key, slots, flags, platform, progress); } Bundle Sign(const std::string &root, Folder &folder, const std::string &key, std::map &remote, const std::string &requirements, const Functor &alter, const Progress &progress) { @@ -2549,7 +2627,7 @@ Bundle Sign(const std::string &root, Folder &folder, const std::string &key, std case MH_CIGAM: case MH_CIGAM_64: folder.Save(name, true, flag, fun([&](std::streambuf &save) { Slots slots; - Sign(header.bytes, size, data, hash, save, identifier, "", "", key, slots, length, 0, false, Progression(progress, root + name)); + Sign(header.bytes, size, data, hash, save, identifier, "", false, "", key, slots, length, 0, false, Progression(progress, root + name)); })); return; } @@ -2678,7 +2756,7 @@ Bundle Sign(const std::string &root, Folder &folder, const std::string &key, std Slots slots; slots[1] = local.at(info); slots[3] = local.at(signature); - bundle.hash = Sign(NULL, 0, buffer, local[executable], save, identifier, entitlements, requirements, key, slots, length, 0, false, Progression(progress, root + executable)); + bundle.hash = Sign(NULL, 0, buffer, local[executable], save, identifier, entitlements, false, requirements, key, slots, length, 0, false, Progression(progress, root + executable)); })); })); @@ -2697,6 +2775,13 @@ Bundle Sign(const std::string &root, Folder &folder, const std::string &key, con #endif } +static void usage(const char *argv0) { + fprintf(stderr, "usage: %s -S[entitlements.xml] \n", argv0); + fprintf(stderr, " %s -e MobileSafari\n", argv0); + fprintf(stderr, " %s -S cat\n", argv0); + fprintf(stderr, " %s -Stfp.xml gdb\n", argv0); +} + #ifndef LDID_NOTOOLS int main(int argc, char *argv[]) { #ifndef LDID_NOSMIME @@ -2714,6 +2799,8 @@ int main(int argc, char *argv[]) { bool flag_e(false); bool flag_q(false); + bool flag_H(false); + #ifndef LDID_NOFLAGT bool flag_T(false); #endif @@ -2728,6 +2815,8 @@ int main(int argc, char *argv[]) { bool flag_u(false); + bool flag_M(false); + uint32_t flags(0); bool platform(false); @@ -2749,11 +2838,8 @@ int main(int argc, char *argv[]) { std::vector files; if (argc == 1) { - fprintf(stderr, "usage: %s -S[entitlements.xml] \n", argv[0]); - fprintf(stderr, " %s -e MobileSafari\n", argv[0]); - fprintf(stderr, " %s -S cat\n", argv[0]); - fprintf(stderr, " %s -Stfp.xml gdb\n", argv[0]); - exit(0); + usage(argv[0]); + return 0; } for (int argi(1); argi != argc; ++argi) @@ -2783,6 +2869,27 @@ int main(int argc, char *argv[]) { case 'q': flag_q = true; break; + case 'H': { + const char *hash = argv[argi] + 2; + + if (!flag_H) { + flag_H = true; + + do_sha1 = false; + do_sha256 = false; + + fprintf(stderr, "WARNING: -H is only present for compatibility with a fork of ldid\n"); + fprintf(stderr, " you should NOT be manually specifying the hash algorithm\n"); + } + + if (false); + else if (strcmp(hash, "sha1") == 0) + do_sha1 = true; + else if (strcmp(hash, "sha256") == 0) + do_sha256 = true; + else _assert(false); + } break; + case 'Q': { const char *xml = argv[argi] + 2; requirements.open(xml, O_RDONLY, PROT_READ, MAP_PRIVATE); @@ -2849,6 +2956,10 @@ int main(int argc, char *argv[]) { } break; + case 'M': + flag_M = true; + break; + case 'K': if (argv[argi][2] != '\0') key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE); @@ -2876,16 +2987,16 @@ int main(int argc, char *argv[]) { } break; default: - goto usage; + usage(argv[0]); + return 1; break; } _assert(flag_S || key.empty()); _assert(flag_S || flag_I == NULL); - if (files.empty()) usage: { - exit(0); - } + if (files.empty()) + return 0; size_t filei(0), filee(0); _foreach (file, files) try { @@ -2913,7 +3024,7 @@ int main(int argc, char *argv[]) { ldid::Unsign(input.data(), input.size(), output, dummy_); else { std::string identifier(flag_I ?: split.base.c_str()); - ldid::Sign(input.data(), input.size(), output, identifier, entitlements, requirements, key, slots, flags, platform, dummy_); + ldid::Sign(input.data(), input.size(), output, identifier, entitlements, flag_M, requirements, key, slots, flags, platform, dummy_); } Commit(path, temp);