X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..7e6b461318c8a779d91381531435a68ee4e8b6ed:/OSX/libsecurity_codesigning/lib/resources.cpp?ds=inline diff --git a/OSX/libsecurity_codesigning/lib/resources.cpp b/OSX/libsecurity_codesigning/lib/resources.cpp index 5695800e..221e2984 100644 --- a/OSX/libsecurity_codesigning/lib/resources.cpp +++ b/OSX/libsecurity_codesigning/lib/resources.cpp @@ -61,9 +61,8 @@ static string removeTrailingSlash(string path) // Construction and maintainance // ResourceBuilder::ResourceBuilder(const std::string &root, const std::string &relBase, - CFDictionaryRef rulesDict, CodeDirectory::HashAlgorithm hashType, bool strict, const MacOSErrorSet& toleratedErrors) - : mHashType(hashType), - mCheckUnreadable(strict && toleratedErrors.find(errSecCSSignatureNotVerifiable) == toleratedErrors.end()), + CFDictionaryRef rulesDict, bool strict, const MacOSErrorSet& toleratedErrors) + : mCheckUnreadable(strict && toleratedErrors.find(errSecCSSignatureNotVerifiable) == toleratedErrors.end()), mCheckUnknownType(strict && toleratedErrors.find(errSecCSResourceNotSupported) == toleratedErrors.end()) { assert(!root.empty()); @@ -75,7 +74,7 @@ ResourceBuilder::ResourceBuilder(const std::string &root, const std::string &rel UnixError::throwMe(); mRelBase = realroot; if (mRoot != mRelBase && mRelBase != mRoot + "/Contents") - MacOSError::throwMe(errSecCSInternalError); + MacOSError::throwMe(errSecCSBadBundleFormat); const char * paths[2] = { mRoot.c_str(), NULL }; mFTS = fts_open((char * const *)paths, FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR, NULL); if (!mFTS) @@ -139,10 +138,15 @@ static bool findStringEndingNoCase(const char *path, const char * end) void ResourceBuilder::scan(Scanner next) { bool first = true; - + while (FTSENT *ent = fts_read(mFTS)) { static const char ds_store[] = ".DS_Store"; - const char *relpath = ent->fts_path + mRoot.size() + 1; // skip prefix + "/" + const char *relpath = ent->fts_path + mRoot.size(); // skip prefix + + if (strlen(relpath) > 0) { + relpath += 1; // skip "/" + } + std::string rp; if (mRelBase != mRoot) { assert(mRelBase == mRoot + "/Contents"); @@ -153,7 +157,7 @@ void ResourceBuilder::scan(Scanner next) } switch (ent->fts_info) { case FTS_F: - secdebug("rdirenum", "file %s", ent->fts_path); + secinfo("rdirenum", "file %s", ent->fts_path); GKBIS_Num_files++; // These are checks for the gatekeeper collection @@ -170,7 +174,7 @@ void ResourceBuilder::scan(Scanner next) break; case FTS_SL: // symlinks cannot ever be nested code, so quietly convert to resource file - secdebug("rdirenum", "symlink %s", ent->fts_path); + secinfo("rdirenum", "symlink %s", ent->fts_path); GKBIS_Num_symlinks++; if (strcasecmp(ent->fts_name, ds_store) == 0) @@ -181,10 +185,10 @@ void ResourceBuilder::scan(Scanner next) next(ent, rule->flags & ~nested, string(relpath), rule); break; case FTS_D: - secdebug("rdirenum", "entering %s", ent->fts_path); + secinfo("rdirenum", "entering %s", ent->fts_path); GKBIS_Num_dirs++; - if (!first) { // skip root directory (relpath invalid) + if (!first) { // skip root directory if (Rule *rule = findRule(relpath)) { if (rule->flags & nested) { if (strchr(ent->fts_name, '.')) { // nested, has extension -> treat as nested bundle @@ -204,15 +208,15 @@ void ResourceBuilder::scan(Scanner next) break; case FTS_DP: - secdebug("rdirenum", "leaving %s", ent->fts_path); + secinfo("rdirenum", "leaving %s", ent->fts_path); break; case FTS_DNR: - secdebug("rdirenum", "cannot read directory %s", ent->fts_path); + secinfo("rdirenum", "cannot read directory %s", ent->fts_path); if (mCheckUnreadable) MacOSError::throwMe(errSecCSSignatureNotVerifiable); break; default: - secdebug("rdirenum", "type %d (errno %d): %s", + secinfo("rdirenum", "type %d (errno %d): %s", ent->fts_info, ent->fts_errno, ent->fts_path); if (mCheckUnknownType) MacOSError::throwMe(errSecCSResourceNotSupported); @@ -249,21 +253,28 @@ bool ResourceBuilder::includes(string path) const ResourceBuilder::Rule *ResourceBuilder::findRule(string path) const { Rule *bestRule = NULL; - secdebug("rscan", "test %s", path.c_str()); + secinfo("rscan", "test %s", path.c_str()); for (Rules::const_iterator it = mRules.begin(); it != mRules.end(); ++it) { Rule *rule = *it; - secdebug("rscan", "try %s", rule->source.c_str()); + secinfo("rscan", "try %s", rule->source.c_str()); if (rule->match(path.c_str())) { - secdebug("rscan", "match"); + secinfo("rscan", "match"); if (rule->flags & exclusion) { - secdebug("rscan", "excluded"); + secinfo("rscan", "excluded"); return rule; } if (!bestRule || rule->weight > bestRule->weight) bestRule = rule; + + +#if TARGET_OS_WATCH +/* rdar://problem/30517969 */ + if (bestRule && bestRule->weight == rule->weight && !(bestRule->flags & omitted) && (rule->flags & omitted)) + bestRule = rule; +#endif } } - secdebug("rscan", "choosing %s (%d,0x%x)", + secinfo("rscan", "choosing %s (%d,0x%x)", bestRule ? bestRule->source.c_str() : "NOTHING", bestRule ? bestRule->weight : 0, bestRule ? bestRule->flags : 0); @@ -274,15 +285,51 @@ ResourceBuilder::Rule *ResourceBuilder::findRule(string path) const // // Hash a file and return a CFDataRef with the hash // -CFDataRef ResourceBuilder::hashFile(const char *path) const +CFDataRef ResourceBuilder::hashFile(const char *path, CodeDirectory::HashAlgorithm type) { UnixPlusPlus::AutoFileDesc fd(path); fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass) - MakeHash hasher(this); + RefPointer hasher(CodeDirectory::hashFor(type)); hashFileData(fd, hasher.get()); - Hashing::Byte digest[hasher->digestLength()]; - hasher->finish(digest); - return CFDataCreate(NULL, digest, sizeof(digest)); + vector digest_vector(hasher->digestLength()); + hasher->finish(digest_vector.data()); + return CFDataCreate(NULL, digest_vector.data(), + digest_vector.size() * sizeof(Hashing::Byte)); +} + + +// +// Hash a file to multiple hash types and return a dictionary suitable to form a resource seal +// +CFMutableDictionaryRef ResourceBuilder::hashFile(const char *path, CodeDirectory::HashAlgorithms types, bool strictCheck) +{ + UnixPlusPlus::AutoFileDesc fd(path); + fd.fcntl(F_NOCACHE, true); // turn off page caching (one-pass) + if (strictCheck) + if (fd.hasExtendedAttribute(XATTR_RESOURCEFORK_NAME) || fd.hasExtendedAttribute(XATTR_FINDERINFO_NAME)) + MacOSError::throwMe(errSecCSInvalidAssociatedFileData); + CFRef result = makeCFMutableDictionary(); + CFMutableDictionaryRef resultRef = result; + CodeDirectory::multipleHashFileData(fd, 0, types, ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) { + size_t length = hasher->digestLength(); + vector digest_vector(length); + hasher->finish(digest_vector.data()); + CFDictionaryAddValue(resultRef, CFTempString(hashName(type)), CFTempData(digest_vector.data(), length)); + }); + return result.yield(); +} + + +std::string ResourceBuilder::hashName(CodeDirectory::HashAlgorithm type) +{ + switch (type) { + case kSecCodeSignatureHashSHA1: + return "hash"; + default: + char name[20]; + snprintf(name, sizeof(name), "hash%d", int(type)); + return name; + } } @@ -294,7 +341,7 @@ ResourceBuilder::Rule::Rule(const std::string &pattern, unsigned w, uint32_t 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)", + secinfo("csresource", "%p rule %s added (weight %d, flags 0x%x)", this, pattern.c_str(), w, f); } @@ -321,7 +368,7 @@ 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)) + if (strchr("\\[]{}().+*?^$|", c)) r.push_back('\\'); r.push_back(c); } @@ -333,29 +380,43 @@ std::string ResourceBuilder::escapeRE(const std::string &s) // Resource Seals // ResourceSeal::ResourceSeal(CFTypeRef it) - : mDict(NULL), mHash(NULL), mRequirement(NULL), mLink(NULL), mFlags(0) + : mDict(NULL), mRequirement(NULL), mLink(NULL), mFlags(0) { if (it == NULL) MacOSError::throwMe(errSecCSResourcesInvalid); - if (CFGetTypeID(it) == CFDataGetTypeID()) { - mHash = CFDataRef(it); - } else { - int optional = 0; + if (CFGetTypeID(it) == CFDataGetTypeID()) // old-style form with just a hash + mDict.take(cfmake("{hash=%O}", it)); + else if (CFGetTypeID(it) == CFDictionaryGetTypeID()) mDict = CFDictionaryRef(it); - bool err; - if (CFDictionaryGetValue(mDict, CFSTR("requirement"))) - err = !cfscan(mDict, "{requirement=%SO,?optional=%B}", &mRequirement, &optional); - else if (CFDictionaryGetValue(mDict, CFSTR("symlink"))) - err = !cfscan(mDict, "{symlink=%SO,?optional=%B}", &mLink, &optional); - else - err = !cfscan(mDict, "{hash=%XO,?optional=%B}", &mHash, &optional); - if (err) - MacOSError::throwMe(errSecCSResourcesInvalid); - if (optional) - mFlags |= ResourceBuilder::optional; - if (mRequirement) - mFlags |= ResourceBuilder::nested; - } + else + MacOSError::throwMe(errSecCSResourcesInvalid); + + int optional = 0; + bool err; + if (CFDictionaryGetValue(mDict, CFSTR("requirement"))) + err = !cfscan(mDict, "{requirement=%SO,?optional=%B}", &mRequirement, &optional); + else if (CFDictionaryGetValue(mDict, CFSTR("symlink"))) + err = !cfscan(mDict, "{symlink=%SO,?optional=%B}", &mLink, &optional); + else + err = !cfscan(mDict, "{?optional=%B}", &optional); + if (err) + MacOSError::throwMe(errSecCSResourcesInvalid); + if (optional) + mFlags |= ResourceBuilder::optional; + if (mRequirement) + mFlags |= ResourceBuilder::nested; +} + + +const Hashing::Byte *ResourceSeal::hash(CodeDirectory::HashAlgorithm type) const +{ + std::string name = ResourceBuilder::hashName(type); + CFTypeRef hash = CFDictionaryGetValue(mDict, CFTempString(name)); + if (hash == NULL) // pre-agility fallback + hash = CFDictionaryGetValue(mDict, CFSTR("hash")); + if (hash == NULL || CFGetTypeID(hash) != CFDataGetTypeID()) + MacOSError::throwMe(errSecCSResourcesInvalid); + return CFDataGetBytePtr(CFDataRef(hash)); }