#include <openssl/pem.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
+
+#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
+#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
+#endif
-#include <plist/plist++.h>
+#include <plist/plist.h>
#include "ldid.hpp"
extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval);
static void sha1(uint8_t *hash, const void *data, size_t size) {
- SHA1(static_cast<const uint8_t *>(data), size, hash);
+ LDID_SHA1(static_cast<const uint8_t *>(data), size, hash);
}
struct CodesignAllocation {
{
private:
std::vector<char> &hash_;
- SHA_CTX context_;
+ LDID_SHA1_CTX context_;
public:
HashBuffer(std::vector<char> &hash) :
hash_(hash)
{
- SHA1_Init(&context_);
+ LDID_SHA1_Init(&context_);
}
~HashBuffer() {
- hash_.resize(SHA_DIGEST_LENGTH);
- SHA1_Final(reinterpret_cast<uint8_t *>(hash_.data()), &context_);
+ hash_.resize(LDID_SHA1_DIGEST_LENGTH);
+ LDID_SHA1_Final(reinterpret_cast<uint8_t *>(hash_.data()), &context_);
}
virtual std::streamsize xsputn(const char_type *data, std::streamsize size) {
- SHA1_Update(&context_, data, size);
+ LDID_SHA1_Update(&context_, data, size);
return size;
}
special = std::max(special, slot.first);
uint32_t normal((size + PageSize_ - 1) / PageSize_);
- alloc = Align(alloc + (special + normal) * SHA_DIGEST_LENGTH, 16);
+ alloc = Align(alloc + (special + normal) * LDID_SHA1_DIGEST_LENGTH, 16);
return alloc;
}), fun([&](std::streambuf &output, size_t limit, const std::string &overlap, const char *top) -> size_t {
Blobs blobs;
CodeDirectory directory;
directory.version = Swap(uint32_t(0x00020001));
directory.flags = Swap(uint32_t(0));
- directory.hashOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory) + identifier.size() + 1 + SHA_DIGEST_LENGTH * special));
+ directory.hashOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory) + identifier.size() + 1 + LDID_SHA1_DIGEST_LENGTH * special));
directory.identOffset = Swap(uint32_t(sizeof(Blob) + sizeof(CodeDirectory)));
directory.nSpecialSlots = Swap(special);
directory.codeLimit = Swap(uint32_t(limit));
directory.nCodeSlots = Swap(normal);
- directory.hashSize = SHA_DIGEST_LENGTH;
+ directory.hashSize = LDID_SHA1_DIGEST_LENGTH;
directory.hashType = CS_HASHTYPE_SHA1;
directory.spare1 = 0x00;
directory.pageSize = PageShift_;
put(data, identifier.c_str(), identifier.size() + 1);
- uint8_t storage[special + normal][SHA_DIGEST_LENGTH];
- uint8_t (*hashes)[SHA_DIGEST_LENGTH] = storage + special;
+ uint8_t storage[special + normal][LDID_SHA1_DIGEST_LENGTH];
+ uint8_t (*hashes)[LDID_SHA1_DIGEST_LENGTH] = storage + special;
memset(storage, 0, sizeof(*storage) * special);
if (Starts(name, ".ldid."))
continue;
+ bool directory;
+
+#ifdef __WIN32__
+ struct stat info;
+ _syscall(stat(path.c_str(), &info));
+ if (false);
+ else if (S_ISDIR(info.st_mode))
+ directory = true;
+ else if (S_ISREG(info.st_mode))
+ directory = false;
+ else
+ _assert_(false, "st_mode=%x", info.st_mode);
+#else
switch (child->d_type) {
case DT_DIR:
- Find(root, base + name + "/", code);
- break;
-
+ directory = true;
+ break;
case DT_REG:
- code(base + name, fun([&](const Functor<void (std::streambuf &, std::streambuf &)> &code) {
- std::string access(root + base + name);
- _assert_(Open(access, fun([&](std::streambuf &data) {
- NullBuffer save;
- code(data, save);
- })), "open(): %s", access.c_str());
- }));
- break;
-
+ directory = false;
+ break;
default:
_assert_(false, "d_type=%u", child->d_type);
- break;
}
+#endif
+
+ if (directory)
+ Find(root, base + name + "/", code);
+ else
+ code(base + name, fun([&](const Functor<void (std::streambuf &, std::streambuf &)> &code) {
+ std::string access(root + base + name);
+ _assert_(Open(access, fun([&](std::streambuf &data) {
+ NullBuffer save;
+ code(data, save);
+ })), "open(): %s", access.c_str());
+ }));
}
}
Find(path, "", code);
}
-SubFolder::SubFolder(Folder *parent, const std::string &path) :
+SubFolder::SubFolder(Folder &parent, const std::string &path) :
parent_(parent),
path_(path)
{
}
void SubFolder::Save(const std::string &path, const Functor<void (std::streambuf &)> &code) {
- return parent_->Save(path_ + path, code);
+ return parent_.Save(path_ + path, code);
}
bool SubFolder::Open(const std::string &path, const Functor<void (std::streambuf &)> &code) {
- return parent_->Open(path_ + path, code);
+ return parent_.Open(path_ + path, code);
}
void SubFolder::Find(const std::string &path, const Functor<void (const std::string &, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &)> &code) {
- return parent_->Find(path_ + path, code);
+ return parent_.Find(path_ + path, code);
+}
+
+UnionFolder::UnionFolder(Folder &parent) :
+ parent_(parent)
+{
+}
+
+void UnionFolder::Save(const std::string &path, const Functor<void (std::streambuf &)> &code) {
+ return parent_.Save(path, code);
+}
+
+bool UnionFolder::Open(const std::string &path, const Functor<void (std::streambuf &)> &code) {
+ auto file(files_.find(path));
+ if (file == files_.end())
+ return parent_.Open(path, code);
+
+ auto &data(file->second);
+ data.pubseekpos(0, std::ios::in);
+ code(data);
+ return true;
+}
+
+void UnionFolder::Find(const std::string &path, const Functor<void (const std::string &, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &)> &code) {
+ parent_.Find(path, fun([&](const std::string &name, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &save) {
+ if (files_.find(name) == files_.end())
+ code(name, save);
+ }));
+
+ for (auto &file : files_)
+ code(file.first, fun([&](const Functor<void (std::streambuf &, std::streambuf &)> &code) {
+ parent_.Save(file.first, fun([&](std::streambuf &save) {
+ file.second.pubseekpos(0, std::ios::in);
+ code(file.second, save);
+ }));
+ }));
}
static size_t copy(std::streambuf &source, std::streambuf &target) {
return total;
}
-static PList::Structure *plist(const std::string &data) {
- if (!Starts(data, "bplist00"))
- return PList::Structure::FromXml(data);
- std::vector<char> bytes(data.data(), data.data() + data.size());
- return PList::Structure::FromBin(bytes);
+static plist_t plist(const std::string &data) {
+ plist_t plist(NULL);
+ if (Starts(data, "bplist00"))
+ plist_from_bin(data.data(), data.size(), &plist);
+ else
+ plist_from_xml(data.data(), data.size(), &plist);
+ _assert(plist != NULL);
+ return plist;
}
-static void plist_d(std::streambuf &buffer, const Functor<void (PList::Dictionary *)> &code) {
+static void plist_d(std::streambuf &buffer, const Functor<void (plist_t)> &code) {
std::stringbuf data;
copy(buffer, data);
- PList::Structure *structure(plist(data.str()));
- _scope({ delete structure; });
- auto dictionary(dynamic_cast<PList::Dictionary *>(structure));
- _assert(dictionary != NULL);
- code(dictionary);
+ auto node(plist(data.str()));
+ _scope({ plist_free(node); });
+ _assert(plist_get_node_type(node) == PLIST_DICT);
+ code(node);
}
-static std::string plist_s(PList::Node *node) {
- auto value(dynamic_cast<PList::String *>(node));
- _assert(value != NULL);
- return value->GetValue();
+static std::string plist_s(plist_t node) {
+ _assert(node != NULL);
+ _assert(plist_get_node_type(node) == PLIST_STRING);
+ char *data;
+ plist_get_string_val(node, &data);
+ _scope({ free(data); });
+ return data;
}
enum Mode {
}
};
-std::string Bundle(const std::string &root, Folder &folder, const std::string &key, std::map<std::string, std::vector<char>> &remote) {
+std::string Bundle(const std::string &root, Folder &folder, const std::string &key, std::map<std::string, std::vector<char>> &remote, const std::string &entitlements) {
std::string executable;
std::string identifier;
static const std::string info("Info.plist");
_assert_(folder.Open(info, fun([&](std::streambuf &buffer) {
- plist_d(buffer, fun([&](PList::Dictionary *dictionary) {
- executable = plist_s(((*dictionary)["CFBundleExecutable"]));
- identifier = plist_s(((*dictionary)["CFBundleIdentifier"]));
+ plist_d(buffer, fun([&](plist_t node) {
+ executable = plist_s(plist_dict_get_item(node, "CFBundleExecutable"));
+ identifier = plist_s(plist_dict_get_item(node, "CFBundleIdentifier"));
}));
})), "open(): Info.plist");
static const std::string signature("_CodeSignature/CodeResources");
folder.Open(signature, fun([&](std::streambuf &buffer) {
- plist_d(buffer, fun([&](PList::Dictionary *dictionary) {
+ plist_d(buffer, fun([&](plist_t node) {
// XXX: maybe attempt to preserve existing rules
}));
}));
if (!nested(name))
return;
auto bundle(root + Split(name).dir);
- SubFolder subfolder(&folder, bundle);
- Bundle(bundle, subfolder, key, local);
+ SubFolder subfolder(folder, bundle);
+ Bundle(bundle, subfolder, key, local, "");
}));
folder.Find("", fun([&](const std::string &name, const Functor<void (const Functor<void (std::streambuf &, std::streambuf &)> &)> &code) {
copy(data, proxy);
}));
- _assert(hash.size() == SHA_DIGEST_LENGTH);
+ _assert(hash.size() == LDID_SHA1_DIGEST_LENGTH);
}));
- PList::Dictionary plist;
+ auto plist(plist_new_dict());
+ _scope({ plist_free(plist); });
for (const auto &version : versions) {
- PList::Dictionary files;
+ auto files(plist_new_dict());
+ plist_dict_set_item(plist, ("files" + version.first).c_str(), files);
for (const auto &rule : version.second)
rule.Compile();
for (const auto &rule : version.second)
if (rule(hash.first)) {
if (rule.mode_ == NoMode)
- files.Set(hash.first, PList::Data(hash.second));
+ plist_dict_set_item(files, hash.first.c_str(), plist_new_data(hash.second.data(), hash.second.size()));
else if (rule.mode_ == OptionalMode) {
- PList::Dictionary entry;
- entry.Set("hash", PList::Data(hash.second));
- entry.Set("optional", PList::Boolean(true));
- files.Set(hash.first, entry);
+ auto entry(plist_new_dict());
+ plist_dict_set_item(entry, "hash", plist_new_data(hash.second.data(), hash.second.size()));
+ plist_dict_set_item(entry, "optional", plist_new_bool(true));
+ plist_dict_set_item(files, hash.first.c_str(), entry);
}
break;
}
-
- plist.Set("files" + version.first, files);
}
for (const auto &version : versions) {
- PList::Dictionary rules;
+ auto rules(plist_new_dict());
+ plist_dict_set_item(plist, ("rules" + version.first).c_str(), rules);
std::multiset<const Rule *, RuleCode> ordered;
for (const auto &rule : version.second)
for (const auto &rule : ordered)
if (rule->weight_ == 1 && rule->mode_ == NoMode)
- rules.Set(rule->code_, PList::Boolean(true));
+ plist_dict_set_item(rules, rule->code_.c_str(), plist_new_bool(true));
else {
- PList::Dictionary entry;
+ auto entry(plist_new_dict());
+ plist_dict_set_item(rules, rule->code_.c_str(), entry);
switch (rule->mode_) {
case NoMode:
break;
case OmitMode:
- entry.Set("omit", PList::Boolean(true));
+ plist_dict_set_item(entry, "omit", plist_new_bool(true));
break;
case OptionalMode:
- entry.Set("optional", PList::Boolean(true));
+ plist_dict_set_item(entry, "optional", plist_new_bool(true));
break;
case NestedMode:
- entry.Set("nested", PList::Boolean(true));
+ plist_dict_set_item(entry, "nested", plist_new_bool(true));
break;
case TopMode:
- entry.Set("top", PList::Boolean(true));
+ plist_dict_set_item(entry, "top", plist_new_bool(true));
break;
}
if (rule->weight_ >= 10000)
- entry.Set("weight", PList::Integer(rule->weight_));
+ plist_dict_set_item(entry, "weight", plist_new_uint(rule->weight_));
else if (rule->weight_ != 1)
- entry.Set("weight", PList::Real(rule->weight_));
-
- rules.Set(rule->code_, entry);
+ plist_dict_set_item(entry, "weight", plist_new_real(rule->weight_));
}
-
- plist.Set("rules" + version.first, rules);
}
folder.Save(signature, fun([&](std::streambuf &save) {
HashProxy proxy(local[signature], save);
- auto xml(plist.ToXml());
- put(proxy, xml.data(), xml.size());
+ char *xml(NULL);
+ uint32_t size;
+ plist_to_xml(plist, &xml, &size);
+ _scope({ free(xml); });
+ put(proxy, xml, size);
}));
folder.Open(executable, fun([&](std::streambuf &buffer) {
slots[3] = local.at(signature);
HashProxy proxy(local[executable], save);
- Sign(data.data(), data.size(), proxy, identifier, "", key, slots);
+ Sign(data.data(), data.size(), proxy, identifier, entitlements, key, slots);
}));
}));
unsigned number(strtoul(slot, &arge, 0));
_assert(arge == colon);
std::vector<char> &hash(slots[number]);
- hash.resize(SHA_DIGEST_LENGTH);
+ hash.resize(LDID_SHA1_DIGEST_LENGTH);
sha1(reinterpret_cast<uint8_t *>(hash.data()), file.data(), file.size());
} break;
_assert(!flag_r);
ldid::DiskFolder folder(path);
std::map<std::string, std::vector<char>> hashes;
- path += "/" + Bundle("", folder, key, hashes);
+ path += "/" + Bundle("", folder, key, hashes, entitlements);
} else if (flag_S || flag_r) {
Map input(path, O_RDONLY, PROT_READ, MAP_PRIVATE);
uint32_t begin = Swap(super->index[index].offset);
struct CodeDirectory *directory = reinterpret_cast<struct CodeDirectory *>(blob + begin);
- uint8_t (*hashes)[SHA_DIGEST_LENGTH] = reinterpret_cast<uint8_t (*)[SHA_DIGEST_LENGTH]>(blob + begin + Swap(directory->hashOffset));
+ 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)