// 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());
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)
}
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
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)
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)
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);
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);
//
// 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<ResourceBuilder> hasher(this);
+ RefPointer<DynamicHash> hasher(CodeDirectory::hashFor(type));
hashFileData(fd, hasher.get());
Hashing::Byte digest[hasher->digestLength()];
hasher->finish(digest);
}
+//
+// 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<CFMutableDictionaryRef> result = makeCFMutableDictionary();
+ CFMutableDictionaryRef resultRef = result;
+ CodeDirectory::multipleHashFileData(fd, 0, types, ^(CodeDirectory::HashAlgorithm type, Security::DynamicHash *hasher) {
+ size_t length = hasher->digestLength();
+ Hashing::Byte digest[length];
+ hasher->finish(digest);
+ CFDictionaryAddValue(resultRef, CFTempString(hashName(type)), CFTempData(digest, 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;
+ }
+}
+
+
//
// Regex matching objects
//
{
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);
}
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);
}
// 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<CFDictionaryRef>("{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));
}