/* * Copyright (c) 2006 Apple Computer, 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 #include namespace Security { namespace CodeSigning { // // Construction and maintainance // 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; } // // 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(0); 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 makeCFDictionary(2, CFSTR("rules"), mRawRules.get(), CFSTR("files"), files.get() ); } // // Hash a file and return a CFDataRef with the hash // CFDataRef ResourceBuilder::hashFile(const char *path) { CFRef 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)); } // // 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); } } // // 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) || size_t(CFDataGetLength(mHash)) != SHA1::digestLength) MacOSError::throwMe(errSecCSResourcesInvalid); } } } // end namespace CodeSigning } // end namespace Security