mirror of
https://github.com/google/santa.git
synced 2026-01-21 03:58:11 -05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a65b646df | ||
|
|
24c715aae9 | ||
|
|
9ab85768bd | ||
|
|
16458d96e7 | ||
|
|
b307dd17af | ||
|
|
313552352c | ||
|
|
543ac7c649 | ||
|
|
dacff76694 | ||
|
|
c134169ea1 | ||
|
|
e252945047 | ||
|
|
f8cfcaab20 | ||
|
|
528237a239 |
@@ -82,3 +82,4 @@ myclean:
|
||||
@rm -f com.google.santad.plist
|
||||
@rm -f com.google.santagui.plist
|
||||
@rm -f install.sh
|
||||
@rm -f uninstall.sh
|
||||
|
||||
@@ -19,8 +19,8 @@ user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
/bin/rm -f /Library/LaunchAgents/com.google.santagui.plist
|
||||
/bin/rm -f /Library/LaunchDaemons/com.google.santad.plist
|
||||
/bin/rm -f /private/etc/asl/com.google.santa.asl.conf
|
||||
/bin/rm -f /usr/local/santactl # just a symlink
|
||||
/bin/rm -f /usr/local/bin/santactl # just a symlink
|
||||
#uncomment to remove the config file and all databases, log files
|
||||
#/bin/rm -rf /var/db/santa
|
||||
#/bin/rm -f /var/log/santa*
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
1
Podfile
1
Podfile
@@ -18,6 +18,7 @@ target :santactl do
|
||||
pod 'MOLAuthenticatingURLSession'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLFCMClient'
|
||||
end
|
||||
|
||||
target :LogicTests do
|
||||
|
||||
@@ -7,6 +7,8 @@ PODS:
|
||||
- MOLCertificate (1.5)
|
||||
- MOLCodesignChecker (1.5):
|
||||
- MOLCertificate (~> 1.3)
|
||||
- MOLFCMClient (1.1):
|
||||
- MOLAuthenticatingURLSession (~> 2.1)
|
||||
- OCMock (3.3.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
@@ -14,6 +16,7 @@ DEPENDENCIES:
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- MOLFCMClient
|
||||
- OCMock
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
@@ -21,8 +24,9 @@ SPEC CHECKSUMS:
|
||||
MOLAuthenticatingURLSession: 2f0fd35f641bc857ee1b026021dbd759955adaa3
|
||||
MOLCertificate: c39cae866d24d36fbc78032affff83d401b5384a
|
||||
MOLCodesignChecker: fc9c64147811d7b0d0739127003e0630dff9213a
|
||||
MOLFCMClient: f1684219facbffdb060ff4ab18b1825bcd4c75bc
|
||||
OCMock: f3f61e6eaa16038c30caa5798c5e49d3307b6f22
|
||||
|
||||
PODFILE CHECKSUM: bc456d69693ca262c781dbbde40529a9474b84b5
|
||||
PODFILE CHECKSUM: b20628b5933f54525daf0dcc5534512b1cb134c8
|
||||
|
||||
COCOAPODS: 1.0.1
|
||||
|
||||
@@ -179,9 +179,15 @@
|
||||
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 */; };
|
||||
C776A1071DEE160500A56616 /* SNTCommandSyncManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C776A1061DEE160500A56616 /* SNTCommandSyncManager.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 */
|
||||
|
||||
@@ -418,8 +424,14 @@
|
||||
BE53E1EAE84D54E7FCB22FD5 /* libPods-santactl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santactl.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C72E8D931D7F399900C86DD3 /* SNTCommandFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFileInfoTest.m; sourceTree = "<group>"; };
|
||||
C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandCheckCache.m; sourceTree = "<group>"; };
|
||||
C776A1051DEE160500A56616 /* SNTCommandSyncManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncManager.h; sourceTree = "<group>"; };
|
||||
C776A1061DEE160500A56616 /* SNTCommandSyncManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncManager.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 */
|
||||
|
||||
@@ -570,6 +582,8 @@
|
||||
0D41640419197AD7006A356A /* SNTCommandSyncEventUpload.m */,
|
||||
0DC5D86F192160180078A5C0 /* SNTCommandSyncLogUpload.h */,
|
||||
0DC5D870192160180078A5C0 /* SNTCommandSyncLogUpload.m */,
|
||||
C776A1051DEE160500A56616 /* SNTCommandSyncManager.h */,
|
||||
C776A1061DEE160500A56616 /* SNTCommandSyncManager.m */,
|
||||
0D0A1EC4191AB9B000B8450F /* SNTCommandSyncPostflight.h */,
|
||||
0D0A1EC5191AB9B000B8450F /* SNTCommandSyncPostflight.m */,
|
||||
0DCD605A19117A90006B445C /* SNTCommandSyncPreflight.h */,
|
||||
@@ -725,6 +739,8 @@
|
||||
0DCD605419115D17006B445C /* SNTXPCControlInterface.m */,
|
||||
0DC8C9E3180CC3BC00FCFB29 /* SNTXPCNotifierInterface.h */,
|
||||
0DCD604E19115A06006B445C /* SNTXPCNotifierInterface.m */,
|
||||
C7FB56F41DBFB480004E14EF /* SNTXPCSyncdInterface.h */,
|
||||
C7FB56F51DBFB480004E14EF /* SNTXPCSyncdInterface.m */,
|
||||
);
|
||||
path = common;
|
||||
sourceTree = "<group>";
|
||||
@@ -752,6 +768,8 @@
|
||||
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
|
||||
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */,
|
||||
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */,
|
||||
C7FB56FE1DBFC213004E14EF /* SNTSyncdQueue.h */,
|
||||
C7FB56FF1DBFC213004E14EF /* SNTSyncdQueue.m */,
|
||||
0D3AF83118F87CEF0087BCEE /* Resources */,
|
||||
);
|
||||
path = santad;
|
||||
@@ -1295,6 +1313,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 +1350,7 @@
|
||||
0DCD605919115E5A006B445C /* SNTXPCNotifierInterface.m in Sources */,
|
||||
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */,
|
||||
0D202D1B1CDD465400A88F16 /* SNTCommandSyncState.m in Sources */,
|
||||
C73A4B9A1DC10753007B6789 /* SNTSyncdQueue.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1345,11 +1365,13 @@
|
||||
0DCD605619115D17006B445C /* SNTXPCControlInterface.m in Sources */,
|
||||
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */,
|
||||
0D9184B81CD2F32D0004E859 /* SNTCommandSyncStage.m in Sources */,
|
||||
C776A1071DEE160500A56616 /* SNTCommandSyncManager.m in Sources */,
|
||||
0D35BDC418FDA5D100921A21 /* SNTXPCConnection.m in Sources */,
|
||||
0DCD605C19117A90006B445C /* SNTCommandSyncPreflight.m in Sources */,
|
||||
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 +1434,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 */,
|
||||
|
||||
@@ -135,4 +135,12 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
});
|
||||
}
|
||||
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
|
||||
NSUserNotification *un = [[NSUserNotification alloc] init];
|
||||
un.title = @"Santa";
|
||||
un.hasActionButton = NO;
|
||||
un.informativeText = message ?: @"Requested application can now be run";
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -143,9 +143,14 @@ extern NSString *const kDefaultConfigFilePath;
|
||||
@property(readonly, nonatomic) NSString *machineOwner;
|
||||
|
||||
///
|
||||
/// The last date of successful sync.
|
||||
/// The last date of a successful full sync.
|
||||
///
|
||||
@property(nonatomic) NSDate *syncLastSuccess;
|
||||
@property(nonatomic) NSDate *fullSyncLastSuccess;
|
||||
|
||||
///
|
||||
/// The last date of a successful rule sync.
|
||||
///
|
||||
@property(nonatomic) NSDate *ruleSyncLastSuccess;
|
||||
|
||||
///
|
||||
/// If YES a clean sync is required.
|
||||
|
||||
@@ -52,7 +52,8 @@ static NSString *const kModeNotificationMonitor = @"ModeNotificationMonitor";
|
||||
static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
|
||||
|
||||
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
|
||||
static NSString *const kSyncLastSuccess = @"SyncLastSuccess";
|
||||
static NSString *const kFullSyncLastSuccess = @"FullSyncLastSuccess";
|
||||
static NSString *const kRuleSyncLastSuccess = @"RuleSyncLastSuccess";
|
||||
static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
|
||||
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
|
||||
@@ -259,12 +260,22 @@ static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return self.configData[kServerAuthRootsFileKey];
|
||||
}
|
||||
|
||||
- (NSDate *)syncLastSuccess {
|
||||
return self.configData[kSyncLastSuccess];
|
||||
- (NSDate *)fullSyncLastSuccess {
|
||||
return self.configData[kFullSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)syncLastSuccess {
|
||||
self.configData[kSyncLastSuccess] = syncLastSuccess;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)fullSyncLastSuccess {
|
||||
self.configData[kFullSyncLastSuccess] = fullSyncLastSuccess;
|
||||
[self saveConfigToDisk];
|
||||
self.ruleSyncLastSuccess = fullSyncLastSuccess;
|
||||
}
|
||||
|
||||
- (NSDate *)ruleSyncLastSuccess {
|
||||
return self.configData[kRuleSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)ruleSyncLastSuccess {
|
||||
self.configData[kRuleSyncLastSuccess] = ruleSyncLastSuccess;
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,8 +72,8 @@
|
||||
- (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)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
@@ -85,6 +83,14 @@
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)())reply;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCControlInterface : NSObject
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
@protocol SNTNotifierXPC
|
||||
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message;
|
||||
- (void)postClientModeNotification:(SNTClientMode)clientmode;
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
@interface SNTXPCNotifierInterface : NSObject
|
||||
|
||||
34
Source/common/SNTXPCSyncdInterface.h
Normal file
34
Source/common/SNTXPCSyncdInterface.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/// 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;
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply;
|
||||
@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
|
||||
23
Source/common/SNTXPCSyncdInterface.m
Normal file
23
Source/common/SNTXPCSyncdInterface.m
Normal 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
|
||||
@@ -275,9 +275,9 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
if (!fi.csc) {
|
||||
NSError *error;
|
||||
fi.csc = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.filePath error:&error];
|
||||
fi.csc = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.path(fi) error:&error];
|
||||
}
|
||||
[[fi.daemonConn remoteObjectProxy] decisionForFilePath:fi.filePath
|
||||
[[fi.daemonConn remoteObjectProxy] decisionForFilePath:fi.path(fi)
|
||||
fileSHA256:fi.propertyMap[kSHA256](fi)
|
||||
signingCertificate:fi.csc.leafCertificate
|
||||
reply:^(SNTEventState state) {
|
||||
|
||||
@@ -102,9 +102,18 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss Z";
|
||||
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] syncLastSuccess];
|
||||
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] fullSyncLastSuccess];
|
||||
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
|
||||
NSDate *lastRuleSyncSuccess = [[SNTConfigurator configurator] ruleSyncLastSuccess];
|
||||
NSString *lastRuleSyncSuccessStr =
|
||||
[dateFormatter stringFromDate:lastRuleSyncSuccess] ?: lastSyncSuccessStr;
|
||||
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
|
||||
__block BOOL pushNotifications;
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] pushNotifications:^(BOOL response) {
|
||||
pushNotifications = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
// Wait a maximum of 5s for stats collected from daemon to arrive.
|
||||
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
|
||||
@@ -114,7 +123,7 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
if ([arguments containsObject:@"--json"]) {
|
||||
NSDictionary *stats = @{
|
||||
@"daemon" : @{
|
||||
@"mode" : clientMode,
|
||||
@"mode" : clientMode ?: @"null",
|
||||
@"file_logging" : @(fileLogging),
|
||||
@"watchdog_cpu_events" : @(cpuEvents),
|
||||
@"watchdog_ram_events" : @(ramEvents),
|
||||
@@ -130,9 +139,11 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"events_pending_upload" : @(eventCount),
|
||||
},
|
||||
@"sync" : @{
|
||||
@"server" : syncURLStr,
|
||||
@"server" : syncURLStr ?: @"null",
|
||||
@"clean_required" : @(syncCleanReqd),
|
||||
@"last_successful" : lastSyncSuccessStr
|
||||
@"last_successful_full" : lastSyncSuccessStr ?: @"null",
|
||||
@"last_successful_rule" : lastRuleSyncSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"Connected" : @"Disconnected"
|
||||
},
|
||||
};
|
||||
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
|
||||
@@ -142,22 +153,25 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf("%s\n", [statsStr UTF8String]);
|
||||
} else {
|
||||
printf(">>> Daemon Info\n");
|
||||
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
|
||||
printf(" %-22s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
|
||||
printf(" %-22s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-22s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
|
||||
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
|
||||
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(" %-22s | %lld\n", "Kernel cache count", cacheCount);
|
||||
printf(" %-25s | %lld\n", "Kernel cache count", cacheCount);
|
||||
printf(">>> Database Info\n");
|
||||
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
|
||||
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
|
||||
|
||||
if (syncURLStr) {
|
||||
printf(">>> Sync Info\n");
|
||||
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
|
||||
printf(" %-22s | %s\n", "Last Successful Sync", [lastSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Last Successful Full Sync", [lastSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Last Successful Rule Sync", [lastRuleSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Push Notifications",
|
||||
(pushNotifications ? "Connected" : "Disconnected"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,7 @@
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import <MOLAuthenticatingURLSession.h>
|
||||
|
||||
#import "SNTCommandSyncEventUpload.h"
|
||||
#import "SNTCommandSyncLogUpload.h"
|
||||
#import "SNTCommandSyncPostflight.h"
|
||||
#import "SNTCommandSyncPreflight.h"
|
||||
#import "SNTCommandSyncRuleDownload.h"
|
||||
#import "SNTCommandSyncState.h"
|
||||
#import "SNTCommandSyncManager.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDropRootPrivs.h"
|
||||
#import "SNTLogging.h"
|
||||
@@ -29,19 +22,22 @@
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandSync : NSObject<SNTCommand>
|
||||
@property SNTCommandSyncState *syncState;
|
||||
@property SNTXPCConnection *listener;
|
||||
@property SNTCommandSyncManager *syncManager;
|
||||
@end
|
||||
|
||||
@implementation SNTCommandSync
|
||||
|
||||
REGISTER_COMMAND_NAME(@"sync")
|
||||
|
||||
#pragma mark SNTCommand protocol methods
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresDaemonConn {
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
@@ -63,36 +59,11 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
SNTCommandSync *s = [[self alloc] init];
|
||||
|
||||
// Gather some data needed during some sync stages
|
||||
s.syncState = [[SNTCommandSyncState alloc] init];
|
||||
|
||||
s.syncState.syncBaseURL = config.syncBaseURL;
|
||||
if (s.syncState.syncBaseURL.absoluteString.length == 0) {
|
||||
LOGE(@"Missing SyncBaseURL. Can't sync without it.");
|
||||
exit(1);
|
||||
} else if (![s.syncState.syncBaseURL.scheme isEqual:@"https"]) {
|
||||
LOGW(@"SyncBaseURL is not over HTTPS!");
|
||||
}
|
||||
|
||||
s.syncState.machineID = config.machineID;
|
||||
if (s.syncState.machineID.length == 0) {
|
||||
LOGE(@"Missing Machine ID. Can't sync without it.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
s.syncState.machineOwner = config.machineOwner;
|
||||
if (s.syncState.machineOwner.length == 0) {
|
||||
s.syncState.machineOwner = @"";
|
||||
LOGW(@"Missing Machine Owner.");
|
||||
}
|
||||
|
||||
[[daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
|
||||
s.syncState.xsrfToken = token;
|
||||
}];
|
||||
[daemonConn resume];
|
||||
BOOL daemon = [arguments containsObject:@"--daemon"];
|
||||
s.syncManager = [[SNTCommandSyncManager alloc] initWithDaemonConnection:daemonConn
|
||||
isDaemon:daemon];
|
||||
|
||||
// Dropping root privileges to the 'nobody' user causes the default NSURLCache to throw
|
||||
// sandbox errors, which are benign but annoying. This line disables the cache entirely.
|
||||
@@ -100,138 +71,40 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
diskCapacity:0
|
||||
diskPath:nil]];
|
||||
|
||||
if (!s.syncManager.daemon) return [s.syncManager fullSync];
|
||||
[s syncdWithDaemonConnection:daemonConn];
|
||||
}
|
||||
|
||||
MOLAuthenticatingURLSession *authURLSession = [[MOLAuthenticatingURLSession alloc] init];
|
||||
authURLSession.userAgent = @"santactl-sync/";
|
||||
NSString *santactlVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
if (santactlVersion) {
|
||||
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
|
||||
}
|
||||
authURLSession.refusesRedirects = YES;
|
||||
authURLSession.serverHostname = s.syncState.syncBaseURL.host;
|
||||
authURLSession.loggingBlock = ^(NSString *line) {
|
||||
LOGD(@"%@", line);
|
||||
#pragma mark daemon methods
|
||||
|
||||
- (void)syncdWithDaemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
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.syncManager;
|
||||
self.listener.acceptedHandler = ^{
|
||||
LOGD(@"santad <--> santactl connections established");
|
||||
dispatch_semaphore_signal(sema);
|
||||
};
|
||||
|
||||
// Configure server auth
|
||||
if ([config syncServerAuthRootsFile]) {
|
||||
authURLSession.serverRootsPemFile = [config syncServerAuthRootsFile];
|
||||
} else if ([config syncServerAuthRootsData]) {
|
||||
authURLSession.serverRootsPemData = [config syncServerAuthRootsData];
|
||||
}
|
||||
|
||||
// Configure client auth
|
||||
if ([config syncClientAuthCertificateFile]) {
|
||||
authURLSession.clientCertFile = [config syncClientAuthCertificateFile];
|
||||
authURLSession.clientCertPassword = [config syncClientAuthCertificatePassword];
|
||||
} else if ([config syncClientAuthCertificateCn]) {
|
||||
authURLSession.clientCertCommonName = [config syncClientAuthCertificateCn];
|
||||
} else if ([config syncClientAuthCertificateIssuer]) {
|
||||
authURLSession.clientCertIssuerCn = [config syncClientAuthCertificateIssuer];
|
||||
}
|
||||
|
||||
s.syncState.session = [authURLSession session];
|
||||
s.syncState.daemonConn = daemonConn;
|
||||
|
||||
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];
|
||||
} else {
|
||||
return [s preflight];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)preflight {
|
||||
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Preflight complete");
|
||||
if (self.syncState.uploadLogURL) {
|
||||
return [self logUpload];
|
||||
} else {
|
||||
return [self eventUpload];
|
||||
}
|
||||
} else {
|
||||
LOGE(@"Preflight failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)logUpload {
|
||||
SNTCommandSyncLogUpload *p = [[SNTCommandSyncLogUpload alloc] initWithState:self.syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Log upload complete");
|
||||
} else {
|
||||
LOGE(@"Log upload failed, continuing anyway");
|
||||
}
|
||||
return [self eventUpload];
|
||||
}
|
||||
|
||||
- (void)eventUpload {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Event upload complete");
|
||||
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");
|
||||
self.listener.invalidationHandler = ^{
|
||||
// If santad is unloaded kill santactl
|
||||
LOGD(@"exiting");
|
||||
exit(0);
|
||||
} else {
|
||||
LOGE(@"Event upload failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
[self.listener resume];
|
||||
|
||||
- (void)ruleDownload {
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:self.syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
if (self.syncState.bundleBinaryRequests.count) {
|
||||
return [self eventUploadBundleBinaries];
|
||||
}
|
||||
return [self postflight];
|
||||
} else {
|
||||
LOGE(@"Rule download failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// Tell daemon to connect back to the above listener.
|
||||
[[daemonConn remoteObjectProxy] setSyncdListener:listener.endpoint];
|
||||
|
||||
- (void)eventUploadBundleBinaries {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
|
||||
if ([p syncBundleEvents]) {
|
||||
LOGD(@"Event upload for bundle binaries complete");
|
||||
} else {
|
||||
LOGW(@"Event upload for bundle binary search failed");
|
||||
// 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(syncdWithDaemonConnection:) withObject:daemonConn];
|
||||
}
|
||||
return [self postflight];
|
||||
}
|
||||
|
||||
- (void)postflight {
|
||||
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:self.syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Postflight complete");
|
||||
LOGI(@"Sync completed successfully");
|
||||
exit(0);
|
||||
} else {
|
||||
LOGE(@"Postflight failed");
|
||||
exit(1);
|
||||
}
|
||||
[self.syncManager fullSyncSecondsFromNow:15];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,6 +31,7 @@ extern NSString *const kWhitelistRegex;
|
||||
extern NSString *const kBlacklistRegex;
|
||||
extern NSString *const kBinaryRuleCount;
|
||||
extern NSString *const kCertificateRuleCount;
|
||||
extern NSString *const kFCMToken;
|
||||
|
||||
extern NSString *const kEvents;
|
||||
extern NSString *const kFileSHA256;
|
||||
@@ -88,3 +89,8 @@ extern NSString *const kRuleCustomMsg;
|
||||
extern NSString *const kCursor;
|
||||
|
||||
extern NSString *const kBackoffInterval;
|
||||
|
||||
extern NSString *const kFullSync;
|
||||
extern NSString *const kRuleSync;
|
||||
extern NSString *const kConfigSync;
|
||||
extern NSString *const kLogSync;
|
||||
|
||||
@@ -33,6 +33,7 @@ NSString *const kWhitelistRegex = @"whitelist_regex";
|
||||
NSString *const kBlacklistRegex = @"blacklist_regex";
|
||||
NSString *const kBinaryRuleCount = @"binary_rule_count";
|
||||
NSString *const kCertificateRuleCount = @"certificate_rule_count";
|
||||
NSString *const kFCMToken = @"fcm_token";
|
||||
|
||||
NSString *const kEvents = @"events";
|
||||
NSString *const kFileSHA256 = @"file_sha256";
|
||||
@@ -90,3 +91,8 @@ NSString *const kRuleCustomMsg = @"custom_msg";
|
||||
NSString *const kCursor = @"cursor";
|
||||
|
||||
NSString *const kBackoffInterval = @"backoff";
|
||||
|
||||
NSString *const kFullSync = @"full_sync";
|
||||
NSString *const kRuleSync = @"rule_sync";
|
||||
NSString *const kConfigSync = @"config_sync";
|
||||
NSString *const kLogSync = @"log_sync";
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
@interface SNTCommandSyncEventUpload : SNTCommandSyncStage
|
||||
|
||||
- (BOOL)syncSingleEventWithSHA256:(NSString *)sha256;
|
||||
- (BOOL)uploadEvents:(NSArray *)events;
|
||||
|
||||
- (BOOL)syncBundleEvents;
|
||||
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
53
Source/santactl/Commands/sync/SNTCommandSyncManager.h
Normal file
53
Source/santactl/Commands/sync/SNTCommandSyncManager.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/// 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"
|
||||
|
||||
@class SNTXPCConnection;
|
||||
|
||||
///
|
||||
/// Handles push notifications and periodic syncing with a sync server.
|
||||
///
|
||||
@interface SNTCommandSyncManager : NSObject<SNTSyncdXPC>
|
||||
|
||||
@property(readonly, nonatomic) BOOL daemon;
|
||||
|
||||
///
|
||||
/// Use the designated initializer initWithDaemonConnection:isDaemon:
|
||||
///
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
///
|
||||
/// Designated initializer.
|
||||
///
|
||||
/// @param daemonConn A connection to santad.
|
||||
/// @param daemon Set to YES if periodic syncing should occur.
|
||||
/// Set to NO if a single sync should be performed. NO is default.
|
||||
///
|
||||
- (instancetype)initWithDaemonConnection:(SNTXPCConnection *)daemonConn
|
||||
isDaemon:(BOOL)daemon NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
///
|
||||
/// Perform a full sync immediately. Non-blocking.
|
||||
/// If a full sync is already running new requests will be dropped.
|
||||
///
|
||||
- (void)fullSync;
|
||||
|
||||
///
|
||||
/// Perform a full sync seconds from now. Non-blocking.
|
||||
/// If a full sync is already running new requests will be dropped.
|
||||
///
|
||||
- (void)fullSyncSecondsFromNow:(uint64_t)seconds;
|
||||
|
||||
@end
|
||||
475
Source/santactl/Commands/sync/SNTCommandSyncManager.m
Normal file
475
Source/santactl/Commands/sync/SNTCommandSyncManager.m
Normal file
@@ -0,0 +1,475 @@
|
||||
/// 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 "SNTCommandSyncManager.h"
|
||||
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
#import <MOLAuthenticatingURLSession.h>
|
||||
#import <MOLFCMClient/MOLFCMClient.h>
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTCommandSyncConstants.h"
|
||||
#import "SNTCommandSyncEventUpload.h"
|
||||
#import "SNTCommandSyncLogUpload.h"
|
||||
#import "SNTCommandSyncPostflight.h"
|
||||
#import "SNTCommandSyncPreflight.h"
|
||||
#import "SNTCommandSyncRuleDownload.h"
|
||||
#import "SNTCommandSyncState.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
|
||||
// Syncing time constants
|
||||
const uint64_t kFullSyncInterval = 600;
|
||||
const uint64_t kFullSyncFCMInterval = 14400;
|
||||
const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
|
||||
@interface SNTCommandSyncManager () {
|
||||
SCNetworkReachabilityRef _reachability;
|
||||
}
|
||||
@property(nonatomic) dispatch_source_t fullSyncTimer;
|
||||
@property(nonatomic) dispatch_source_t ruleSyncTimer;
|
||||
@property(nonatomic) NSCache *dispatchLock;
|
||||
@property(nonatomic) NSCache *ruleSyncCache;
|
||||
@property MOLFCMClient *FCMClient;
|
||||
@property(nonatomic) SNTXPCConnection *daemonConn;
|
||||
@property BOOL targetedRuleSync;
|
||||
@property(nonatomic) BOOL reachable;
|
||||
@end
|
||||
|
||||
// Called when the network state changes
|
||||
static void reachabilityHandler(
|
||||
SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
|
||||
SNTCommandSyncManager *commandSyncManager = (__bridge SNTCommandSyncManager *)info;
|
||||
// Only call the setter when there is a change. This will filter out the redundant calls to this
|
||||
// callback whenever the network interface states change.
|
||||
if (commandSyncManager.reachable != (flags & kSCNetworkReachabilityFlagsReachable)) {
|
||||
commandSyncManager.reachable = (flags & kSCNetworkReachabilityFlagsReachable);
|
||||
}
|
||||
}
|
||||
|
||||
@implementation SNTCommandSyncManager
|
||||
|
||||
#pragma mark init
|
||||
|
||||
- (instancetype)initWithDaemonConnection:(SNTXPCConnection *)daemonConn isDaemon:(BOOL)daemon {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_daemonConn = daemonConn;
|
||||
_daemon = daemon;
|
||||
_fullSyncTimer = [self createSyncTimerWithBlock:^{
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncFCMInterval];
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) return;
|
||||
[self lockAction:kFullSync];
|
||||
[self preflight];
|
||||
[self unlockAction:kFullSync];
|
||||
}];
|
||||
_ruleSyncTimer = [self createSyncTimerWithBlock:^{
|
||||
dispatch_source_set_timer(self.ruleSyncTimer,
|
||||
DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 0);
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) return;
|
||||
[self lockAction:kRuleSync];
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
syncState.targetedRuleSync = self.targetedRuleSync;
|
||||
syncState.ruleSyncCache = self.ruleSyncCache;
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
} else {
|
||||
LOGE(@"Rule download failed");
|
||||
}
|
||||
self.targetedRuleSync = NO;
|
||||
[self unlockAction:kRuleSync];
|
||||
}];
|
||||
_dispatchLock = [[NSCache alloc] init];
|
||||
_ruleSyncCache = [[NSCache alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark SNTSyncdXPC protocol methods
|
||||
|
||||
- (void)postEventToSyncServer:(SNTStoredEvent *)event {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc]
|
||||
initWithState:[self createSyncState]];
|
||||
if (event && [p uploadEvents:@[event]]) {
|
||||
LOGD(@"Event upload complete");
|
||||
} else {
|
||||
LOGE(@"Event upload failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds {
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:seconds];
|
||||
}
|
||||
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply {
|
||||
reply((self.FCMClient.FCMToken != nil));
|
||||
}
|
||||
|
||||
#pragma mark push notification methods
|
||||
|
||||
- (void)listenForPushNotificationsWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
if ([self.FCMClient.FCMToken isEqualToString:syncState.FCMToken]) {
|
||||
LOGD(@"Continue with the current FCMToken");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD(@"Start listening for push notifications");
|
||||
|
||||
WEAKIFY(self);
|
||||
|
||||
[self.FCMClient disconnect];
|
||||
NSString *machineID = syncState.machineID;
|
||||
self.FCMClient = [[MOLFCMClient alloc] initWithFCMToken:syncState.FCMToken
|
||||
sessionConfiguration:syncState.session.configuration.copy
|
||||
messageHandler:^(NSDictionary *message) {
|
||||
if (!message || [message isEqual:@{}]) return;
|
||||
STRONGIFY(self);
|
||||
LOGD(@"%@", message);
|
||||
[self.FCMClient acknowledgeMessage:message];
|
||||
[self processFCMMessage:message withMachineID:machineID];
|
||||
}];
|
||||
|
||||
self.FCMClient.connectionErrorHandler = ^(NSError *error) {
|
||||
STRONGIFY(self);
|
||||
LOGE(@"FCM connection error: %@", error);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncInterval];
|
||||
};
|
||||
|
||||
self.FCMClient.loggingBlock = ^(NSString *log) {
|
||||
LOGD(@"%@", log);
|
||||
};
|
||||
|
||||
[self.FCMClient connect];
|
||||
}
|
||||
|
||||
- (void)processFCMMessage:(NSDictionary *)FCMmessage withMachineID:(NSString *)machineID {
|
||||
NSData *entryData;
|
||||
|
||||
// Sort through the entries in the FCM message.
|
||||
for (NSDictionary *entry in FCMmessage[@"data"]) {
|
||||
if ([entry[@"key"] isEqualToString:@"blob"]) {
|
||||
entryData = [entry[@"value"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryData) {
|
||||
LOGD(@"Push notification message is not in the expected format...dropping message");
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *actionMessage = [NSJSONSerialization JSONObjectWithData:entryData
|
||||
options:NSJSONReadingAllowFragments
|
||||
error:&error];
|
||||
if (!actionMessage) {
|
||||
LOGD(@"Unable to parse push notification message value: %@", error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the file name and hash in a cache. When the rule is actually added, use the cache
|
||||
// to build a user notification.
|
||||
NSString *fileHash = actionMessage[@"file_hash"];
|
||||
NSString *fileName = actionMessage[@"file_name"];
|
||||
if (fileName && fileHash) {
|
||||
[self.ruleSyncCache setObject:fileName forKey:fileHash];
|
||||
}
|
||||
|
||||
NSString *action = actionMessage[@"action"];
|
||||
if (action) {
|
||||
LOGD(@"Push notification action: %@ received", action);
|
||||
} else {
|
||||
LOGD(@"Push notification message contains no action");
|
||||
}
|
||||
|
||||
if ([action isEqualToString:kFullSync]) {
|
||||
[self fullSync];
|
||||
} else if ([action isEqualToString:kRuleSync]) {
|
||||
NSString *targetMachineID = actionMessage[@"target_host_id"];
|
||||
if (![targetMachineID isKindOfClass:[NSNull class]] &&
|
||||
[targetMachineID.lowercaseString isEqualToString:machineID.lowercaseString]) {
|
||||
self.targetedRuleSync = YES;
|
||||
[self ruleSync];
|
||||
} else {
|
||||
uint32_t delaySeconds = arc4random_uniform(kGlobalRuleSyncLeeway);
|
||||
LOGD(@"Staggering rule download, %u second delay for this machine", delaySeconds);
|
||||
[self ruleSyncSecondsFromNow:delaySeconds];
|
||||
}
|
||||
} else if ([action isEqualToString:kConfigSync]) {
|
||||
[self fullSync];
|
||||
} else if ([action isEqualToString:kLogSync]) {
|
||||
[self fullSync];
|
||||
} else {
|
||||
LOGD(@"Unrecognised action: %@", action);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark sync timer control
|
||||
|
||||
- (void)fullSync {
|
||||
[self fullSyncSecondsFromNow:0];
|
||||
}
|
||||
|
||||
- (void)fullSyncSecondsFromNow:(uint64_t)seconds {
|
||||
if (![self checkLockAction:kFullSync]) {
|
||||
LOGD(@"%@ in progress, dropping reschedule request", kFullSync);
|
||||
return;
|
||||
}
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:seconds];
|
||||
}
|
||||
|
||||
- (void)ruleSync {
|
||||
[self ruleSyncSecondsFromNow:0];
|
||||
}
|
||||
|
||||
- (void)ruleSyncSecondsFromNow:(uint64_t)seconds {
|
||||
if (![self checkLockAction:kRuleSync]) {
|
||||
LOGD(@"%@ in progress, dropping reschedule request", kRuleSync);
|
||||
return;
|
||||
}
|
||||
[self rescheduleTimerQueue:self.ruleSyncTimer secondsFromNow:seconds];
|
||||
}
|
||||
|
||||
- (void)rescheduleTimerQueue:(dispatch_source_t)timerQueue secondsFromNow:(uint64_t)seconds {
|
||||
uint64_t interval = seconds * NSEC_PER_SEC;
|
||||
uint64_t leeway = (seconds * 0.5) * NSEC_PER_SEC;
|
||||
dispatch_source_set_timer(timerQueue, dispatch_walltime(NULL, interval), interval, leeway);
|
||||
}
|
||||
|
||||
#pragma mark syncing chain
|
||||
|
||||
- (void)preflight {
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Preflight complete");
|
||||
|
||||
// Clean up reachability if it was started for a non-network error
|
||||
[self stopReachability];
|
||||
|
||||
// Start listening for push notifications with a full sync every kFullSyncFCMInterval or
|
||||
// revert to full syncing every kFullSyncInterval.
|
||||
if (syncState.daemon && syncState.FCMToken) {
|
||||
[self listenForPushNotificationsWithSyncState:syncState];
|
||||
} else if (syncState.daemon) {
|
||||
LOGD(@"FCMToken not provided. Sync every %llu min.", kFullSyncInterval / 60);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncInterval];
|
||||
}
|
||||
|
||||
if (syncState.uploadLogURL) {
|
||||
return [self logUploadWithSyncState:syncState];
|
||||
} else {
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
}
|
||||
} else {
|
||||
if (!syncState.daemon) {
|
||||
LOGE(@"Preflight failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
LOGE(@"Preflight failed, will try again once %@ is reachable",
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
[self startReachability];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)logUploadWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncLogUpload *p = [[SNTCommandSyncLogUpload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Log upload complete");
|
||||
} else {
|
||||
LOGE(@"Log upload failed, continuing anyway");
|
||||
}
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
}
|
||||
|
||||
- (void)eventUploadWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Event upload complete");
|
||||
return [self ruleDownloadWithSyncState:syncState];
|
||||
} else {
|
||||
LOGE(@"Event upload failed, aborting run");
|
||||
if (!syncState.daemon) exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)ruleDownloadWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
if (syncState.bundleBinaryRequests.count) {
|
||||
return [self eventUploadBundleBinariesWithSyncState:syncState];
|
||||
}
|
||||
return [self postflightWithSyncState:syncState];
|
||||
} else {
|
||||
LOGE(@"Rule download failed, aborting run");
|
||||
if (!syncState.daemon) exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)eventUploadBundleBinariesWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if ([p syncBundleEvents]) {
|
||||
LOGD(@"Event upload for bundle binaries complete");
|
||||
} else {
|
||||
LOGW(@"Event upload for bundle binary search failed");
|
||||
}
|
||||
return [self postflightWithSyncState:syncState];
|
||||
}
|
||||
|
||||
- (void)postflightWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Postflight complete");
|
||||
LOGI(@"Sync completed successfully");
|
||||
if (!syncState.daemon) exit(0);
|
||||
} else {
|
||||
LOGE(@"Postflight failed");
|
||||
if (!syncState.daemon) exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark internal helpers
|
||||
|
||||
- (dispatch_source_t)createSyncTimerWithBlock:(void (^)())block {
|
||||
dispatch_source_t timerQueue = dispatch_source_create(
|
||||
DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
dispatch_source_set_event_handler(timerQueue, block);
|
||||
dispatch_resume(timerQueue);
|
||||
return timerQueue;
|
||||
}
|
||||
|
||||
- (SNTCommandSyncState *)createSyncState {
|
||||
// Gather some data needed during some sync stages
|
||||
SNTCommandSyncState *syncState = [[SNTCommandSyncState alloc] init];
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
syncState.syncBaseURL = config.syncBaseURL;
|
||||
if (syncState.syncBaseURL.absoluteString.length == 0) {
|
||||
LOGE(@"Missing SyncBaseURL. Can't sync without it.");
|
||||
if (!syncState.daemon) exit(1);
|
||||
} else if (![syncState.syncBaseURL.scheme isEqual:@"https"]) {
|
||||
LOGW(@"SyncBaseURL is not over HTTPS!");
|
||||
}
|
||||
|
||||
syncState.machineID = config.machineID;
|
||||
if (syncState.machineID.length == 0) {
|
||||
LOGE(@"Missing Machine ID. Can't sync without it.");
|
||||
if (!syncState.daemon) exit(1);
|
||||
}
|
||||
|
||||
syncState.machineOwner = config.machineOwner;
|
||||
if (syncState.machineOwner.length == 0) {
|
||||
syncState.machineOwner = @"";
|
||||
LOGW(@"Missing Machine Owner.");
|
||||
}
|
||||
|
||||
[[self.daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
|
||||
syncState.xsrfToken = token;
|
||||
}];
|
||||
|
||||
MOLAuthenticatingURLSession *authURLSession = [[MOLAuthenticatingURLSession alloc] init];
|
||||
authURLSession.userAgent = @"santactl-sync/";
|
||||
NSString *santactlVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
if (santactlVersion) {
|
||||
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
|
||||
}
|
||||
authURLSession.refusesRedirects = YES;
|
||||
authURLSession.serverHostname = syncState.syncBaseURL.host;
|
||||
authURLSession.loggingBlock = ^(NSString *line) {
|
||||
LOGD(@"%@", line);
|
||||
};
|
||||
|
||||
// Configure server auth
|
||||
if ([config syncServerAuthRootsFile]) {
|
||||
authURLSession.serverRootsPemFile = [config syncServerAuthRootsFile];
|
||||
} else if ([config syncServerAuthRootsData]) {
|
||||
authURLSession.serverRootsPemData = [config syncServerAuthRootsData];
|
||||
}
|
||||
|
||||
// Configure client auth
|
||||
if ([config syncClientAuthCertificateFile]) {
|
||||
authURLSession.clientCertFile = [config syncClientAuthCertificateFile];
|
||||
authURLSession.clientCertPassword = [config syncClientAuthCertificatePassword];
|
||||
} else if ([config syncClientAuthCertificateCn]) {
|
||||
authURLSession.clientCertCommonName = [config syncClientAuthCertificateCn];
|
||||
} else if ([config syncClientAuthCertificateIssuer]) {
|
||||
authURLSession.clientCertIssuerCn = [config syncClientAuthCertificateIssuer];
|
||||
}
|
||||
|
||||
syncState.session = [authURLSession session];
|
||||
syncState.daemonConn = self.daemonConn;
|
||||
syncState.daemon = self.daemon;
|
||||
|
||||
return syncState;
|
||||
}
|
||||
|
||||
- (void)lockAction:(NSString *)action {
|
||||
[self.dispatchLock setObject:@YES forKey:action];
|
||||
}
|
||||
|
||||
- (void)unlockAction:(NSString *)action {
|
||||
[self.dispatchLock removeObjectForKey:action];
|
||||
}
|
||||
|
||||
- (BOOL)checkLockAction:(NSString *)action {
|
||||
return ([self.dispatchLock objectForKey:action] == nil);
|
||||
}
|
||||
|
||||
#pragma mark reachability methods
|
||||
|
||||
- (void)setReachable:(BOOL)reachable {
|
||||
_reachable = reachable;
|
||||
if (reachable) {
|
||||
[self stopReachability];
|
||||
[self fullSync];
|
||||
}
|
||||
}
|
||||
|
||||
// Start listening for network state changes on a background thread
|
||||
- (void)startReachability {
|
||||
if (_reachability) return;
|
||||
const char *nodename = [[SNTConfigurator configurator] syncBaseURL].absoluteString.UTF8String;
|
||||
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, nodename);
|
||||
SCNetworkReachabilityContext context = {
|
||||
.info = (__bridge void *)self
|
||||
};
|
||||
if (SCNetworkReachabilitySetCallback(_reachability, reachabilityHandler, &context)) {
|
||||
SCNetworkReachabilitySetDispatchQueue(
|
||||
_reachability, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
} else {
|
||||
[self stopReachability];
|
||||
}
|
||||
}
|
||||
|
||||
// Stop listening for network state changes
|
||||
- (void)stopReachability {
|
||||
if (_reachability) {
|
||||
SCNetworkReachabilitySetDispatchQueue(_reachability, NULL);
|
||||
CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -74,10 +74,9 @@
|
||||
|
||||
if (!resp) return NO;
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] intValue];
|
||||
if (self.syncState.eventBatchSize == 0) {
|
||||
self.syncState.eventBatchSize = 50;
|
||||
}
|
||||
self.syncState.FCMToken = resp[kFCMToken];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] intValue] ?: 50;
|
||||
|
||||
self.syncState.uploadLogURL = [NSURL URLWithString:resp[kUploadLogsURL]];
|
||||
|
||||
|
||||
@@ -67,7 +67,26 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
sema = dispatch_semaphore_create(0);
|
||||
[[self.daemonConn remoteObjectProxy] setRuleSyncLastSuccess:[NSDate date] reply:^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
}];
|
||||
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
|
||||
LOGI(@"Added %lu rules", self.syncState.downloadedRules.count);
|
||||
|
||||
if (self.syncState.targetedRuleSync) {
|
||||
for (SNTRule *r in self.syncState.downloadedRules) {
|
||||
NSString *fileName = [[self.syncState.ruleSyncCache objectForKey:r.shasum] copy];
|
||||
[self.syncState.ruleSyncCache removeObjectForKey:r.shasum];
|
||||
if (fileName) {
|
||||
NSString *message = [NSString stringWithFormat:@"%@ can now be run", fileName];
|
||||
[[self.daemonConn remoteObjectProxy]
|
||||
postRuleSyncNotificationWithCustomMessage:message reply:^{}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
@property(readwrite) NSURLSession *urlSession;
|
||||
@property(readwrite) SNTCommandSyncState *syncState;
|
||||
@property(readwrite) SNTXPCConnection *daemonConn;
|
||||
@property BOOL xsrfFetched;
|
||||
|
||||
@end
|
||||
|
||||
@@ -170,9 +171,9 @@
|
||||
}
|
||||
|
||||
- (BOOL)fetchXSRFToken {
|
||||
__block BOOL success = NO;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{ // only fetch token once per session
|
||||
BOOL success = NO;
|
||||
if (!self.xsrfFetched) { // only fetch token once per session
|
||||
self.xsrfFetched = YES;
|
||||
NSString *stageName = [@"xsrf" stringByAppendingFormat:@"/%@", self.syncState.machineID];
|
||||
NSURL *u = [NSURL URLWithString:stageName relativeToURL:self.syncState.syncBaseURL];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:u];
|
||||
@@ -188,7 +189,7 @@
|
||||
} else {
|
||||
LOGD(@"Failed to retrieve XSRF token");
|
||||
}
|
||||
});
|
||||
};
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
/// An XSRF token to send in the headers with each request.
|
||||
@property NSString *xsrfToken;
|
||||
|
||||
/// A FCM token to subscribe to push notifications.
|
||||
@property(copy) NSString *FCMToken;
|
||||
|
||||
/// Machine identifier and owner.
|
||||
@property(copy) NSString *machineID;
|
||||
@property(copy) NSString *machineOwner;
|
||||
@@ -56,4 +59,13 @@
|
||||
/// Rules downloaded from server.
|
||||
@property NSMutableArray *downloadedRules;
|
||||
|
||||
/// Returns YES if the santactl session is running as a daemon, returns NO otherwise.
|
||||
@property BOOL daemon;
|
||||
|
||||
/// Returns YES if the session is targeted for this machine, returns NO otherwise.
|
||||
@property BOOL targetedRuleSync;
|
||||
|
||||
/// Reference to the sync manager's ruleSyncCache. Used to lookup binary names for notifications.
|
||||
@property(weak) NSCache *ruleSyncCache;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#import "SNTEventTable.h"
|
||||
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTEventTable
|
||||
@@ -66,13 +65,26 @@
|
||||
newVersion = 2;
|
||||
}
|
||||
|
||||
if (version < 3) {
|
||||
// Clean-up: Disable AUTOINCREMENT on idx column
|
||||
[db executeUpdate:@"CREATE TABLE 'events_tmp' ("
|
||||
@"'idx' INTEGER PRIMARY KEY,"
|
||||
@"'filesha256' TEXT NOT NULL,"
|
||||
@"'eventdata' BLOB);"];
|
||||
[db executeUpdate:@"INSERT INTO events_tmp SELECT * FROM events"];
|
||||
[db executeUpdate:@"DROP TABLE events"];
|
||||
[db executeUpdate:@"ALTER TABLE events_tmp RENAME TO events"];
|
||||
newVersion = 3;
|
||||
}
|
||||
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
#pragma mark Loading / Storing
|
||||
|
||||
- (BOOL)addStoredEvent:(SNTStoredEvent *)event {
|
||||
if (!event.fileSHA256 ||
|
||||
if (!event.idx ||
|
||||
!event.fileSHA256 ||
|
||||
!event.filePath ||
|
||||
!event.occurrenceDate ||
|
||||
!event.decision) return NO;
|
||||
@@ -86,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;
|
||||
@@ -103,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];
|
||||
|
||||
@@ -152,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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,14 +142,13 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply {
|
||||
[self rescheduleSyncSecondsFromNow:seconds];
|
||||
[[SNTConfigurator configurator] setSyncBackOff:YES];
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setFullSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setSyncLastSuccess:date];
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setRuleSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -225,4 +186,41 @@ double watchdogRAMPeak = 0;
|
||||
self.notQueue.notifierConnection = c;
|
||||
}
|
||||
|
||||
#pragma mark syncd Ops
|
||||
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener {
|
||||
// Only allow one active syncd connection
|
||||
if (self.syncdQueue.syncdConnection) return;
|
||||
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
|
||||
c.remoteInterface = [SNTXPCSyncdInterface syncdInterface];
|
||||
c.invalidationHandler = ^{
|
||||
[self.syncdQueue stopSyncingEvents];
|
||||
self.syncdQueue.syncdConnection = nil;
|
||||
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();
|
||||
}
|
||||
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply {
|
||||
[self.syncdQueue.syncdConnection.remoteObjectProxy isFCMListening:^(BOOL response) {
|
||||
reply(response);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)())reply {
|
||||
[[self.notQueue.notifierConnection remoteObjectProxy]
|
||||
postRuleSyncNotificationWithCustomMessage:message];
|
||||
reply();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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;
|
||||
|
||||
///
|
||||
|
||||
@@ -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 {
|
||||
|
||||
28
Source/santad/SNTSyncdQueue.h
Normal file
28
Source/santad/SNTSyncdQueue.h
Normal 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
|
||||
72
Source/santad/SNTSyncdQueue.m
Normal file
72
Source/santad/SNTSyncdQueue.m
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
ruleTable:self.mockRuleDatabase
|
||||
eventTable:self.mockEventDatabase
|
||||
notifierQueue:nil
|
||||
syncdQueue:nil
|
||||
eventLog:nil];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user