diff --git a/Source/common/SNTFileInfo.m b/Source/common/SNTFileInfo.m index d237cae1..533b8641 100644 --- a/Source/common/SNTFileInfo.m +++ b/Source/common/SNTFileInfo.m @@ -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; } } diff --git a/Source/common/SNTLogging.h b/Source/common/SNTLogging.h index e5e0b387..ae79d252 100644 --- a/Source/common/SNTLogging.h +++ b/Source/common/SNTLogging.h @@ -24,13 +24,13 @@ #include #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 diff --git a/Source/santa-driver/SantaDecisionManager.cc b/Source/santa-driver/SantaDecisionManager.cc index 88e3b758..c8e8bf10 100644 --- a/Source/santa-driver/SantaDecisionManager.cc +++ b/Source/santa-driver/SantaDecisionManager.cc @@ -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(10000, 2); + root_decision_cache_ = new SantaCache(5000, 2); + non_root_decision_cache_ = new SantaCache(500, 2); vnode_pid_map_ = new SantaCache(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* 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; } } diff --git a/Source/santa-driver/SantaDecisionManager.h b/Source/santa-driver/SantaDecisionManager.h index 181174bb..ddae66e7 100644 --- a/Source/santa-driver/SantaDecisionManager.h +++ b/Source/santa-driver/SantaDecisionManager.h @@ -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 *decision_cache_; + SantaCache *root_decision_cache_; + SantaCache *non_root_decision_cache_; SantaCache *vnode_pid_map_; + /** + Return the correct cache for a given identifier. + + @param identifier The identifier + @return SantaCache* The cache to use + */ + SantaCache* 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_; diff --git a/Source/santa-driver/SantaDriverClient.cc b/Source/santa-driver/SantaDriverClient.cc index eff88efe..32219013 100644 --- a/Source/santa-driver/SantaDriverClient.cc +++ b/Source/santa-driver/SantaDriverClient.cc @@ -133,7 +133,7 @@ IOReturn SantaDriverClient::allow_binary( SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target); if (!me) return kIOReturnBadArgument; - const uint64_t vnode_id = static_cast(*arguments->scalarInput); + const uint64_t vnode_id = static_cast(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(*arguments->scalarInput); + const uint64_t vnode_id = static_cast(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(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(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 } }; diff --git a/Source/santad/SNTApplication.m b/Source/santad/SNTApplication.m index cb77a37d..17700ccc 100644 --- a/Source/santad/SNTApplication.m +++ b/Source/santad/SNTApplication.m @@ -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 diff --git a/Source/santad/SNTDaemonControlController.m b/Source/santad/SNTDaemonControlController.m index 672f2be1..cb362cd2 100644 --- a/Source/santad/SNTDaemonControlController.m +++ b/Source/santad/SNTDaemonControlController.m @@ -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(); } diff --git a/Source/santad/SNTDriverManager.h b/Source/santad/SNTDriverManager.h index 8276a913..41bf0f74 100644 --- a/Source/santad/SNTDriverManager.h +++ b/Source/santad/SNTDriverManager.h @@ -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 diff --git a/Source/santad/SNTDriverManager.m b/Source/santad/SNTDriverManager.m index 6b1e9921..d8dc0656 100644 --- a/Source/santad/SNTDriverManager.m +++ b/Source/santad/SNTDriverManager.m @@ -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 { diff --git a/Source/santad/SNTEventLog.m b/Source/santad/SNTEventLog.m index 52a1b0fa..ed757d21 100644 --- a/Source/santad/SNTEventLog.m +++ b/Source/santad/SNTEventLog.m @@ -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"] ?: @"", diff --git a/Tests/KernelTests/main.mm b/Tests/KernelTests/main.mm index 3e4f551d..2ca2f901 100644 --- a/Tests/KernelTests/main.mm +++ b/Tests/KernelTests/main.mm @@ -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