run santactl as a sync daemon (#129)

* run santactl as a sync daemon
This commit is contained in:
Tom Burgin
2016-11-16 14:41:12 -05:00
committed by GitHub
parent c134169ea1
commit dacff76694
21 changed files with 360 additions and 160 deletions

View File

@@ -179,9 +179,14 @@
C714F8B11D8044D400700EDF /* SNTCommandFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD5FBE1909D64A006B445C /* SNTCommandFileInfo.m */; };
C714F8B21D8044FE00700EDF /* SNTCommandController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */; };
C72E8D941D7F399900C86DD3 /* SNTCommandFileInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = C72E8D931D7F399900C86DD3 /* SNTCommandFileInfoTest.m */; };
C73A4B9A1DC10753007B6789 /* SNTSyncdQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */; };
C73A4B9B1DC10758007B6789 /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */; };
C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */; };
C795ED901D80A5BE007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; };
C795ED911D80B66B007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; };
C7FB56F61DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */; };
C7FB56F71DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */; };
C7FB57001DBFC213004E14EF /* SNTSyncdQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */; };
EFD8E30D32F6128B9E833D64 /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 873978BCE4B0DBD2A89C99D1 /* libPods-LogicTests.a */; };
/* End PBXBuildFile section */
@@ -420,6 +425,10 @@
C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandCheckCache.m; sourceTree = "<group>"; };
C795ED8E1D80A5BE007CFF42 /* SNTPolicyProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTPolicyProcessor.h; sourceTree = "<group>"; };
C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTPolicyProcessor.m; sourceTree = "<group>"; };
C7FB56F41DBFB480004E14EF /* SNTXPCSyncdInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTXPCSyncdInterface.h; sourceTree = "<group>"; };
C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTXPCSyncdInterface.m; sourceTree = "<group>"; };
C7FB56FE1DBFC213004E14EF /* SNTSyncdQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTSyncdQueue.h; sourceTree = "<group>"; };
C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTSyncdQueue.m; sourceTree = "<group>"; };
D227889DF327E7D3532FE00B /* Pods-Santa.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -725,6 +734,8 @@
0DCD605419115D17006B445C /* SNTXPCControlInterface.m */,
0DC8C9E3180CC3BC00FCFB29 /* SNTXPCNotifierInterface.h */,
0DCD604E19115A06006B445C /* SNTXPCNotifierInterface.m */,
C7FB56F41DBFB480004E14EF /* SNTXPCSyncdInterface.h */,
C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */,
);
path = common;
sourceTree = "<group>";
@@ -752,6 +763,8 @@
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */,
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */,
C7FB56FE1DBFC213004E14EF /* SNTSyncdQueue.h */,
C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */,
0D3AF83118F87CEF0087BCEE /* Resources */,
);
path = santad;
@@ -1295,6 +1308,7 @@
0D88680C1AC48A1400B86659 /* SNTSystemInfo.m in Sources */,
0D536EDC1B94E9230039A26D /* SNTEventLog.m in Sources */,
0DEA5F7D1CF64EB600704398 /* SNTCommandSyncRuleDownload.m in Sources */,
C73A4B9B1DC10758007B6789 /* SNTXPCSyncdInterface.m in Sources */,
0DB77FDB1CD14093004DF060 /* SNTBlockMessage.m in Sources */,
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
0D202D191CDD2EE500A88F16 /* SNTCommandSyncTest.m in Sources */,
@@ -1331,6 +1345,7 @@
0DCD605919115E5A006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */,
0D202D1B1CDD465400A88F16 /* SNTCommandSyncState.m in Sources */,
C73A4B9A1DC10753007B6789 /* SNTSyncdQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1350,6 +1365,7 @@
0D41640519197AD7006A356A /* SNTCommandSyncEventUpload.m in Sources */,
0D42D2B919D2042900955F08 /* SNTConfigurator.m in Sources */,
0DF395641AB76A7900CBC520 /* NSData+Zlib.m in Sources */,
C7FB56F71DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */,
0D10BE871A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */,
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */,
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */,
@@ -1412,10 +1428,12 @@
0D10BE861A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */,
0D63DD5C1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
0DCD604B19105433006B445C /* SNTStoredEvent.m in Sources */,
C7FB57001DBFC213004E14EF /* SNTSyncdQueue.m in Sources */,
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */,
0D9A7F421759330500035EB5 /* main.m in Sources */,
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */,
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */,
C7FB56F61DBFB480004E14EF /* SNTXPCSyncdInterface.m in Sources */,
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */,

