#include "resources.h"
#include <Security/CSCommon.h>
#include <security_codesigning/cfmunge.h>
namespace Security {
namespace CodeSigning {
ResourceBuilder::ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict)
: ResourceEnumerator(root)
{
CFDictionary rules(rulesDict, errSecCSResourceRulesInvalid);
rules.apply(this, &ResourceBuilder::addRule);
mRawRules = rules;
}
ResourceBuilder::~ResourceBuilder()
{
for (Rules::iterator it = mRules.begin(); it != mRules.end(); ++it)
delete *it;
}
void ResourceBuilder::addRule(CFTypeRef key, CFTypeRef value)
{
string pattern = cfString(key, errSecCSResourceRulesInvalid);
unsigned weight = 1;
uint32_t flags = 0;
if (CFGetTypeID(value) == CFBooleanGetTypeID()) {
if (value == kCFBooleanFalse)
flags |= omitted;
} else {
CFDictionary rule(value, errSecCSResourceRulesInvalid);
if (CFNumberRef weightRef = rule.get<CFNumberRef>("weight"))
weight = cfNumber<unsigned int>(weightRef);
if (CFBooleanRef omitRef = rule.get<CFBooleanRef>("omit"))
if (omitRef == kCFBooleanTrue)
flags |= omitted;
if (CFBooleanRef optRef = rule.get<CFBooleanRef>("optional"))
if (optRef == kCFBooleanTrue)
flags |= optional;
}
addRule(new Rule(pattern, weight, flags));
}
FTSENT *ResourceBuilder::next(string &path, Rule * &rule)
{
while (FTSENT *ent = ResourceEnumerator::next(path)) {
Rule *bestRule = NULL;
for (Rules::const_iterator it = mRules.begin(); it != mRules.end(); ++it) {
Rule *rule = *it;
if (rule->match(path.c_str())) {
if (rule->flags & exclusion) {
bestRule = NULL;
break;
}
if (!bestRule || rule->weight > bestRule->weight)
bestRule = rule;
}
}
if (!bestRule || (bestRule->flags & omitted))
continue;
rule = bestRule;
return ent;
}
return NULL;
}
CFDictionaryRef ResourceBuilder::build()
{
secdebug("codesign", "start building resource directory");
CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(0);
string path;
Rule *rule;
while (FTSENT *ent = next(path, rule)) {
assert(rule);
CFRef<CFDataRef> hash = hashFile(ent->fts_accpath);
if (rule->flags == 0) { cfadd(files, "{%s=%O}", path.c_str(), hash.get());
secdebug("csresource", "%s added simple (rule %p)", path.c_str(), rule);
} else { cfadd(files, "{%s={hash=%O,optional=%B}}",
path.c_str(), hash.get(), rule->flags & optional);
secdebug("csresource", "%s added complex (rule %p)", path.c_str(), rule);
}
}
secdebug("codesign", "finished code directory with %d entries",
int(CFDictionaryGetCount(files)));
return makeCFDictionary(2,
CFSTR("rules"), mRawRules.get(),
CFSTR("files"), files.get()
);
}
CFDataRef ResourceBuilder::hashFile(const char *path)
{
CFRef<CFDataRef> data = cfLoadFile(path);
secdebug("rdirenum", " %s (%d bytes)", path, int(CFDataGetLength(data)));
SHA1 hasher;
hasher(CFDataGetBytePtr(data), CFDataGetLength(data));
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
hasher.finish(digest);
return CFDataCreate(NULL, digest, sizeof(digest));
}
ResourceBuilder::Rule::Rule(const std::string &pattern, unsigned w, uint32_t f)
: weight(w), flags(f)
{
if (::regcomp(this, pattern.c_str(), REG_EXTENDED | REG_NOSUB)) MacOSError::throwMe(errSecCSResourceRulesInvalid);
secdebug("csresource", "%p rule %s added (weight %d, flags 0x%x)",
this, pattern.c_str(), w, f);
}
ResourceBuilder::Rule::~Rule()
{
::regfree(this);
}
bool ResourceBuilder::Rule::match(const char *s) const
{
switch (::regexec(this, s, 0, NULL, 0)) {
case 0:
return true;
case REG_NOMATCH:
return false;
default:
MacOSError::throwMe(errSecCSResourceRulesInvalid);
}
}
ResourceSeal::ResourceSeal(CFTypeRef it)
{
if (it == NULL)
MacOSError::throwMe(errSecCSResourcesInvalid);
if (CFGetTypeID(it) == CFDataGetTypeID()) {
mHash = CFDataRef(it);
mOptional = false;
} else {
mOptional = false;
if (!cfscan(it, "{hash=%XO,?optional=%B}", &mHash, &mOptional)
|| size_t(CFDataGetLength(mHash)) != SHA1::digestLength)
MacOSError::throwMe(errSecCSResourcesInvalid);
}
}
} }