diff --git a/Source/common/SNTLogging.m b/Source/common/SNTLogging.m index 4d88bc4e6..262a97aac 100644 --- a/Source/common/SNTLogging.m +++ b/Source/common/SNTLogging.m @@ -88,7 +88,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) { break; case LOG_LEVEL_DEBUG: levelName = "D"; - syslogLevel = ASL_LEVEL_DEBUG; + // Log debug messages at the same ASL level as INFO. + // While it would make sense to use DEBUG, watching debug-level logs + // in Console means enabling all debug logs, which is absurdly noisy. + syslogLevel = ASL_LEVEL_NOTICE; break; } diff --git a/Source/santad/EventProviders/SNTCachingEndpointSecurityManager.mm b/Source/santad/EventProviders/SNTCachingEndpointSecurityManager.mm index 18d711196..412cf6688 100644 --- a/Source/santad/EventProviders/SNTCachingEndpointSecurityManager.mm +++ b/Source/santad/EventProviders/SNTCachingEndpointSecurityManager.mm @@ -17,7 +17,7 @@ #import "Source/common/SantaCache.h" #import "Source/common/SNTLogging.h" -#include +#include #include uint64_t GetCurrentUptime() { @@ -28,7 +28,6 @@ uint64_t GetCurrentUptime() { } @implementation SNTCachingEndpointSecurityManager { - std::atomic _compilerPIDs[PID_MAX]; SantaCache *_decisionCache; } @@ -56,10 +55,20 @@ - (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) { // If item is in cache but hasn't received a response yet, sleep for a bit. // If item is not in cache, break out of loop and forward request to callback. if (RESPONSE_VALID(return_action)) { - if (return_action == ACTION_RESPOND_ALLOW) { - es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true); - } else { - es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false); + switch (return_action) { + case ACTION_RESPOND_ALLOW: + es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true); + break; + case ACTION_RESPOND_ALLOW_COMPILER: { + pid_t pid = audit_token_to_pid(m->process->audit_token); + [self setIsCompilerPID:pid]; + // Don't let ES cache compilers + es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false); + break; + } + default: + es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false); + break; } return YES; } else if (return_action == ACTION_REQUEST_BINARY || return_action == ACTION_RESPOND_ACK) { @@ -70,7 +79,7 @@ - (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) { } } - [self addToCache:vnode_id decision:ACTION_REQUEST_BINARY timeout:0]; + [self addToCache:vnode_id decision:ACTION_REQUEST_BINARY currentTicks:0]; return NO; } @@ -79,25 +88,24 @@ - (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm es_respond_result_t ret; switch (action) { case ACTION_RESPOND_ALLOW_COMPILER: - if (sm.pid >= PID_MAX) { - LOGE(@"Unable to watch compiler pid=%d >= pid_max=%d", sm.pid, PID_MAX); - } else { - LOGD(@"Watching compiler pid=%d path=%s", sm.pid, sm.path); - self->_compilerPIDs[sm.pid].store(true); - } - // Allow the exec, but don't cache the decision so subsequent execs of the compiler get - // marked appropriately. + [self setIsCompilerPID:sm.pid]; + + // Allow the exec and cache in our internal cache but don't let ES cache, because then + // we won't see future execs of the compiler in order to record the PID. + [self addToCache:sm.vnode_id + decision:ACTION_RESPOND_ALLOW_COMPILER + currentTicks:GetCurrentUptime()]; ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message, ES_AUTH_RESULT_ALLOW, false); break; case ACTION_RESPOND_ALLOW: case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: - [self addToCache:sm.vnode_id decision:ACTION_RESPOND_ALLOW timeout:GetCurrentUptime()]; + [self addToCache:sm.vnode_id decision:ACTION_RESPOND_ALLOW currentTicks:GetCurrentUptime()]; ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message, ES_AUTH_RESULT_ALLOW, true); break; case ACTION_RESPOND_DENY: - [self addToCache:sm.vnode_id decision:ACTION_RESPOND_DENY timeout:GetCurrentUptime()]; + [self addToCache:sm.vnode_id decision:ACTION_RESPOND_DENY currentTicks:GetCurrentUptime()]; OS_FALLTHROUGH; case ACTION_RESPOND_TOOLONG: ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message, @@ -114,7 +122,7 @@ - (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm - (void)addToCache:(santa_vnode_id_t)identifier decision:(santa_action_t)decision - timeout:(uint64_t)microsecs { + currentTicks:(uint64_t)microsecs { switch (decision) { case ACTION_REQUEST_BINARY: _decisionCache->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0); diff --git a/Source/santad/EventProviders/SNTEndpointSecurityManager.h b/Source/santad/EventProviders/SNTEndpointSecurityManager.h index c924449d8..2fcfcab49 100644 --- a/Source/santad/EventProviders/SNTEndpointSecurityManager.h +++ b/Source/santad/EventProviders/SNTEndpointSecurityManager.h @@ -25,6 +25,10 @@ const pid_t PID_MAX = 99999; @interface SNTEndpointSecurityManager : NSObject - (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file; +- (BOOL)isCompilerPID:(pid_t)pid; +- (void)setIsCompilerPID:(pid_t)pid; +- (void)setNotCompilerPID:(pid_t)pid; + @property(readonly, nonatomic) es_client_t *client; @end diff --git a/Source/santad/EventProviders/SNTEndpointSecurityManager.mm b/Source/santad/EventProviders/SNTEndpointSecurityManager.mm index 3a8048228..601d69abe 100644 --- a/Source/santad/EventProviders/SNTEndpointSecurityManager.mm +++ b/Source/santad/EventProviders/SNTEndpointSecurityManager.mm @@ -25,7 +25,9 @@ #include -@interface SNTEndpointSecurityManager () +@interface SNTEndpointSecurityManager () { + std::atomic _compilerPIDs[PID_MAX]; +} @property(nonatomic) SNTPrefixTree *prefixTree; @property(nonatomic, copy) void (^decisionCallback)(santa_message_t); @@ -36,9 +38,7 @@ @interface SNTEndpointSecurityManager () @end -@implementation SNTEndpointSecurityManager { - std::atomic _compilerPIDs[PID_MAX]; -} +@implementation SNTEndpointSecurityManager - (instancetype)init API_AVAILABLE(macos(10.15)) { self = [super init]; @@ -107,7 +107,7 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { [self removeCacheEntryForVnodeID:[self vnodeIDForFile:m->event.close.target]]; // Create a transitive rule if the file was modified by a running compiler - if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) { + if ([self isCompilerPID:pid]) { santa_message_t sm = {}; BOOL truncated = [self populateBufferFromESFile:m->event.close.target buffer:sm.path @@ -117,6 +117,9 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { sm.path, pid); break; } + if ([@(sm.path) hasPrefix:@"/dev/"]) { + break; + } sm.action = ACTION_NOTIFY_WHITELIST; sm.pid = pid; sm.pidversion = pidversion; @@ -129,7 +132,7 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { } case ES_EVENT_TYPE_NOTIFY_RENAME: { // Create a transitive rule if the file was renamed by a running compiler - if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) { + if ([self isCompilerPID:pid]) { santa_message_t sm = {}; BOOL truncated = [self populateRenamedNewPathFromESMessage:m->event.rename buffer:sm.path @@ -139,6 +142,9 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { sm.path, pid); break; } + if ([@(sm.path) hasPrefix:@"/dev/"]) { + break; + } sm.action = ACTION_NOTIFY_WHITELIST; sm.pid = pid; sm.pidversion = pidversion; @@ -151,7 +157,7 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { } case ES_EVENT_TYPE_NOTIFY_EXIT: { // Update the set of running compiler PIDs - if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false); + [self setNotCompilerPID:pid]; // Skip the standard pipeline and just log. if (![config enableForkAndExitLogging]) return; @@ -191,12 +197,12 @@ - (void)establishClient API_AVAILABLE(macos(10.15)) { switch (m->action_type) { case ES_ACTION_TYPE_AUTH: { - // Create a timer to deny the execution 2 seconds before the deadline, + // Create a timer to deny the execution 5 seconds before the deadline, // if a response hasn't already been sent. This block will still be enqueued if - // the the deadline - 2 secs is < DISPATCH_TIME_NOW. - // As of 10.15.2, a typical deadline is 60 seconds. + // the the deadline - 5 secs is < DISPATCH_TIME_NOW. + // As of 10.15.5, a typical deadline is 60 seconds. auto responded = std::make_shared>(false); - dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -2), self.esAuthQueue, ^(void) { + dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -5), self.esAuthQueue, ^(void) { if (responded->load()) return; LOGE(@"Deadline reached: deny pid=%d ret=%d", pid, es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false)); @@ -446,12 +452,8 @@ - (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm es_respond_result_t ret; switch (action) { case ACTION_RESPOND_ALLOW_COMPILER: - if (sm.pid >= PID_MAX) { - LOGE(@"Unable to watch compiler pid=%d >= pid_max=%d", sm.pid, PID_MAX); - } else { - LOGD(@"Watching compiler pid=%d path=%s", sm.pid, sm.path); - self->_compilerPIDs[sm.pid].store(true); - } + [self setIsCompilerPID:sm.pid]; + // Allow the exec, but don't cache the decision so subsequent execs of the compiler get // marked appropriately. ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message, @@ -564,4 +566,23 @@ - (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file { }; } +- (BOOL)isCompilerPID:(pid_t)pid { + return (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()); +} + +- (void)setIsCompilerPID:(pid_t)pid { + if (pid < 1) { + LOGE(@"Unable to watch compiler pid=%d", pid); + } else if (pid >= PID_MAX) { + LOGE(@"Unable to watch compiler pid=%d >= PID_MAX(%d)", pid, PID_MAX); + } else { + self->_compilerPIDs[pid].store(true); + LOGD(@"Watching compiler pid=%d", pid); + } +} + +- (void)setNotCompilerPID:(pid_t)pid { + if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false); +} + @end diff --git a/version.bzl b/version.bzl index 76fd1c148..9c7fd17be 100644 --- a/version.bzl +++ b/version.bzl @@ -1,3 +1,3 @@ """The version for all Santa components.""" -SANTA_VERSION = "2021.2" +SANTA_VERSION = "2021.3"