X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_codesigning/lib/resources.cpp diff --git a/libsecurity_codesigning/lib/resources.cpp b/libsecurity_codesigning/lib/resources.cpp new file mode 100644 index 00000000..785eb0a7 --- /dev/null +++ b/libsecurity_codesigning/lib/resources.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// resource directory construction and verification +// +#include "resources.h" +#include "csutilities.h" +#include +#include +#include + +namespace Security { +namespace CodeSigning { + + +// +// Construction and maintainance +// +ResourceBuilder::ResourceBuilder(const std::string &root, CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType) + : ResourceEnumerator(root), mHashType(hashType) +{ + 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; +} + + +// +// Parse and add one matching rule +// +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("weight")) + weight = cfNumber(weightRef); + if (CFBooleanRef omitRef = rule.get("omit")) + if (omitRef == kCFBooleanTrue) + flags |= omitted; + if (CFBooleanRef optRef = rule.get("optional")) + if (optRef == kCFBooleanTrue) + flags |= optional; + } + addRule(new Rule(pattern, weight, flags)); +} + + +// +// Locate the next non-ignored file, look up its rule, and return it. +// Returns NULL when we're out of files. +// +FTSENT *ResourceBuilder::next(string &path, Rule * &rule) +{ + while (FTSENT *ent = ResourceEnumerator::next(path)) { + // find best matching rule + 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; +} + + +// +// Build the ResourceDirectory given the currently established rule set. +// +CFDictionaryRef ResourceBuilder::build() +{ + secdebug("codesign", "start building resource directory"); + CFRef files = makeCFMutableDictionary(); + + string path; + Rule *rule; + while (FTSENT *ent = next(path, rule)) { + assert(rule); + CFRef hash = hashFile(ent->fts_accpath); + if (rule->flags == 0) { // default case - plain hash + cfadd(files, "{%s=%O}", path.c_str(), hash.get()); + secdebug("csresource", "%s added simple (rule %p)", path.c_str(), rule); + } else { // more complicated - use a sub-dictionary + 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 cfmake("{rules=%O,files=%O}", mRawRules.get(), files.get()); +} + + +// +// Hash a file and return a CFDataRef with the hash +// +CFDataRef ResourceBuilder::hashFile(const char *path) +{ + UnixPlusPlus::AutoFileDesc fd(path); + fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass) + MakeHash hasher(this); + hashFileData(fd, hasher.get()); + Hashing::Byte digest[hasher->digestLength()]; + hasher->finish(digest); + return CFDataCreate(NULL, digest, sizeof(digest)); +} + + +// +// Regex matching objects +// +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)) //@@@ REG_ICASE? + 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); + } +} + + +std::string ResourceBuilder::escapeRE(const std::string &s) +{ + string r; + for (string::const_iterator it = s.begin(); it != s.end(); ++it) { + char c = *it; + if (strchr("\\[]{}().+*", c)) + r.push_back('\\'); + r.push_back(c); + } + return r; +} + + +// +// Resource Seals +// +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)) + MacOSError::throwMe(errSecCSResourcesInvalid); + } +} + + +} // end namespace CodeSigning +} // end namespace Security