X-Git-Url: https://git.saurik.com/ldid.git/blobdiff_plain/98bc517f582c91e6c0577f4dfdff33753f2876e5..refs/heads/master:/ldid.cpp?ds=sidebyside diff --git a/ldid.cpp b/ldid.cpp index f5f1bb5..0769e7e 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -44,6 +44,7 @@ #ifndef LDID_NOSMIME #include +#include #include #include #include @@ -634,6 +635,7 @@ static std::string der(const std::pair &value) { return data.str(); } +#ifndef LDID_NOPLIST static std::string der(plist_t data) { switch (const auto type = plist_get_node_type(data)) { case PLIST_BOOLEAN: { @@ -714,6 +716,7 @@ static std::string der(plist_t data) { } break; } } +#endif static inline uint16_t Swap_(uint16_t value) { return @@ -1155,10 +1158,12 @@ extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval); struct Algorithm { size_t size_; uint8_t type_; + int nid_; - Algorithm(size_t size, uint8_t type) : + Algorithm(size_t size, uint8_t type, int nid) : size_(size), - type_(type) + type_(type), + nid_(nid) { } @@ -1175,7 +1180,7 @@ struct AlgorithmSHA1 : Algorithm { AlgorithmSHA1() : - Algorithm(LDID_SHA1_DIGEST_LENGTH, CS_HASHTYPE_SHA160_160) + Algorithm(LDID_SHA1_DIGEST_LENGTH, CS_HASHTYPE_SHA160_160, NID_sha1) { } @@ -1205,7 +1210,7 @@ struct AlgorithmSHA256 : Algorithm { AlgorithmSHA256() : - Algorithm(LDID_SHA256_DIGEST_LENGTH, CS_HASHTYPE_SHA256_256) + Algorithm(LDID_SHA256_DIGEST_LENGTH, CS_HASHTYPE_SHA256_256, NID_sha256) { } @@ -1823,12 +1828,79 @@ class Stuff { } }; +class Octet { + private: + ASN1_OCTET_STRING *value_; + + public: + Octet() : + value_(ASN1_OCTET_STRING_new()) + { + _assert(value_ != NULL); + } + + Octet(const std::string &value) : + Octet() + { + _assert(ASN1_STRING_set(value_, value.data(), value.size())); + } + + Octet(const uint8_t *data, size_t size) : + Octet() + { + _assert(ASN1_STRING_set(value_, data, size)); + } + + Octet(const Octet &value) = delete; + + ~Octet() { + if (value_ != NULL) + ASN1_OCTET_STRING_free(value_); + } + + void release() { + value_ = NULL; + } + + operator ASN1_OCTET_STRING *() const { + return value_; + } +}; + +typedef struct { + ASN1_OBJECT *algorithm; + ASN1_OCTET_STRING *value; +} APPLE_CDHASH; + +DECLARE_ASN1_FUNCTIONS(APPLE_CDHASH) + +ASN1_NDEF_SEQUENCE(APPLE_CDHASH) = { + ASN1_SIMPLE(APPLE_CDHASH, algorithm, ASN1_OBJECT), + ASN1_SIMPLE(APPLE_CDHASH, value, ASN1_OCTET_STRING), +} ASN1_NDEF_SEQUENCE_END(APPLE_CDHASH) + +IMPLEMENT_ASN1_FUNCTIONS(APPLE_CDHASH) + +typedef struct { + ASN1_OBJECT *object; + STACK_OF(APPLE_CDHASH) *cdhashes; +} APPLE_CDATTR; + +DECLARE_ASN1_FUNCTIONS(APPLE_CDATTR) + +ASN1_NDEF_SEQUENCE(APPLE_CDATTR) = { + ASN1_SIMPLE(APPLE_CDATTR, object, ASN1_OBJECT), + ASN1_SET_OF(APPLE_CDATTR, cdhashes, APPLE_CDHASH), +} ASN1_NDEF_SEQUENCE_END(APPLE_CDATTR) + +IMPLEMENT_ASN1_FUNCTIONS(APPLE_CDATTR) + class Signature { private: PKCS7 *value_; public: - Signature(const Stuff &stuff, const Buffer &data, const std::string &xml) { + Signature(const Stuff &stuff, const Buffer &data, const std::string &xml, const ldid::Hash &hash) { value_ = PKCS7_new(); _assert(value_ != NULL); @@ -1839,27 +1911,73 @@ 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))); + STACK_OF(X509_ATTRIBUTE) *attributes(sk_X509_ATTRIBUTE_new_null()); + _assert(attributes != NULL); + _scope({ sk_X509_ATTRIBUTE_pop_free(attributes, X509_ATTRIBUTE_free); }); + + // 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); + { + auto attribute(X509_ATTRIBUTE_create(NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data))); + _assert(attribute != NULL); + _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0); + } + + { + auto cdattr(APPLE_CDATTR_new()); + _assert(cdattr != NULL); + _scope({ APPLE_CDATTR_free(cdattr); }); - ASN1_OCTET_STRING *string(ASN1_OCTET_STRING_new()); - _assert(string != NULL); - try { - _assert(ASN1_STRING_set(string, xml.data(), xml.size())); + static auto nid(OBJ_create("1.2.840.113635.100.9.2", "apple-2", "Apple 2")); + cdattr->object = OBJ_nid2obj(nid); - static auto nid(OBJ_create("1.2.840.113635.100.9.1", "", "")); - _assert(PKCS7_add_signed_attribute(info, nid, V_ASN1_OCTET_STRING, string)); - } catch (...) { - ASN1_OCTET_STRING_free(string); - throw; + for (Algorithm *pointer : GetAlgorithms()) { + Algorithm &algorithm(*pointer); + APPLE_CDHASH *cdhash(APPLE_CDHASH_new()); + _assert(cdhash != NULL); + _assert(sk_push((_STACK *) cdattr->cdhashes, cdhash) != 0); + cdhash->algorithm = OBJ_nid2obj(algorithm.nid_); + Octet string(algorithm[hash], algorithm.size_); + cdhash->value = string; + string.release(); + } + + // in e20b57270dece66ce2c68aeb5d14dd6d9f3c5d68 OpenSSL removed a "hack" + // in the process, they introduced a useful bug in X509_ATTRIBUTE_set1_data + // however, I don't want to rely on that or detect the bypass before it + // so, instead, I create my own compatible attribute and re-serialize it :/ + + ASN1_STRING *seq(ASN1_STRING_new()); + _assert(seq != NULL); + _scope({ ASN1_STRING_free(seq); }); + seq->length = ASN1_item_i2d((ASN1_VALUE *) cdattr, &seq->data, ASN1_ITEM_rptr(APPLE_CDATTR)); + + X509_ATTRIBUTE *attribute(NULL); + const unsigned char *data(seq->data); + _assert(d2i_X509_ATTRIBUTE(&attribute, &data, seq->length) != 0); + _assert(attribute != NULL); + _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0); + } + + { + // XXX: move the "cdhashes" plist code to here and remove xml argument + + Octet string(xml); + static auto nid(OBJ_create("1.2.840.113635.100.9.1", "apple-1", "Apple 1")); + auto attribute(X509_ATTRIBUTE_create(nid, V_ASN1_OCTET_STRING, string)); + _assert(attribute != NULL); + string.release(); + _assert(sk_X509_ATTRIBUTE_push(attributes, attribute) != 0); } + _assert(PKCS7_set_signed_attributes(info, attributes) != 0); + PKCS7_set_detached(value_, 1); + // XXX: this is the same as PKCS7_final(value_, data, PKCS7_BINARY) BIO *bio(PKCS7_dataInit(value_, NULL)); _assert(bio != NULL); @@ -2354,6 +2472,8 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st plist_dict_set_item(plist, "cdhashes", cdhashes); #endif + ldid::Hash hash; + unsigned total(0); for (Algorithm *pointer : GetAlgorithms()) { Algorithm &algorithm(*pointer); @@ -2362,16 +2482,17 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st const auto &blob(blobs[total == 0 ? CSSLOT_CODEDIRECTORY : CSSLOT_ALTERNATE + total - 1]); ++total; - std::vector hash; algorithm(hash, blob.data(), blob.size()); - hash.resize(20); + + std::vector cdhash(algorithm[hash], algorithm[hash] + algorithm.size_); + cdhash.resize(20); #ifdef LDID_NOPLIST - auto value(CFDataCreate(kCFAllocatorDefault, reinterpret_cast(hash.data()), hash.size())); + auto value(CFDataCreate(kCFAllocatorDefault, reinterpret_cast(cdhash.data()), cdhash.size())); _scope({ CFRelease(value); }); CFArrayAppendValue(cdhashes, value); #else - plist_array_append_item(cdhashes, plist_new_data(hash.data(), hash.size())); + plist_array_append_item(cdhashes, plist_new_data(cdhash.data(), cdhash.size())); #endif } @@ -2393,7 +2514,7 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st Stuff stuff(key); Buffer bio(sign); - Signature signature(stuff, sign, std::string(xml, size)); + Signature signature(stuff, sign, std::string(xml, size), hash); Buffer result(signature); std::string value(result); put(data, value.data(), value.size()); @@ -2501,8 +2622,7 @@ void DiskFolder::Find(const std::string &root, const std::string &base, const Fu void DiskFolder::Save(const std::string &path, bool edit, const void *flag, const Functor &code) { if (!edit) { - // XXX: use nullbuf - std::stringbuf save; + NullBuffer save; code(save); } else { std::filebuf save; @@ -2781,7 +2901,7 @@ struct State { } }; -Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &remote, const std::string &requirements, const Functor &alter, const Progress &progress) { +Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &local, const std::string &requirements, const Functor &alter, const Progress &progress) { std::string executable; std::string identifier; @@ -2860,8 +2980,6 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta rules2.insert(Rule{20, NoMode, "^version\\.plist$"}); } - State local; - std::string failure(mac ? "Contents/|Versions/[^/]*/Resources/" : ""); Expression nested("^(Frameworks/[^/]*\\.framework|PlugIns/[^/]*\\.appex(()|/[^/]*.app))/(" + failure + ")Info\\.plist$"); std::map bundles; @@ -2869,16 +2987,18 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta folder.Find("", fun([&](const std::string &name) { if (!nested(name)) return; - auto bundle(root + Split(name).dir); + auto bundle(Split(name).dir); if (mac) { _assert(!bundle.empty()); bundle = Split(bundle.substr(0, bundle.size() - 1)).dir; } SubFolder subfolder(folder, bundle); - bundles[nested[1]] = Sign(bundle, subfolder, key, local, "", Starts(name, "PlugIns/") ? alter : + State remote; + bundles[nested[1]] = Sign(root + bundle, subfolder, key, remote, "", Starts(name, "PlugIns/") ? alter : static_cast &>(fun([&](const std::string &, const std::string &) -> std::string { return entitlements; })) , progress); + local.Merge(bundle, remote); }), fun([&](const std::string &name, const Functor &read) { })); @@ -3065,7 +3185,6 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta })); })); - remote.Merge(root, local); return bundle; } @@ -3111,6 +3230,7 @@ int main(int argc, char *argv[]) { bool flag_r(false); bool flag_e(false); bool flag_q(false); + bool flag_k(false); bool flag_H(false); bool flag_h(false); @@ -3280,6 +3400,10 @@ int main(int argc, char *argv[]) { flag_M = true; break; + case 'k': + flag_k = true; + break; + case 'K': if (argv[argi][2] != '\0') key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE); @@ -3459,11 +3583,13 @@ int main(int argc, char *argv[]) { if (Swap(super->index[index].type) == CSSLOT_REQUIREMENTS) { uint32_t begin = Swap(super->index[index].offset); struct Blob *requirement = reinterpret_cast(blob + begin); + // XXX: this is obviously wrong. but like, -Q is also wrong?! + // maybe I can fix all of this just by fixing both -q and -Q? fwrite(requirement, 1, Swap(requirement->length), stdout); } } - if (flag_s) { + if (flag_k) { _assert(signature != NULL); uint32_t data = mach_header.Swap(signature->dataoff); @@ -3473,19 +3599,44 @@ int main(int argc, char *argv[]) { struct SuperBlob *super = reinterpret_cast(blob); for (size_t index(0); index != Swap(super->count); ++index) - if (Swap(super->index[index].type) == CSSLOT_CODEDIRECTORY) { + if (Swap(super->index[index].type) == CSSLOT_SIGNATURESLOT) { + uint32_t begin = Swap(super->index[index].offset); + struct Blob *signature = reinterpret_cast(blob + begin); + fwrite(signature + 1, 1, Swap(signature->length) - sizeof(*signature), stdout); + } + } + + if (flag_s) { + _assert(signature != NULL); + + auto algorithms(GetAlgorithms()); + + uint32_t data = mach_header.Swap(signature->dataoff); + + uint8_t *top = reinterpret_cast(mach_header.GetBase()); + uint8_t *blob = top + data; + struct SuperBlob *super = reinterpret_cast(blob); + + for (size_t index(0); index != Swap(super->count); ++index) { + auto type(Swap(super->index[index].type)); + if ((type == CSSLOT_CODEDIRECTORY || type >= CSSLOT_ALTERNATE) && type != CSSLOT_SIGNATURESLOT) { uint32_t begin = Swap(super->index[index].offset); + uint32_t end = index + 1 == Swap(super->count) ? Swap(super->blob.length) : Swap(super->index[index + 1].offset); struct CodeDirectory *directory = reinterpret_cast(blob + begin + sizeof(Blob)); + auto type(directory->hashType); + _assert(type > 0 && type <= algorithms.size()); + auto &algorithm(*algorithms[type - 1]); - uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = reinterpret_cast(blob + begin + Swap(directory->hashOffset)); + uint8_t *hashes = reinterpret_cast(blob + begin + Swap(directory->hashOffset)); uint32_t pages = Swap(directory->nCodeSlots); - if (pages != 1) + if (pages != 0) { for (size_t i = 0; i != pages - 1; ++i) - LDID_SHA1(top + PageSize_ * i, PageSize_, hashes[i]); - if (pages != 0) - LDID_SHA1(top + PageSize_ * (pages - 1), ((data - 1) % PageSize_) + 1, hashes[pages - 1]); + algorithm(hashes + i * algorithm.size_, top + PageSize_ * i, PageSize_); + algorithm(hashes + (pages - 1) * algorithm.size_, top + PageSize_ * (pages - 1), ((data - 1) % PageSize_) + 1); + } } + } } if (flag_h) {