X-Git-Url: https://git.saurik.com/ldid.git/blobdiff_plain/261b99639c4ab7c53058a3a1a5906e566a5c5210..f5db0fc2c3ba3137c5e8b76d577ccd58ae342a63:/ldid.cpp?ds=sidebyside diff --git a/ldid.cpp b/ldid.cpp index ffc2821..9619daf 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -42,13 +42,34 @@ #include #include +#ifndef LDID_NOSMIME #include #include #include #include +#endif + +#ifdef __APPLE__ +#include +#define LDID_SHA1_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH +#define LDID_SHA1 CC_SHA1 +#define LDID_SHA1_CTX CC_SHA1_CTX +#define LDID_SHA1_Init CC_SHA1_Init +#define LDID_SHA1_Update CC_SHA1_Update +#define LDID_SHA1_Final CC_SHA1_Final +#else #include +#define LDID_SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH +#define LDID_SHA1 SHA1 +#define LDID_SHA1_CTX SHA_CTX +#define LDID_SHA1_Init SHA1_Init +#define LDID_SHA1_Update SHA1_Update +#define LDID_SHA1_Final SHA1_Final +#endif -#include +#ifndef LDID_NOPLIST +#include +#endif #include "ldid.hpp" @@ -57,11 +78,20 @@ #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) @@ -73,7 +103,8 @@ int error(errno); \ if (error == EINTR) \ continue; \ - for (auto success : (long[]) {__VA_ARGS__}) \ + /* XXX: EINTR is included in this list to fix g++ */ \ + for (auto success : (long[]) {EINTR, __VA_ARGS__}) \ if (error == success) \ return (decltype(expr)) -success; \ _assert_(false, "errno=%u", error); \ @@ -839,10 +870,12 @@ struct CodeDirectory { 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) { - SHA1(static_cast(data), size, hash); + LDID_SHA1(static_cast(data), size, hash); } struct CodesignAllocation { @@ -1184,6 +1217,7 @@ static size_t put(std::streambuf &output, uint32_t magic, const Blobs &blobs) { return offset; } +#ifndef LDID_NOSMIME class Buffer { private: BIO *bio_; @@ -1297,6 +1331,7 @@ class Signature { return value_; } }; +#endif class NullBuffer : public std::streambuf @@ -1316,22 +1351,22 @@ class HashBuffer : { private: std::vector &hash_; - SHA_CTX context_; + LDID_SHA1_CTX context_; public: HashBuffer(std::vector &hash) : hash_(hash) { - SHA1_Init(&context_); + LDID_SHA1_Init(&context_); } ~HashBuffer() { - hash_.resize(SHA_DIGEST_LENGTH); - SHA1_Final(reinterpret_cast(hash_.data()), &context_); + hash_.resize(LDID_SHA1_DIGEST_LENGTH); + LDID_SHA1_Final(reinterpret_cast(hash_.data()), &context_); } virtual std::streamsize xsputn(const char_type *data, std::streamsize size) { - SHA1_Update(&context_, data, size); + LDID_SHA1_Update(&context_, data, size); return size; } @@ -1386,8 +1421,13 @@ class Split { static void mkdir_p(const std::string &path) { if (path.empty()) return; +#ifdef __WIN32__ + if (_syscall(mkdir(path.c_str()), EEXIST) == -EEXIST) + return; +#else if (_syscall(mkdir(path.c_str(), 0755), EEXIST) == -EEXIST) return; +#endif auto slash(path.rfind('/', path.size() - 1)); if (slash == std::string::npos) return; @@ -1449,7 +1489,7 @@ void Sign(const void *idata, size_t isize, std::streambuf &output, const std::st special = std::max(special, slot.first); uint32_t normal((size + PageSize_ - 1) / PageSize_); - alloc = Align(alloc + (special + normal) * SHA_DIGEST_LENGTH, 16); + 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 { Blobs blobs; @@ -1482,12 +1522,12 @@ void Sign(const void *idata, size_t isize, std::streambuf &output, const std::st CodeDirectory directory; directory.version = Swap(uint32_t(0x00020001)); directory.flags = Swap(uint32_t(0)); - directory.hashOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory) + identifier.size() + 1 + SHA_DIGEST_LENGTH * special)); + directory.hashOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory) + identifier.size() + 1 + LDID_SHA1_DIGEST_LENGTH * special)); directory.identOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory))); directory.nSpecialSlots = Swap(special); directory.codeLimit = Swap(uint32_t(limit)); directory.nCodeSlots = Swap(normal); - directory.hashSize = SHA_DIGEST_LENGTH; + directory.hashSize = LDID_SHA1_DIGEST_LENGTH; directory.hashType = CS_HASHTYPE_SHA1; directory.spare1 = 0x00; directory.pageSize = PageShift_; @@ -1496,8 +1536,8 @@ void Sign(const void *idata, size_t isize, std::streambuf &output, const std::st put(data, identifier.c_str(), identifier.size() + 1); - uint8_t storage[special + normal][SHA_DIGEST_LENGTH]; - uint8_t (*hashes)[SHA_DIGEST_LENGTH] = storage + special; + uint8_t storage[special + normal][LDID_SHA1_DIGEST_LENGTH]; + uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = storage + special; memset(storage, 0, sizeof(*storage) * special); @@ -1522,6 +1562,7 @@ void Sign(const void *idata, size_t isize, std::streambuf &output, const std::st insert(blobs, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY, data); } +#ifndef LDID_NOSMIME if (!key.empty()) { std::stringbuf data; const std::string &sign(blobs[CSSLOT_CODEDIRECTORY]); @@ -1536,6 +1577,7 @@ void Sign(const void *idata, size_t isize, std::streambuf &output, const std::st insert(blobs, CSSLOT_SIGNATURESLOT, CSMAGIC_BLOBWRAPPER, data); } +#endif return put(output, CSMAGIC_EMBEDDED_SIGNATURE, blobs); })); @@ -1578,25 +1620,41 @@ void DiskFolder::Find(const std::string &root, const std::string &base, const Fu if (Starts(name, ".ldid.")) continue; + bool directory; + +#ifdef __WIN32__ + struct stat info; + _syscall(stat(path.c_str(), &info)); + if (false); + else if (S_ISDIR(info.st_mode)) + directory = true; + else if (S_ISREG(info.st_mode)) + directory = false; + else + _assert_(false, "st_mode=%x", info.st_mode); +#else switch (child->d_type) { case DT_DIR: - Find(root, base + name + "/", code); - break; - + directory = true; + break; case DT_REG: - code(base + name, fun([&](const Functor &code) { - std::string access(root + base + name); - _assert_(Open(access, fun([&](std::streambuf &data) { - NullBuffer save; - code(data, save); - })), "open(): %s", access.c_str()); - })); - break; - + directory = false; + break; default: _assert_(false, "d_type=%u", child->d_type); - break; } +#endif + + if (directory) + Find(root, base + name + "/", code); + else + code(base + name, fun([&](const Functor &code) { + std::string access(root + base + name); + _assert_(Open(access, fun([&](std::streambuf &data) { + NullBuffer save; + code(data, save); + })), "open(): %s", access.c_str()); + })); } } @@ -1621,22 +1679,57 @@ void DiskFolder::Find(const std::string &path, const Functor &code) { - return parent_->Save(path_ + path, code); + return parent_.Save(path_ + path, code); } bool SubFolder::Open(const std::string &path, const Functor &code) { - return parent_->Open(path_ + path, code); + return parent_.Open(path_ + path, code); } void SubFolder::Find(const std::string &path, const Functor &)> &)> &code) { - return parent_->Find(path_ + path, code); + return parent_.Find(path_ + path, code); +} + +UnionFolder::UnionFolder(Folder &parent) : + parent_(parent) +{ +} + +void UnionFolder::Save(const std::string &path, const Functor &code) { + return parent_.Save(path, code); +} + +bool UnionFolder::Open(const std::string &path, const Functor &code) { + auto file(files_.find(path)); + if (file == files_.end()) + return parent_.Open(path, code); + + auto &data(file->second); + data.pubseekpos(0, std::ios::in); + code(data); + return true; +} + +void UnionFolder::Find(const std::string &path, const Functor &)> &)> &code) { + parent_.Find(path, fun([&](const std::string &name, const Functor &)> &save) { + if (files_.find(name) == files_.end()) + code(name, save); + })); + + for (auto &file : files_) + code(file.first, fun([&](const Functor &code) { + parent_.Save(file.first, fun([&](std::streambuf &save) { + file.second.pubseekpos(0, std::ios::in); + code(file.second, save); + })); + })); } static size_t copy(std::streambuf &source, std::streambuf &target) { @@ -1652,28 +1745,35 @@ static size_t copy(std::streambuf &source, std::streambuf &target) { return total; } -static PList::Structure *plist(const std::string &data) { - if (!Starts(data, "bplist00")) - return PList::Structure::FromXml(data); - std::vector bytes(data.data(), data.data() + data.size()); - return PList::Structure::FromBin(bytes); +#ifndef LDID_NOPLIST +static plist_t plist(const std::string &data) { + plist_t plist(NULL); + if (Starts(data, "bplist00")) + plist_from_bin(data.data(), data.size(), &plist); + else + plist_from_xml(data.data(), data.size(), &plist); + _assert(plist != NULL); + return plist; } -static void plist_d(std::streambuf &buffer, const Functor &code) { +static void plist_d(std::streambuf &buffer, const Functor &code) { std::stringbuf data; copy(buffer, data); - PList::Structure *structure(plist(data.str())); - _scope({ delete structure; }); - auto dictionary(dynamic_cast(structure)); - _assert(dictionary != NULL); - code(dictionary); + auto node(plist(data.str())); + _scope({ plist_free(node); }); + _assert(plist_get_node_type(node) == PLIST_DICT); + code(node); } -static std::string plist_s(PList::Node *node) { - auto value(dynamic_cast(node)); - _assert(value != NULL); - return value->GetValue(); +static std::string plist_s(plist_t node) { + _assert(node != NULL); + _assert(plist_get_node_type(node) == PLIST_STRING); + char *data; + plist_get_string_val(node, &data); + _scope({ free(data); }); + return data; } +#endif enum Mode { NoMode, @@ -1750,16 +1850,17 @@ struct RuleCode { } }; -std::string Bundle(const std::string &root, Folder &folder, const std::string &key, std::map> &remote) { +#ifndef LDID_NOPLIST +std::string Bundle(const std::string &root, Folder &folder, const std::string &key, std::map> &remote, const std::string &entitlements) { std::string executable; std::string identifier; static const std::string info("Info.plist"); _assert_(folder.Open(info, fun([&](std::streambuf &buffer) { - plist_d(buffer, fun([&](PList::Dictionary *dictionary) { - executable = plist_s(((*dictionary)["CFBundleExecutable"])); - identifier = plist_s(((*dictionary)["CFBundleIdentifier"])); + plist_d(buffer, fun([&](plist_t node) { + executable = plist_s(plist_dict_get_item(node, "CFBundleExecutable")); + identifier = plist_s(plist_dict_get_item(node, "CFBundleIdentifier")); })); })), "open(): Info.plist"); @@ -1771,7 +1872,7 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k static const std::string signature("_CodeSignature/CodeResources"); folder.Open(signature, fun([&](std::streambuf &buffer) { - plist_d(buffer, fun([&](PList::Dictionary *dictionary) { + plist_d(buffer, fun([&](plist_t node) { // XXX: maybe attempt to preserve existing rules })); })); @@ -1810,8 +1911,8 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k if (!nested(name)) return; auto bundle(root + Split(name).dir); - SubFolder subfolder(&folder, bundle); - Bundle(bundle, subfolder, key, local); + SubFolder subfolder(folder, bundle); + Bundle(bundle, subfolder, key, local, ""); })); folder.Find("", fun([&](const std::string &name, const Functor &)> &code) { @@ -1827,13 +1928,15 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k copy(data, proxy); })); - _assert(hash.size() == SHA_DIGEST_LENGTH); + _assert(hash.size() == LDID_SHA1_DIGEST_LENGTH); })); - PList::Dictionary plist; + auto plist(plist_new_dict()); + _scope({ plist_free(plist); }); for (const auto &version : versions) { - PList::Dictionary files; + auto files(plist_new_dict()); + plist_dict_set_item(plist, ("files" + version.first).c_str(), files); for (const auto &rule : version.second) rule.Compile(); @@ -1842,22 +1945,21 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k for (const auto &rule : version.second) if (rule(hash.first)) { if (rule.mode_ == NoMode) - files.Set(hash.first, PList::Data(hash.second)); + plist_dict_set_item(files, hash.first.c_str(), plist_new_data(hash.second.data(), hash.second.size())); else if (rule.mode_ == OptionalMode) { - PList::Dictionary entry; - entry.Set("hash", PList::Data(hash.second)); - entry.Set("optional", PList::Boolean(true)); - files.Set(hash.first, entry); + auto entry(plist_new_dict()); + plist_dict_set_item(entry, "hash", plist_new_data(hash.second.data(), hash.second.size())); + plist_dict_set_item(entry, "optional", plist_new_bool(true)); + plist_dict_set_item(files, hash.first.c_str(), entry); } break; } - - plist.Set("files" + version.first, files); } for (const auto &version : versions) { - PList::Dictionary rules; + auto rules(plist_new_dict()); + plist_dict_set_item(plist, ("rules" + version.first).c_str(), rules); std::multiset ordered; for (const auto &rule : version.second) @@ -1865,42 +1967,42 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k for (const auto &rule : ordered) if (rule->weight_ == 1 && rule->mode_ == NoMode) - rules.Set(rule->code_, PList::Boolean(true)); + plist_dict_set_item(rules, rule->code_.c_str(), plist_new_bool(true)); else { - PList::Dictionary entry; + auto entry(plist_new_dict()); + plist_dict_set_item(rules, rule->code_.c_str(), entry); switch (rule->mode_) { case NoMode: break; case OmitMode: - entry.Set("omit", PList::Boolean(true)); + plist_dict_set_item(entry, "omit", plist_new_bool(true)); break; case OptionalMode: - entry.Set("optional", PList::Boolean(true)); + plist_dict_set_item(entry, "optional", plist_new_bool(true)); break; case NestedMode: - entry.Set("nested", PList::Boolean(true)); + plist_dict_set_item(entry, "nested", plist_new_bool(true)); break; case TopMode: - entry.Set("top", PList::Boolean(true)); + plist_dict_set_item(entry, "top", plist_new_bool(true)); break; } if (rule->weight_ >= 10000) - entry.Set("weight", PList::Integer(rule->weight_)); + plist_dict_set_item(entry, "weight", plist_new_uint(rule->weight_)); else if (rule->weight_ != 1) - entry.Set("weight", PList::Real(rule->weight_)); - - rules.Set(rule->code_, entry); + plist_dict_set_item(entry, "weight", plist_new_real(rule->weight_)); } - - plist.Set("rules" + version.first, rules); } folder.Save(signature, fun([&](std::streambuf &save) { HashProxy proxy(local[signature], save); - auto xml(plist.ToXml()); - put(proxy, xml.data(), xml.size()); + char *xml(NULL); + uint32_t size; + plist_to_xml(plist, &xml, &size); + _scope({ free(xml); }); + put(proxy, xml, size); })); folder.Open(executable, fun([&](std::streambuf &buffer) { @@ -1915,7 +2017,7 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k slots[3] = local.at(signature); HashProxy proxy(local[executable], save); - Sign(data.data(), data.size(), proxy, identifier, "", key, slots); + Sign(data.data(), data.size(), proxy, identifier, entitlements, key, slots); })); })); @@ -1924,11 +2026,14 @@ std::string Bundle(const std::string &root, Folder &folder, const std::string &k return executable; } +#endif } int main(int argc, char *argv[]) { +#ifndef LDID_NOSMIME OpenSSL_add_all_algorithms(); +#endif union { uint16_t word; @@ -1940,7 +2045,9 @@ int main(int argc, char *argv[]) { bool flag_r(false); bool flag_e(false); +#ifndef LDID_NOFLAGT bool flag_T(false); +#endif bool flag_S(false); bool flag_s(false); @@ -1955,8 +2062,10 @@ int main(int argc, char *argv[]) { const char *flag_I(NULL); +#ifndef LDID_NOFLAGT bool timeh(false); uint32_t timev(0); +#endif Map entitlements; Map key; @@ -1993,7 +2102,7 @@ int main(int argc, char *argv[]) { unsigned number(strtoul(slot, &arge, 0)); _assert(arge == colon); std::vector &hash(slots[number]); - hash.resize(SHA_DIGEST_LENGTH); + hash.resize(LDID_SHA1_DIGEST_LENGTH); sha1(reinterpret_cast(hash.data()), file.data(), file.size()); } break; @@ -2036,6 +2145,7 @@ int main(int argc, char *argv[]) { key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE); break; +#ifndef LDID_NOFLAGT case 'T': { flag_T = true; if (argv[argi][2] == '-') @@ -2046,6 +2156,7 @@ int main(int argc, char *argv[]) { _assert(arge == argv[argi] + strlen(argv[argi])); } } break; +#endif case 'I': { flag_I = argv[argi] + 2; @@ -2071,10 +2182,14 @@ int main(int argc, char *argv[]) { _syscall(stat(path.c_str(), &info)); if (S_ISDIR(info.st_mode)) { +#ifndef LDID_NOPLIST _assert(!flag_r); ldid::DiskFolder folder(path); std::map> hashes; - path += "/" + Bundle("", folder, key, 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); @@ -2092,7 +2207,15 @@ int main(int argc, char *argv[]) { 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()) { @@ -2117,6 +2240,7 @@ int main(int argc, char *argv[]) { signature = reinterpret_cast(load_command); else if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64) encryption = reinterpret_cast(load_command); +#ifndef LDID_NOFLAGT else if (cmd == LC_ID_DYLIB) { volatile struct dylib_command *dylib_command(reinterpret_cast(load_command)); @@ -2133,6 +2257,7 @@ int main(int argc, char *argv[]) { dylib_command->dylib.timestamp = mach_header.Swap(timed); } } +#endif } if (flag_D) { @@ -2171,7 +2296,7 @@ int main(int argc, char *argv[]) { uint32_t begin = Swap(super->index[index].offset); struct CodeDirectory *directory = reinterpret_cast(blob + begin); - uint8_t (*hashes)[SHA_DIGEST_LENGTH] = reinterpret_cast(blob + begin + Swap(directory->hashOffset)); + uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = reinterpret_cast(blob + begin + Swap(directory->hashOffset)); uint32_t pages = Swap(directory->nCodeSlots); if (pages != 1)