View File

@@ -20,7 +20,7 @@
@interface SNTStoredEvent : NSObject<NSSecureCoding>
///
/// An index for this event, empty unless the event came from the database.
/// An index for this event, randomly generated during initialization.
///
@property NSNumber *idx;

View File

@@ -57,6 +57,14 @@
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
}
- (instancetype)init {
self = [super init];
if (self) {
_idx = @(arc4random());
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {

View File

@@ -41,9 +41,7 @@
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(NSError *error))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply;
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
- (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256
@@ -74,7 +72,6 @@
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply;
- (void)xsrfToken:(void (^)(NSString *))reply;
- (void)setXsrfToken:(NSString *)token reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
@@ -85,6 +82,12 @@
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
///
/// Syncd Ops
///
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
@end
@interface SNTXPCControlInterface : NSObject

View File

@@ -0,0 +1,33 @@
/// Copyright 2016 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 "SNTCommonEnums.h"
@class SNTStoredEvent;
/// Protocol implemented by santactl and utilized by santad
@protocol SNTSyncdXPC
- (void)postEventToSyncServer:(SNTStoredEvent *)event;
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds;
@end
@interface SNTXPCSyncdInterface : NSObject
///
/// Returns an initialized NSXPCInterface for the SNTSyncdXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)syncdInterface;
@end

View File

@@ -0,0 +1,23 @@
/// Copyright 2016 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 "SNTXPCSyncdInterface.h"
@implementation SNTXPCSyncdInterface
+ (NSXPCInterface *)syncdInterface {
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncdXPC)];
}
@end

View File

@@ -25,23 +25,30 @@
#import "SNTConfigurator.h"
#import "SNTDropRootPrivs.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
#import "SNTXPCSyncdInterface.h"
@interface SNTCommandSync : NSObject<SNTCommand>
@interface SNTCommandSync : NSObject<SNTCommand, SNTSyncdXPC>
@property SNTCommandSyncState *syncState;
@property SNTXPCConnection *listener;
@property dispatch_source_t syncTimer;
@property BOOL isDaemon;
@end
@implementation SNTCommandSync
REGISTER_COMMAND_NAME(@"sync")
#pragma mark SNTCommand protocol methods
+ (BOOL)requiresRoot {
return NO;
}
+ (BOOL)requiresDaemonConn {
return YES;
return NO;
}
+ (NSString *)shortHelpText {
@@ -64,7 +71,6 @@ REGISTER_COMMAND_NAME(@"sync")
}
SNTConfigurator *config = [SNTConfigurator configurator];
SNTCommandSync *s = [[self alloc] init];
// Gather some data needed during some sync stages
@@ -89,7 +95,8 @@ REGISTER_COMMAND_NAME(@"sync")
s.syncState.machineOwner = @"";
LOGW(@"Missing Machine Owner.");
}
[daemonConn resume];
[[daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
s.syncState.xsrfToken = token;
}];
@@ -132,25 +139,85 @@ REGISTER_COMMAND_NAME(@"sync")
s.syncState.session = [authURLSession session];
s.syncState.daemonConn = daemonConn;
s.isDaemon = [arguments containsObject:@"--daemon"];
if ([arguments containsObject:@"singleevent"]) {
NSUInteger idx = [arguments indexOfObject:@"singleevent"] + 1;
if (idx >= arguments.count) {
LOGI(@"singleevent takes an argument");
exit(1);
}
NSString *obj = arguments[idx];
if (obj.length != 64) {
LOGI(@"singleevent passed without SHA-256 as next argument");
exit(1);
}
return [s eventUploadSingleEvent:obj];
if (s.isDaemon) {
[s syncd];
} else {
return [s preflight];
[s preflight];
}
}
#pragma mark daemon methods
- (void)syncd {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
// Create listener for return connection from daemon.
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
self.listener.exportedInterface = [SNTXPCSyncdInterface syncdInterface];
self.listener.exportedObject = self;
self.listener.acceptedHandler = ^{
LOGD(@"santad <--> santactl connections established");
dispatch_semaphore_signal(sema);
};
self.listener.invalidationHandler = ^{
// If santad is unloaded kill santactl
LOGD(@"exiting");
exit(0);
};
[self.listener resume];
// Tell daemon to connect back to the above listener.
[[self.syncState.daemonConn remoteObjectProxy] setSyncdListener:listener.endpoint];
// Now wait for the connection to come in.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self performSelectorInBackground:@selector(syncd) withObject:nil];
}
self.syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
}
- (dispatch_source_t)createSyncTimer {
dispatch_source_t syncTimerQ = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_set_event_handler(syncTimerQ, ^{
[self rescheduleSyncSecondsFromNow:600];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
[self preflight];
});
dispatch_resume(syncTimerQ);
return syncTimerQ;
}
#pragma mark SNTSyncdXPC protocol methods
- (void)postEventToSyncServer:(SNTStoredEvent *)event {
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
if (event && [p uploadEvents:@[event]]) {
LOGD(@"Event upload complete");
} else {
LOGE(@"Event upload failed");
}
}
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds {
uint64_t interval = seconds * NSEC_PER_SEC;
uint64_t leeway = (seconds * 0.05) * NSEC_PER_SEC;
dispatch_source_set_timer(self.syncTimer, dispatch_walltime(NULL, interval), interval, leeway);
}
#pragma mark sync methods
- (void)preflight {
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
if ([p sync]) {
@@ -162,7 +229,7 @@ REGISTER_COMMAND_NAME(@"sync")
}
} else {
LOGE(@"Preflight failed, aborting run");
exit(1);
if (!self.isDaemon) exit(1);
}
}
@@ -183,18 +250,7 @@ REGISTER_COMMAND_NAME(@"sync")
return [self ruleDownload];
} else {
LOGE(@"Event upload failed, aborting run");
exit(1);
}
}
- (void)eventUploadSingleEvent:(NSString *)sha256 {
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
if ([p syncSingleEventWithSHA256:sha256]) {
LOGD(@"Event upload complete");
exit(0);
} else {
LOGE(@"Event upload failed");
exit(1);
if (!self.isDaemon) exit(1);
}
}
@@ -208,7 +264,7 @@ REGISTER_COMMAND_NAME(@"sync")
return [self postflight];
} else {
LOGE(@"Rule download failed, aborting run");
exit(1);
if (!self.isDaemon) exit(1);
}
}
@@ -227,10 +283,10 @@ REGISTER_COMMAND_NAME(@"sync")
if ([p sync]) {
LOGD(@"Postflight complete");
LOGI(@"Sync completed successfully");
exit(0);
if (!self.isDaemon) exit(0);
} else {
LOGE(@"Postflight failed");
exit(1);
if (!self.isDaemon) exit(1);
}
}

