mirror of
https://github.com/google/santa.git
synced 2026-01-14 16:58:02 -05:00
388 lines
14 KiB
Plaintext
388 lines
14 KiB
Plaintext
/// Copyright 2015-2022 Google Inc. All rights reserved.
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
|
|
#import "Source/santad/SNTDaemonControlController.h"
|
|
|
|
#import <MOLXPCConnection/MOLXPCConnection.h>
|
|
|
|
#include <memory>
|
|
|
|
#import "Source/common/SNTCachedDecision.h"
|
|
#import "Source/common/SNTCommonEnums.h"
|
|
#import "Source/common/SNTConfigurator.h"
|
|
#import "Source/common/SNTLogging.h"
|
|
#import "Source/common/SNTMetricSet.h"
|
|
#import "Source/common/SNTRule.h"
|
|
#import "Source/common/SNTRuleIdentifiers.h"
|
|
#import "Source/common/SNTStoredEvent.h"
|
|
#import "Source/common/SNTStrengthify.h"
|
|
#import "Source/common/SNTXPCNotifierInterface.h"
|
|
#import "Source/common/SNTXPCSyncServiceInterface.h"
|
|
#import "Source/santad/DataLayer/SNTEventTable.h"
|
|
#import "Source/santad/DataLayer/SNTRuleTable.h"
|
|
#import "Source/santad/SNTDatabaseController.h"
|
|
#import "Source/santad/SNTNotificationQueue.h"
|
|
#import "Source/santad/SNTPolicyProcessor.h"
|
|
#import "Source/santad/SNTSyncdQueue.h"
|
|
|
|
using santa::AuthResultCache;
|
|
using santa::FlushCacheMode;
|
|
using santa::FlushCacheReason;
|
|
using santa::Logger;
|
|
using santa::WatchItems;
|
|
using santa::WatchItemsState;
|
|
|
|
// Globals used by the santad watchdog thread
|
|
uint64_t watchdogCPUEvents = 0;
|
|
uint64_t watchdogRAMEvents = 0;
|
|
double watchdogCPUPeak = 0;
|
|
double watchdogRAMPeak = 0;
|
|
|
|
@interface SNTDaemonControlController ()
|
|
@property SNTPolicyProcessor *policyProcessor;
|
|
@property SNTNotificationQueue *notQueue;
|
|
@property SNTSyncdQueue *syncdQueue;
|
|
@end
|
|
|
|
@implementation SNTDaemonControlController {
|
|
std::shared_ptr<AuthResultCache> _authResultCache;
|
|
std::shared_ptr<Logger> _logger;
|
|
std::shared_ptr<WatchItems> _watchItems;
|
|
}
|
|
|
|
- (instancetype)initWithAuthResultCache:(std::shared_ptr<AuthResultCache>)authResultCache
|
|
notificationQueue:(SNTNotificationQueue *)notQueue
|
|
syncdQueue:(SNTSyncdQueue *)syncdQueue
|
|
logger:(std::shared_ptr<Logger>)logger
|
|
watchItems:(std::shared_ptr<WatchItems>)watchItems {
|
|
self = [super init];
|
|
if (self) {
|
|
_logger = logger;
|
|
_policyProcessor =
|
|
[[SNTPolicyProcessor alloc] initWithRuleTable:[SNTDatabaseController ruleTable]];
|
|
_authResultCache = authResultCache;
|
|
_watchItems = std::move(watchItems);
|
|
_notQueue = notQueue;
|
|
_syncdQueue = syncdQueue;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark Cache ops
|
|
|
|
- (void)cacheCounts:(void (^)(uint64_t, uint64_t))reply {
|
|
NSArray<NSNumber *> *counts = self->_authResultCache->CacheCounts();
|
|
reply([counts[0] unsignedLongLongValue], [counts[1] unsignedLongLongValue]);
|
|
}
|
|
|
|
- (void)flushCache:(void (^)(BOOL))reply {
|
|
self->_authResultCache->FlushCache(FlushCacheMode::kAllCaches,
|
|
FlushCacheReason::kExplicitCommand);
|
|
reply(YES);
|
|
}
|
|
|
|
- (void)checkCacheForVnodeID:(SantaVnode)vnodeID withReply:(void (^)(SNTAction))reply {
|
|
reply(self->_authResultCache->CheckCache(vnodeID));
|
|
}
|
|
|
|
#pragma mark Database ops
|
|
|
|
- (void)databaseRuleCounts:(void (^)(RuleCounts ruleTypeCounts))reply {
|
|
SNTRuleTable *rdb = [SNTDatabaseController ruleTable];
|
|
RuleCounts ruleCounts{
|
|
.binary = [rdb binaryRuleCount],
|
|
.certificate = [rdb certificateRuleCount],
|
|
.compiler = [rdb compilerRuleCount],
|
|
.transitive = [rdb transitiveRuleCount],
|
|
.teamID = [rdb teamIDRuleCount],
|
|
.signingID = [rdb signingIDRuleCount],
|
|
.cdhash = [rdb cdhashRuleCount],
|
|
};
|
|
|
|
reply(ruleCounts);
|
|
}
|
|
|
|
- (void)databaseRuleAddRules:(NSArray *)rules
|
|
ruleCleanup:(SNTRuleCleanup)cleanupType
|
|
reply:(void (^)(NSError *error))reply {
|
|
SNTRuleTable *ruleTable = [SNTDatabaseController ruleTable];
|
|
|
|
// If any rules are added that are not plain allowlist rules, then flush decision cache.
|
|
// In particular, the addition of allowlist compiler rules should cause a cache flush.
|
|
// We also flush cache if a allowlist compiler rule is replaced with a allowlist rule.
|
|
BOOL flushCache =
|
|
((cleanupType != SNTRuleCleanupNone) || [ruleTable addedRulesShouldFlushDecisionCache:rules]);
|
|
|
|
NSError *error;
|
|
[ruleTable addRules:rules ruleCleanup:cleanupType error:&error];
|
|
|
|
// Whenever we add rules, we can also check for and remove outdated transitive rules.
|
|
[ruleTable removeOutdatedTransitiveRules];
|
|
|
|
// The actual cache flushing happens after the new rules have been added to the database.
|
|
if (flushCache) {
|
|
LOGI(@"Flushing caches");
|
|
self->_authResultCache->FlushCache(FlushCacheMode::kAllCaches, FlushCacheReason::kRulesChanged);
|
|
}
|
|
|
|
reply(error);
|
|
}
|
|
|
|
- (void)databaseEventCount:(void (^)(int64_t count))reply {
|
|
reply([[SNTDatabaseController eventTable] pendingEventsCount]);
|
|
}
|
|
|
|
- (void)databaseEventsPending:(void (^)(NSArray *events))reply {
|
|
reply([[SNTDatabaseController eventTable] pendingEvents]);
|
|
}
|
|
|
|
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids {
|
|
[[SNTDatabaseController eventTable] deleteEventsWithIds:ids];
|
|
}
|
|
|
|
- (void)databaseRuleForIdentifiers:(SNTRuleIdentifiers *)identifiers
|
|
reply:(void (^)(SNTRule *))reply {
|
|
reply([[SNTDatabaseController ruleTable] ruleForIdentifiers:[identifiers toStruct]]);
|
|
}
|
|
|
|
- (void)staticRuleCount:(void (^)(int64_t count))reply {
|
|
reply([SNTConfigurator configurator].staticRules.count);
|
|
}
|
|
|
|
- (void)retrieveAllRules:(void (^)(NSArray<SNTRule *> *, NSError *))reply {
|
|
SNTConfigurator *config = [SNTConfigurator configurator];
|
|
|
|
// Do not return any rules if syncBaseURL is set and return an error.
|
|
if (config.syncBaseURL) {
|
|
reply(@[], [NSError errorWithDomain:@"com.google.santad"
|
|
code:403 // (TODO) define error code
|
|
userInfo:@{NSLocalizedDescriptionKey : @"SyncBaseURL is set"}]);
|
|
return;
|
|
}
|
|
|
|
NSArray<SNTRule *> *rules = [[SNTDatabaseController ruleTable] retrieveAllRules];
|
|
reply(rules, nil);
|
|
}
|
|
|
|
#pragma mark Decision Ops
|
|
|
|
- (void)decisionForFilePath:(NSString *)filePath
|
|
identifiers:(SNTRuleIdentifiers *)identifiers
|
|
reply:(void (^)(SNTEventState))reply {
|
|
reply([self.policyProcessor decisionForFilePath:filePath identifiers:identifiers].decision);
|
|
}
|
|
|
|
#pragma mark Config Ops
|
|
|
|
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply {
|
|
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
|
|
}
|
|
|
|
- (void)watchItemsState:(void (^)(BOOL, uint64_t, NSString *, NSString *, NSTimeInterval))reply {
|
|
std::optional<WatchItemsState> optionalState = self->_watchItems->State();
|
|
|
|
if (!optionalState.has_value()) {
|
|
reply(NO, 0, nil, nil, 0);
|
|
} else {
|
|
WatchItemsState state = optionalState.value();
|
|
|
|
reply(YES, state.rule_count, state.policy_version, state.config_path,
|
|
state.last_config_load_epoch);
|
|
}
|
|
}
|
|
|
|
- (void)clientMode:(void (^)(SNTClientMode))reply {
|
|
reply([[SNTConfigurator configurator] clientMode]);
|
|
}
|
|
|
|
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setSyncServerClientMode:mode];
|
|
reply();
|
|
}
|
|
|
|
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply {
|
|
reply([[SNTConfigurator configurator] fullSyncLastSuccess]);
|
|
}
|
|
|
|
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setFullSyncLastSuccess:date];
|
|
reply();
|
|
}
|
|
|
|
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply {
|
|
reply([[SNTConfigurator configurator] ruleSyncLastSuccess]);
|
|
}
|
|
|
|
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setRuleSyncLastSuccess:date];
|
|
reply();
|
|
}
|
|
|
|
- (void)syncTypeRequired:(void (^)(SNTSyncType))reply {
|
|
reply([[SNTConfigurator configurator] syncTypeRequired]);
|
|
}
|
|
|
|
- (void)setSyncTypeRequired:(SNTSyncType)syncType reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setSyncTypeRequired:syncType];
|
|
reply();
|
|
}
|
|
|
|
- (void)setAllowedPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
|
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
|
options:0
|
|
error:NULL];
|
|
[[SNTConfigurator configurator] setSyncServerAllowedPathRegex:re];
|
|
reply();
|
|
}
|
|
|
|
- (void)setBlockedPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
|
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
|
options:0
|
|
error:NULL];
|
|
[[SNTConfigurator configurator] setSyncServerBlockedPathRegex:re];
|
|
reply();
|
|
}
|
|
|
|
- (void)blockUSBMount:(void (^)(BOOL))reply {
|
|
reply([[SNTConfigurator configurator] blockUSBMount]);
|
|
}
|
|
|
|
- (void)setBlockUSBMount:(BOOL)enabled reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setBlockUSBMount:enabled];
|
|
reply();
|
|
}
|
|
|
|
- (void)remountUSBMode:(void (^)(NSArray<NSString *> *))reply {
|
|
reply([[SNTConfigurator configurator] remountUSBMode]);
|
|
}
|
|
|
|
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setRemountUSBMode:remountUSBMode];
|
|
reply();
|
|
}
|
|
|
|
- (void)setOverrideFileAccessAction:(NSString *)action reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setSyncServerOverrideFileAccessAction:action];
|
|
reply();
|
|
}
|
|
|
|
- (void)enableBundles:(void (^)(BOOL))reply {
|
|
reply([SNTConfigurator configurator].enableBundles);
|
|
}
|
|
|
|
- (void)setEnableBundles:(BOOL)enableBundles reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setEnableBundles:enableBundles];
|
|
reply();
|
|
}
|
|
|
|
- (void)enableTransitiveRules:(void (^)(BOOL))reply {
|
|
reply([SNTConfigurator configurator].enableTransitiveRules);
|
|
}
|
|
|
|
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setEnableTransitiveRules:enabled];
|
|
reply();
|
|
}
|
|
|
|
- (void)enableAllEventUpload:(void (^)(BOOL))reply {
|
|
reply([SNTConfigurator configurator].enableAllEventUpload);
|
|
}
|
|
|
|
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setEnableAllEventUpload:enabled];
|
|
reply();
|
|
}
|
|
|
|
- (void)disableUnknownEventUpload:(void (^)(BOOL))reply {
|
|
reply([SNTConfigurator configurator].disableUnknownEventUpload);
|
|
}
|
|
|
|
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
|
|
[[SNTConfigurator configurator] setDisableUnknownEventUpload:enabled];
|
|
reply();
|
|
}
|
|
|
|
#pragma mark Metrics Ops
|
|
|
|
- (void)metrics:(void (^)(NSDictionary *))reply {
|
|
SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
|
|
reply([metricSet export]);
|
|
}
|
|
|
|
#pragma mark GUI Ops
|
|
|
|
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener {
|
|
// This will leak the underlying NSXPCConnection when "fast user switching" occurs.
|
|
// It is not worth the trouble to fix. Maybe future self will feel differently.
|
|
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithListener:listener];
|
|
c.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
|
|
[c resume];
|
|
self.notQueue.notifierConnection = c;
|
|
}
|
|
|
|
#pragma mark syncd Ops
|
|
|
|
- (void)pushNotifications:(void (^)(BOOL))reply {
|
|
[self.syncdQueue.syncConnection.remoteObjectProxy isFCMListening:^(BOOL response) {
|
|
reply(response);
|
|
}];
|
|
}
|
|
|
|
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)(void))reply {
|
|
[[self.notQueue.notifierConnection remoteObjectProxy]
|
|
postRuleSyncNotificationWithCustomMessage:message];
|
|
reply();
|
|
}
|
|
|
|
///
|
|
/// Used by SantaGUI sync the offending event and potentially all the related events,
|
|
/// if the sync server has not seen them before.
|
|
///
|
|
/// @param event The offending event, fileBundleHash & fileBundleBinaryCount need to be populated.
|
|
/// @param events Next bundle events.
|
|
///
|
|
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events {
|
|
SNTEventTable *eventTable = [SNTDatabaseController eventTable];
|
|
|
|
// Delete the event cached by the execution controller.
|
|
[eventTable deleteEventWithId:event.idx];
|
|
|
|
// Add the updated event.
|
|
[eventTable addStoredEvent:event];
|
|
|
|
// Log all of the generated bundle events.
|
|
self->_logger->LogBundleHashingEvents(events);
|
|
|
|
WEAKIFY(self);
|
|
|
|
// Sync the updated event. If the sync server needs the related events, add them to the eventTable
|
|
// and upload them too.
|
|
[self.syncdQueue addBundleEvent:event
|
|
reply:^(SNTBundleEventAction action) {
|
|
STRONGIFY(self);
|
|
switch (action) {
|
|
case SNTBundleEventActionDropEvents: break;
|
|
case SNTBundleEventActionStoreEvents:
|
|
[eventTable addStoredEvents:events];
|
|
break;
|
|
case SNTBundleEventActionSendEvents:
|
|
[eventTable addStoredEvents:events];
|
|
[self.syncdQueue addEvents:events isFromBundle:YES];
|
|
break;
|
|
}
|
|
}];
|
|
}
|
|
|
|
@end
|