]> git.saurik.com Git - ldid.git/blobdiff - ldid.cpp
Support signing dyld (sbingner tested this patch).
[ldid.git] / ldid.cpp
index 7d933b6218e73b0a7bc61b54e32ae3c5229d9564..7154e10b418b6df1da663044d972762e3cb39d8e 100644 (file)
--- 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<Algorithm *> &GetAlgorithms() {
     static AlgorithmSHA1 sha1;
     static AlgorithmSHA256 sha256;
 
-    static Algorithm *array[] = {
-        &sha1,
-        &sha256,
-    };
+    static std::vector<Algorithm *> algorithms;
+    if (algorithms.empty()) {
+        if (do_sha1)
+            algorithms.push_back(&sha1);
+        if (do_sha256)
+            algorithms.push_back(&sha256);
+    }
 
-    static std::vector<Algorithm *> 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<void (const char *data, size_t size)> &entitle) {
+    _foreach (load_command, mach_header.GetLoadCommands())
+        if (mach_header.Swap(load_command->cmd) == LC_CODE_SIGNATURE) {
+            auto signature(reinterpret_cast<struct linkedit_data_command *>(load_command));
+            auto offset(mach_header.Swap(signature->dataoff));
+            auto pointer(reinterpret_cast<uint8_t *>(mach_header.GetBase()) + offset);
+            auto super(reinterpret_cast<struct SuperBlob *>(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<struct Blob *>(pointer + begin));
+                    auto writ(Swap(blob->length) - sizeof(*blob));
+                    entitle(reinterpret_cast<char *>(blob + 1), writ);
+                }
+        }
+}
+
 std::string Analyze(const void *data, size_t size) {
     std::string entitlements;
 
     FatHeader fat_header(const_cast<void *>(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<struct linkedit_data_command *>(load_command));
-                auto offset(mach_header.Swap(signature->dataoff));
-                auto pointer(reinterpret_cast<uint8_t *>(mach_header.GetBase()) + offset);
-                auto super(reinterpret_cast<struct SuperBlob *>(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<struct Blob *>(pointer + begin));
-                        auto writ(Swap(blob->length) - sizeof(*blob));
-
-                        if (entitlements.empty())
-                            entitlements.assign(reinterpret_cast<char *>(blob + 1), writ);
-                        else
-                            _assert(entitlements.compare(0, entitlements.size(), reinterpret_cast<char *>(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<size_t (const MachHeader &, size_t)> &allocate, const Functor<size_t (const MachHeader &, std::streambuf &output, size_t, const std::string &, const char *, const Progress &)> &save, const Progress &progress) {
+static void Allocate(const void *idata, size_t isize, std::streambuf &output, const Functor<size_t (const MachHeader &, Baton &, size_t)> &allocate, const Functor<size_t (const MachHeader &, const Baton &, std::streambuf &output, size_t, const std::string &, const char *, const Progress &)> &save, const Progress &progress) {
     FatHeader source(const_cast<void *>(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, progress);
+    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<std::string, Hash> &remote, const std::string &requirements, const Functor<std::string (const std::string &, const std::string &)> &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] <binary>\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<std::string> files;
 
     if (argc == 1) {
-        fprintf(stderr, "usage: %s -S[entitlements.xml] <binary>\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);