View File

@@ -16,7 +16,7 @@
@interface SNTCommandSyncEventUpload : SNTCommandSyncStage
- (BOOL)syncSingleEventWithSHA256:(NSString *)sha256;
- (BOOL)uploadEvents:(NSArray *)events;
- (BOOL)syncBundleEvents;

View File

@@ -44,17 +44,6 @@
return (dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) == 0);
}
- (BOOL)syncSingleEventWithSHA256:(NSString *)sha256 {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[self.daemonConn remoteObjectProxy] databaseEventForSHA256:sha256 reply:^(SNTStoredEvent *e) {
if (e) {
[self uploadEvents:@[ e ]];
}
dispatch_semaphore_signal(sema);
}];
return (dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) == 0);
}
- (BOOL)syncBundleEvents {
NSMutableArray *newEvents = [NSMutableArray array];
for (NSString *bundlePath in [NSSet setWithArray:self.syncState.bundleBinaryRequests]) {

View File

@@ -74,10 +74,7 @@
if (!resp) return NO;
self.syncState.eventBatchSize = [resp[kBatchSize] intValue];
if (self.syncState.eventBatchSize == 0) {
self.syncState.eventBatchSize = 50;
}
self.syncState.eventBatchSize = [resp[kBatchSize] intValue] ?: 50;
self.syncState.uploadLogURL = [NSURL URLWithString:resp[kUploadLogsURL]];

View File

@@ -44,15 +44,6 @@
///
- (NSUInteger)pendingEventsCount;
///
/// Retrieve an event from the database with a given SHA-256. If multiple events
/// exist for the same SHA-256, just the first is returned.
///
/// @param sha256 a SHA-256 of the binary to return an event for.
/// @return a single SNTStoredEvent.
///
- (SNTStoredEvent *)pendingEventForSHA256:(NSString *)sha256;
///
/// Delete a single event from the database using its index.
///

View File

@@ -15,7 +15,6 @@
#import "SNTEventTable.h"
#import "MOLCertificate.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
@implementation SNTEventTable
@@ -84,7 +83,8 @@
#pragma mark Loading / Storing
- (BOOL)addStoredEvent:(SNTStoredEvent *)event {
if (!event.fileSHA256 ||
if (!event.idx ||
!event.fileSHA256 ||
!event.filePath ||
!event.occurrenceDate ||
!event.decision) return NO;
@@ -98,8 +98,9 @@
__block BOOL success = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
success = [db executeUpdate:@"INSERT INTO 'events' (filesha256, eventdata) VALUES (?, ?)",
event.fileSHA256, eventData];
success = [db executeUpdate:@"INSERT INTO 'events' (idx, filesha256, eventdata)"
@"VALUES (?, ?, ?)",
event.idx, event.fileSHA256, eventData];
}];
return success;
@@ -115,26 +116,6 @@
return eventsPending;
}
- (SNTStoredEvent *)pendingEventForSHA256:(NSString *)sha256 {
__block SNTStoredEvent *storedEvent;
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs =
[db executeQuery:@"SELECT * FROM events WHERE filesha256=? LIMIT 1;", sha256];
if ([rs next]) {
storedEvent = [self eventFromResultSet:rs];
if (!storedEvent) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
}];
return storedEvent;
}
- (NSArray *)pendingEvents {
NSMutableArray *pendingEvents = [[NSMutableArray alloc] init];
@@ -164,7 +145,7 @@
@try {
event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
event.idx = event.idx ?: @((uint32_t)[rs intForColumn:@"idx"]);
} @catch (NSException *exception) {
}

View File

@@ -23,6 +23,7 @@
#import "SNTDaemonControlController.h"
#import "SNTDatabaseController.h"
#import "SNTDriverManager.h"
#import "SNTDropRootPrivs.h"
#import "SNTEventLog.h"
#import "SNTEventTable.h"
#import "SNTExecutionController.h"
@@ -30,6 +31,7 @@
#import "SNTLogging.h"
#import "SNTNotificationQueue.h"
#import "SNTRuleTable.h"
#import "SNTSyncdQueue.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -68,11 +70,18 @@
}
SNTNotificationQueue *notQueue = [[SNTNotificationQueue alloc] init];
SNTSyncdQueue *syncdQueue = [[SNTSyncdQueue alloc] init];
// Establish XPC listener for santactl connections
// Restart santactl if it goes down
syncdQueue.invalidationHandler = ^{
[self startSyncd];
};
// Establish XPC listener for Santa and santactl connections
SNTDaemonControlController *dc = [[SNTDaemonControlController alloc] init];
dc.driverManager = _driverManager;
dc.notQueue = notQueue;
dc.syncdQueue = syncdQueue;
_controlConnection =
[[SNTXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceId]];
@@ -81,6 +90,7 @@
[_controlConnection resume];
__block SNTClientMode origMode = [[SNTConfigurator configurator] clientMode];
__block NSURL *origSyncURL = [[SNTConfigurator configurator] syncBaseURL];
_configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^(unsigned long data) {
if (data & DISPATCH_VNODE_ATTRIB) {
@@ -107,6 +117,14 @@
[self.driverManager flushCache];
}
}
// Start santactl if the syncBaseURL changed from nil --> somthing
NSURL *syncURL = [[SNTConfigurator configurator] syncBaseURL];
if (!origSyncURL && syncURL) {
origSyncURL = syncURL;
LOGI(@"SyncBaseURL added, starting santactl.");
[self startSyncd];
}
}
}];
@@ -117,13 +135,29 @@
ruleTable:ruleTable
eventTable:eventTable
notifierQueue:notQueue
syncdQueue:syncdQueue
eventLog:_eventLog];
// Start up santactl as a daemon if a sync server exists.
[self startSyncd];
if (!_execController) return nil;
}
return self;
}
- (void)startSyncd {
if (![[SNTConfigurator configurator] syncBaseURL]) return;
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--daemon", "--syslog", NULL));
}
}
- (void)start {
LOGI(@"Connected to driver, activating.");

View File

@@ -16,6 +16,7 @@
@class SNTDriverManager;
@class SNTNotificationQueue;
@class SNTSyncdQueue;
///
/// SNTDaemonControlController handles all of the RPCs from santactl
@@ -24,5 +25,6 @@
@property SNTDriverManager *driverManager;
@property SNTNotificationQueue *notQueue;
@property SNTSyncdQueue *syncdQueue;
@end

View File

@@ -18,15 +18,16 @@
#import "SNTConfigurator.h"
#import "SNTDatabaseController.h"
#import "SNTDriverManager.h"
#import "SNTDropRootPrivs.h"
#import "SNTEventTable.h"
#import "SNTLogging.h"
#import "SNTNotificationQueue.h"
#import "SNTPolicyProcessor.h"
#import "SNTRule.h"
#import "SNTRuleTable.h"
#import "SNTSyncdQueue.h"
#import "SNTXPCConnection.h"
#import "SNTXPCNotifierInterface.h"
#import "SNTXPCSyncdInterface.h"
// Globals used by the santad watchdog thread
uint64_t watchdogCPUEvents = 0;
@@ -36,7 +37,6 @@ double watchdogRAMPeak = 0;
@interface SNTDaemonControlController ()
@property NSString *_syncXsrfToken;
@property dispatch_source_t syncTimer;
@property SNTPolicyProcessor *policyProcessor;
@end
@@ -45,46 +45,12 @@ double watchdogRAMPeak = 0;
- (instancetype)init {
self = [super init];
if (self) {
_syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
_policyProcessor = [[SNTPolicyProcessor alloc] initWithRuleTable:
[SNTDatabaseController ruleTable]];
}
return self;
}
- (dispatch_source_t)createSyncTimer {
dispatch_source_t syncTimerQ = dispatch_source_create(
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_set_event_handler(syncTimerQ, ^{
[self rescheduleSyncSecondsFromNow:600];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
}
});
dispatch_resume(syncTimerQ);
return syncTimerQ;
}
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds {
uint64_t interval = seconds * NSEC_PER_SEC;
uint64_t leeway = (seconds * 0.05) * NSEC_PER_SEC;
dispatch_source_set_timer(self.syncTimer, dispatch_walltime(NULL, interval), interval, leeway);
}
#pragma mark Kernel ops
- (void)cacheCount:(void (^)(int64_t))reply {
@@ -127,10 +93,6 @@ double watchdogRAMPeak = 0;
reply([[SNTDatabaseController eventTable] pendingEventsCount]);
}
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply {
reply([[SNTDatabaseController eventTable] pendingEventForSHA256:sha256]);
}
- (void)databaseEventsPending:(void (^)(NSArray *events))reply {
reply([[SNTDatabaseController eventTable] pendingEvents]);
}
@@ -180,12 +142,6 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply {
[self rescheduleSyncSecondsFromNow:seconds];
[[SNTConfigurator configurator] setSyncBackOff:YES];
reply();
}
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
[[SNTConfigurator configurator] setSyncLastSuccess:date];
reply();
@@ -225,4 +181,26 @@ double watchdogRAMPeak = 0;
self.notQueue.notifierConnection = c;
}
#pragma mark syncd Ops
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener {
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
c.remoteInterface = [SNTXPCSyncdInterface syncdInterface];
c.invalidationHandler = ^{
[self.syncdQueue stopSyncingEvents];
self.syncdQueue.invalidationHandler();
};
c.acceptedHandler = ^{
[self.syncdQueue startSyncingEvents];
};
[c resume];
self.syncdQueue.syncdConnection = c;
}
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply {
[[self.syncdQueue.syncdConnection remoteObjectProxy] rescheduleSyncSecondsFromNow:seconds];
[[SNTConfigurator configurator] setSyncBackOff:YES];
reply();
}
@end

