mirror of
https://github.com/google/santa.git
synced 2026-04-24 03:00:12 -04:00
santa-driver: Split cache for root/non-root volume
Split the kernel-land cache into 2 separate caches, one for the root volume and one for secondary volumes. When an unmount happens, clear the non-root cache to ensure no overlap with filesystem IDs.
This commit is contained in:
committed by
Russell Hancox
parent
0544011ee0
commit
616fd9570f
@@ -555,8 +555,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
NSData *d = [self.fileHandle readDataOfLength:range.length];
|
||||
if (d.length != range.length) return nil;
|
||||
return d;
|
||||
}
|
||||
@catch (NSException *e) {
|
||||
} @catch (NSException *e) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
#include <IOKit/IOLib.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOGD(...) IOLog("D santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGD(format, ...) IOLog("D santa-driver: " format "\n", ##__VA_ARGS__);
|
||||
#else // DEBUG
|
||||
#define LOGD(...)
|
||||
#define LOGD(format, ...)
|
||||
#endif // DEBUG
|
||||
#define LOGI(...) IOLog("I santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGW(...) IOLog("W santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGE(...) IOLog("E santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGI(format, ...) IOLog("I santa-driver: " format "\n", ##__VA_ARGS__);
|
||||
#define LOGW(format, ...) IOLog("W santa-driver: " format "\n", ##__VA_ARGS__);
|
||||
#define LOGE(format, ...) IOLog("E santa-driver: " format "\n", ##__VA_ARGS__);
|
||||
|
||||
#else // KERNEL
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ bool SantaDecisionManager::init() {
|
||||
decision_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
log_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
|
||||
decision_cache_ = new SantaCache<uint64_t>(10000, 2);
|
||||
root_decision_cache_ = new SantaCache<uint64_t>(5000, 2);
|
||||
non_root_decision_cache_ = new SantaCache<uint64_t>(500, 2);
|
||||
vnode_pid_map_ = new SantaCache<uint64_t>(2000, 5);
|
||||
|
||||
decision_dataqueue_ = IOSharedDataQueue::withEntries(
|
||||
@@ -40,6 +41,12 @@ bool SantaDecisionManager::init() {
|
||||
kMaxLogQueueEvents, sizeof(santa_message_t));
|
||||
if (!log_dataqueue_) return kIOReturnNoMemory;
|
||||
|
||||
vfs_context_t ctx = vfs_context_create(NULL);
|
||||
vnode_t root = vfs_rootvnode();
|
||||
root_vsid_ = GetVnodeIDForVnode(ctx, root) >> 32;
|
||||
vnode_put(root);
|
||||
vfs_context_rele(ctx);
|
||||
|
||||
client_pid_ = 0;
|
||||
|
||||
ts_ = { .tv_sec = kRequestLoopSleepMilliseconds / 1000,
|
||||
@@ -49,7 +56,8 @@ bool SantaDecisionManager::init() {
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
delete decision_cache_;
|
||||
delete root_decision_cache_;
|
||||
delete non_root_decision_cache_;
|
||||
delete vnode_pid_map_;
|
||||
|
||||
if (decision_dataqueue_lock_) {
|
||||
@@ -195,15 +203,28 @@ kern_return_t SantaDecisionManager::StopListener() {
|
||||
|
||||
#pragma mark Cache Management
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<uint64_t>* SantaDecisionManager::CacheForIdentifier(const uint64_t identifier) {
|
||||
return (identifier >> 32 == root_vsid_) ? root_decision_cache_ : non_root_decision_cache_;
|
||||
}
|
||||
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
uint64_t identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
// If a previous entry was not found and the new entry is not `REQUEST_BINARY`, remove the
|
||||
// existing entry. This is to prevent adding an ALLOW to the cache after a write has occurred.
|
||||
if (decision_cache_->set(identifier, val) == 0 && decision != ACTION_REQUEST_BINARY) {
|
||||
decision_cache_->remove(identifier);
|
||||
if (decision_cache->set(identifier, val) == 0 && decision != ACTION_REQUEST_BINARY) {
|
||||
decision_cache->remove(identifier);
|
||||
}
|
||||
|
||||
if (unlikely(!identifier)) return;
|
||||
@@ -211,17 +232,18 @@ void SantaDecisionManager::AddToCache(
|
||||
}
|
||||
|
||||
void SantaDecisionManager::RemoveFromCache(uint64_t identifier) {
|
||||
decision_cache_->remove(identifier);
|
||||
CacheForIdentifier(identifier)->remove(identifier);
|
||||
if (unlikely(!identifier)) return;
|
||||
wakeup((void *)identifier);
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::CacheCount() const {
|
||||
return decision_cache_->count();
|
||||
return root_decision_cache_->count() + non_root_decision_cache_->count();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache() {
|
||||
decision_cache_->clear();
|
||||
void SantaDecisionManager::ClearCache(bool non_root_only) {
|
||||
if (!non_root_only) root_decision_cache_->clear();
|
||||
non_root_decision_cache_->clear();
|
||||
}
|
||||
|
||||
#pragma mark Decision Fetching
|
||||
@@ -230,7 +252,9 @@ santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
auto result = ACTION_UNSET;
|
||||
uint64_t decision_time = 0;
|
||||
|
||||
uint64_t cache_val = decision_cache_->get(identifier);
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
uint64_t cache_val = decision_cache->get(identifier);
|
||||
if (cache_val == 0) return result;
|
||||
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
@@ -241,7 +265,7 @@ santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
if (result == ACTION_RESPOND_DENY) {
|
||||
auto expiry_time = decision_time + (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
if (expiry_time < GetCurrentUptime()) {
|
||||
decision_cache_->remove(identifier);
|
||||
decision_cache->remove(identifier);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class SantaDecisionManager : public OSObject {
|
||||
uint64_t CacheCount() const;
|
||||
|
||||
/// Clears the cache.
|
||||
void ClearCache();
|
||||
void ClearCache(bool non_root_only = true);
|
||||
|
||||
/// Increments the count of active callbacks pending.
|
||||
void IncrementListenerInvocations();
|
||||
@@ -244,9 +244,22 @@ class SantaDecisionManager : public OSObject {
|
||||
}
|
||||
|
||||
private:
|
||||
SantaCache<uint64_t> *decision_cache_;
|
||||
SantaCache<uint64_t> *root_decision_cache_;
|
||||
SantaCache<uint64_t> *non_root_decision_cache_;
|
||||
SantaCache<uint64_t> *vnode_pid_map_;
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<uint64_t>* CacheForIdentifier(const uint64_t identifier);
|
||||
|
||||
// This is the file system ID of the root filesystem,
|
||||
// used to determine which cache to use for requests
|
||||
uint32_t root_vsid_;
|
||||
|
||||
lck_grp_t *sdm_lock_grp_;
|
||||
lck_grp_attr_t *sdm_lock_grp_attr_;
|
||||
lck_attr_t *sdm_lock_attr_;
|
||||
|
||||
@@ -133,7 +133,7 @@ IOReturn SantaDriverClient::allow_binary(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(*arguments->scalarInput);
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_ALLOW);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
@@ -144,7 +144,7 @@ IOReturn SantaDriverClient::deny_binary(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(*arguments->scalarInput);
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_DENY);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
@@ -155,7 +155,8 @@ IOReturn SantaDriverClient::clear_cache(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
me->decisionManager->ClearCache();
|
||||
const bool non_root_only = static_cast<const bool>(arguments->scalarInput[0]);
|
||||
me->decisionManager->ClearCache(non_root_only);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -174,7 +175,7 @@ IOReturn SantaDriverClient::check_cache(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
uint64_t input = *arguments->scalarInput;
|
||||
const uint64_t input = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
arguments->scalarOutput[0] = me->decisionManager->GetFromCache(input);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
@@ -195,7 +196,7 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
{ &SantaDriverClient::open, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::allow_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 1, 0 },
|
||||
{ &SantaDriverClient::check_cache, 1, 0, 1, 0 }
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
origMode = newMode;
|
||||
if (newMode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,20 +253,26 @@
|
||||
void diskAppearedCallback(DADiskRef disk, void *context) {
|
||||
SNTApplication *app = (__bridge SNTApplication *)context;
|
||||
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
|
||||
if (![props[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
[app.eventLog logDiskAppeared:props];
|
||||
}
|
||||
|
||||
void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef keys, void *context) {
|
||||
SNTApplication *app = (__bridge SNTApplication *)context;
|
||||
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
|
||||
if (![props[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
if (props[@"DAVolumePath"]) [app.eventLog logDiskAppeared:props];
|
||||
}
|
||||
|
||||
void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
SNTApplication *app = (__bridge SNTApplication *)context;
|
||||
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
|
||||
if (![props[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
[app.eventLog logDiskDisappeared:props];
|
||||
[app.driverManager flushCacheNonRootOnly:YES];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -62,7 +62,7 @@ double watchdogRAMPeak = 0;
|
||||
}
|
||||
|
||||
- (void)flushCache:(void (^)(BOOL))reply {
|
||||
reply([self.driverManager flushCache]);
|
||||
reply([self.driverManager flushCacheNonRootOnly:NO]);
|
||||
}
|
||||
|
||||
- (void)checkCacheForVnodeID:(uint64_t)vnodeID withReply:(void (^)(santa_action_t))reply {
|
||||
@@ -86,7 +86,7 @@ double watchdogRAMPeak = 0;
|
||||
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", SNTRuleStateWhitelist];
|
||||
if ([rules filteredArrayUsingPredicate:p].count || cleanSlate) {
|
||||
LOGI(@"Received non-whitelist rule, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
|
||||
reply(error);
|
||||
@@ -170,7 +170,7 @@ double watchdogRAMPeak = 0;
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setWhitelistPathRegex:re];
|
||||
LOGI(@"Received new whitelist regex, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ double watchdogRAMPeak = 0;
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
|
||||
LOGI(@"Received new blacklist regex, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
reply();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@
|
||||
///
|
||||
/// Flush the kernel's binary cache.
|
||||
///
|
||||
- (BOOL)flushCache;
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly;
|
||||
|
||||
///
|
||||
/// Check the kernel cache for a VnodeID
|
||||
///
|
||||
-(santa_action_t)checkCache:(uint64_t)vnodeID;
|
||||
- (santa_action_t)checkCache:(uint64_t)vnodeID;
|
||||
|
||||
@end
|
||||
|
||||
@@ -180,9 +180,14 @@ static const int MAX_DELAY = 15;
|
||||
return cache_count;
|
||||
}
|
||||
|
||||
- (BOOL)flushCache {
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly {
|
||||
const uint64_t nonRoot = nonRootOnly;
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientClearCache, 0, 0, 0, 0) == KERN_SUCCESS;
|
||||
kSantaUserClientClearCache,
|
||||
&nonRoot,
|
||||
1,
|
||||
0,
|
||||
0) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
- (santa_action_t)checkCache:(uint64_t)vnodeID {
|
||||
|
||||
@@ -216,8 +216,6 @@
|
||||
}
|
||||
|
||||
- (void)logDiskAppeared:(NSDictionary *)diskProperties {
|
||||
if (![diskProperties[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
NSString *dmgPath = @"";
|
||||
NSString *serial = @"";
|
||||
if ([diskProperties[@"DADeviceModel"] isEqual:@"Disk Image"]) {
|
||||
@@ -252,8 +250,6 @@
|
||||
}
|
||||
|
||||
- (void)logDiskDisappeared:(NSDictionary *)diskProperties {
|
||||
if (![diskProperties[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
LOGI(@"action=DISKDISAPPEAR|mount=%@|volume=%@|bsdname=%@",
|
||||
[diskProperties[@"DAVolumePath"] path] ?: @"",
|
||||
diskProperties[@"DAVolumeName"] ?: @"",
|
||||
|
||||
@@ -104,7 +104,8 @@
|
||||
|
||||
/// Call in-kernel function: |kSantaUserClientClearCache|
|
||||
- (void)flushCache {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, 0, 0, 0, 0);
|
||||
uint64_t nonRootOnly = 0;
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, &nonRootOnly, 1, 0, 0);
|
||||
}
|
||||
|
||||
#pragma mark - Connection Tests
|
||||
|
||||
Reference in New Issue
Block a user