* Copyright (C) 2007-2015 Jay Freeman (saurik)
*/
+/* SPDX-License-Identifier: AGPL-3.0-only */
/* GNU Affero General Public License, Version 3 {{{ */
/*
* This program is free software: you can redistribute it and/or modify
#include <sys/stat.h>
#include <sys/types.h>
-#ifndef LDID_NOSMIME
#include <openssl/opensslv.h>
-# if OPENSSL_VERSION_NUMBER >= 0x30000000
+# if OPENSSL_VERSION_MAJOR >= 3
# include <openssl/provider.h>
# endif
#include <openssl/err.h>
#include <openssl/pem.h>
+#include <openssl/cms.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
#include <openssl/ui.h>
-#endif
-#ifdef __APPLE__
-#include <CommonCrypto/CommonDigest.h>
-
-#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
-
-#define LDID_SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH
-#define LDID_SHA256 CC_SHA256
-#define LDID_SHA256_CTX CC_SHA256_CTX
-#define LDID_SHA256_Init CC_SHA256_Init
-#define LDID_SHA256_Update CC_SHA256_Update
-#define LDID_SHA256_Final CC_SHA256_Final
-#else
#include <openssl/sha.h>
#define LDID_SHA1_DIGEST_LENGTH SHA_DIGEST_LENGTH
#define LDID_SHA256_Init SHA256_Init
#define LDID_SHA256_Update SHA256_Update
#define LDID_SHA256_Final SHA256_Final
-#endif
-#ifndef LDID_NOPLIST
#include <plist/plist.h>
-#elif __APPLE__
-#include <CoreFoundation/CoreFoundation.h>
-#endif
#include "ldid.hpp"
+#include "machine.h"
+
#define _assert___(line) \
#line
#define _assert__(line) \
for (auto success : (long[]) {EINTR, __VA_ARGS__}) \
if (error == success) \
return (decltype(expr)) -success; \
- _assert_(false, "errno=%u", error); \
+ fprintf(stderr, "ldid: %s: %s\n", __func__, strerror(error)); \
+ exit(1); \
} }()
#define _trace() \
#define _packed \
__attribute__((packed))
-#ifndef LDID_NOSMIME
std::string password;
-#endif
+std::vector<std::string> cleanup;
template <typename Type_>
struct Iterator_ {
#define _scope(function) \
_scope_(__COUNTER__, function)
-#define CPU_ARCH_MASK uint32_t(0xff000000)
-#define CPU_ARCH_ABI64 uint32_t(0x01000000)
-#define CPU_ARCH_ABI64_32 uint32_t(0x02000000)
-
-#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)
-#define CPU_TYPE_ARM64_32 (CPU_TYPE_ARM | CPU_ARCH_ABI64_32)
-
struct fat_header {
uint32_t magic;
uint32_t nfat_arch;
put(stream, padding, size);
}
+/*
+ * Heavily based on zsign's _GenerateASN1Type(): https://github.com/zhlynn/zsign/blob/44f15cae53e4a5a000fa7486dd72f472a4c75ee4/openssl.cpp#L116
+ * SPDX-License-Identifier: BSD-3-Clause OR AGPL-3.0-only
+ */
+static ASN1_TYPE *GenerateASN1Type(const std::string &value)
+{
+ std::string asn1String = "asn1=SEQUENCE:A\n[A]\nC=OBJECT:sha256\nB=FORMAT:HEX,OCT:" + value + "\n";
+
+ BIO *bio = BIO_new(BIO_s_mem());
+ BIO_puts(bio, asn1String.c_str());
+ _scope({ BIO_free(bio); });
+
+ CONF *conf = NCONF_new(NULL);
+ _scope({ NCONF_free(conf); });
+
+ long line = -1;
+ int result = NCONF_load_bio(conf, bio, &line);
+ if (result <= 0)
+ {
+ printf("Error generating ASN1 Type: %d (Line %ld)\n", result, line);
+ ERR_print_errors_fp(stdout);
+ return NULL;
+ }
+
+ char *string = NCONF_get_string(conf, "default", "asn1");
+ if (string == NULL)
+ {
+ ERR_print_errors_fp(stdout);
+ return NULL;
+ }
+
+ ASN1_TYPE *type = ASN1_generate_nconf(string, conf);
+ return type;
+}
+
template <typename Type_>
Type_ Align(Type_ value, size_t align) {
value += align - 1;
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: {
} break;
case PLIST_REAL: {
- _assert(false);
+ fprintf(stderr, "ldid: Invalid plist entry type\n");
+ exit(1);
} break;
case PLIST_DATE: {
- _assert(false);
+ fprintf(stderr, "ldid: Invalid plist entry type\n");
+ exit(1);
} break;
case PLIST_DATA: {
} break;
default: {
- _assert_(false, "unsupported plist type %d", type);
+ fprintf(stderr, "ldid: Unsupported plist type %d", type);
+ exit(1);
} break;
}
}
-#endif
static inline uint16_t Swap_(uint16_t value) {
return
break;
default:
- _assert(false);
+ fprintf(stderr, "ldid: Unknown header magic\nAre you sure that is a Mach-O?\n");
+ exit(1);
}
void *post = mach_header_ + 1;
post = (uint32_t *) post + 1;
load_command_ = (struct load_command *) post;
- _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
- );
+ if (Swap(mach_header_->filetype) != MH_EXECUTE &&
+ Swap(mach_header_->filetype) != MH_DYLIB &&
+ Swap(mach_header_->filetype) != MH_DYLINKER &&
+ Swap(mach_header_->filetype) != MH_BUNDLE) {
+ fprintf(stderr, "ldid: Unsupported Mach-O type\n");
+ exit(1);
+ }
}
bool Bits64() const {
#define APPLE_ADS_OID APPLE_OID, 0x64
#define APPLE_EXTENSION_OID APPLE_ADS_OID, 6
-#ifndef LDID_NOFLAGT
-extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
-#endif
struct Algorithm {
size_t size_;
static std::vector<Algorithm *> algorithms;
if (algorithms.empty()) {
- if (do_sha256)
- algorithms.push_back(&sha256);
if (do_sha1)
algorithms.push_back(&sha1);
+ if (do_sha256)
+ algorithms.push_back(&sha256);
}
return algorithms;
}
void open(const char *path, int flags) {
- _assert(file_ == -1);
- file_ = _syscall(::open(path, flags));
+ file_ = ::open(path, flags);
+ if (file_ == -1) {
+ fprintf(stderr, "ldid: %s: %s\n", path, strerror(errno));
+ exit(1);
+ }
}
int file() const {
void *data_;
size_t size_;
- void clear() {
- if (data_ == NULL)
- return;
- _syscall(munmap(data_, size_));
- data_ = NULL;
- size_ = 0;
- }
-
public:
Map() :
data_(NULL),
open(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
}
+ void clear() {
+ if (data_ == NULL)
+ return;
+ _syscall(munmap(data_, size_));
+ data_ = NULL;
+ size_ = 0;
+ }
+
void *data() const {
return data_;
}
return std::string(static_cast<char *>(data_), size_);
}
};
-#endif
+#endif // LDID_NOTOOLS
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())
return offset;
}
-#ifndef LDID_NOSMIME
class Buffer {
private:
BIO *bio_;
Buffer(PKCS7 *pkcs) :
Buffer()
{
- _assert(i2d_PKCS7_bio(bio_, pkcs) != 0);
+ if(i2d_PKCS7_bio(bio_, pkcs) == 0){
+ fprintf(stderr, "ldid: An error occured while getting the PKCS12 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ }
+
+ Buffer(CMS_ContentInfo *cms) :
+ Buffer()
+ {
+ _assert(i2d_CMS_bio(bio_, cms) != 0);
}
+
~Buffer() {
BIO_free_all(bio_);
}
value_(d2i_PKCS12_bio(bio, NULL)),
ca_(NULL)
{
- _assert(value_ != NULL);
+ if(value_ == NULL){
+ fprintf(stderr, "ldid: An error occured while getting the PKCS12 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
if (!PKCS12_verify_mac(value_, "", 0) && password.empty()) {
char passbuf[2048];
password = passbuf;
}
- _assert(PKCS12_parse(value_, password.c_str(), &key_, &cert_, &ca_) != 0);
- _assert(key_ != NULL);
- _assert(cert_ != NULL);
+ if(PKCS12_parse(value_, password.c_str(), &key_, &cert_, &ca_) <= 0){
+ fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ if(key_ == NULL || cert_ == NULL){
+ fprintf(stderr, "ldid: An error occured while parsing: %s\nYour p12 cert might not be valid\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
if (ca_ == NULL)
ca_ = sk_X509_new_null();
- _assert(ca_ != NULL);
+ if(ca_ == NULL){
+ fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
}
Stuff(const std::string &data) :
class Signature {
private:
- PKCS7 *value_;
+ CMS_ContentInfo *value_;
public:
- Signature(const Stuff &stuff, const Buffer &data, const std::string &xml) {
- value_ = PKCS7_new();
- _assert(value_ != NULL);
+ Signature(const Stuff &stuff, const Buffer &data, const std::string &xml,const std::vector<char>& alternateCDSHA256) {
+ //
+ int flags = CMS_PARTIAL | CMS_DETACHED | CMS_NOSMIMECAP | CMS_BINARY;
+ //--------------------------------------------
+ auto issuer_name(X509_get_issuer_name(stuff));
+ _assert(issuer_name != NULL);
+ std::string issuer;
+ auto index(X509_NAME_get_index_by_NID(issuer_name, NID_commonName, -1));
+ _assert(index >= 0);
+ auto next(X509_NAME_get_index_by_NID(issuer_name, NID_commonName, index));
+ _assert(next == -1);
+ auto entry(X509_NAME_get_entry(issuer_name, index));
+ _assert(entry != NULL);
+ auto asn(X509_NAME_ENTRY_get_data(entry));
+ _assert(asn != NULL);
+ issuer.assign(reinterpret_cast<const char *>(ASN1_STRING_get0_data(asn)), ASN1_STRING_length(asn));
+
+ CMS_ContentInfo *stream = CMS_sign(NULL, NULL, stuff, NULL, flags);
+
+ CMS_SignerInfo *info = CMS_add1_signer(stream, stuff, stuff, EVP_sha256(), flags);
+
+
+ // Hash Agility
+ ASN1_OBJECT *obj = OBJ_txt2obj("1.2.840.113635.100.9.1", 1);
+ CMS_signed_add1_attr_by_OBJ(info, obj, 0x4, xml.c_str(), (int)xml.size());
+
+ // CDHashes (iOS 15.1+)
+ std::string sha256;
+ for (size_t i = 0; i < alternateCDSHA256.size(); i++)
+ {
+ char buf[16] = {0};
+ sprintf(buf, "%02X", (uint8_t)alternateCDSHA256[i]);
+ sha256 += buf;
+ }
- _assert(PKCS7_set_type(value_, NID_pkcs7_signed));
- _assert(PKCS7_content_new(value_, NID_pkcs7_data));
+ X509_ATTRIBUTE *attribute = X509_ATTRIBUTE_new();
- STACK_OF(X509) *certs(stuff);
- for (unsigned i(0), e(sk_X509_num(certs)); i != e; i++)
- _assert(PKCS7_add_certificate(value_, sk_X509_value(certs, e - i - 1)));
+ ASN1_OBJECT *obj2 = OBJ_txt2obj("1.2.840.113635.100.9.2", 1);
+ X509_ATTRIBUTE_set1_object(attribute, obj2);
- // 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)));
+ ASN1_TYPE *type256 = GenerateASN1Type(sha256);
+ if (type256 != NULL)
+ {
+ X509_ATTRIBUTE_set1_data(attribute, V_ASN1_SEQUENCE, type256->value.asn1_string->data, type256->value.asn1_string->length);
+ }
- PKCS7_set_detached(value_, 1);
+ CMS_signed_add1_attr(info, attribute);
- ASN1_OCTET_STRING *string(ASN1_OCTET_STRING_new());
- _assert(string != NULL);
- try {
- _assert(ASN1_STRING_set(string, xml.data(), xml.size()));
+ CMS_final(stream, data, NULL, flags);
- 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;
- }
- // 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));
+ value_ = stream;
+ _assert(value_ != NULL);
}
~Signature() {
- PKCS7_free(value_);
+ CMS_ContentInfo_free(value_);
}
-
- operator PKCS7 *() const {
+ operator CMS_ContentInfo *() const {
return value_;
}
};
-#endif
class NullBuffer :
public std::streambuf
std::string temp(split.dir + ".ldid." + split.base);
mkdir_p(split.dir);
_assert_(file.open(temp.c_str(), std::ios::out | std::ios::trunc | std::ios::binary) == &file, "open(): %s", temp.c_str());
+ cleanup.push_back(temp);
return temp;
}
}
_syscall(rename(temp.c_str(), path.c_str()));
+ cleanup.erase(std::remove(cleanup.begin(), cleanup.end(), temp), cleanup.end());
}
-#endif
+#endif // LDID_NOTOOLS
namespace ldid {
-#ifndef LDID_NOSMIME
static void get(std::string &value, X509_NAME *name, int nid) {
auto index(X509_NAME_get_index_by_NID(name, nid, -1));
_assert(index >= 0);
_assert(asn != NULL);
value.assign(reinterpret_cast<const char *>(ASN1_STRING_get0_data(asn)), ASN1_STRING_length(asn));
}
-#endif
static void req(std::streambuf &buffer, uint32_t value) {
value = Swap(value);
std::string team;
std::string common;
-#ifndef LDID_NOSMIME
if (!key.empty()) {
Stuff stuff(key);
auto name(X509_get_subject_name(stuff));
- _assert(name != NULL);
+ if(name == NULL){
+ fprintf(stderr, "ldid: Your certificate might not be valid: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
get(team, name, NID_organizationalUnitName);
get(common, name, NID_commonName);
}
-#endif
std::stringbuf backing;
alloc += sizeof(struct BlobIndex);
alloc += backing.str().size();
-#ifdef LDID_NOPLIST
- baton.entitlements_ = entitlements;
-#else
if (merge)
Analyze(mach_header, fun([&](const char *data, size_t size) {
baton.entitlements_.assign(data, size);
baton.entitlements_.assign(xml, size);
}
-#endif
if (!baton.entitlements_.empty()) {
special = std::max(special, CSSLOT_ENTITLEMENTS);
for (Algorithm *algorithm : GetAlgorithms())
alloc = Align(alloc + directory + (special + normal) * algorithm->size_, 16);
-#ifndef LDID_NOSMIME
if (!key.empty()) {
alloc += sizeof(struct BlobIndex);
alloc += sizeof(struct Blob);
alloc += certificate;
}
-#endif
return alloc;
}), fun([&](const MachHeader &mach_header, const Baton &baton, std::streambuf &output, size_t limit, size_t left, size_t right, const std::string &overlap, const char *top, const Progress &progress) -> size_t {
put(data, baton.entitlements_.data(), baton.entitlements_.size());
insert(blobs, CSSLOT_ENTITLEMENTS, CSMAGIC_EMBEDDED_ENTITLEMENTS, data);
-#ifndef LDID_NOPLIST
auto entitlements(plist(baton.entitlements_));
_scope({ plist_free(entitlements); });
- _assert(plist_get_node_type(entitlements) == PLIST_DICT);
+ if (plist_get_node_type(entitlements) != PLIST_DICT) {
+ fprintf(stderr, "ldid: Entitlements should be a plist dicionary\n");
+ exit(1);
+ }
const auto entitled([&](const char *key) {
auto item(plist_dict_get_item(entitlements, key));
execs |= kSecCodeExecSegCanLoadCdHash;
if (entitled("com.apple.private.amfi.can-execute-cdhash"))
execs |= kSecCodeExecSegCanExecCdHash;
-#endif
}
if (!baton.derformat_.empty()) {
++total;
}
-#ifndef LDID_NOSMIME
if (!key.empty()) {
-#ifdef LDID_NOPLIST
- auto plist(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
- _scope({ CFRelease(plist); });
-
- auto cdhashes(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
- _scope({ CFRelease(cdhashes); });
-
- CFDictionarySetValue(plist, CFSTR("cdhashes"), cdhashes);
-#else
auto plist(plist_new_dict());
_scope({ plist_free(plist); });
auto cdhashes(plist_new_array());
plist_dict_set_item(plist, "cdhashes", cdhashes);
-#endif
+
+ std::vector<char> alternateCDSHA256;
unsigned total(0);
for (Algorithm *pointer : GetAlgorithms()) {
algorithm(hash, blob.data(), blob.size());
hash.resize(20);
-#ifdef LDID_NOPLIST
- auto value(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(hash.data()), hash.size()));
- _scope({ CFRelease(value); });
- CFArrayAppendValue(cdhashes, value);
-#else
+ if (algorithm.type_ == CS_HASHTYPE_SHA256_256){
+ alternateCDSHA256 = hash;
+ }
+
+
plist_array_append_item(cdhashes, plist_new_data(hash.data(), hash.size()));
-#endif
}
-#ifdef LDID_NOPLIST
- auto created(CFPropertyListCreateXMLData(kCFAllocatorDefault, plist));
- _scope({ CFRelease(created); });
- auto xml(reinterpret_cast<const char *>(CFDataGetBytePtr(created)));
- auto size(CFDataGetLength(created));
-#else
char *xml(NULL);
uint32_t size;
plist_to_xml(plist, &xml, &size);
_scope({ free(xml); });
-#endif
std::stringbuf data;
const std::string &sign(blobs[CSSLOT_CODEDIRECTORY]);
Stuff stuff(key);
Buffer bio(sign);
- Signature signature(stuff, sign, std::string(xml, size));
+ Signature signature(stuff, sign, std::string(xml, size), alternateCDSHA256);
Buffer result(signature);
std::string value(result);
put(data, value.data(), value.size());
const auto &save(insert(blobs, CSSLOT_SIGNATURESLOT, CSMAGIC_BLOBWRAPPER, data));
_assert(save.size() <= certificate);
}
-#endif
return put(output, CSMAGIC_EMBEDDED_SIGNATURE, blobs);
}), progress);
void DiskFolder::Find(const std::string &path, const Functor<void (const std::string &)> &code, const Functor<void (const std::string &, const Functor<std::string ()> &)> &link) const {
Find(path, "", code, link);
}
-#endif
+#endif // LDID_NOTOOLS
SubFolder::SubFolder(Folder &parent, const std::string &path) :
parent_(parent),
}
}
-#ifndef LDID_NOPLIST
static plist_t plist(const std::string &data) {
if (data.empty())
return plist_new_dict();
_scope({ free(data); });
return data;
}
-#endif
enum Mode {
NoMode,
}
};
-#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, 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;
}
};
-Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &remote, const std::string &requirements, const Functor<std::string (const std::string &, const std::string &)> &alter, const Progress &progress) {
+Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &local, const std::string &requirements, const Functor<std::string (const std::string &, const std::string &)> &alter, const Progress &progress) {
std::string executable;
std::string identifier;
else if (parent.Look("Resources/" + info)) {
info = "Resources/" + info;
return "";
- } else _assert_(false, "cannot find Info.plist");
+ } else {
+ fprintf(stderr, "ldid: Could not find Info.plist\n");
+ exit(1);
+ }
}());
folder.Open(info, fun([&](std::streambuf &buffer, size_t length, const void *flag) {
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<std::string, Bundle> bundles;
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<const Functor<std::string (const std::string &, const std::string &)> &>(fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }))
, progress);
+ local.Merge(bundle, remote);
}), fun([&](const std::string &name, const Functor<std::string ()> &read) {
}));
}));
}));
- remote.Merge(root, local);
return bundle;
}
State local;
return Sign(root, folder, key, local, requirements, alter, progress);
}
-#endif
#endif
}
static void usage(const char *argv0) {
fprintf(stderr, "Link Identity Editor %s\n\n", LDID_VERSION);
- fprintf(stderr, "usage: %s [-Acputype:subtype] [-a]\n", argv0);
- fprintf(stderr, " [-C[adhoc | enforcement | expires | hard |\n");
- fprintf(stderr, " host | kill | library-validation | restrict | runtime]] [-D] [-d]\n");
- fprintf(stderr, " [-e] [-h] [-Kkey.p12 [-Upassword]] [-M] [-P] [-q] [-r | -Sfile | -s]\n");
- fprintf(stderr, " [-Ttimestamp] [-u] file ...\n\n");
+ fprintf(stderr, "Usage: %s [-Acputype:subtype] [-a] [-C[adhoc | enforcement | expires | hard |\n", argv0);
+ fprintf(stderr, " host | kill | library-validation | restrict | runtime]] [-D] [-d]\n");
+ fprintf(stderr, " [-Enum:file] [-e] [-H[sha1 | sha256]] [-h] [-Iname]\n");
+ fprintf(stderr, " [-Kkey.p12 [-Upassword]] [-M] [-P] [-Qrequirements.xml] [-q]\n");
+ fprintf(stderr, " [-r | -Sfile.xml | -s] [-Ttimestamp] [-u] [-arch arch_type] file ...\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -S[file.xml] Pseudo-sign using the entitlements in file.xml\n");
fprintf(stderr, " -Kkey.p12 Sign using private key in key.p12\n");
fprintf(stderr, "More information: 'man ldid'\n");
}
+void cleanupfunc(void) {
+ for (const auto &temp : cleanup)
+ remove(temp.c_str());
+}
+
#ifndef LDID_NOTOOLS
int main(int argc, char *argv[]) {
-#ifndef LDID_NOSMIME
+ std::atexit(cleanupfunc);
OpenSSL_add_all_algorithms();
-# if OPENSSL_VERSION_NUMBER >= 0x30000000
+# if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER *legacy = OSSL_PROVIDER_load(NULL, "legacy");
OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(NULL, "default");
# endif
-#endif
union {
uint16_t word;
bool flag_H(false);
bool flag_h(false);
-#ifndef LDID_NOFLAGT
- bool flag_T(false);
-#endif
bool flag_S(false);
bool flag_s(false);
const char *flag_I(NULL);
-#ifndef LDID_NOFLAGT
- bool timeh(false);
- uint32_t timev(0);
-#endif
Map entitlements;
Map requirements;
for (int argi(1); argi != argc; ++argi)
if (argv[argi][0] != '-')
files.push_back(argv[argi]);
- else switch (argv[argi][1]) {
+ else if (strcmp(argv[argi], "-arch") == 0) {
+ bool foundarch = false;
+ flag_A = true;
+ argi++;
+ if (argi == argc) {
+ fprintf(stderr, "ldid: -arch must be followed by an architecture string\n");
+ exit(1);
+ }
+ for (int i = 0; archs[i].name != NULL; i++) {
+ if (strcmp(archs[i].name, argv[argi]) == 0) {
+ flag_CPUType = archs[i].cputype;
+ flag_CPUSubtype = archs[i].cpusubtype;
+ foundarch = true;
+ }
+ if (foundarch)
+ break;
+ }
+
+ if (!foundarch) {
+ fprintf(stderr, "error: unknown architecture specification flag: -arch %s\n", argv[argi]);
+ exit(1);
+ }
+ } else switch (argv[argi][1]) {
case 'r':
- _assert(!flag_s);
- _assert(!flag_S);
+ if (flag_s || flag_S) {
+ fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
+ exit(1);
+ }
flag_r = true;
break;
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);
do_sha1 = true;
else if (strcmp(hash, "sha256") == 0)
do_sha256 = true;
- else _assert(false);
+ else {
+ fprintf(stderr, "ldid: only sha1 and sha256 are supported at this time\n");
+ exit(1);
+ }
} break;
case 'h': flag_h = true; break;
case 'a': flag_a = true; break;
case 'A':
- _assert(!flag_A);
+ if (flag_A) {
+ fprintf(stderr, "ldid: -A can only be specified once\n");
+ exit(1);
+ }
flag_A = true;
if (argv[argi][2] != '\0') {
const char *cpu = argv[argi] + 2;
flags |= kSecCodeSignatureLibraryValidation;
else if (strcmp(name, "runtime") == 0)
flags |= kSecCodeSignatureRuntime;
- else _assert(false);
+ else {
+ fprintf(stderr, "ldid: -C: Unsupported option\n");
+ exit(1);
+ }
} break;
case 'P':
break;
case 's':
- _assert(!flag_r);
- _assert(!flag_S);
+ if (flag_r || flag_S) {
+ fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
+ exit(1);
+ }
flag_s = true;
+ entitlements.clear();
+ flag_M = true;
break;
case 'S':
- _assert(!flag_r);
- _assert(!flag_s);
+ if (flag_r || flag_s) {
+ fprintf(stderr, "ldid: Can only specify one of -r, -S, -s\n");
+ exit(1);
+ }
flag_S = true;
if (argv[argi][2] != '\0') {
const char *xml = argv[argi] + 2;
key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE);
break;
-#ifndef LDID_NOFLAGT
- case 'T': {
- flag_T = true;
- if (argv[argi][2] == '-')
- timeh = true;
- else {
- char *arge;
- timev = strtoul(argv[argi] + 2, &arge, 0);
- _assert(arge == argv[argi] + strlen(argv[argi]));
- }
- } break;
-#endif
case 'u': {
flag_u = true;
}
_assert(flag_S || key.empty());
- _assert(flag_S || flag_I == NULL);
+ if (flag_I != NULL && !flag_S) {
+ fprintf(stderr, "ldid: -I requires -S\n");
+ exit(1);
+ }
if (flag_d && !flag_h) {
flag_h = true;
std::string path(file);
struct stat info;
- _syscall(stat(path.c_str(), &info));
+ if (stat(path.c_str(), &info) == -1) {
+ fprintf(stderr, "ldid: %s: %s\n", path.c_str(), strerror(errno));
+ exit(1);
+ }
if (S_ISDIR(info.st_mode)) {
- _assert(flag_S);
-#ifndef LDID_NOPLIST
+ if (!flag_S) {
+ fprintf(stderr, "ldid: Only -S can be used on directories\n");
+ exit(1);
+ }
ldid::DiskFolder folder(path + "/");
path += "/" + Sign("", folder, key, requirements, ldid::fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }), dummy_).path;
-#else
- _assert(false);
-#endif
} else if (flag_S || flag_r) {
Map input(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
Commit(path, temp);
}
- bool modify(false);
-#ifndef LDID_NOFLAGT
- if (flag_T)
- modify = true;
-#endif
- if (flag_s)
- modify = true;
-
- Map mapping(path, modify);
+ Map mapping(path, false);
FatHeader fat_header(mapping.data(), mapping.size());
_foreach (mach_header, fat_header.GetMachHeaders()) {
}
}
}
-#ifndef LDID_NOFLAGT
- else if (cmd == LC_ID_DYLIB) {
- volatile struct dylib_command *dylib_command(reinterpret_cast<struct dylib_command *>(load_command));
-
- if (flag_T) {
- uint32_t timed;
-
- if (!timeh)
- timed = timev;
- else {
- dylib_command->dylib.timestamp = 0;
- timed = hash(reinterpret_cast<uint8_t *>(mach_header.GetBase()), mach_header.GetSize(), timev);
- }
-
- dylib_command->dylib.timestamp = mach_header.Swap(timed);
- }
- }
-#endif
}
if (flag_d && encryption != NULL) {
encryption->cryptid = mach_header.Swap(0);
}
- if (flag_e) {
- _assert(signature != NULL);
+ if ((flag_e || flag_q || flag_h) && signature == NULL) {
+ fprintf(stderr, "ldid: -e, -q, and -h requre a signed binary\n");
+ exit(1);
+ }
+ if (flag_e) {
uint32_t data = mach_header.Swap(signature->dataoff);
uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
}
if (flag_q) {
- _assert(signature != NULL);
-
uint32_t data = mach_header.Swap(signature->dataoff);
uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
}
}
- if (flag_s) {
- _assert(signature != NULL);
-
- uint32_t data = mach_header.Swap(signature->dataoff);
-
- uint8_t *top = reinterpret_cast<uint8_t *>(mach_header.GetBase());
- uint8_t *blob = top + data;
- struct SuperBlob *super = reinterpret_cast<struct SuperBlob *>(blob);
-
- for (size_t index(0); index != Swap(super->count); ++index)
- if (Swap(super->index[index].type) == CSSLOT_CODEDIRECTORY) {
- uint32_t begin = Swap(super->index[index].offset);
- struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin + sizeof(Blob));
-
- uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = reinterpret_cast<uint8_t (*)[LDID_SHA1_DIGEST_LENGTH]>(blob + begin + Swap(directory->hashOffset));
- uint32_t pages = Swap(directory->nCodeSlots);
-
- if (pages != 1)
- 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]);
- }
- }
-
if (flag_h) {
- _assert(signature != NULL);
-
auto algorithms(GetAlgorithms());
uint32_t data = mach_header.Swap(signature->dataoff);
++filei;
}
-#ifndef LDID_NOSMINE
-# if OPENSSL_VERSION_NUM >= 0x30000000
+# if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER_unload(legacy);
OSSL_PROVIDER_unload(deflt);
# endif
-#endif
return filee;
}
-#endif
+#endif // LDID_NOTOOLS