View File

@@ -21,6 +21,7 @@
@class SNTEventTable;
@class SNTNotificationQueue;
@class SNTRuleTable;
@class SNTSyncdQueue;
///
/// SNTExecutionController is responsible for handling binary execution requests:
@@ -36,6 +37,7 @@
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierQueue:(SNTNotificationQueue *)notifierQueue
syncdQueue:(SNTSyncdQueue *)syncdQueue
eventLog:(SNTEventLog *)eventLog;
///

View File

@@ -36,6 +36,7 @@
#import "SNTRule.h"
#import "SNTRuleTable.h"
#import "SNTStoredEvent.h"
#import "SNTSyncdQueue.h"
@interface SNTExecutionController ()
@property SNTDriverManager *driverManager;
@@ -44,8 +45,8 @@
@property SNTNotificationQueue *notifierQueue;
@property SNTPolicyProcessor *policyProcessor;
@property SNTRuleTable *ruleTable;
@property SNTSyncdQueue *syncdQueue;
@property NSCache<NSString *, NSDate *> *uploadBackoff;
@property dispatch_queue_t eventQueue;
@end
@@ -57,6 +58,7 @@
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierQueue:(SNTNotificationQueue *)notifierQueue
syncdQueue:(SNTSyncdQueue *)syncdQueue
eventLog:(SNTEventLog *)eventLog {
self = [super init];
if (self) {
@@ -64,11 +66,10 @@
_ruleTable = ruleTable;
_eventTable = eventTable;
_notifierQueue = notifierQueue;
_syncdQueue = syncdQueue;
_eventLog = eventLog;
_policyProcessor = [[SNTPolicyProcessor alloc] initWithRuleTable:_ruleTable];
_uploadBackoff = [[NSCache alloc] init];
_uploadBackoff.countLimit = 128;
_eventQueue = dispatch_queue_create("com.google.santad.event_upload", DISPATCH_QUEUE_SERIAL);
// This establishes the XPC connection between libsecurity and syspolicyd.
@@ -245,7 +246,7 @@
}
/**
This runs `santactl sync` for the event that was just saved, so that the user
This sends the event that was just saved to santactl for immediate upload, so that the user
has something to vote in straight away.
This method is always called on a serial queue to keep this low-priority method
@@ -259,24 +260,7 @@
![[SNTConfigurator configurator] syncBaseURL] ||
[[SNTConfigurator configurator] syncBackOff]) return;
// The event upload is skipped if an event upload has been initiated for it in the
// last 10 minutes.
NSDate *backoff = [self.uploadBackoff objectForKey:event.fileSHA256];
NSDate *now = [NSDate date];
if (([now timeIntervalSince1970] - [backoff timeIntervalSince1970]) < 600) return;
[self.uploadBackoff setObject:now forKey:event.fileSHA256];
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog",
"singleevent", [event.fileSHA256 UTF8String], NULL));
}
[self.syncdQueue addEvent:event];
}
- (void)printMessage:(NSString *)msg toTTYForPID:(pid_t)pid {

View File

@@ -0,0 +1,28 @@
/// Copyright 2016 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.
@class SNTStoredEvent;
@class SNTXPCConnection;
@interface SNTSyncdQueue : NSObject
@property(nonatomic) SNTXPCConnection *syncdConnection;
@property(copy) void (^invalidationHandler)();
@property(copy) void (^acceptedHandler)();
- (void)addEvent:(SNTStoredEvent *)event;
- (void)startSyncingEvents;
- (void)stopSyncingEvents;
@end

View File

@@ -0,0 +1,72 @@
/// Copyright 2016 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 "SNTSyncdQueue.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCSyncdInterface.h"
@interface SNTSyncdQueue ()
@property NSCache<NSString *, NSDate *> *uploadBackoff;
@property dispatch_queue_t syncdQueue;
@property dispatch_semaphore_t sema;
@end
@implementation SNTSyncdQueue
- (instancetype)init {
self = [super init];
if (self) {
_uploadBackoff = [[NSCache alloc] init];
_uploadBackoff.countLimit = 128;
_syncdQueue = dispatch_queue_create("com.google.syncd_queue", DISPATCH_QUEUE_SERIAL);
_sema = dispatch_semaphore_create(0);
}
return self;
}
- (void)addEvent:(SNTStoredEvent *)event {
// The event upload is skipped if an event upload has been initiated for it in the
// last 10 minutes.
NSDate *backoff = [self.uploadBackoff objectForKey:event.fileSHA256];
NSDate *now = [NSDate date];
if (([now timeIntervalSince1970] - [backoff timeIntervalSince1970]) < 600) return;
[self.uploadBackoff setObject:now forKey:event.fileSHA256];
// Hold events for a few seconds to allow santad and santactl to establish connections.
// If the connections are not established in time drop the event from the queue.
// They will be uploaded during a full sync.
dispatch_async(self.syncdQueue, ^{
if (!dispatch_semaphore_wait(self.sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
[self.syncdConnection.remoteObjectProxy postEventToSyncServer:event];
// Let em flow
dispatch_semaphore_signal(self.sema);
} else {
LOGI(@"Dropping event %@ from com.google.syncd_queue", event.fileSHA256);
}
});
}
- (void)startSyncingEvents {
dispatch_semaphore_signal(self.sema);
}
- (void)stopSyncingEvents {
self.sema = dispatch_semaphore_create(0);
}
@end

View File

@@ -46,6 +46,7 @@
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:@"/usr/bin/false"];
SNTStoredEvent *event;
event = [[SNTStoredEvent alloc] init];
event.idx = @(arc4random());
event.filePath = @"/usr/bin/false";
event.fileSHA256 = [binInfo SHA256];
event.signingChain = [csInfo certificates];
@@ -67,7 +68,7 @@
SNTStoredEvent *event = [self createTestEvent];
[self.sut addStoredEvent:event];
SNTStoredEvent *storedEvent = [self.sut pendingEventForSHA256:event.fileSHA256];
SNTStoredEvent *storedEvent = [self.sut pendingEvents].firstObject;
XCTAssertNotNil(storedEvent);
XCTAssertEqualObjects(event.filePath, storedEvent.filePath);
XCTAssertEqualObjects(event.signingChain, storedEvent.signingChain);
@@ -81,8 +82,7 @@
[self.sut addStoredEvent:newEvent];
XCTAssertEqual(self.sut.pendingEventsCount, 1);
SNTStoredEvent *storedEvent = [self.sut pendingEventForSHA256:newEvent.fileSHA256];
[self.sut deleteEventWithId:storedEvent.idx];
[self.sut deleteEventWithId:newEvent.idx];
XCTAssertEqual(self.sut.pendingEventsCount, 0);
}

View File

@@ -66,6 +66,7 @@
ruleTable:self.mockRuleDatabase
eventTable:self.mockEventDatabase
notifierQueue:nil
syncdQueue:nil
eventLog:nil];
}