mirror of
https://github.com/google/santa.git
synced 2026-01-22 12:38:06 -05:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01df4623c7 | ||
|
|
c9cb91a22e | ||
|
|
1f9d60aecc | ||
|
|
52c5b5aade |
@@ -196,7 +196,7 @@
|
||||
/// If YES, enables bundle detection for blocked events. This property is not stored on disk.
|
||||
/// Its value is set by a sync server that supports bundles. Defaults to NO.
|
||||
///
|
||||
@property BOOL bundlesEnabled;
|
||||
@property BOOL enableBundles;
|
||||
|
||||
#pragma mark Transitive Whitelisting Settings
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
/// whitelist any executables that they produce. If NO, SNTRuleStateWhitelistCompiler rules are
|
||||
/// interpreted as if they were simply SNTRuleStateWhitelist rules. Defaults to NO.
|
||||
///
|
||||
@property BOOL transitiveWhitelistingEnabled;
|
||||
@property BOOL enableTransitiveWhitelisting;
|
||||
|
||||
#pragma mark Server Auth Settings
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration"
|
||||
|
||||
// The keys managed by a sync server or mobileconfig.
|
||||
static NSString *const kClientModeKey = @"ClientMode";
|
||||
static NSString *const kTransitiveWhitelistingEnabledKey = @"TransitiveWhitelistingEnabled";
|
||||
static NSString *const kEnableTransitiveWhitelistingKey = @"EnableTransitiveWhitelisting";
|
||||
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
|
||||
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
|
||||
|
||||
@@ -95,7 +95,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
Class data = [NSData class];
|
||||
_syncServerKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kTransitiveWhitelistingEnabledKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
kFullSyncLastSuccess : date,
|
||||
@@ -104,7 +104,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
};
|
||||
_forcedConfigKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kTransitiveWhitelistingEnabledKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kFileChangesRegexKey : re,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
@@ -290,8 +290,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingTransitiveWhitelistingEnabled {
|
||||
return [self configStateSet];
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableTransitiveWhitelisting {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
@@ -318,12 +318,16 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)transitiveWhitelistingEnabled {
|
||||
return [self.configState[kTransitiveWhitelistingEnabledKey] boolValue];
|
||||
- (BOOL)enableTransitiveWhitelisting {
|
||||
NSNumber *n = self.syncState[kEnableTransitiveWhitelistingKey];
|
||||
if (n) {
|
||||
return [n boolValue];
|
||||
}
|
||||
return [self.configState[kEnableTransitiveWhitelistingKey] boolValue];
|
||||
}
|
||||
|
||||
- (void)setTransitiveWhitelistingEnabled:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kTransitiveWhitelistingEnabledKey value:@(enabled)];
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kEnableTransitiveWhitelistingKey value:@(enabled)];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)whitelistPathRegex {
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
- (void)setTransitiveWhitelistingEnabled:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
///
|
||||
/// Kernel ops
|
||||
///
|
||||
- (void)cacheCounts:(void (^)(uint64_t count))reply;
|
||||
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
|
||||
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
|
||||
@@ -73,8 +73,8 @@
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply;
|
||||
- (void)transitiveWhitelistingEnabled:(void (^)(BOOL))reply;
|
||||
- (void)enableBundles:(void (^)(BOOL))reply;
|
||||
- (void)enableTransitiveWhitelisting:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
|
||||
@@ -41,7 +41,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<santa_vnode_id_t, uint64_t>(10000, 2);
|
||||
root_decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(5000, 2);
|
||||
non_root_decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(500, 2);
|
||||
vnode_pid_map_ = new SantaCache<santa_vnode_id_t, uint64_t>(2000, 5);
|
||||
compiler_pid_set_ = new SantaCache<pid_t, pid_t>(500, 5);
|
||||
|
||||
@@ -54,12 +55,14 @@ bool SantaDecisionManager::init() {
|
||||
if (!log_dataqueue_) return kIOReturnNoMemory;
|
||||
|
||||
client_pid_ = 0;
|
||||
root_fsid_ = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
delete decision_cache_;
|
||||
delete root_decision_cache_;
|
||||
delete non_root_decision_cache_;
|
||||
delete vnode_pid_map_;
|
||||
|
||||
StopPidMonitorThreads();
|
||||
@@ -102,6 +105,17 @@ void SantaDecisionManager::ConnectClient(pid_t pid) {
|
||||
|
||||
client_pid_ = pid;
|
||||
|
||||
// Determine root fsid
|
||||
vfs_context_t ctx = vfs_context_create(nullptr);
|
||||
if (ctx) {
|
||||
vnode_t root = vfs_rootvnode();
|
||||
if (root) {
|
||||
root_fsid_ = GetVnodeIDForVnode(ctx, root).fsid;
|
||||
vnode_put(root);
|
||||
}
|
||||
vfs_context_rele(ctx);
|
||||
}
|
||||
|
||||
// Any decisions made while the daemon wasn't
|
||||
// connected should be cleared
|
||||
ClearCache();
|
||||
@@ -292,14 +306,27 @@ uint32_t SantaDecisionManager::PidMonitorSleepTimeMilliseconds() const {
|
||||
|
||||
#pragma mark Cache Management
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *SantaDecisionManager::CacheForIdentifier(
|
||||
const santa_vnode_id_t identifier) {
|
||||
return (identifier.fsid == root_fsid_) ? root_decision_cache_ : non_root_decision_cache_;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
santa_vnode_id_t identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
switch (decision) {
|
||||
case ACTION_REQUEST_BINARY:
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
decision_cache->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
decision_cache->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
@@ -307,15 +334,15 @@ void SantaDecisionManager::AddToCache(
|
||||
case ACTION_RESPOND_DENY: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
if (!decision_cache_->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache_->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
if (!decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
decision_cache_->set(identifier, val, 0);
|
||||
decision_cache->set(identifier, val, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -327,21 +354,26 @@ void SantaDecisionManager::AddToCache(
|
||||
|
||||
void SantaDecisionManager::RemoveFromCache(santa_vnode_id_t identifier) {
|
||||
if (unlikely(identifier.fsid == 0 && identifier.fileid == 0)) return;
|
||||
decision_cache_->remove(identifier);
|
||||
CacheForIdentifier(identifier)->remove(identifier);
|
||||
wakeup((void *)identifier.unsafe_simple_id());
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::CacheCount() const {
|
||||
return decision_cache_->count();
|
||||
uint64_t SantaDecisionManager::RootCacheCount() const {
|
||||
return root_decision_cache_->count();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache() {
|
||||
decision_cache_->clear();
|
||||
uint64_t SantaDecisionManager::NonRootCacheCount() const {
|
||||
return non_root_decision_cache_->count();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache(bool non_root_only) {
|
||||
if (!non_root_only) root_decision_cache_->clear();
|
||||
non_root_decision_cache_->clear();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::CacheBucketCount(
|
||||
uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket) {
|
||||
decision_cache_->bucket_counts(per_bucket_counts, array_size, start_bucket);
|
||||
root_decision_cache_->bucket_counts(per_bucket_counts, array_size, start_bucket);
|
||||
}
|
||||
|
||||
#pragma mark Decision Fetching
|
||||
@@ -350,7 +382,9 @@ santa_action_t SantaDecisionManager::GetFromCache(santa_vnode_id_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.
|
||||
@@ -361,7 +395,7 @@ santa_action_t SantaDecisionManager::GetFromCache(santa_vnode_id_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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,11 +123,14 @@ class SantaDecisionManager : public OSObject {
|
||||
void RemoveFromCache(santa_vnode_id_t identifier);
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
uint64_t CacheCount() const;
|
||||
|
||||
/// Clears the cache.
|
||||
void ClearCache();
|
||||
uint64_t RootCacheCount() const;
|
||||
uint64_t NonRootCacheCount() const;
|
||||
|
||||
/**
|
||||
Clears the cache(s). If non_root_only is true, only the non-root cache
|
||||
is cleared.
|
||||
*/
|
||||
void ClearCache(bool non_root_only = false);
|
||||
|
||||
/**
|
||||
Fills out the per_bucket_counts array with the number of items in each bucket in the
|
||||
@@ -325,10 +328,23 @@ class SantaDecisionManager : public OSObject {
|
||||
return (uint64_t)((sec * 1000000) + usec);
|
||||
}
|
||||
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *root_decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *non_root_decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *vnode_pid_map_;
|
||||
SantaCache<pid_t, pid_t> *compiler_pid_set_;
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<santa_vnode_id_t, uint64_t>* CacheForIdentifier(const santa_vnode_id_t identifier);
|
||||
|
||||
// This is the file system ID of the root filesystem,
|
||||
// used to determine which cache to use for requests
|
||||
uint64_t root_fsid_;
|
||||
|
||||
lck_grp_t *sdm_lock_grp_;
|
||||
lck_grp_attr_t *sdm_lock_grp_attr_;
|
||||
lck_attr_t *sdm_lock_attr_;
|
||||
|
||||
@@ -189,7 +189,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;
|
||||
}
|
||||
@@ -212,7 +213,8 @@ IOReturn SantaDriverClient::cache_count(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
arguments->scalarOutput[0] = me->decisionManager->CacheCount();
|
||||
arguments->scalarOutput[0] = me->decisionManager->RootCacheCount();
|
||||
arguments->scalarOutput[1] = me->decisionManager->NonRootCacheCount();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
@@ -263,9 +265,9 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
{ &SantaDriverClient::allow_compiler, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::acknowledge_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::remove_cache_entry, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 1, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 2, 0 },
|
||||
{ &SantaDriverClient::check_cache, 0, sizeof(santa_vnode_id_t), 1, 0 },
|
||||
{ &SantaDriverClient::cache_bucket_count, 0, sizeof(santa_bucket_count_t),
|
||||
0, sizeof(santa_bucket_count_t) },
|
||||
|
||||
@@ -69,7 +69,7 @@ REGISTER_COMMAND_NAME(@"cachehistogram")
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("%4llu: %llu\n", k, v);
|
||||
printf("%4llu bucket[s] have %llu %s\n", v, k, k > 1 ? "entries" : "entry");
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
|
||||
@@ -87,10 +87,11 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
|
||||
|
||||
// Kext status
|
||||
__block uint64_t cacheCount = -1;
|
||||
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t count) {
|
||||
cacheCount = count;
|
||||
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
|
||||
rootCacheCount = rootCache;
|
||||
nonRootCacheCount = nonRootCache;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
@@ -145,18 +146,18 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
}];
|
||||
}
|
||||
|
||||
__block BOOL bundlesEnabled = NO;
|
||||
__block BOOL enableBundles = NO;
|
||||
if ([[SNTConfigurator configurator] syncBaseURL]) {
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] bundlesEnabled:^(BOOL response) {
|
||||
bundlesEnabled = response;
|
||||
[[self.daemonConn remoteObjectProxy] enableBundles:^(BOOL response) {
|
||||
enableBundles = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
}
|
||||
|
||||
__block BOOL transitiveWhitelistingEnabled = NO;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] transitiveWhitelistingEnabled:^(BOOL response) {
|
||||
[[self.daemonConn remoteObjectProxy] enableTransitiveWhitelisting:^(BOOL response) {
|
||||
transitiveWhitelistingEnabled = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
@@ -187,7 +188,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"watchdog_ram_peak" : @(ramPeak),
|
||||
},
|
||||
@"kernel" : @{
|
||||
@"cache_count" : @(cacheCount),
|
||||
@"root_cache_count" : @(rootCacheCount),
|
||||
@"non_root_cache_count": @(nonRootCacheCount),
|
||||
},
|
||||
@"database" : @{
|
||||
@"binary_rules" : @(binaryRuleCount),
|
||||
@@ -202,7 +204,7 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"last_successful_full" : fullSyncLastSuccessStr ?: @"null",
|
||||
@"last_successful_rule" : ruleSyncLastSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"Connected" : @"Disconnected",
|
||||
@"bundle_scanning" : @(bundlesEnabled),
|
||||
@"bundle_scanning" : @(enableBundles),
|
||||
@"transitive_whitelisting" : @(transitiveWhitelistingEnabled),
|
||||
},
|
||||
};
|
||||
@@ -219,7 +221,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
printf(">>> Kernel Info\n");
|
||||
printf(" %-25s | %lld\n", "Cache count", cacheCount);
|
||||
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
|
||||
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
|
||||
printf(">>> Database Info\n");
|
||||
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
@@ -235,7 +238,7 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(" %-25s | %s\n", "Last Successful Rule Sync", [ruleSyncLastSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Push Notifications",
|
||||
(pushNotifications ? "Connected" : "Disconnected"));
|
||||
printf(" %-25s | %s\n", "Bundle Scanning", (bundlesEnabled ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Bundle Scanning", (enableBundles ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Transitive Whitelisting",
|
||||
(transitiveWhitelistingEnabled ? "Yes" : "No"));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
return (@"If Santa is configured to synchronize with a server, "
|
||||
@"this is the command used for syncing.\n\n"
|
||||
@"Options:\n"
|
||||
@" --clean: Perform a clean sync, erasing all existing rules and requesting a"
|
||||
@" --clean: Perform a clean sync, erasing all existing rules and requesting a\n"
|
||||
@" clean sync from the server.");
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,10 @@ extern NSString *const kTransitiveRuleCount;
|
||||
extern NSString *const kFCMToken;
|
||||
extern NSString *const kFCMFullSyncInterval;
|
||||
extern NSString *const kFCMGlobalRuleSyncDeadline;
|
||||
extern NSString *const kBundlesEnabled;
|
||||
extern NSString *const kTransitiveWhitelistingEnabled;
|
||||
extern NSString *const kEnableBundles;
|
||||
extern NSString *const kEnableBundles_OLD;
|
||||
extern NSString *const kEnableTransitiveWhitelisting;
|
||||
extern NSString *const kEnableTransitiveWhitelisting_OLD;
|
||||
|
||||
extern NSString *const kEvents;
|
||||
extern NSString *const kFileSHA256;
|
||||
|
||||
@@ -38,8 +38,12 @@ NSString *const kTransitiveRuleCount = @"transitive_rule_count";
|
||||
NSString *const kFCMToken = @"fcm_token";
|
||||
NSString *const kFCMFullSyncInterval = @"fcm_full_sync_interval";
|
||||
NSString *const kFCMGlobalRuleSyncDeadline = @"fcm_global_rule_sync_deadline";
|
||||
NSString *const kBundlesEnabled = @"bundles_enabled";
|
||||
NSString *const kTransitiveWhitelistingEnabled = @"transitive_whitelisting_enabled";
|
||||
|
||||
// NOTE: Both of the _OLD values will be removed at some indeterminate point in the future.
|
||||
NSString *const kEnableBundles = @"enable_bundles";
|
||||
NSString *const kEnableBundles_OLD = @"bundles_enabled";
|
||||
NSString *const kEnableTransitiveWhitelisting = @"enabled_transitive_whitelisting";
|
||||
NSString *const kEnableTransitiveWhitelisting_OLD = @"transitive_whitelisting_enabled";
|
||||
|
||||
NSString *const kEvents = @"events";
|
||||
NSString *const kFileSHA256 = @"file_sha256";
|
||||
|
||||
@@ -87,17 +87,23 @@
|
||||
if (!resp) return NO;
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setBundlesEnabled:[resp[kBundlesEnabled] boolValue] reply:^{
|
||||
NSNumber *enableBundles = resp[kEnableBundles];
|
||||
if (!enableBundles) {
|
||||
enableBundles = resp[kEnableBundles_OLD];
|
||||
}
|
||||
[[self.daemonConn remoteObjectProxy] setEnableBundles:[enableBundles boolValue] reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_enter(group);
|
||||
if ([resp[kTransitiveWhitelistingEnabled] respondsToSelector:@selector(boolValue)]) {
|
||||
BOOL enabled = [resp[kTransitiveWhitelistingEnabled] boolValue];
|
||||
[[self.daemonConn remoteObjectProxy] setTransitiveWhitelistingEnabled:enabled reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
NSNumber *enableTransitiveWhitelisting = resp[kEnableTransitiveWhitelisting];
|
||||
if (!enableTransitiveWhitelisting) {
|
||||
enableTransitiveWhitelisting = resp[kEnableTransitiveWhitelisting_OLD];
|
||||
}
|
||||
BOOL enabled = [enableTransitiveWhitelisting boolValue];
|
||||
[[self.daemonConn remoteObjectProxy] setEnableTransitiveWhitelisting:enabled reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
|
||||
self.syncState.FCMToken = resp[kFCMToken];
|
||||
|
||||
@@ -40,7 +40,9 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
@implementation SNTRuleTable
|
||||
|
||||
- (NSArray *)criticalSystemBinaryPaths {
|
||||
return @[ @"/usr/libexec/trustd", @"/usr/sbin/securityd", @"/usr/libexec/xpcproxy" ];
|
||||
return @[
|
||||
@"/usr/libexec/trustd", @"/usr/sbin/securityd", @"/usr/libexec/xpcproxy", @"/usr/sbin/ocspd"
|
||||
];
|
||||
}
|
||||
|
||||
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
|
||||
|
||||
@@ -264,7 +264,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
if (![props[@"DAVolumeMountable"] boolValue]) return;
|
||||
|
||||
[app.eventLog logDiskDisappeared:props];
|
||||
[app.driverManager flushCache];
|
||||
[app.driverManager flushCacheNonRootOnly:YES];
|
||||
}
|
||||
|
||||
- (void)startSyncd {
|
||||
@@ -316,7 +316,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
if (!new && !old) return;
|
||||
if (![new.pattern isEqualToString:old.pattern]) {
|
||||
LOGI(@"Changed [white|black]list regex, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,7 +324,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
- (void)clientModeDidChange:(SNTClientMode)clientMode {
|
||||
if (clientMode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
[[self.notQueue.notifierConnection remoteObjectProxy] postClientModeNotification:clientMode];
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ double watchdogRAMPeak = 0;
|
||||
|
||||
#pragma mark Kernel ops
|
||||
|
||||
- (void)cacheCounts:(void (^)(uint64_t))reply {
|
||||
uint64_t count = [self.driverManager cacheCount];
|
||||
reply(count);
|
||||
- (void)cacheCounts:(void (^)(uint64_t, uint64_t))reply {
|
||||
NSArray<NSNumber *> *counts = [self.driverManager cacheCounts];
|
||||
reply([counts[0] unsignedLongLongValue], [counts[1] unsignedLongLongValue]);
|
||||
}
|
||||
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply {
|
||||
@@ -80,7 +80,7 @@ double watchdogRAMPeak = 0;
|
||||
}
|
||||
|
||||
- (void)flushCache:(void (^)(BOOL))reply {
|
||||
reply([self.driverManager flushCache]);
|
||||
reply([self.driverManager flushCacheNonRootOnly:NO]);
|
||||
}
|
||||
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply {
|
||||
@@ -121,7 +121,7 @@ double watchdogRAMPeak = 0;
|
||||
// The actual cache flushing happens after the new rules have been added to the database.
|
||||
if (flushCache) {
|
||||
LOGI(@"Flushing decision cache");
|
||||
[self.driverManager flushCache];
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
|
||||
reply(error);
|
||||
@@ -224,21 +224,21 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].bundlesEnabled);
|
||||
- (void)enableBundles:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].enableBundles);
|
||||
}
|
||||
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setBundlesEnabled:bundlesEnabled];
|
||||
- (void)setEnableBundles:(BOOL)enableBundles reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setEnableBundles:enableBundles];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)transitiveWhitelistingEnabled:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].transitiveWhitelistingEnabled);
|
||||
- (void)enableTransitiveWhitelisting:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].enableTransitiveWhitelisting);
|
||||
}
|
||||
|
||||
- (void)setTransitiveWhitelistingEnabled:(BOOL)enabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setTransitiveWhitelistingEnabled:enabled];
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setEnableTransitiveWhitelisting:enabled];
|
||||
reply();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
///
|
||||
/// Get the number of binaries in the kernel's caches.
|
||||
///
|
||||
- (uint64_t)cacheCount;
|
||||
- (NSArray<NSNumber *> *)cacheCounts;
|
||||
|
||||
///
|
||||
/// Return an array representing all buckets in the kernel's decision cache where each number
|
||||
@@ -61,7 +61,7 @@
|
||||
///
|
||||
/// Flush the kernel's binary cache.
|
||||
///
|
||||
- (BOOL)flushCache;
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly;
|
||||
|
||||
///
|
||||
/// Check the kernel cache for a VnodeID.
|
||||
|
||||
@@ -213,9 +213,9 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
|
||||
}
|
||||
}
|
||||
|
||||
- (uint64_t)cacheCount {
|
||||
uint32_t input_count = 1;
|
||||
uint64_t cache_counts[1] = {0};
|
||||
- (NSArray<NSNumber *> *)cacheCounts {
|
||||
uint32_t input_count = 2;
|
||||
uint64_t cache_counts[2] = {0, 0};
|
||||
|
||||
IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientCacheCount,
|
||||
@@ -224,14 +224,16 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
|
||||
cache_counts,
|
||||
&input_count);
|
||||
|
||||
return cache_counts[0];
|
||||
return @[ @(cache_counts[0]), @(cache_counts[1]) ];
|
||||
}
|
||||
|
||||
- (BOOL)flushCache {
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly {
|
||||
const uint64_t nonRoot = nonRootOnly;
|
||||
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientClearCache,
|
||||
0,
|
||||
0,
|
||||
&nonRoot,
|
||||
1,
|
||||
0,
|
||||
0) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
if (action != ACTION_RESPOND_ALLOW && action != ACTION_RESPOND_ALLOW_COMPILER) {
|
||||
[_eventLog logDeniedExecution:cd withMessage:message];
|
||||
|
||||
if ([[SNTConfigurator configurator] bundlesEnabled] && binInfo.bundle) {
|
||||
if ([[SNTConfigurator configurator] enableBundles] && binInfo.bundle) {
|
||||
// If the binary is part of a bundle, find and hash all the related binaries in the bundle.
|
||||
// Let the GUI know hashing is needed. Once the hashing is complete the GUI will send a
|
||||
// message to santad to perform the upload logic for bundles.
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
// If transitive whitelisting is enabled, then SNTRuleStateWhiteListCompiler rules
|
||||
// become SNTEventStateAllowCompiler decisions. Otherwise we treat the rule as if
|
||||
// it were SNTRuleStateWhitelist.
|
||||
if ([[SNTConfigurator configurator] transitiveWhitelistingEnabled]) {
|
||||
if ([[SNTConfigurator configurator] enableTransitiveWhitelisting]) {
|
||||
cd.decision = SNTEventStateAllowCompiler;
|
||||
} else {
|
||||
cd.decision = SNTEventStateAllow;
|
||||
@@ -74,7 +74,7 @@
|
||||
// If transitive whitelisting is enabled, then SNTRuleStateWhitelistTransitive
|
||||
// rules become SNTEventStateAllowTransitive decisions. Otherwise, we treat the
|
||||
// rule as if it were SNTRuleStateUnknown.
|
||||
if ([[SNTConfigurator configurator] transitiveWhitelistingEnabled]) {
|
||||
if ([[SNTConfigurator configurator] enableTransitiveWhitelisting]) {
|
||||
cd.decision = SNTEventStateAllowTransitive;
|
||||
return cd;
|
||||
} else {
|
||||
|
||||
@@ -151,7 +151,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
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
- (void)testBinaryWhitelistCompilerRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator transitiveWhitelistingEnabled]).andReturn(YES);
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(YES);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistCompiler;
|
||||
@@ -184,7 +184,7 @@
|
||||
- (void)testBinaryWhitelistCompilerRuleDisabled {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator transitiveWhitelistingEnabled]).andReturn(NO);
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(NO);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistCompiler;
|
||||
@@ -200,7 +200,7 @@
|
||||
- (void)testBinaryWhitelistTransitiveRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator transitiveWhitelistingEnabled]).andReturn(YES);
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(YES);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistTransitive;
|
||||
@@ -217,7 +217,7 @@
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
OCMStub([self.mockConfigurator transitiveWhitelistingEnabled]).andReturn(NO);
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(NO);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistTransitive;
|
||||
|
||||
Reference in New Issue
Block a user