**/
/* }}} */
+#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
#include <sys/types.h>
-#ifndef LDID_NOSMIME
#include <openssl/opensslv.h>
# 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_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
-
-#define LDID_SHA256_DIGEST_LENGTH SHA256_DIGEST_LENGTH
-#define LDID_SHA256 SHA256
-#define LDID_SHA256_CTX SHA256_CTX
-#define LDID_SHA256_Init SHA256_Init
-#define LDID_SHA256_Update SHA256_Update
-#define LDID_SHA256_Final SHA256_Final
-#endif
+#include <openssl/evp.h>
-#ifndef LDID_NOPLIST
#include <plist/plist.h>
-#elif __APPLE__
-#include <CoreFoundation/CoreFoundation.h>
-#endif
#include "ldid.hpp"
#define _packed \
__attribute__((packed))
-#ifndef LDID_NOSMIME
std::string password;
-#endif
+std::vector<std::string> cleanup;
template <typename Type_>
struct Iterator_ {
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;
}
}
-#endif
static inline uint16_t Swap_(uint16_t value) {
return
#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_;
Algorithm
{
AlgorithmSHA1() :
- Algorithm(LDID_SHA1_DIGEST_LENGTH, CS_HASHTYPE_SHA160_160)
+ Algorithm(SHA_DIGEST_LENGTH, CS_HASHTYPE_SHA160_160)
{
}
}
void operator ()(uint8_t *hash, const void *data, size_t size) const {
- LDID_SHA1(static_cast<const uint8_t *>(data), size, hash);
+ SHA1(static_cast<const uint8_t *>(data), size, hash);
}
void operator ()(ldid::Hash &hash, const void *data, size_t size) const {
}
void operator ()(std::vector<char> &hash, const void *data, size_t size) const {
- hash.resize(LDID_SHA1_DIGEST_LENGTH);
+ hash.resize(SHA_DIGEST_LENGTH);
return operator ()(reinterpret_cast<uint8_t *>(hash.data()), data, size);
}
Algorithm
{
AlgorithmSHA256() :
- Algorithm(LDID_SHA256_DIGEST_LENGTH, CS_HASHTYPE_SHA256_256)
+ Algorithm(SHA256_DIGEST_LENGTH, CS_HASHTYPE_SHA256_256)
{
}
}
void operator ()(uint8_t *hash, const void *data, size_t size) const {
- LDID_SHA256(static_cast<const uint8_t *>(data), size, hash);
+ SHA256(static_cast<const uint8_t *>(data), size, hash);
}
void operator ()(ldid::Hash &hash, const void *data, size_t size) const {
}
void operator ()(std::vector<char> &hash, const void *data, size_t size) const {
- hash.resize(LDID_SHA256_DIGEST_LENGTH);
+ hash.resize(SHA256_DIGEST_LENGTH);
return operator ()(reinterpret_cast<uint8_t *>(hash.data()), data, size);
}
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())
if (entitlements.empty())
entitlements.assign(data, size);
else
- _assert(entitlements.compare(0, entitlements.size(), data, size) == 0);
+ if (entitlements.compare(0, entitlements.size(), data, size) != 0) {
+ fprintf(stderr, "ldid: Entitlements do not match\n");
+ exit(1);
+ }
}));
return entitlements;
_foreach (load_command, mach_header.GetLoadCommands()) {
uint32_t cmd(mach_header.Swap(load_command->cmd));
- if (false);
- else if (cmd == LC_CODE_SIGNATURE)
+ if (cmd == LC_CODE_SIGNATURE)
signature = reinterpret_cast<struct linkedit_data_command *>(load_command);
else if (cmd == LC_SYMTAB)
symtab = reinterpret_cast<struct symtab_command *>(load_command);
return offset;
}
-#ifndef LDID_NOSMIME
class Buffer {
private:
BIO *bio_;
Buffer(PKCS7 *pkcs) :
Buffer()
{
- 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));
+ if (i2d_PKCS7_bio(bio_, pkcs) == 0){
+ fprintf(stderr, "ldid: An error occured while getting the PKCS7 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)
{
- if(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);
}
password = passbuf;
}
- if(PKCS12_parse(value_, password.c_str(), &key_, &cert_, &ca_) <= 0){
+ 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){
+ 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();
- if(ca_ == NULL){
+ if (ca_ == NULL){
fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL));
exit(1);
}
}
};
+// xina fix;
+struct SEQUENCE_hash_sha1 {
+ uint8_t SEQUENCE[2] = {0x30, 0x1d}; // size
+ uint8_t OBJECT_IDENTIFIER[7] = {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}; // OBJECT IDENTIFIER 1.3.14.3.2.26 sha1 (OIW)
+ uint8_t hash_size[2] = {0x04, 0x14};
+ char hash[20];
+};
+
+struct SEQUENCE_hash_sha256 {
+ uint8_t SEQUENCE[2] = {0x30, 0x2d}; // size
+ uint8_t OBJECT_IDENTIFIER[11] = {0x06 ,0x09 ,0x60, 0x86, 0x48, 0x01 ,0x65, 0x03, 0x04, 0x02, 0x01}; // 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm)
+ uint8_t hash_size[2] = {0x04, 0x20}; // hash size
+ char hash[32];
+};
+
class Signature {
private:
- CMS_ContentInfo *value_;
+ PKCS7 *value_;
public:
- 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;
+ Signature(const Stuff &stuff, const Buffer &data, const std::string &xml, const std::vector<char>& alternateCDSHA1, const std::vector<char>& alternateCDSHA256) {
+ value_ = PKCS7_new();
+ if (value_ == NULL){
+ fprintf(stderr, "ldid: An error occured while getting creating PKCS7 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
}
- X509_ATTRIBUTE *attribute = X509_ATTRIBUTE_new();
+ if (PKCS7_set_type(value_, NID_pkcs7_signed) == 0 ||
+ PKCS7_content_new(value_, NID_pkcs7_data) == 0) {
+ fprintf(stderr, "ldid: An error occured while getting creating PKCS7 file: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+
+ STACK_OF(X509) *certs(stuff);
+ for (unsigned i(0), e(sk_X509_num(certs)); i != e; i++) {
+ if (PKCS7_add_certificate(value_, sk_X509_value(certs, e - i - 1)) == 0) {
+ fprintf(stderr, "ldid: An error occured while signing: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ }
+
+ auto info(PKCS7_sign_add_signer(value_, stuff, stuff, NULL, PKCS7_NOSMIMECAP));
+ if (info == NULL){
+ fprintf(stderr, "ldid: An error occured while signing: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ X509_ATTRIBUTE *attribute = X509_ATTRIBUTE_new();
ASN1_OBJECT *obj2 = OBJ_txt2obj("1.2.840.113635.100.9.2", 1);
X509_ATTRIBUTE_set1_object(attribute, obj2);
+ if (alternateCDSHA1.size() != 0) {
+ // xina fix;
+ SEQUENCE_hash_sha1 seq1;
+ memcpy((void *)seq1.hash, (void *)alternateCDSHA1.data(), alternateCDSHA1.size());
+ X509_ATTRIBUTE_set1_data(attribute, V_ASN1_SEQUENCE, &seq1, sizeof(seq1));
+ }
+ if (alternateCDSHA256.size() != 0) {
+ // xina fix;
+ SEQUENCE_hash_sha256 seq256;
+ memcpy((void *)seq256.hash, (void *)alternateCDSHA256.data(), alternateCDSHA256.size());
+ X509_ATTRIBUTE_set1_data(attribute, V_ASN1_SEQUENCE, &seq256, sizeof(seq256));
+ }
- 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);
+ STACK_OF(X509_ATTRIBUTE) *sk = PKCS7_get_signed_attributes(info);
+ if (!sk_X509_ATTRIBUTE_push(sk, attribute)) {
+ fprintf(stderr, "ldid: sk_X509_ATTRIBUTE_push failed: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
}
- CMS_signed_add1_attr(info, attribute);
+ PKCS7_set_detached(value_, 1);
- CMS_final(stream, data, NULL, flags);
+ ASN1_OCTET_STRING *string(ASN1_OCTET_STRING_new());
+ if (string == NULL) {
+ fprintf(stderr, "ldid: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ try {
+ if (ASN1_STRING_set(string, xml.data(), xml.size()) == 0) {
+ fprintf(stderr, "ldid: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
- value_ = stream;
- _assert(value_ != NULL);
+ static auto nid(OBJ_create("1.2.840.113635.100.9.1", "", ""));
+ if (PKCS7_add_signed_attribute(info, nid, V_ASN1_OCTET_STRING, string) == 0) {
+ fprintf(stderr, "ldid: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
+ } catch (...) {
+ ASN1_OCTET_STRING_free(string);
+ throw;
+ }
+
+ if (PKCS7_final(value_, data, PKCS7_BINARY) == 0) {
+ fprintf(stderr, "ldid: Failed to sign: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
}
~Signature() {
- CMS_ContentInfo_free(value_);
+ PKCS7_free(value_);
}
- operator CMS_ContentInfo *() const {
+
+ operator PKCS7 *() const {
return value_;
}
};
-#endif
class NullBuffer :
public std::streambuf
private:
ldid::Hash &hash_;
- LDID_SHA1_CTX sha1_;
- LDID_SHA256_CTX sha256_;
+ EVP_MD_CTX *sha1_;
+ EVP_MD_CTX *sha256_;
public:
HashBuffer(ldid::Hash &hash) :
hash_(hash)
{
- LDID_SHA1_Init(&sha1_);
- LDID_SHA256_Init(&sha256_);
+ sha1_ = EVP_MD_CTX_new();
+ sha256_ = EVP_MD_CTX_new();
+
+ EVP_DigestInit_ex(sha1_, EVP_get_digestbyname("sha1"), nullptr);
+ EVP_DigestInit_ex(sha256_, EVP_get_digestbyname("sha256"), nullptr);
}
~HashBuffer() {
- LDID_SHA1_Final(reinterpret_cast<uint8_t *>(hash_.sha1_), &sha1_);
- LDID_SHA256_Final(reinterpret_cast<uint8_t *>(hash_.sha256_), &sha256_);
+ EVP_DigestFinal_ex(sha1_, reinterpret_cast<uint8_t *>(hash_.sha1_), nullptr);
+ EVP_DigestFinal_ex(sha256_, reinterpret_cast<uint8_t *>(hash_.sha256_), nullptr);
+
+ EVP_MD_CTX_free(sha1_);
+ EVP_MD_CTX_free(sha256_);
}
virtual std::streamsize xsputn(const char_type *data, std::streamsize size) {
- LDID_SHA1_Update(&sha1_, data, size);
- LDID_SHA256_Update(&sha256_, data, size);
+ EVP_DigestUpdate(sha1_, data, size);
+ EVP_DigestUpdate(sha256_, data, size);
return size;
}
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);
+ if (index < 0) {
+ fprintf(stderr, "ldid: An error occursed while parsing the certificate: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
auto next(X509_NAME_get_index_by_NID(name, nid, index));
- _assert(next == -1);
+ if (next != -1) {
+ fprintf(stderr, "ldid: An error occursed while parsing the certificate: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
auto entry(X509_NAME_get_entry(name, index));
- _assert(entry != NULL);
+ if (entry == NULL) {
+ fprintf(stderr, "ldid: An error occursed while parsing the certificate: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
auto asn(X509_NAME_ENTRY_get_data(entry));
- _assert(asn != NULL);
+ if (asn == NULL) {
+ fprintf(stderr, "ldid: An error occursed while parsing the certificate: %s\n", ERR_error_string(ERR_get_error(), NULL));
+ exit(1);
+ }
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));
- if(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);
if (!baton.entitlements_.empty() || !entitlements.empty()) {
auto combined(plist(baton.entitlements_));
_scope({ plist_free(combined); });
- _assert(plist_get_node_type(combined) == PLIST_DICT);
+ if (plist_get_node_type(combined) != PLIST_DICT) {
+ fprintf(stderr, "ldid: Existing entitlements are in wrong format\n");
+ exit(1);
+ };
auto merging(plist(entitlements));
_scope({ plist_free(merging); });
- _assert(plist_get_node_type(merging) == PLIST_DICT);
+ if (plist_get_node_type(merging) != PLIST_DICT) {
+ fprintf(stderr, "ldid: Entitlements need a root key of dict\n");
+ exit(1);
+ };
plist_dict_iter iterator(NULL);
plist_dict_new_iter(merging, &iterator);
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); });
if (plist_get_node_type(entitlements) != PLIST_DICT) {
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;
+ std::vector<char> alternateCDSHA1;
unsigned total(0);
for (Algorithm *pointer : GetAlgorithms()) {
std::vector<char> hash;
algorithm(hash, blob.data(), blob.size());
- hash.resize(20);
-
- if (algorithm.type_ == CS_HASHTYPE_SHA256_256){
+ if (algorithm.type_ == CS_HASHTYPE_SHA256_256)
alternateCDSHA256 = hash;
- }
-
+ else if (algorithm.type_ == CS_HASHTYPE_SHA160_160)
+ alternateCDSHA1 = hash;
+ 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
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), alternateCDSHA256);
+ Signature signature(stuff, sign, std::string(xml, size), alternateCDSHA1, 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);
#ifdef __WIN32__
struct stat info;
_syscall(stat((path + name).c_str(), &info));
- if (false);
- else if (S_ISDIR(info.st_mode))
+ if (S_ISDIR(info.st_mode))
directory = true;
else if (S_ISREG(info.st_mode))
directory = false;
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();
plist_from_bin(data.data(), data.size(), &plist);
else
plist_from_xml(data.data(), data.size(), &plist);
- _assert(plist != NULL);
+ if (plist == NULL) {
+ fprintf(stderr, "ldid: Failed to parse plist\n");
+ exit(1);
+ }
return plist;
}
copy(buffer, data, length, dummy_);
auto node(plist(data.str()));
_scope({ plist_free(node); });
- _assert(plist_get_node_type(node) == PLIST_DICT);
+ if (plist_get_node_type(node) != PLIST_DICT) {
+ fprintf(stderr, "ldid: Unexpected plist type. Expected <dict>\n");
+ exit(1);
+ }
code(node);
}
static std::string plist_s(plist_t node) {
- _assert(node != NULL);
- _assert(plist_get_node_type(node) == PLIST_STRING);
+ if (node == NULL)
+ return NULL;
+ if (plist_get_node_type(node) != PLIST_STRING) {
+ fprintf(stderr, "ldid: Unexpected plist type. Expected <string>\n");
+ exit(1);
+ }
char *data;
plist_get_string_val(node, &data);
_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;
if (parent.Look(info))
return "";
mac = true;
- if (false);
- else if (parent.Look("Contents/" + info))
+ if (parent.Look("Contents/" + info))
return "Contents/";
else if (parent.Look("Resources/" + info)) {
info = "Resources/" + info;
folder.Open(info, fun([&](std::streambuf &buffer, size_t length, const void *flag) {
plist_d(buffer, length, fun([&](plist_t node) {
- executable = plist_s(plist_dict_get_item(node, "CFBundleExecutable"));
- identifier = plist_s(plist_dict_get_item(node, "CFBundleIdentifier"));
+ plist_t nodebuf(plist_dict_get_item(node, "CFBundleExecutable"));
+ if (nodebuf == NULL) {
+ fprintf(stderr, "ldid: Cannot find key CFBundleExecutable\n");
+ exit(1);
+ }
+ executable = plist_s(nodebuf);
+ nodebuf = plist_dict_get_item(node, "CFBundleIdentifier");
+ if (nodebuf == NULL) {
+ fprintf(stderr, "ldid: Cannot find key CFBundleIdentifier\n");
+ exit(1);
+ }
+ identifier = plist_s(nodebuf);
}));
}));
State local;
return Sign(root, folder, key, local, requirements, alter, progress);
}
-#endif
#endif
}
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, " [-r | -Sfile.xml | -s] [-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_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;
case 'E': {
const char *string = argv[argi] + 2;
const char *colon = strchr(string, ':');
- _assert(colon != NULL);
+ if (colon == NULL) {
+ usage(argv[0]);
+ exit(1);
+ }
Map file(colon + 1, O_RDONLY, PROT_READ, MAP_PRIVATE);
char *arge;
unsigned number(strtoul(string, &arge, 0));
- _assert(arge == colon);
+ if (arge != colon || (number == 0 && errno == EINVAL)) {
+ usage(argv[0]);
+ exit(1);
+ }
auto &slot(slots[number]);
for (Algorithm *algorithm : GetAlgorithms())
(*algorithm)(slot, file.data(), file.size());
do_sha256 = false;
}
- if (false);
- else if (strcmp(hash, "sha1") == 0)
+ if (strcmp(hash, "sha1") == 0)
do_sha1 = true;
else if (strcmp(hash, "sha256") == 0)
do_sha256 = true;
if (argv[argi][2] != '\0') {
const char *cpu = argv[argi] + 2;
const char *colon = strchr(cpu, ':');
- _assert(colon != NULL);
+ if (colon == NULL) {
+ usage(argv[0]);
+ exit(1);
+ }
char *arge;
flag_CPUType = strtoul(cpu, &arge, 0);
- _assert(arge == colon);
+ if (arge != colon || (flag_CPUType == 0 && errno == EINVAL)) {
+ usage(argv[0]);
+ exit(1);
+ }
flag_CPUSubtype = strtoul(colon + 1, &arge, 0);
- _assert(arge == argv[argi] + strlen(argv[argi]));
+ if (arge != argv[argi] + strlen(argv[argi]) || (flag_CPUSubtype == 0 && errno == EINVAL)) {
+ usage(argv[0]);
+ exit(1);
+ }
}
break;
case 'C': {
const char *name = argv[argi] + 2;
- if (false);
- else if (strcmp(name, "host") == 0)
+ if (strcmp(name, "host") == 0)
flags |= kSecCodeSignatureHost;
else if (strcmp(name, "adhoc") == 0)
flags |= kSecCodeSignatureAdhoc;
exit(1);
}
flag_s = true;
+ entitlements.clear();
+ flag_M = true;
break;
case 'S':
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 'T': break;
case 'u': {
flag_u = true;
break;
}
- _assert(flag_S || key.empty());
- _assert(flag_S || flag_I == NULL);
-
- if (flag_d && !flag_h) {
- flag_h = true;
- fprintf(stderr, "WARNING: -d also (temporarily) does the behavior of -h for compatibility with a fork of ldid\n");
+ if (flag_I != NULL && !flag_S) {
+ fprintf(stderr, "ldid: -I requires -S\n");
+ exit(1);
}
if (files.empty())
fprintf(stderr, "ldid: Only -S can be used on directories\n");
exit(1);
}
-#ifndef LDID_NOPLIST
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, flag_D ? true : false);
FatHeader fat_header(mapping.data(), mapping.size());
_foreach (mach_header, fat_header.GetMachHeaders()) {
_foreach (load_command, mach_header.GetLoadCommands()) {
uint32_t cmd(mach_header.Swap(load_command->cmd));
- if (false);
- else if (cmd == LC_CODE_SIGNATURE)
+ if (cmd == LC_CODE_SIGNATURE)
signature = reinterpret_cast<struct linkedit_data_command *>(load_command);
else if (cmd == LC_ENCRYPTION_INFO || cmd == LC_ENCRYPTION_INFO_64)
encryption = reinterpret_cast<struct encryption_info_command *>(load_command);
}
}
}
-#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) {
}
if (flag_D) {
- _assert(encryption != NULL);
+ if (encryption == NULL) {
+ fprintf(stderr, "ldid: -D requires an encrypted binary\n");
+ exit(1);
+ }
encryption->cryptid = mach_header.Swap(0);
}
- if ((flag_e || flag_q || flag_s || flag_h) && signature == NULL) {
- fprintf(stderr, "ldid: -e, -q, -s, and -h requre a signed binary\n");
+ 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_s) {
- 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) {
+ char *buf = _syscall(realpath(file.c_str(), NULL));
+ printf("Executable=%s\n", buf);
+ free(buf);
+
auto algorithms(GetAlgorithms());
uint32_t data = mach_header.Swap(signature->dataoff);
size_t size_;
Algorithm &algorithm_;
std::string hash_;
+ uint32_t offset;
};
std::map<uint8_t, Candidate> candidates;
+ uint32_t cmsBegin = 0, cmsEnd = 0;
for (size_t index(0); index != Swap(super->count); ++index) {
auto type(Swap(super->index[index].type));
auto &algorithm(*algorithms[type - 1]);
uint8_t hash[algorithm.size_];
algorithm(hash, blob + begin, end - begin);
- candidates.insert({type, {directory, end - begin, algorithm, Hex(hash, 20)}});
+ candidates.insert({type, {directory, end - begin, algorithm, Hex(hash, 20), begin}});
+ } else if (type == CSSLOT_SIGNATURESLOT) {
+ cmsBegin = Swap(super->index[index].offset);
+ cmsEnd = index + 1 == Swap(super->count) ? Swap(super->blob.length) : Swap(super->index[index + 1].offset);
}
}
const auto directory(best->second.directory_);
const auto flags(Swap(directory->flags));
+ printf("Identifier=%s\n", blob + best->second.offset + Swap(directory->identOffset));
+
std::string names;
if (flags & kSecCodeSignatureHost)
names += ",host";
printf("Hash choices=%s\n", choices.c_str() + 1);
printf("CDHash=%s\n", best->second.hash_.c_str());
+
+ if (cmsBegin != 0 && cmsEnd != 0) {
+ // This loads the CMS blob and parses each X509 cert in the blob to extract the
+ // common name and print it as "Authority=%s"
+ Buffer bio(reinterpret_cast<const char *>(blob) + cmsBegin + sizeof(Blob), cmsEnd - cmsBegin);
+ PKCS7 *p7 = NULL;
+ if ((p7 = d2i_PKCS7_bio(bio, NULL)) == NULL) {
+ // In order to follow codesign, we just ignore errors
+ printf("Authority=(unavailable)\n");
+ } else {
+ STACK_OF(X509) *certs = NULL;
+ switch (OBJ_obj2nid(p7->type)) {
+ case NID_pkcs7_signed:
+ if (p7->d.sign != NULL)
+ certs = p7->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped:
+ if (p7->d.signed_and_enveloped != NULL)
+ certs = p7->d.signed_and_enveloped->cert;
+ break;
+ default:
+ break;
+ }
+ if (certs != NULL) {
+ X509 *x;
+ for (int i = 0; i < sk_X509_num(certs); i++) {
+ x = sk_X509_value(certs, i);
+ int lastpos = -1;
+ X509_NAME *nm = X509_get_subject_name(x);
+ X509_NAME_ENTRY *e;
+
+ for (;;) {
+ lastpos = X509_NAME_get_index_by_NID(nm, NID_commonName, lastpos);
+ if (lastpos == -1)
+ break;
+ e = X509_NAME_get_entry(nm, lastpos);
+ ASN1_STRING *s = X509_NAME_ENTRY_get_data(e);
+ printf("Authority=%s\n", reinterpret_cast<const char *>(ASN1_STRING_get0_data(s)));
+ }
+ }
+ } else {
+ printf("Authority=(unavailable)\n");
+ }
+ }
+ PKCS7_free(p7);
+ }
+
+ if (Swap(directory->teamIDOffset) > 0)
+ printf("TeamIdentifier=%s\n", blob + best->second.offset + Swap(directory->teamIDOffset));
+ else
+ printf("TeamIdentifier=not set\n");
}
}
++filei;
}
-#ifndef LDID_NOSMIME
# if OPENSSL_VERSION_MAJOR >= 3
OSSL_PROVIDER_unload(legacy);
OSSL_PROVIDER_unload(deflt);
# endif
-#endif
return filee;
}
-#endif
+#endif // LDID_NOTOOLS