Compare commits

...

28 Commits

Author SHA1 Message Date
Tom Burgin
b824a8e3e0 santad: only store events if there is a sync server configured (#721)
* santad: only store events if there is a sync server configured

* SNTExecutionControllerTest stub sync server

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-01-27 15:55:51 -05:00
Kent Ma
25bf2a93e4 Add DiskArbitrationTestUtil to shim out DiskArbitration for unit testing (#720) 2022-01-25 13:45:03 -05:00
Russell Hancox
f1ea1b369f santactl/fileinfo: Switch certIndex to an NSNumber (#719)
* santactl/fileinfo: Switch certIndex to an NSNumber
2022-01-25 12:50:04 -05:00
Tom Burgin
5503a88308 rule download: return early on daemon timeout (#718)
* rule download: return early on daemon timeout

* wording update

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-01-21 17:19:44 +00:00
Kent Ma
8cf0f8217d Add clang_analyzer generation (#717)
Fix warnings for unused variables. The other warnings are more
nontrivial & require some light refactoring to fix, and will come in a followup PR.
2022-01-21 17:14:44 +00:00
Russell Hancox
22799ffc2a Conf: Delete and clean-up ASL conf, enable signaling on newsyslog.conf. (#716)
* Conf: Delete and clean-up ASL conf, enable signaling on newsyslog.conf.

The ASL config is a remnant from when Santa did all logging via ASL before Apple deprecated ASL and replaced it with ULS, which doesn't allow redirecting messages to a file. The old config wasn't causing any problems except that it was handling battling newsyslog for rotation and had different parameters.

The signal change in the newsyslog.conf causes newsyslog to fallback on sending a (harmless) SIGHUP to syslogd, which has no effect on Santa except it also triggers a 10s sleep inside newsyslog between renaming the old file and beginning the compression, which is plenty of time for santad to notice the rename and start writing new logs to the newer file.
2022-01-19 11:29:39 -05:00
Pete Markowsky
cb61d0cc99 Create test suites for each component (#702)
Create test suites for each component.
2022-01-18 17:00:44 -05:00
Pete Markowsky
fb7447ceba Fix off-by one error in strlcpy. (#715) 2022-01-18 15:31:30 -05:00
Russell Hancox
45e51e9c09 santactl/fileinfo: Clarify valid index for cert-index (#714) 2022-01-13 14:35:30 -05:00
Russell Hancox
b0f0cdd4e6 santactl/fileinfo: Update --cert-index usage (#713)
* santactl/fileinfo: Update --cert-index usage.

Fixes #710
2022-01-13 13:04:38 -05:00
Kent Ma
65090d3ef2 Support rule downloading of Team ID rules (#709)
* Support syncing Team ID rules and using 'identifier' instead of 'sha256' in sync rules
2022-01-13 10:55:14 -05:00
Russell Hancox
9c80f79d82 Sync: Allow configuring proxies (#708)
* Sync: Allow configuring proxies

Fixes #672
2022-01-13 15:04:11 +00:00
Kent Ma
93adaea81e Add clang annotation for fallthrough (#712) 2022-01-12 13:56:47 -05:00
Russell Hancox
a125b340a5 santad: Don't use proc_pidpath when using ES (#707) 2022-01-11 20:32:29 -05:00
Kent Ma
fbd0de3d48 Add test coverage for syncing USB mounting options (#711) 2022-01-11 17:13:37 -05:00
Russell Hancox
6f2ae62bce Project: Explicitly set calendar on ISO8601 dates (#706) 2022-01-06 09:33:04 -05:00
Christopher Sauer
da29b20473 Update hedron_compile_commands (#704) 2021-12-30 07:59:35 -05:00
Kent Ma
197109a8ee USB mass storage blocking and remounting (#685)
* USB mass storage blocking.

* Add the sync service and config key for enabling mass USB storage blocking
* Update docs with the sync service key
* Add ability to forcibly remount USBs with different flags
* update EndpointSecurityTestUtil and tests that use it to properly handle multiple ES clients
2021-12-16 13:38:48 -05:00
Kent Ma
91f3168c7a Update santactl rule text to have accurate text for team IDs (#701) 2021-12-14 11:42:53 -05:00
Russell Hancox
a00ec41518 Project: Bump version to 2022.1 (#700) 2021-12-13 13:28:16 -05:00
Russell Hancox
c32248aaf7 santad: Fix PrinterProxy workaround for Monterey+ (#698) 2021-12-13 15:24:58 +00:00
Pete Markowsky
afd97bdf3e Removed the check for export metrics in santad. (#697)
Remove the check for export metrics in santad

Metrics are always collected but only exported to a monitoring system when all of the necessary config options are set. Since they're always collected santactl metrics should always return metrics data.
2021-12-13 10:23:05 -05:00
Tom Burgin
73c4875b1f santasyncservice: move sync code to the santasyncservice dir (#696)
* sync: move sync code from santactl dir to santasyncservice dir

* clang-format

* fix tests

* s/SNTCommandSync/SNTSync

* s/SNTCommandSync/SNTSync on content
2021-12-08 18:11:56 -05:00
Pete Markowsky
916fc8c0e6 Add a simple event counter to SNTExecutionController (#694)
Add a simple event counter for events per response.
2021-12-08 17:35:37 -05:00
Kent Ma
e59e6105f3 Update the Santa version number to 2021.9 (#695) 2021-12-08 17:01:43 -05:00
Pete Markowsky
216ac811eb Fix issue with reregistering metrics. (#693) 2021-12-07 15:32:15 -05:00
Pete Markowsky
48f92f5913 Ignore VSCode directories (#692) 2021-12-07 14:23:13 -05:00
Russell Hancox
6bb08d0490 Project: Add bazel commands extractor for VSCode integration (#690) 2021-12-06 13:39:23 -05:00
78 changed files with 1635 additions and 421 deletions

3
.gitignore vendored
View File

@@ -15,3 +15,6 @@ tulsigen-*
*.p12
*.keychain
*.swp
compile_commands.json
.cache/
.vscode/*

14
BUILD
View File

@@ -99,7 +99,6 @@ genrule(
"Conf/com.google.santa.metricservice.plist",
"Conf/com.google.santad.plist",
"Conf/com.google.santa.plist",
"Conf/com.google.santa.asl.conf",
"Conf/com.google.santa.newsyslog.conf",
"Conf/Package/Distribution.xml",
"Conf/Package/notarization_tool.sh",
@@ -231,18 +230,11 @@ genrule(
test_suite(
name = "unit_tests",
tests = [
"//Source/common:SNTFileInfoTest",
"//Source/common:SNTMetricSetTest",
"//Source/common:SNTPrefixTreeTest",
"//Source/common:SantaCacheTest",
"//Source/common:unit_tests",
"//Source/santactl:unit_tests",
"//Source/santad:SNTApplicationCoreMetricsTest",
"//Source/santad:SNTApplicationTest",
"//Source/santad:SNTEndpointSecurityManagerTest",
"//Source/santad:SNTEventTableTest",
"//Source/santad:SNTExecutionControllerTest",
"//Source/santad:SNTRuleTableTest",
"//Source/santad:unit_tests",
"//Source/santametricservice:unit_tests",
"//Source/santasyncservice:unit_tests",
],
)

View File

@@ -1,6 +0,0 @@
# Copy this file to /etc/asl to log all messages from santa-driver to the log file
> /var/db/santa/santa.log format="[$((Time)(ISO8601Z.3))] $Message" mode=0644 rotate=seq compress file_max=25M all_max=100M uid=0 gid=0
? [= Sender kernel] [S= Message santa-driver:] claim
? [= Sender kernel] [S= Message santa-driver:] file /var/db/santa/santa.log
? [= Facility com.google.santa] claim
? [= Facility com.google.santa] file /var/db/santa/santa.log

View File

@@ -1,2 +1,2 @@
# logfilename [owner:group] mode count size(KiB) when flags [/pid_file] # [sig_num]
/var/db/santa/santa.log root:wheel 644 10 25000 * NZ
/var/db/santa/santa.log root:wheel 644 10 25000 * Z

View File

@@ -46,6 +46,7 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
/bin/rm /usr/sbin/santactl >/dev/null 2>&1
/bin/rm -rf /Applications/Santa.app 2>&1
/bin/rm -rf /Library/Extensions/santa-driver.kext 2>&1
/bin/rm /etc/asl/com.google.santa.asl.conf
# Copy new files.
/bin/mkdir -p /var/db/santa
@@ -63,7 +64,6 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
/bin/cp ${CONF}/com.google.santa.bundleservice.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santa.metricservice.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
/bin/cp ${CONF}/com.google.santa.asl.conf /etc/asl/
/bin/cp ${CONF}/com.google.santa.newsyslog.conf /etc/newsyslog.d/
# Reload syslogd to pick up ASL configuration change.

View File

@@ -16,10 +16,10 @@
#include <iostream>
#include <vector>
#include <SNTCommandSyncConstants.h>
#include <SNTCommandSyncRuleDownload.h>
#include <SNTCommandSyncState.h>
#include <SNTRule.h>
#include <SNTSyncConstants.h>
#include <SNTSyncRuleDownload.h>
#include <SNTSyncState.h>
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
NSData *buffer = [NSData dataWithBytes:static_cast<const void *>(data) length:size];
@@ -41,12 +41,12 @@ extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size
return 0;
}
SNTCommandSyncState *state = [[SNTCommandSyncState alloc] init];
SNTSyncState *state = [[SNTSyncState alloc] init];
if (!state) {
return 0;
}
SNTCommandSyncRuleDownload *obj = [[SNTCommandSyncRuleDownload alloc] initWithState:state];
SNTSyncRuleDownload *obj = [[SNTSyncRuleDownload alloc] initWithState:state];
if (!obj) {
return 0;
}

View File

@@ -277,3 +277,14 @@ santa_unit_test(
srcs = ["SNTMetricSetTest.m"],
deps = [":SNTMetricSet"],
)
test_suite(
name = "unit_tests",
tests = [
":SNTFileInfoTest",
":SNTMetricSetTest",
":SNTPrefixTreeTest",
":SantaCacheTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -254,6 +254,14 @@
///
@property(readonly, nonatomic) NSURL *syncBaseURL;
///
/// Proxy settings for syncing.
/// This dictionary is passed directly to NSURLSession. The allowed keys
/// are loosely documented at
/// https://developer.apple.com/documentation/cfnetwork/global_proxy_settings_constants.
///
@property(readonly, nonatomic) NSDictionary *syncProxyConfig;
///
/// The machine owner.
///
@@ -274,6 +282,23 @@
///
@property(nonatomic) BOOL syncCleanRequired;
///
/// USB Mount Blocking. Defaults to false.
///
@property(nonatomic) BOOL blockUSBMount;
///
/// Comma-seperated `$ mount -o` arguments used for forced remounting of USB devices. Default
/// to fully allow/deny without remounting if unset.
///
@property(nonatomic) NSArray<NSString *> *remountUSBMode;
///
/// When `blockUSBMount` is set, this is the message shown to the user when a device is blocked
/// If this message is not configured, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *usbBlockMessage;
///
/// If set, this over-rides the default machine ID used for syncing.
///

View File

@@ -45,6 +45,7 @@ static NSString *const kMobileConfigDomain = @"com.google.santa";
/// The keys managed by a mobileconfig.
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
static NSString *const kSyncProxyConfigKey = @"SyncProxyConfiguration";
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString *const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -95,6 +96,8 @@ static NSString *const kFCMAPIKey = @"FCMAPIKey";
// The keys managed by a sync server or mobileconfig.
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kBlockUSBMountKey = @"BlockUSBMount";
static NSString *const kRemountUSBModeKey = @"RemountUSBMode";
static NSString *const kEnableTransitiveRulesKey = @"EnableTransitiveRules";
static NSString *const kEnableTransitiveRulesKeyDeprecated = @"EnableTransitiveWhitelisting";
static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
@@ -131,6 +134,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kAllowedPathRegexKeyDeprecated : re,
kBlockedPathRegexKey : re,
kBlockedPathRegexKeyDeprecated : re,
kBlockUSBMountKey : number,
kRemountUSBModeKey : array,
kFullSyncLastSuccess : date,
kRuleSyncLastSuccess : date,
kSyncCleanRequired : number
@@ -145,6 +150,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kAllowedPathRegexKeyDeprecated : re,
kBlockedPathRegexKey : re,
kBlockedPathRegexKeyDeprecated : re,
kBlockUSBMountKey : number,
kRemountUSBModeKey : array,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kAboutText : string,
@@ -156,6 +163,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kSyncBaseURLKey : string,
kSyncProxyConfigKey : dictionary,
kClientAuthCertificateFileKey : string,
kClientAuthCertificatePasswordKey : string,
kClientAuthCertificateCNKey : string,
@@ -486,6 +494,20 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return filters;
}
- (void)setRemountUSBMode:(NSArray<NSString *> *)args {
[self updateSyncStateForKey:kRemountUSBModeKey value:args];
}
- (NSArray<NSString *> *)remountUSBMode {
NSArray<NSString *> *args = self.configState[kRemountUSBModeKey];
for (id arg in args) {
if (![arg isKindOfClass:[NSString class]]) {
return nil;
}
}
return args;
}
- (NSURL *)syncBaseURL {
NSString *urlString = self.configState[kSyncBaseURLKey];
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
@@ -493,6 +515,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return url;
}
- (NSDictionary *)syncProxyConfig {
return self.configState[kSyncProxyConfigKey];
}
- (BOOL)enablePageZeroProtection {
NSNumber *number = self.configState[kEnablePageZeroProtectionKey];
return number ? [number boolValue] : YES;
@@ -679,6 +705,15 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return (self.fcmProject.length && self.fcmEntity.length && self.fcmAPIKey.length);
}
- (void)setBlockUSBMount:(BOOL)enabled {
[self updateSyncStateForKey:kBlockUSBMountKey value:@(enabled)];
}
- (BOOL)blockUSBMount {
NSNumber *number = self.configState[kBlockUSBMountKey];
return number ? [number boolValue] : NO;
}
///
/// Returns YES if all of the necessary options are set to export metrics, NO
/// otherwise.

View File

@@ -521,8 +521,7 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
SNTMetricCounter *c = [[SNTMetricCounter alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
[self registerMetric:c];
return c;
return (SNTMetricCounter *)[self registerMetric:c];
}
- (SNTMetricInt64Gauge *)int64GaugeWithName:(NSString *)name
@@ -531,8 +530,7 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
SNTMetricInt64Gauge *g = [[SNTMetricInt64Gauge alloc] initWithName:name
fieldNames:fieldNames
helpText:helpText];
[self registerMetric:g];
return g;
return (SNTMetricInt64Gauge *)[self registerMetric:g];
}
- (SNTMetricDoubleGauge *)doubleGaugeWithName:(NSString *)name
@@ -542,8 +540,7 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
fieldNames:fieldNames
helpText:helpText];
[self registerMetric:g];
return g;
return (SNTMetricDoubleGauge *)[self registerMetric:g];
}
- (SNTMetricStringGauge *)stringGaugeWithName:(NSString *)name
@@ -553,8 +550,7 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
fieldNames:fieldNames
helpText:helpText];
[self registerMetric:s];
return s;
return (SNTMetricStringGauge *)[self registerMetric:s];
}
- (SNTMetricBooleanGauge *)booleanGaugeWithName:(NSString *)name
@@ -564,8 +560,7 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
fieldNames:fieldNames
helpText:helpText];
[self registerMetric:b];
return b;
return (SNTMetricBooleanGauge *)[self registerMetric:b];
}
- (void)addConstantStringWithName:(NSString *)name

View File

@@ -76,6 +76,19 @@
XCTAssertEqualObjects([c export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricCounter *a = [metricSet counterWithName:@"/santa/counter"
fieldNames:@[]
helpText:@"Test counter."];
SNTMetricCounter *b = [metricSet counterWithName:@"/santa/counter"
fieldNames:@[]
helpText:@"Test counter."];
XCTAssertEqual(a, b, @"Unexpected new counter returned.");
}
@end
@implementation SNTMetricBooleanGaugeTest
@@ -114,6 +127,20 @@
NSDictionary *output = [b export];
XCTAssertEqualObjects(output, expected);
}
- (void)testAddingBooleanWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricBooleanGauge *a = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
SNTMetricBooleanGauge *b = [metricSet booleanGaugeWithName:@"/santa/daemon_connected"
fieldNames:@[]
helpText:@"Is the daemon connected."];
XCTAssertEqual(a, b, @"Unexpected new boolean gauge returned.");
}
@end
@implementation SNTMetricGaugeInt64Test
@@ -168,6 +195,20 @@
XCTAssertEqualObjects([g export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricInt64Gauge *a = [metricSet int64GaugeWithName:@"/santa/int64gauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricInt64Gauge *b = [metricSet int64GaugeWithName:@"/santa/int64gauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricDoubleGaugeTest
@@ -227,6 +268,19 @@
};
XCTAssertEqualObjects([g export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricDoubleGauge *a = [metricSet doubleGaugeWithName:@"/santa/doublegauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricDoubleGauge *b = [metricSet doubleGaugeWithName:@"/santa/doublegauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricStringGaugeTest
@@ -240,6 +294,7 @@
[s set:@"testValue" forFieldValues:@[]];
XCTAssertEqualObjects([s getStringValueForFieldValues:@[]], @"testValue");
}
- (void)testExportNSDictionary {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricStringGauge *s = [metricSet stringGaugeWithName:@"/santa/mode"
@@ -264,6 +319,20 @@
XCTAssertEqualObjects([s export], expected);
}
- (void)testAddingMetricWithSameSchema {
SNTMetricSet *metricSet = [[SNTMetricSet alloc] init];
SNTMetricStringGauge *a = [metricSet stringGaugeWithName:@"/santa/stringgauge"
fieldNames:@[]
helpText:@"Test gauge."];
SNTMetricStringGauge *b = [metricSet stringGaugeWithName:@"/santa/stringgauge"
fieldNames:@[]
helpText:@"Test gauge."];
XCTAssertEqual(a, b, @"Unexpected new gauge returned.");
}
@end
@implementation SNTMetricSetTest
@@ -550,7 +619,6 @@
XCTAssertEqualObjects([metricSet export], expected);
}
@end
@implementation SNTMetricSetHelperFunctionsTest

View File

@@ -47,6 +47,8 @@
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
- (void)setAllowedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
- (void)setBlockedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
- (void)setBlockUSBMount:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply;
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;

View File

@@ -19,25 +19,7 @@ objc_library(
"Commands/SNTCommandVersion.m",
"Commands/SNTCommandMetrics.h",
"Commands/SNTCommandMetrics.m",
"Commands/sync/NSData+Zlib.h",
"Commands/sync/NSData+Zlib.m",
"Commands/sync/SNTCommandSync.m",
"Commands/sync/SNTCommandSyncConstants.h",
"Commands/sync/SNTCommandSyncConstants.m",
"Commands/sync/SNTCommandSyncEventUpload.h",
"Commands/sync/SNTCommandSyncEventUpload.m",
"Commands/sync/SNTCommandSyncManager.h",
"Commands/sync/SNTCommandSyncManager.m",
"Commands/sync/SNTCommandSyncPostflight.h",
"Commands/sync/SNTCommandSyncPostflight.m",
"Commands/sync/SNTCommandSyncPreflight.h",
"Commands/sync/SNTCommandSyncPreflight.m",
"Commands/sync/SNTCommandSyncRuleDownload.h",
"Commands/sync/SNTCommandSyncRuleDownload.m",
"Commands/sync/SNTCommandSyncStage.h",
"Commands/sync/SNTCommandSyncStage.m",
"Commands/sync/SNTCommandSyncState.h",
"Commands/sync/SNTCommandSyncState.m",
"Commands/SNTCommandSync.m",
] + select({
"//:opt_build": [],
"//conditions:default": [
@@ -50,7 +32,6 @@ objc_library(
sdk_dylibs = ["libz"],
sdk_frameworks = ["IOKit"],
deps = [
":FCM_lib",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
@@ -65,25 +46,14 @@ objc_library(
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncdInterface",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/santasyncservice:sync_lib",
"@FMDB",
"@MOLAuthenticatingURLSession",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
objc_library(
name = "FCM_lib",
srcs = ["Commands/sync/SNTCommandSyncFCM.m"],
hdrs = ["Commands/sync/SNTCommandSyncFCM.h"],
sdk_frameworks = ["SystemConfiguration"],
deps = [
"@MOLAuthenticatingURLSession",
],
)
macos_command_line_application(
name = "santactl",
bundle_id = "com.google.santa.ctl",
@@ -124,56 +94,6 @@ santa_unit_test(
],
)
santa_unit_test(
name = "SNTCommandSyncTest",
srcs = [
"Commands/sync/NSData+Zlib.h",
"Commands/sync/NSData+Zlib.m",
"Commands/sync/SNTCommandSync.m",
"Commands/sync/SNTCommandSyncConstants.h",
"Commands/sync/SNTCommandSyncConstants.m",
"Commands/sync/SNTCommandSyncEventUpload.h",
"Commands/sync/SNTCommandSyncEventUpload.m",
"Commands/sync/SNTCommandSyncManager.h",
"Commands/sync/SNTCommandSyncManager.m",
"Commands/sync/SNTCommandSyncPostflight.h",
"Commands/sync/SNTCommandSyncPostflight.m",
"Commands/sync/SNTCommandSyncPreflight.h",
"Commands/sync/SNTCommandSyncPreflight.m",
"Commands/sync/SNTCommandSyncRuleDownload.h",
"Commands/sync/SNTCommandSyncRuleDownload.m",
"Commands/sync/SNTCommandSyncStage.h",
"Commands/sync/SNTCommandSyncStage.m",
"Commands/sync/SNTCommandSyncState.h",
"Commands/sync/SNTCommandSyncState.m",
"Commands/sync/SNTCommandSyncTest.m",
"SNTCommand.h",
"SNTCommand.m",
"SNTCommandController.h",
"SNTCommandController.m",
],
resources = glob([
"Commands/sync/testdata/*.json",
"Commands/sync/testdata/*.plist",
]),
sdk_dylibs = ["libz"],
deps = [
":FCM_lib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncdInterface",
"@MOLAuthenticatingURLSession",
"@MOLXPCConnection",
"@OCMock",
],
)
santa_unit_test(
name = "SNTCommandMetricsTest",
srcs = ["Commands/SNTCommandMetricsTest.m"],
@@ -191,7 +111,6 @@ test_suite(
tests = [
":SNTCommandFileInfoTest",
":SNTCommandMetricsTest",
":SNTCommandSyncTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -71,7 +71,7 @@ NSString *formattedStringForKeyArray(NSArray<NSString *> *array) {
// Properties set from commandline flags
@property(nonatomic) BOOL recursive;
@property(nonatomic) BOOL jsonOutput;
@property(nonatomic) int certIndex; // 0 means no cert-index specified
@property(nonatomic) NSNumber *certIndex;
@property(nonatomic, copy) NSArray<NSString *> *outputKeyList;
@property(nonatomic, copy) NSDictionary<NSString *, NSRegularExpression *> *outputFilters;
@@ -163,9 +163,8 @@ REGISTER_COMMAND_NAME(@"fileinfo")
@"%@\n"
@" --cert-index: Supply an integer corresponding to a certificate of the\n"
@" signing chain to show info only for that certificate.\n"
@" 1 for the leaf certificate\n"
@" -1 for the root certificate\n"
@" 2 and up for the intermediates / root\n"
@" 0 up to n for the leaf certificate up to the root\n"
@" -1 down to -n-1 for the root certificate down to the leaf\n"
@"\n"
@" --filter: Use predicates of the form 'key=regex' to filter out which files\n"
@" are displayed. Valid keys are the same as for --key. Value is a\n"
@@ -590,12 +589,24 @@ REGISTER_COMMAND_NAME(@"fileinfo")
// First build up a dictionary containing all the information we want to print out
NSMutableDictionary *outputDict = [NSMutableDictionary dictionary];
if (self.certIndex) {
int index = [self.certIndex intValue];
// --cert-index flag implicitly means that we want only the signing chain. So we find the
// specified certificate in the signing chain, then print out values for all keys in cert.
NSArray *signingChain = self.propertyMap[kSigningChain](self, fileInfo);
if (!signingChain || !signingChain.count) return; // check signing chain isn't empty
int index = (self.certIndex == -1) ? (int)signingChain.count - 1 : self.certIndex - 1;
if (index < 0 || index >= (int)signingChain.count) return; // check that index is valid
if (index < 0) {
index = (int)signingChain.count - -(index);
if (index < 0) {
fprintf(stderr, "Invalid --cert-index: %d\n", index);
return;
}
} else {
if (index >= (int)signingChain.count) {
fprintf(stderr, "Invalid --cert-index: %d\n", index);
return;
}
}
NSDictionary *cert = signingChain[index];
// Check if we should skip over this item based on outputFilters.
@@ -702,14 +713,12 @@ REGISTER_COMMAND_NAME(@"fileinfo")
}
int index = 0;
NSScanner *scanner = [NSScanner scannerWithString:arguments[i]];
if (![scanner scanInt:&index] || !scanner.atEnd || index == 0 || index < -1) {
[self
printErrorUsageAndExit:
[NSString stringWithFormat:@"\n\"%@\" is an invalid argument for --cert-index\n"
@" --cert-index argument must be one of -1, 1, 2, 3, ...",
arguments[i]]];
if (![scanner scanInt:&index] || !scanner.atEnd) {
[self printErrorUsageAndExit:
[NSString stringWithFormat:@"\n\"%@\" is an invalid argument for --cert-index\n",
arguments[i]]];
}
self.certIndex = index;
self.certIndex = @(index);
} else if ([arg caseInsensitiveCompare:@"--key"] == NSOrderedSame) {
i += 1; // advance to next argument and grab the key
if (i >= nargs || [arguments[i] hasPrefix:@"--"]) {

View File

@@ -24,7 +24,7 @@
typedef id (^SNTAttributeBlock)(SNTCommandFileInfo *, SNTFileInfo *);
@property(nonatomic) BOOL recursive;
@property(nonatomic) BOOL jsonOutput;
@property(nonatomic) int certIndex;
@property(nonatomic) NSNumber *certIndex;
@property(nonatomic, copy) NSArray<NSString *> *outputKeyList;
@property(nonatomic) NSDictionary<NSString *, SNTAttributeBlock> *propertyMap;
+ (NSArray *)fileInfoKeys;
@@ -71,7 +71,7 @@ typedef id (^SNTAttributeBlock)(SNTCommandFileInfo *, SNTFileInfo *);
- (void)testParseArgumentsCertIndex {
NSArray *filePaths = [self.cfi parseArguments:@[ @"--cert-index", @"1", @"/usr/bin/yes" ]];
XCTAssertEqual(self.cfi.certIndex, 1);
XCTAssertEqual([self.cfi.certIndex intValue], 1);
XCTAssertTrue([filePaths containsObject:@"/usr/bin/yes"]);
}

View File

@@ -148,7 +148,7 @@ REGISTER_COMMAND_NAME(@"rule")
}
if (check) {
if (!newRule.identifier) return [self printErrorUsageAndExit:@"--check requires --sha256"];
if (!newRule.identifier) return [self printErrorUsageAndExit:@"--check requires --identifier"];
return [self printStateOfRule:newRule daemonConnection:self.daemonConn];
}
@@ -170,7 +170,7 @@ REGISTER_COMMAND_NAME(@"rule")
if (newRule.state == SNTRuleStateUnknown) {
[self printErrorUsageAndExit:@"No state specified"];
} else if (!newRule.identifier) {
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
[self printErrorUsageAndExit:@"Either SHA-256, team ID, or path to file must be specified"];
}
[[self.daemonConn remoteObjectProxy]
@@ -183,10 +183,25 @@ REGISTER_COMMAND_NAME(@"rule")
LOGD(@"Failure reason: %@", error.localizedFailureReason);
exit(1);
} else {
NSString *ruleType;
switch (newRule.type) {
case SNTRuleTypeCertificate:
case SNTRuleTypeBinary: {
ruleType = @"SHA-256";
break;
}
case SNTRuleTypeTeamID: {
ruleType = @"Team ID";
break;
}
default: ruleType = @"(Unknown type)";
}
if (newRule.state == SNTRuleStateRemove) {
printf("Removed rule for SHA-256: %s.\n", [newRule.identifier UTF8String]);
printf("Removed rule for %s: %s.\n", [ruleType UTF8String],
[newRule.identifier UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.identifier UTF8String]);
printf("Added rule for %s: %s.\n", [ruleType UTF8String],
[newRule.identifier UTF8String]);
}
exit(0);
}

View File

@@ -19,13 +19,13 @@
#import "Source/common/SNTDropRootPrivs.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncManager.h"
#import "Source/santactl/SNTCommand.h"
#import "Source/santactl/SNTCommandController.h"
#import "Source/santasyncservice/SNTSyncManager.h"
@interface SNTCommandSync : SNTCommand <SNTCommandProtocol>
@property MOLXPCConnection *listener;
@property SNTCommandSyncManager *syncManager;
@property SNTSyncManager *syncManager;
@end
@implementation SNTCommandSync
@@ -68,8 +68,8 @@ REGISTER_COMMAND_NAME(@"sync")
}
BOOL daemon = [arguments containsObject:@"--daemon"];
self.syncManager = [[SNTCommandSyncManager alloc] initWithDaemonConnection:self.daemonConn
isDaemon:daemon];
self.syncManager = [[SNTSyncManager alloc] initWithDaemonConnection:self.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.

View File

@@ -1 +0,0 @@
{"rules": [{"rule_type": "CERTIFICATE", "policy": "BLACKLIST", "sha256": "7846698e47ef41be80b83fb9e2b98fa6dc46c9188b068bff323c302955a00142", "custom_msg": "Hi There"}]}

View File

@@ -14,6 +14,8 @@ objc_library(
"DataLayer/SNTRuleTable.m",
"EventProviders/SNTCachingEndpointSecurityManager.h",
"EventProviders/SNTCachingEndpointSecurityManager.mm",
"EventProviders/SNTDeviceManager.h",
"EventProviders/SNTDeviceManager.mm",
"EventProviders/SNTDriverManager.h",
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
@@ -99,6 +101,27 @@ objc_library(
"EndpointSecurity",
"bsm",
],
sdk_frameworks = [
"DiskArbitration",
"IOKit",
],
)
objc_library(
name = "DiskArbitrationTestLib",
testonly = 1,
srcs = [
"EventProviders/DiskArbitrationTestUtil.h",
"EventProviders/DiskArbitrationTestUtil.mm",
],
sdk_dylibs = [
"EndpointSecurity",
"bsm",
],
sdk_frameworks = [
"DiskArbitration",
"IOKit",
],
)
macos_bundle(
@@ -140,6 +163,7 @@ santa_unit_test(
"//Source/common:SNTFileInfo",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTPrefixTree",
"//Source/common:SNTRule",
"//Source/common:SNTXPCNotifierInterface",
@@ -199,6 +223,7 @@ santa_unit_test(
"EventProviders/SNTEndpointSecurityManagerTest.mm",
"EventProviders/SNTEventProvider.h",
],
minimum_os_version = "10.15",
sdk_dylibs = [
"EndpointSecurity",
"bsm",
@@ -211,6 +236,27 @@ santa_unit_test(
],
)
santa_unit_test(
name = "SNTDeviceManagerTest",
srcs = [
"EventProviders/SNTDeviceManagerTest.mm",
],
minimum_os_version = "10.15",
sdk_dylibs = [
"EndpointSecurity",
"bsm",
],
deps = [
":DiskArbitrationTestLib",
":EndpointSecurityTestLib",
":santad_lib",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
"@OCMock",
],
)
santa_unit_test(
name = "SNTApplicationTest",
srcs = [
@@ -270,3 +316,16 @@ santa_unit_test(
"@OCMock",
],
)
test_suite(
name = "unit_tests",
tests = [
":SNTApplicationCoreMetricsTest",
":SNTApplicationTest",
":SNTEndpointSecurityManagerTest",
":SNTEventTableTest",
":SNTExecutionControllerTest",
":SNTRuleTableTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -0,0 +1,84 @@
/// Copyright 2021 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.
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
// Mock object to point the opaque DADiskRefs to instead.
// Note that this will have undefined behavior for DA functions that aren't
// shimmed out by this utility, as the original DADiskRef refers to a completely
// different struct managed by the CFRuntime.
// https://opensource.apple.com/source/DiskArbitration/DiskArbitration-297.70.1/DiskArbitration/DADisk.c.auto.html
@interface MockDADisk : NSObject
@property(nonatomic) NSDictionary *diskDescription;
@property(nonatomic, readwrite) NSString *name;
@end
typedef void (^MockDADiskAppearedCallback)(DADiskRef ref);
// Singleton mock fixture around all of the DiskArbitration framework functions
@interface MockDiskArbitration : NSObject
@property(nonatomic, readwrite, nonnull)
NSMutableDictionary<NSString *, MockDADisk *> *insertedDevices;
@property(nonatomic, readwrite, nonnull)
NSMutableArray<MockDADiskAppearedCallback> *diskAppearedCallbacks;
@property(nonatomic) BOOL wasRemounted;
@property(nonatomic, nullable) dispatch_queue_t sessionQueue;
- (instancetype _Nonnull)init;
- (void)reset;
// Also triggers DADiskRegisterDiskAppearedCallback
- (void)insert:(MockDADisk *)ref bsdName:(NSString *)bsdName;
// Retrieve an initialized singleton MockDiskArbitration object
+ (instancetype _Nonnull)mockDiskArbitration;
@end
//
// All DiskArbitration functions used in SNTDeviceManager and shimmed out accordingly.
//
CF_EXTERN_C_BEGIN
void DADiskMountWithArguments(DADiskRef _Nonnull disk, CFURLRef __nullable path,
DADiskMountOptions options, DADiskMountCallback __nullable callback,
void *__nullable context,
CFStringRef __nullable arguments[_Nullable]);
DADiskRef __nullable DADiskCreateFromBSDName(CFAllocatorRef __nullable allocator,
DASessionRef session, const char *name);
CFDictionaryRef __nullable DADiskCopyDescription(DADiskRef disk);
void DARegisterDiskAppearedCallback(DASessionRef session, CFDictionaryRef __nullable match,
DADiskAppearedCallback callback, void *__nullable context);
void DARegisterDiskDisappearedCallback(DASessionRef session, CFDictionaryRef __nullable match,
DADiskDisappearedCallback callback,
void *__nullable context);
void DARegisterDiskDescriptionChangedCallback(DASessionRef session,
CFDictionaryRef __nullable match,
CFArrayRef __nullable watch,
DADiskDescriptionChangedCallback callback,
void *__nullable context);
void DASessionSetDispatchQueue(DASessionRef session, dispatch_queue_t __nullable queue);
DASessionRef __nullable DASessionCreate(CFAllocatorRef __nullable allocator);
CF_EXTERN_C_END
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,120 @@
/// Copyright 2021 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 <Foundation/Foundation.h>
#include <stdlib.h>
#import "Source/santad/EventProviders/DiskArbitrationTestUtil.h"
NS_ASSUME_NONNULL_BEGIN
@implementation MockDADisk
@end
@implementation MockDiskArbitration
- (instancetype _Nonnull)init {
self = [super init];
if (self) {
_insertedDevices = [NSMutableDictionary dictionary];
_diskAppearedCallbacks = [NSMutableArray array];
}
return self;
}
- (void)reset {
[self.insertedDevices removeAllObjects];
[self.diskAppearedCallbacks removeAllObjects];
self.sessionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.wasRemounted = NO;
}
- (void)insert:(MockDADisk *)ref bsdName:(NSString *)bsdName {
self.insertedDevices[bsdName] = ref;
for (MockDADiskAppearedCallback callback in self.diskAppearedCallbacks) {
dispatch_sync(self.sessionQueue, ^{
callback((__bridge DADiskRef)ref);
});
}
}
// Retrieve an initialized singleton MockDiskArbitration object
+ (instancetype _Nonnull)mockDiskArbitration {
static MockDiskArbitration *sharedES;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedES = [[MockDiskArbitration alloc] init];
});
return sharedES;
};
@end
void DADiskMountWithArguments(DADiskRef _Nonnull disk, CFURLRef __nullable path,
DADiskMountOptions options, DADiskMountCallback __nullable callback,
void *__nullable context,
CFStringRef __nullable arguments[_Nullable]) {
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
mockDA.wasRemounted = YES;
}
DADiskRef __nullable DADiskCreateFromBSDName(CFAllocatorRef __nullable allocator,
DASessionRef session, const char *name) {
NSString *nsName = [NSString stringWithUTF8String:name];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
MockDADisk *got = mockDA.insertedDevices[nsName];
DADiskRef ref = (__bridge DADiskRef)got;
CFRetain(ref);
return ref;
}
CFDictionaryRef __nullable DADiskCopyDescription(DADiskRef disk) {
CFDictionaryRef description = NULL;
if (disk) {
MockDADisk *mockDisk = (__bridge MockDADisk *)disk;
description = (__bridge_retained CFDictionaryRef)mockDisk.diskDescription;
}
return description;
}
void DARegisterDiskAppearedCallback(DASessionRef session, CFDictionaryRef __nullable match,
DADiskAppearedCallback callback, void *__nullable context) {
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA.diskAppearedCallbacks addObject:^(DADiskRef ref) {
callback(ref, context);
}];
}
void DARegisterDiskDisappearedCallback(DASessionRef session, CFDictionaryRef __nullable match,
DADiskDisappearedCallback callback,
void *__nullable context){};
void DARegisterDiskDescriptionChangedCallback(DASessionRef session,
CFDictionaryRef __nullable match,
CFArrayRef __nullable watch,
DADiskDescriptionChangedCallback callback,
void *__nullable context){};
void DASessionSetDispatchQueue(DASessionRef session, dispatch_queue_t __nullable queue) {
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
mockDA.sessionQueue = queue;
};
DASessionRef __nullable DASessionCreate(CFAllocatorRef __nullable allocator) {
return (__bridge DASessionRef)[MockDiskArbitration mockDiskArbitration];
};
NS_ASSUME_NONNULL_END

View File

@@ -46,7 +46,7 @@ typedef void (^ESCallback)(ESResponse *_Nonnull);
@interface MockEndpointSecurity : NSObject
@property NSMutableArray *_Nonnull subscriptions;
- (void)reset;
- (void)registerResponseCallback:(ESCallback _Nonnull)callback;
- (void)registerResponseCallback:(es_event_type_t)t withCallback:(ESCallback _Nonnull)callback;
- (void)triggerHandler:(es_message_t *_Nonnull)msg;
/// Retrieve an initialized singleton MockEndpointSecurity object

View File

@@ -89,19 +89,22 @@ CF_EXTERN_C_END
@implementation ESResponse
@end
@interface MockEndpointSecurity ()
@property NSMutableArray<ESCallback> *responseCallbacks;
@property NSObject *client;
@interface MockESClient : NSObject
@property NSMutableArray *_Nonnull subscriptions;
@property es_handler_block_t handler;
@end
@implementation MockEndpointSecurity
@implementation MockESClient
- (instancetype)init {
self = [super init];
if (self) {
_responseCallbacks = [NSMutableArray array];
_subscriptions = [NSMutableArray arrayWithCapacity:ES_EVENT_TYPE_LAST];
[self resetSubscriptions];
@synchronized(self) {
_subscriptions = [NSMutableArray arrayWithCapacity:ES_EVENT_TYPE_LAST];
for (size_t i = 0; i < ES_EVENT_TYPE_LAST; i++) {
[self.subscriptions addObject:@NO];
}
}
}
return self;
};
@@ -112,31 +115,78 @@ CF_EXTERN_C_END
}
}
- (void)triggerHandler:(es_message_t *_Nonnull)msg {
self.handler((__bridge es_client_t *_Nullable)self, msg);
}
- (void)dealloc {
@synchronized(self) {
[self.subscriptions removeAllObjects];
}
}
@end
@interface MockEndpointSecurity ()
@property NSMutableArray<MockESClient *> *clients;
// Array of collections of ESCallback blocks
// This should be of size ES_EVENT_TYPE_LAST, allowing for indexing by ES_EVENT_TYPE_xxx members.
@property NSMutableArray<NSMutableArray<ESCallback> *> *responseCallbacks;
@end
@implementation MockEndpointSecurity
- (instancetype)init {
self = [super init];
if (self) {
@synchronized(self) {
_clients = [NSMutableArray array];
_responseCallbacks = [NSMutableArray arrayWithCapacity:ES_EVENT_TYPE_LAST];
for (size_t i = 0; i < ES_EVENT_TYPE_LAST; i++) {
[self.responseCallbacks addObject:[NSMutableArray array]];
}
[self reset];
}
}
return self;
};
- (void)resetResponseCallbacks {
for (NSMutableArray *callback in self.responseCallbacks) {
if (callback != nil) {
[callback removeAllObjects];
}
}
}
- (void)reset {
@synchronized(self) {
[self.responseCallbacks removeAllObjects];
self.handler = nil;
self.client = nil;
[self.clients removeAllObjects];
[self resetResponseCallbacks];
}
};
- (void)newClient:(es_client_t *_Nullable *_Nonnull)client
handler:(es_handler_block_t __strong)handler {
// es_client_t is generally used as a pointer to an opaque struct (secretly a mach port).
// We just want to set it to something nonnull for passing initialization checks. It shouldn't
// ever be directly dereferenced.
self.client = [[NSObject alloc] init];
*client = (__bridge es_client_t *)self.client;
self.handler = handler;
// There is also a few nonnull initialization checks on it.
MockESClient *mockClient = [[MockESClient alloc] init];
*client = (__bridge es_client_t *)mockClient;
mockClient.handler = handler;
[self.clients addObject:mockClient];
}
- (void)triggerHandler:(es_message_t *_Nonnull)msg {
self.handler((__bridge es_client_t *_Nullable)self.client, msg);
for (MockESClient *client in self.clients) {
if (client.subscriptions[msg->event_type]) {
[client triggerHandler:msg];
}
}
}
- (void)registerResponseCallback:(ESCallback _Nonnull)callback {
- (void)registerResponseCallback:(es_event_type_t)t withCallback:(ESCallback _Nonnull)callback {
@synchronized(self) {
[self.responseCallbacks addObject:callback];
[self.responseCallbacks[t] addObject:callback];
}
}
@@ -147,7 +197,7 @@ CF_EXTERN_C_END
ESResponse *response = [[ESResponse alloc] init];
response.result = result;
response.shouldCache = cache;
for (void (^callback)(ESResponse *) in self.responseCallbacks) {
for (void (^callback)(ESResponse *) in self.responseCallbacks[msg->event_type]) {
callback(response);
}
}
@@ -156,10 +206,22 @@ CF_EXTERN_C_END
- (void)setSubscriptions:(const es_event_type_t *_Nonnull)events
event_count:(uint32_t)event_count
value:(NSNumber *)value {
value:(NSNumber *)value
client:(es_client_t *)client {
@synchronized(self) {
MockESClient *toUpdate = nil;
for (MockESClient *c in self.clients) {
if (client == (__bridge es_client_t *)c) {
toUpdate = c;
}
}
if (toUpdate == nil) {
NSLog(@"setting subscription for unknown client");
return;
}
for (size_t i = 0; i < event_count; i++) {
self.subscriptions[events[i]] = value;
toUpdate.subscriptions[events[i]] = value;
}
}
}
@@ -212,7 +274,8 @@ es_return_t es_subscribe(es_client_t *_Nonnull client, const es_event_type_t *_N
uint32_t event_count) {
[[MockEndpointSecurity mockEndpointSecurity] setSubscriptions:events
event_count:event_count
value:@YES];
value:@YES
client:client];
return ES_RETURN_SUCCESS;
}
API_AVAILABLE(macos(10.15))
@@ -221,7 +284,8 @@ es_return_t es_unsubscribe(es_client_t *_Nonnull client, const es_event_type_t *
uint32_t event_count) {
[[MockEndpointSecurity mockEndpointSecurity] setSubscriptions:events
event_count:event_count
value:@NO];
value:@NO
client:client];
return ES_RETURN_SUCCESS;
};

View File

@@ -0,0 +1,33 @@
/// Copyright 2021 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 <DiskArbitration/DiskArbitration.h>
#import <Foundation/Foundation.h>
#include <EndpointSecurity/EndpointSecurity.h>
/*
* Manages DiskArbitration and EndpointSecurity to monitor/block/remount USB
* storage devices.
*/
@interface SNTDeviceManager : NSObject
@property(nonatomic, readwrite) BOOL subscribed;
@property(nonatomic, readwrite) BOOL blockUSBMount;
@property(nonatomic, readwrite, nullable) NSArray<NSString *> *remountArgs;
- (instancetype _Nonnull)init;
- (void)listen;
- (BOOL)subscribed;
@end

View File

@@ -0,0 +1,300 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santad/EventProviders/SNTDeviceManager.h"
#import <DiskArbitration/DiskArbitration.h>
#import <Foundation/Foundation.h>
#include <bsm/libbsm.h>
#include <errno.h>
#include <libproc.h>
#include <sys/mount.h>
#include <atomic>
#include <memory>
#import "Source/common/SNTLogging.h"
#import "Source/santad/Logs/SNTEventLog.h"
void diskMountedCallback(DADiskRef disk, DADissenterRef dissenter, void *context) {
if (dissenter) {
DAReturn status = DADissenterGetStatus(dissenter);
NSString *statusString = (NSString *)DADissenterGetStatusString(dissenter);
IOReturn systemCode = err_get_system(status);
IOReturn subSystemCode = err_get_sub(status);
IOReturn errorCode = err_get_code(status);
LOGE(
@"SNTDeviceManager: dissenter status codes: system: %d, subsystem: %d, err: %d; status: %s",
systemCode, subSystemCode, errorCode, [statusString UTF8String]);
}
}
void diskAppearedCallback(DADiskRef disk, void *context) {
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
SNTEventLog *logger = [SNTEventLog logger];
if (logger) [logger logDiskAppeared:props];
}
void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef keys, void *context) {
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
if (props[@"DAVolumePath"]) {
SNTEventLog *logger = [SNTEventLog logger];
if (logger) [logger logDiskAppeared:props];
}
}
void diskDisappearedCallback(DADiskRef disk, void *context) {
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
SNTEventLog *logger = [SNTEventLog logger];
if (logger) [logger logDiskDisappeared:props];
}
NSArray<NSString *> *maskToMountArgs(long remountOpts) {
NSMutableArray<NSString *> *args = [NSMutableArray array];
if (remountOpts & MNT_RDONLY) [args addObject:@"rdonly"];
if (remountOpts & MNT_NOEXEC) [args addObject:@"noexec"];
if (remountOpts & MNT_NOSUID) [args addObject:@"nosuid"];
if (remountOpts & MNT_DONTBROWSE) [args addObject:@"nobrowse"];
if (remountOpts & MNT_UNKNOWNPERMISSIONS) [args addObject:@"noowners"];
if (remountOpts & MNT_NODEV) [args addObject:@"nodev"];
if (remountOpts & MNT_JOURNALED) [args addObject:@"-j"];
if (remountOpts & MNT_ASYNC) [args addObject:@"async"];
return args;
}
long mountArgsToMask(NSArray<NSString *> *args) {
long flags = 0;
for (NSString *i in args) {
NSString *arg = [i lowercaseString];
if ([arg isEqualToString:@"rdonly"])
flags |= MNT_RDONLY;
else if ([arg isEqualToString:@"noexec"])
flags |= MNT_NOEXEC;
else if ([arg isEqualToString:@"nosuid"])
flags |= MNT_NOSUID;
else if ([arg isEqualToString:@"nobrowse"])
flags |= MNT_DONTBROWSE;
else if ([arg isEqualToString:@"noowners"])
flags |= MNT_UNKNOWNPERMISSIONS;
else if ([arg isEqualToString:@"nodev"])
flags |= MNT_NODEV;
else if ([arg isEqualToString:@"-j"])
flags |= MNT_JOURNALED;
else if ([arg isEqualToString:@"async"])
flags |= MNT_ASYNC;
else
LOGE(@"SNTDeviceManager: unexpected mount arg: %@", arg);
}
return flags;
}
@interface SNTDeviceManager ()
@property DASessionRef diskArbSession;
@property(nonatomic, readonly) es_client_t *client;
@property(nonatomic, readonly) dispatch_queue_t esAuthQueue;
@property(nonatomic, readonly) dispatch_queue_t diskQueue;
@end
@implementation SNTDeviceManager
- (instancetype _Nonnull)init API_AVAILABLE(macos(10.15)) {
self = [super init];
if (self) {
_blockUSBMount = false;
_diskQueue = dispatch_queue_create("com.google.santad.disk_queue", DISPATCH_QUEUE_SERIAL);
_esAuthQueue =
dispatch_queue_create("com.google.santa.daemon.es_device_auth", DISPATCH_QUEUE_CONCURRENT);
_diskArbSession = DASessionCreate(NULL);
DASessionSetDispatchQueue(_diskArbSession, _diskQueue);
if (@available(macos 10.15, *)) [self initES];
}
return self;
}
- (void)initES API_AVAILABLE(macos(10.15)) {
while (!self.client) {
es_client_t *client = NULL;
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
// Set timeout to 5 seconds before the ES deadline.
[self handleESMessageWithTimeout:m
withClient:c
timeout:dispatch_time(m->deadline, NSEC_PER_SEC * -5)];
});
switch (ret) {
case ES_NEW_CLIENT_RESULT_SUCCESS:
LOGI(@"Connected to EndpointSecurity");
_client = client;
return;
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
LOGE(@"Sleeping for 30s before restarting.");
sleep(30);
exit(ret);
default:
LOGE(@"Unable to create es client: %d. Sleeping for a minute.", ret);
sleep(60);
continue;
}
}
}
- (void)listenES API_AVAILABLE(macos(10.15)) {
while (!self.client)
usleep(100000); // 100ms
es_event_type_t events[] = {
ES_EVENT_TYPE_AUTH_MOUNT,
};
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
if (sret != ES_RETURN_SUCCESS)
LOGE(@"SNTDeviceManager: unable to subscribe to auth mount events: %d", sret);
}
- (void)listenDA {
DARegisterDiskAppearedCallback(_diskArbSession, NULL, diskAppearedCallback,
(__bridge void *)self);
DARegisterDiskDescriptionChangedCallback(_diskArbSession, NULL, NULL,
diskDescriptionChangedCallback, (__bridge void *)self);
DARegisterDiskDisappearedCallback(_diskArbSession, NULL, diskDisappearedCallback,
(__bridge void *)self);
}
- (void)listen {
[self listenDA];
if (@available(macos 10.15, *)) [self listenES];
self.subscribed = YES;
}
- (void)handleAuthMount:(const es_message_t *)m
withClient:(es_client_t *)c API_AVAILABLE(macos(10.15)) {
if (!self.blockUSBMount) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
long mountMode = m->event.mount.statfs->f_flags;
pid_t pid = audit_token_to_pid(m->process->audit_token);
LOGI(@"SNTDeviceManager: mount syscall arriving from path: %s, pid: %d, fflags: %lu",
m->process->executable->path.data, pid, mountMode);
DADiskRef disk =
DADiskCreateFromBSDName(NULL, self.diskArbSession, m->event.mount.statfs->f_mntfromname);
CFAutorelease(disk);
// TODO(tnek): Log all of the other attributes available in diskInfo into a structured log format.
NSDictionary *diskInfo = CFBridgingRelease(DADiskCopyDescription(disk));
BOOL isRemovable = [diskInfo[(__bridge NSString *)kDADiskDescriptionMediaRemovableKey] boolValue];
BOOL isUSB =
[diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey] isEqualTo:@"USB"];
if (!isRemovable || !isUSB) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
BOOL shouldRemount = self.remountArgs != nil && [self.remountArgs count] > 0;
if (shouldRemount) {
long remountOpts = mountArgsToMask(self.remountArgs);
if (mountMode & remountOpts) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
long newMode = mountMode | remountOpts;
LOGI(@"SNTDeviceManager: remounting device '%s'->'%s', flags (%lu) -> (%lu)",
m->event.mount.statfs->f_mntfromname, m->event.mount.statfs->f_mntonname, mountMode,
newMode);
[self remount:disk mountMode:newMode];
}
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
}
- (void)remount:(DADiskRef)disk mountMode:(long)remountMask {
NSArray<NSString *> *args = maskToMountArgs(remountMask);
CFStringRef *argv = (CFStringRef *)calloc(args.count + 1, sizeof(CFStringRef));
CFArrayGetValues((__bridge CFArrayRef)args, CFRangeMake(0, (CFIndex)args.count),
(const void **)argv);
DADiskMountWithArguments(disk, NULL, kDADiskMountOptionDefault, diskMountedCallback,
(__bridge void *)self, (CFStringRef *)argv);
free(argv);
}
// handleESMessage handles an ES message synchronously. This will block all incoming ES events
// until either we serve a response or we hit the auth deadline. Prefer [SNTDeviceManager
// handleESMessageWithTimeout]
// TODO(tnek): generalize this timeout handling logic so that EndpointSecurityManager can use it
// too.
- (void)handleESMessageWithTimeout:(const es_message_t *)m
withClient:(es_client_t *)c
timeout:(dispatch_time_t)timeout API_AVAILABLE(macos(10.15)) {
// ES will kill our whole client if we don't meet the es_message auth deadline, so we try to
// gracefully handle it with a deny-by-default in the worst-case before it can do that.
// This isn't an issue for notify events, so we're in no rush for those.
std::shared_ptr<std::atomic<bool>> responded;
if (m->action_type == ES_ACTION_TYPE_AUTH) {
responded = std::make_shared<std::atomic<bool>>(false);
dispatch_after(timeout, self.esAuthQueue, ^(void) {
if (responded->load()) return;
LOGE(@"SNTDeviceManager: deadline reached: deny pid=%d ret=%d",
audit_token_to_pid(m->process->audit_token),
es_respond_auth_result(c, m, ES_AUTH_RESULT_DENY, false));
});
}
// TODO(tnek): migrate to es_retain_message.
es_message_t *mc = es_copy_message(m);
dispatch_async(self.esAuthQueue, ^{
[self handleESMessage:m withClient:c];
if (m->action_type == ES_ACTION_TYPE_AUTH) {
responded->store(true);
}
es_free_message(mc);
});
}
- (void)handleESMessage:(const es_message_t *)m
withClient:(es_client_t *)c API_AVAILABLE(macos(10.15)) {
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_MOUNT: {
[self handleAuthMount:m withClient:c];
// Intentional fallthrough
[[fallthrough]];
}
// TODO(tnek): log any extra data here about mounts.
case ES_EVENT_TYPE_NOTIFY_MOUNT: {
break;
}
default: LOGE(@"SNTDeviceManager: unexpected event type: %d", m->event_type);
}
}
@end

View File

@@ -0,0 +1,157 @@
/// Copyright 2021 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 <DiskArbitration/DiskArbitration.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import <bsm/libbsm.h>
#include <sys/mount.h>
#import "Source/common/SNTConfigurator.h"
#import "Source/santad/EventProviders/SNTDeviceManager.h"
#import "Source/santad/EventProviders/DiskArbitrationTestUtil.h"
#import "Source/santad/EventProviders/EndpointSecurityTestUtil.h"
@interface SNTDeviceManagerTest : XCTestCase
@property id mockConfigurator;
@end
@implementation SNTDeviceManagerTest
- (void)setUp {
[super setUp];
self.mockConfigurator = OCMClassMock([SNTConfigurator class]);
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
OCMStub([self.mockConfigurator eventLogType]).andReturn(-1);
fclose(stdout);
}
- (ESResponse *)triggerTestMount:(SNTDeviceManager *)deviceManager
mockES:(MockEndpointSecurity *)mockES
mockDA:(MockDiskArbitration *)mockDA {
if (!deviceManager.subscribed) {
// [deviceManager listen] is synchronous, but we want to asynchronously dispatch it
// with an enforced timeout to ensure that we never run into issues where the client
// never instantiates.
XCTestExpectation *initExpectation =
[self expectationWithDescription:@"Wait for SNTDeviceManager to instantiate"];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
[deviceManager listen];
});
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
while (!deviceManager.subscribed)
;
[initExpectation fulfill];
});
[self waitForExpectations:@[ initExpectation ] timeout:60.0];
}
struct statfs *fs = static_cast<struct statfs *>(calloc(1, sizeof(struct statfs)));
NSString *test_mntfromname = @"/dev/disk2s1";
NSString *test_mntonname = @"/Volumes/KATE'S 4G";
const char *c_mntfromname = [test_mntfromname UTF8String];
const char *c_mntonname = [test_mntonname UTF8String];
strncpy(fs->f_mntfromname, c_mntfromname, MAXPATHLEN);
strncpy(fs->f_mntonname, c_mntonname, MAXPATHLEN);
MockDADisk *disk = [[MockDADisk alloc] init];
disk.diskDescription = @{
(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey : @"USB",
(__bridge NSString *)kDADiskDescriptionMediaRemovableKey : @YES,
@"DAVolumeMountable" : @YES,
@"DAVolumePath" : test_mntonname,
@"DADeviceModel" : @"Some device model",
@"DADevicePath" : test_mntonname,
@"DADeviceVendor" : @"Some vendor",
@"DAAppearanceTime" : @0,
@"DAMediaBSDName" : test_mntfromname,
};
[mockDA insert:disk bsdName:test_mntfromname];
ESMessage *m = [[ESMessage alloc] initWithBlock:^(ESMessage *m) {
m.binaryPath = @"/System/Library/Filesystems/msdos.fs/Contents/Resources/mount_msdos";
m.message->action_type = ES_ACTION_TYPE_AUTH;
m.message->event_type = ES_EVENT_TYPE_AUTH_MOUNT;
m.message->event = (es_events_t){.mount = {.statfs = fs}};
}];
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_MOUNT
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES triggerHandler:m.message];
[self waitForExpectations:@[ expectation ] timeout:60.0];
free(fs);
return got;
}
- (void)testUSBBlockDisabled {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = NO;
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
}
- (void)testRemount {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
deviceManager.remountArgs = @[ @"noexec", @"rdonly" ];
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, YES);
}
- (void)testBlockNoRemount {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, NO);
}
@end

View File

@@ -352,6 +352,7 @@
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
return;
}
case ES_EVENT_TYPE_NOTIFY_CLOSE: {
sm.action = ACTION_NOTIFY_WRITE;
targetFile = m->event.close.target;

View File

@@ -49,15 +49,16 @@ const NSString *const kBenignPath = @"/some/other/path";
[self expectationWithDescription:@"Wait for santa's Auth dispatch queue"];
__block NSMutableArray<ESResponse *> *events = [NSMutableArray array];
[mockES registerResponseCallback:^(ESResponse *r) {
@synchronized(self) {
[events addObject:r];
}
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_UNLINK
withCallback:^(ESResponse *r) {
@synchronized(self) {
[events addObject:r];
}
if (events.count >= wantNumResp) {
[expectation fulfill];
}
}];
if (events.count >= wantNumResp) {
[expectation fulfill];
}
}];
__block es_file_t dbFile = {.path = MakeStringToken(kEventsDBPath)};
ESMessage *m = [[ESMessage alloc] initWithBlock:^(ESMessage *m) {
@@ -94,10 +95,11 @@ const NSString *const kBenignPath = @"/some/other/path";
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_UNLINK
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
__block es_file_t dbFile = {.path = MakeStringToken(testPath)};
ESMessage *m = [[ESMessage alloc] initWithBlock:^(ESMessage *m) {
@@ -125,10 +127,11 @@ const NSString *const kBenignPath = @"/some/other/path";
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_UNLINK
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
__block es_file_t dbFile = {.path = MakeStringToken(@"/some/other/path")};
ESMessage *m = [[ESMessage alloc] initWithBlock:^(ESMessage *m) {
@@ -160,10 +163,11 @@ const NSString *const kBenignPath = @"/some/other/path";
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_RENAME
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
__block es_file_t otherFile = {.path = MakeStringToken(@"/some/other/path")};
__block es_file_t dbFile = {.path = MakeStringToken(testPath)};
@@ -206,10 +210,11 @@ const NSString *const kBenignPath = @"/some/other/path";
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_RENAME
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
__block es_file_t otherFile = {.path = MakeStringToken(@"/some/other/path")};
__block es_file_t dbFile = {.path = MakeStringToken(testPath)};

View File

@@ -52,6 +52,7 @@
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
_dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierISO8601];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
// Grab the system UUID on init

View File

@@ -62,8 +62,15 @@
if (newpath) {
[outStr appendFormat:@"|newpath=%@", [self sanitizeString:newpath]];
}
char ppath[PATH_MAX] = "(null)";
proc_pidpath(message.pid, ppath, PATH_MAX);
if (message.es_message) {
es_message_t *m = message.es_message;
es_string_token_t path = m->process->executable->path;
strlcpy(ppath, path.data, sizeof(ppath));
} else {
proc_pidpath(message.pid, ppath, PATH_MAX);
}
[outStr
appendFormat:

View File

@@ -15,7 +15,6 @@
#import "Source/santad/SNTApplication.h"
#import "Source/santad/SNTApplicationCoreMetrics.h"
#import <DiskArbitration/DiskArbitration.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTCommonEnums.h"
@@ -30,6 +29,7 @@
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/EventProviders/SNTCachingEndpointSecurityManager.h"
#import "Source/santad/EventProviders/SNTDeviceManager.h"
#import "Source/santad/EventProviders/SNTDriverManager.h"
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
@@ -42,10 +42,10 @@
#import "Source/santad/SNTSyncdQueue.h"
@interface SNTApplication ()
@property DASessionRef diskArbSession;
@property id<SNTEventProvider> eventProvider;
@property SNTExecutionController *execController;
@property SNTCompilerController *compilerController;
@property SNTDeviceManager *deviceManager;
@property MOLXPCConnection *controlConnection;
@property SNTNotificationQueue *notQueue;
@property pid_t syncdPID;
@@ -92,6 +92,12 @@
return nil;
}
_deviceManager = [[SNTDeviceManager alloc] init];
self.deviceManager.blockUSBMount = [configurator blockUSBMount];
if ([configurator remountUSBMode] != nil) {
self.deviceManager.remountArgs = [configurator remountUSBMode];
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
// The filter is reset when santad disconnects from the driver.
// Add the default filters.
@@ -135,6 +141,14 @@
forKeyPath:NSStringFromSelector(@selector(metricExportInterval))
options:bits
context:NULL];
[configurator addObserver:self
forKeyPath:NSStringFromSelector(@selector(blockUSBMount))
options:bits
context:NULL];
[configurator addObserver:self
forKeyPath:NSStringFromSelector(@selector(remountUSBMode))
options:bits
context:NULL];
if (![configurator enableSystemExtension]) {
[configurator addObserver:self
@@ -184,7 +198,7 @@
[self performSelectorInBackground:@selector(beginListeningForDecisionRequests) withObject:nil];
[self performSelectorInBackground:@selector(beginListeningForLogRequests) withObject:nil];
[self performSelectorInBackground:@selector(beginListeningForDiskMounts) withObject:nil];
[self performSelectorInBackground:@selector(beginListeningForMountRequests) withObject:nil];
}
- (void)beginListeningForDecisionRequests {
@@ -239,42 +253,8 @@
}];
}
- (void)beginListeningForDiskMounts {
dispatch_queue_t disk_queue =
dispatch_queue_create("com.google.santad.disk_queue", DISPATCH_QUEUE_SERIAL);
_diskArbSession = DASessionCreate(NULL);
DASessionSetDispatchQueue(_diskArbSession, disk_queue);
DARegisterDiskAppearedCallback(_diskArbSession, NULL, diskAppearedCallback,
(__bridge void *)self);
DARegisterDiskDescriptionChangedCallback(_diskArbSession, NULL, NULL,
diskDescriptionChangedCallback, (__bridge void *)self);
DARegisterDiskDisappearedCallback(_diskArbSession, NULL, diskDisappearedCallback,
(__bridge void *)self);
}
void diskAppearedCallback(DADiskRef disk, void *context) {
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
[[SNTEventLog logger] logDiskAppeared:props];
}
void diskDescriptionChangedCallback(DADiskRef disk, CFArrayRef keys, void *context) {
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
if (props[@"DAVolumePath"]) [[SNTEventLog logger] logDiskAppeared:props];
}
void diskDisappearedCallback(DADiskRef disk, void *context) {
SNTApplication *app = (__bridge SNTApplication *)context;
NSDictionary *props = CFBridgingRelease(DADiskCopyDescription(disk));
if (![props[@"DAVolumeMountable"] boolValue]) return;
[[SNTEventLog logger] logDiskDisappeared:props];
[app.eventProvider flushCacheNonRootOnly:YES];
- (void)beginListeningForMountRequests {
[self.deviceManager listen];
}
// Taken from Apple's Concurrency Programming Guide.
@@ -415,6 +395,26 @@ dispatch_source_t createDispatchTimer(uint64_t interval, uint64_t leeway, dispat
[self stopMetricsPoll];
[self startMetricsPoll];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(blockUSBMount))]) {
BOOL new = [ change[newKey] boolValue ];
BOOL old = [change[oldKey] boolValue];
if (new != old) {
LOGI(@"BlockUSBMount changed: %d -> %d", old, new);
self.deviceManager.blockUSBMount = new;
}
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(remountUSBMode))]) {
NSArray<NSString *> *new = [ change[newKey] isKindOfClass : [NSArray class] ]
? (NSArray<NSString *> *)change[newKey]
: nil;
NSArray<NSString *> *old =
[change[oldKey] isKindOfClass:[NSArray class]] ? (NSArray<NSString *> *)change[oldKey] : nil;
if (![old isEqualToArray:new]) {
LOGI(@"RemountArgs changed: %s -> %s", [[old componentsJoinedByString:@","] UTF8String],
[[new componentsJoinedByString:@","] UTF8String]);
self.deviceManager.remountArgs = new;
}
}
}

View File

@@ -107,9 +107,10 @@
}];
__block BOOL complete = NO;
[mockES registerResponseCallback:^(ESResponse *r) {
complete = YES;
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_EXEC
withCallback:^(ESResponse *r) {
complete = YES;
}];
[self startMeasuring];
[mockES triggerHandler:msg.message];

View File

@@ -69,10 +69,11 @@
XCTestExpectation *expectation =
[self expectationWithDescription:@"Wait for santa's Auth dispatch queue"];
__block ESResponse *got = nil;
[mockES registerResponseCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_EXEC
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
}];
NSString *binaryPath = [NSString pathWithComponents:@[ testPath, binaryName ]];
struct stat fileStat;

View File

@@ -225,6 +225,15 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)setBlockUSBMount:(BOOL)enabled reply:(void (^)(void))reply {
[[SNTConfigurator configurator] setBlockUSBMount:enabled];
reply();
}
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply {
[[SNTConfigurator configurator] setRemountUSBMode:remountUSBMode];
reply();
}
- (void)enableBundles:(void (^)(BOOL))reply {
reply([SNTConfigurator configurator].enableBundles);
}
@@ -246,11 +255,6 @@ double watchdogRAMPeak = 0;
#pragma mark Metrics Ops
- (void)metrics:(void (^)(NSDictionary *))reply {
// If metrics are not enabled send nil back
if (![[SNTConfigurator configurator] exportMetrics]) {
reply(nil);
}
SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
reply([metricSet export]);
}

View File

@@ -18,6 +18,23 @@
#include "Source/common/SNTKernelCommon.h"
#include "Source/santad/EventProviders/SNTEventProvider.h"
const static NSString *kBlockBinary = @"BlockBinary";
const static NSString *kAllowBinary = @"AllowBinary";
const static NSString *kBlockCertificate = @"BlockCertificate";
const static NSString *kAllowCertificate = @"AllowCertificate";
const static NSString *kBlockTeamID = @"BlockTeamID";
const static NSString *kAllowTeamID = @"AllowTeamID";
const static NSString *kBlockScope = @"BlockScope";
const static NSString *kAllowScope = @"AllowScope";
const static NSString *kAllowUnknown = @"AllowUnknown";
const static NSString *kBlockUnknown = @"BlockUnknown";
const static NSString *kAllowCompiler = @"AllowCompiler";
const static NSString *kAllowTransitive = @"AllowTransitive";
const static NSString *kUnknownEventState = @"Unknown";
const static NSString *kBlockPrinterWorkaround = @"BlockPrinterWorkaround";
const static NSString *kAllowNoFileInfo = @"AllowNoFileInfo";
const static NSString *kAllowNullVNode = @"AllowNullVNode";
@class MOLCodesignChecker;
@class SNTDriverManager;
@class SNTEventLog;

View File

@@ -19,6 +19,7 @@
#include <utmpx.h>
#include "Source/common/SNTLogging.h"
#include "Source/common/SNTMetricSet.h"
#import <MOLCodesignChecker/MOLCodesignChecker.h>
@@ -51,12 +52,21 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
@property SNTPolicyProcessor *policyProcessor;
@property SNTRuleTable *ruleTable;
@property SNTSyncdQueue *syncdQueue;
@property SNTMetricCounter *events;
@property dispatch_queue_t eventQueue;
@end
@implementation SNTExecutionController
static NSString *const kPrinterProxyPreMonterey =
(@"/System/Library/Frameworks/Carbon.framework/Versions/Current/"
@"Frameworks/Print.framework/Versions/Current/Plugins/PrinterProxy.app/"
@"Contents/MacOS/PrinterProxy");
static NSString *const kPrinterProxyPostMonterey =
(@"/System/Library/PrivateFrameworks/PrintingPrivate.framework/"
@"Versions/Current/Plugins/PrinterProxy.app/Contents/MacOS/PrinterProxy");
#pragma mark Initializers
- (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
@@ -78,10 +88,37 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
// This establishes the XPC connection between libsecurity and syspolicyd.
// Not doing this causes a deadlock as establishing this link goes through xpcproxy.
(void)[[MOLCodesignChecker alloc] initWithSelf];
SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
_events = [metricSet counterWithName:@"/santa/events"
fieldNames:@[ @"action_response" ]
helpText:@"Events processed by Santa per response"];
}
return self;
}
- (void)incrementEventCounters:(SNTEventState)eventType {
const NSString *eventTypeStr;
switch (eventType) {
case SNTEventStateBlockBinary: eventTypeStr = kBlockBinary; break;
case SNTEventStateAllowBinary: eventTypeStr = kAllowBinary; break;
case SNTEventStateBlockCertificate: eventTypeStr = kBlockCertificate; break;
case SNTEventStateAllowCertificate: eventTypeStr = kAllowCertificate; break;
case SNTEventStateBlockTeamID: eventTypeStr = kBlockTeamID; break;
case SNTEventStateAllowTeamID: eventTypeStr = kAllowTeamID; break;
case SNTEventStateBlockScope: eventTypeStr = kBlockScope; break;
case SNTEventStateAllowScope: eventTypeStr = kAllowScope; break;
case SNTEventStateBlockUnknown: eventTypeStr = kBlockUnknown; break;
case SNTEventStateAllowUnknown: eventTypeStr = kAllowUnknown; break;
case SNTEventStateAllowCompiler: eventTypeStr = kAllowCompiler; break;
case SNTEventStateAllowTransitive: eventTypeStr = kAllowTransitive; break;
default: eventTypeStr = kUnknownEventState; break;
}
[_events incrementForFieldValues:@[ (NSString *)eventTypeStr ]];
}
#pragma mark Binary Validation
- (void)validateBinaryWithMessage:(santa_message_t)message {
@@ -89,6 +126,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
if (unlikely(message.path == NULL)) {
LOGE(@"Path for vnode_id is NULL: %llu/%llu", message.vnode_id.fsid, message.vnode_id.fileid);
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kAllowNullVNode ]];
return;
}
@@ -97,12 +135,14 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
if (unlikely(!binInfo)) {
LOGE(@"Failed to read file %@: %@", @(message.path), fileInfoError.localizedDescription);
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kAllowNoFileInfo ]];
return;
}
// PrinterProxy workaround, see description above the method for more details.
if ([self printerProxyWorkaround:binInfo]) {
[self.eventProvider postAction:ACTION_RESPOND_DENY forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kBlockPrinterWorkaround ]];
return;
}
@@ -111,6 +151,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
LOGD(@"%@ is larger than %zu. Letting santa-driver know we are working on it.", binInfo.path,
kLargeBinarySize);
[self.eventProvider postAction:ACTION_RESPOND_ACK forMessage:message];
// TODO(markowsky): Maybe add a metric here for how many large executables we're seeing.
}
SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo];
@@ -139,6 +180,9 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
// Send the decision to the kernel.
[self.eventProvider postAction:action forMessage:message];
// Increment counters;
[self incrementEventCounters:cd.decision];
// Log to database if necessary.
if (cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler &&
cd.decision != SNTEventStateAllowTransitive && cd.decision != SNTEventStateAllowCertificate &&
@@ -179,9 +223,12 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
se.quarantineTimestamp = binInfo.quarantineTimestamp;
se.quarantineAgentBundleID = binInfo.quarantineAgentBundleID;
dispatch_async(_eventQueue, ^{
[self.eventTable addStoredEvent:se];
});
// Only store events if there is a sync server configured.
if ([SNTConfigurator configurator].syncBaseURL) {
dispatch_async(_eventQueue, ^{
[self.eventTable addStoredEvent:se];
});
}
// If binary was blocked, do the needful
if (action != ACTION_RESPOND_ALLOW && action != ACTION_RESPOND_ALLOW_COMPILER) {
@@ -243,13 +290,10 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
- (BOOL)printerProxyWorkaround:(SNTFileInfo *)fi {
if ([fi.path hasSuffix:@"/Contents/MacOS/PrinterProxy"] &&
[fi.path containsString:@"Library/Printers"]) {
NSString *proxyPath = (@"/System/Library/Frameworks/Carbon.framework/Versions/Current/"
@"Frameworks/Print.framework/Versions/Current/Plugins/PrinterProxy.app/"
@"Contents/MacOS/PrinterProxy");
SNTFileInfo *proxyFi = [[SNTFileInfo alloc] initWithPath:proxyPath];
SNTFileInfo *proxyFi = [self printerProxyFileInfo];
if ([proxyFi.SHA256 isEqual:fi.SHA256]) return NO;
NSFileHandle *inFh = [NSFileHandle fileHandleForReadingAtPath:proxyPath];
NSFileHandle *inFh = [NSFileHandle fileHandleForReadingAtPath:proxyFi.path];
NSFileHandle *outFh = [NSFileHandle fileHandleForWritingAtPath:fi.path];
[outFh writeData:[inFh readDataToEndOfFile]];
[inFh closeFile];
@@ -264,6 +308,15 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
return NO;
}
/**
Returns an SNTFileInfo for the system PrinterProxy path on this system.
*/
- (SNTFileInfo *)printerProxyFileInfo {
SNTFileInfo *proxyInfo = [[SNTFileInfo alloc] initWithPath:kPrinterProxyPostMonterey];
if (!proxyInfo) proxyInfo = [[SNTFileInfo alloc] initWithPath:kPrinterProxyPreMonterey];
return proxyInfo;
}
- (NSString *)ttyPathForPID:(pid_t)pid {
if (pid < 2) return nil; // don't bother even looking for launchd.

View File

@@ -21,6 +21,7 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTMetricSet.h"
#import "Source/common/SNTRule.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
@@ -52,6 +53,8 @@
self.mockConfigurator = OCMClassMock([SNTConfigurator class]);
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
NSURL *url = [NSURL URLWithString:@"https://localhost/test"];
OCMStub([self.mockConfigurator syncBaseURL]).andReturn(url);
self.mockDriverManager = OCMClassMock([SNTDriverManager class]);
@@ -86,6 +89,25 @@
return (santa_vnode_id_t){.fsid = 1234, .fileid = 5678};
}
- (void)checkMetricCounters:(const NSString *)expectedFieldValueName
expected:(NSNumber *)expectedValue {
SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
NSDictionary *eventCounter = [metricSet export][@"metrics"][@"/santa/events"];
BOOL foundField;
for (NSDictionary *fieldValue in eventCounter[@"fields"][@"action_response"]) {
if ([expectedFieldValueName isEqualToString:fieldValue[@"value"]]) {
XCTAssertEqualObjects(expectedValue, fieldValue[@"data"],
@"%@ counter does not match expected value", expectedFieldValueName);
foundField = YES;
break;
}
}
if (!foundField) {
XCTFail(@"failed to find %@ field value", expectedFieldValueName);
}
}
- (void)testBinaryAllowRule {
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
@@ -99,6 +121,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:@"AllowBinary" expected:@2];
}
- (void)testBinaryBlockRule {
@@ -114,6 +137,9 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
// verify that we're incrementing the binary block
[self checkMetricCounters:@"BlockBinary" expected:@1];
}
- (void)testCertificateAllowRule {
@@ -132,6 +158,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowCertificate expected:@1];
}
- (void)testCertificateBlockRule {
@@ -153,6 +180,7 @@
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:@"BlockCertificate" expected:@1];
}
- (void)testBinaryAllowCompilerRule {
@@ -170,6 +198,7 @@
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW_COMPILER
forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowCompiler expected:@1];
}
- (void)testBinaryAllowCompilerRuleDisabled {
@@ -186,6 +215,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowBinary expected:@1];
}
- (void)testBinaryAllowTransitiveRule {
@@ -202,6 +232,8 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:@"AllowBinary" expected:@2];
}
- (void)testBinaryAllowTransitiveRuleDisabled {
@@ -222,6 +254,8 @@
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kAllowBinary expected:@2];
[self checkMetricCounters:kAllowTransitive expected:@1];
}
- (void)testDefaultDecision {
@@ -238,6 +272,15 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kBlockUnknown expected:@2];
[self checkMetricCounters:kAllowUnknown expected:@1];
}
- (void)testMissingShasum {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowScope expected:@1];
}
- (void)testOutOfScope {
@@ -245,11 +288,7 @@
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testMissingShasum {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowScope expected:@2];
}
- (void)testPageZero {
@@ -259,6 +298,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kBlockUnknown expected:@3];
}
@end

View File

@@ -55,12 +55,12 @@ macos_command_line_application(
"--force",
"--options library,kill,runtime",
],
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
}),
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
version = "//:version",
visibility = ["//:santa_package_group"],
deps = [

View File

@@ -29,8 +29,9 @@
formatter = isoFormatter;
} else {
NSDateFormatter *localFormatter = [[NSDateFormatter alloc] init];
[localFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[localFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
localFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
localFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierISO8601];
localFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
formatter = localFormatter;
}

View File

@@ -43,8 +43,8 @@
SNTMetricMonarchJSONFormat *formatter = [[SNTMetricMonarchJSONFormat alloc] init];
NSDictionary *validMetricsDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
NSArray<NSData *> *output = [formatter convert:validMetricsDict error:nil];
output = [formatter convert:validMetricsDict error:NULL];
[formatter convert:validMetricsDict error:nil];
[formatter convert:validMetricsDict error:NULL];
}
@end

View File

@@ -24,7 +24,8 @@
if (self) {
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
_dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
_dateFormatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierISO8601];
_dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
}
return self;
}

View File

@@ -43,8 +43,8 @@
SNTMetricRawJSONFormat *formatter = [[SNTMetricRawJSONFormat alloc] init];
NSDictionary *validMetricsDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
NSArray<NSData *> *output = [formatter convert:validMetricsDict error:nil];
output = [formatter convert:validMetricsDict error:NULL];
[formatter convert:validMetricsDict error:nil];
[formatter convert:validMetricsDict error:NULL];
}
@end

View File

@@ -50,9 +50,9 @@ NSDictionary *validMetricsDict = nil;
- (NSDate *)createNSDateFromDateString:(NSString *)dateString {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
formatter.calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierISO8601];
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
return [formatter dateFromString:dateString];
}

View File

@@ -1,7 +1,100 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
load("//:helper.bzl", "santa_unit_test")
licenses(["notice"])
package(default_visibility = ["//:santa_package_group"])
objc_library(
name = "FCM_lib",
srcs = ["SNTSyncFCM.m"],
hdrs = ["SNTSyncFCM.h"],
sdk_frameworks = ["SystemConfiguration"],
deps = [
"@MOLAuthenticatingURLSession",
],
)
objc_library(
name = "sync_lib",
srcs = [
"NSData+Zlib.h",
"NSData+Zlib.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncManager.m",
"SNTSyncPostflight.h",
"SNTSyncPostflight.m",
"SNTSyncPreflight.h",
"SNTSyncPreflight.m",
"SNTSyncRuleDownload.h",
"SNTSyncRuleDownload.m",
"SNTSyncStage.h",
"SNTSyncStage.m",
"SNTSyncState.h",
"SNTSyncState.m",
],
hdrs = ["SNTSyncManager.h"],
sdk_dylibs = ["libz"],
deps = [
":FCM_lib",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncdInterface",
"@MOLAuthenticatingURLSession",
"@MOLXPCConnection",
],
)
# Using :sync_lib breaks the Zlib category hack used in the tests to
# disable compression, in turn failing the tests. Re-compile here to keep
# the "override" category behavior.
santa_unit_test(
name = "SNTSyncTest",
srcs = [
"NSData+Zlib.h",
"NSData+Zlib.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncPostflight.h",
"SNTSyncPostflight.m",
"SNTSyncPreflight.h",
"SNTSyncPreflight.m",
"SNTSyncRuleDownload.h",
"SNTSyncRuleDownload.m",
"SNTSyncStage.h",
"SNTSyncStage.m",
"SNTSyncState.h",
"SNTSyncState.m",
"SNTSyncTest.m",
],
resources = glob([
"testdata/*.json",
"testdata/*.plist",
]),
sdk_dylibs = ["libz"],
deps = [
":FCM_lib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncdInterface",
"@MOLAuthenticatingURLSession",
"@MOLXPCConnection",
"@OCMock",
],
)
objc_library(
name = "santass_lib",
srcs = [
@@ -26,3 +119,11 @@ macos_command_line_application(
visibility = ["//:santa_package_group"],
deps = [":santass_lib"],
)
test_suite(
name = "unit_tests",
tests = [
":SNTSyncTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -28,6 +28,8 @@ extern NSString *const kUploadLogsURL;
extern NSString *const kClientMode;
extern NSString *const kClientModeMonitor;
extern NSString *const kClientModeLockdown;
extern NSString *const kBlockUSBMount;
extern NSString *const kRemountUSBMode;
extern NSString *const kCleanSync;
extern NSString *const kAllowedPathRegex;
extern NSString *const kAllowedPathRegexDeprecated;
@@ -94,6 +96,7 @@ extern NSString *const kEventUploadBundleBinaries;
extern NSString *const kRules;
extern NSString *const kRuleSHA256;
extern NSString *const kRuleIdentifier;
extern NSString *const kRulePolicy;
extern NSString *const kRulePolicyAllowlist;
extern NSString *const kRulePolicyAllowlistDeprecated;
@@ -107,6 +110,7 @@ extern NSString *const kRulePolicyRemove;
extern NSString *const kRuleType;
extern NSString *const kRuleTypeBinary;
extern NSString *const kRuleTypeCertificate;
extern NSString *const kRuleTypeTeamID;
extern NSString *const kRuleCustomMsg;
extern NSString *const kCursor;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCommandSyncConstants.h"
#import "SNTSyncConstants.h"
NSString *const kXSRFToken = @"X-XSRF-TOKEN";
@@ -26,6 +26,8 @@ NSString *const kRequestCleanSync = @"request_clean_sync";
NSString *const kBatchSize = @"batch_size";
NSString *const kUploadLogsURL = @"upload_logs_url";
NSString *const kClientMode = @"client_mode";
NSString *const kBlockUSBMount = @"block_usb_mount";
NSString *const kRemountUSBMode = @"remount_usb_mode";
NSString *const kClientModeMonitor = @"MONITOR";
NSString *const kClientModeLockdown = @"LOCKDOWN";
NSString *const kCleanSync = @"clean_sync";
@@ -95,6 +97,7 @@ NSString *const kEventUploadBundleBinaries = @"event_upload_bundle_binaries";
NSString *const kRules = @"rules";
NSString *const kRuleSHA256 = @"sha256";
NSString *const kRuleIdentifier = @"identifier";
NSString *const kRulePolicy = @"policy";
NSString *const kRulePolicyAllowlist = @"ALLOWLIST";
NSString *const kRulePolicyAllowlistDeprecated = @"WHITELIST";
@@ -108,6 +111,7 @@ NSString *const kRulePolicyRemove = @"REMOVE";
NSString *const kRuleType = @"rule_type";
NSString *const kRuleTypeBinary = @"BINARY";
NSString *const kRuleTypeCertificate = @"CERTIFICATE";
NSString *const kRuleTypeTeamID = @"TEAMID";
NSString *const kRuleCustomMsg = @"custom_msg";
NSString *const kCursor = @"cursor";

View File

@@ -14,9 +14,9 @@
#import <Foundation/Foundation.h>
#import "SNTCommandSyncStage.h"
#import "SNTSyncStage.h"
@interface SNTCommandSyncEventUpload : SNTCommandSyncStage
@interface SNTSyncEventUpload : SNTSyncStage
- (BOOL)uploadEvents:(NSArray *)events;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncEventUpload.h"
#import "Source/santasyncservice/SNTSyncEventUpload.h"
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -21,11 +21,11 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/NSData+Zlib.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/NSData+Zlib.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTCommandSyncEventUpload
@implementation SNTSyncEventUpload
- (NSURL *)stageURL {
NSString *stageName = [@"eventupload" stringByAppendingFormat:@"/%@", self.syncState.machineID];

View File

@@ -15,30 +15,30 @@
#import <Foundation/Foundation.h>
/** A block that takes a NSString object as an argument. */
typedef void (^SNTCommandSyncFCMTokenHandler)(NSString *);
typedef void (^SNTSyncFCMTokenHandler)(NSString *);
/** A block that takes a NSDictionary object as an argument. */
typedef void (^SNTCommandSyncFCMMessageHandler)(NSDictionary *);
typedef void (^SNTSyncFCMMessageHandler)(NSDictionary *);
/** A block that takes a NSHTTPURLResponse and NSError object as an argument. */
typedef void (^SNTCommandSyncFCMConnectionErrorHandler)(NSHTTPURLResponse *, NSError *);
typedef void (^SNTSyncFCMConnectionErrorHandler)(NSHTTPURLResponse *, NSError *);
/** A block that takes a NSDictionary and NSError object as arguments. */
typedef void (^SNTCommandSyncFCMAcknowledgeErrorHandler)(NSDictionary *, NSError *);
typedef void (^SNTSyncFCMAcknowledgeErrorHandler)(NSDictionary *, NSError *);
@interface SNTCommandSyncFCM : NSObject
@interface SNTSyncFCM : NSObject
/** Returns YES if connected to FCM. */
@property(readonly, nonatomic) BOOL isConnected;
/** A block to be executed when the FCM token changes */
@property(copy) SNTCommandSyncFCMTokenHandler tokenHandler;
@property(copy) SNTSyncFCMTokenHandler tokenHandler;
/** A block to be executed when there is an issue with acknowledging a message. */
@property(copy) SNTCommandSyncFCMAcknowledgeErrorHandler acknowledgeErrorHandler;
@property(copy) SNTSyncFCMAcknowledgeErrorHandler acknowledgeErrorHandler;
/** A block to be executed when there is a non-recoverable issue with the FCM Connection. */
@property(copy) SNTCommandSyncFCMConnectionErrorHandler connectionErrorHandler;
@property(copy) SNTSyncFCMConnectionErrorHandler connectionErrorHandler;
- (instancetype)init NS_UNAVAILABLE;
@@ -60,7 +60,7 @@ typedef void (^SNTCommandSyncFCMAcknowledgeErrorHandler)(NSDictionary *, NSError
* @note If the fatalCodes argument is nil, @[@302, @400, @403] will be used.
* @note If the sessionConfiguration argument is nil, defaultSessionConfiguration will be used.
*
* @return An initialized SNTCommandSyncFCM object
* @return An initialized SNTSyncFCM object
*/
- (instancetype)initWithProject:(NSString *)project
entity:(NSString *)entity
@@ -69,21 +69,20 @@ typedef void (^SNTCommandSyncFCMAcknowledgeErrorHandler)(NSDictionary *, NSError
backoffMax:(uint32_t)backoffMax
fatalCodes:(NSArray<NSNumber *> *)fatalCodes
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler
NS_DESIGNATED_INITIALIZER;
messageHandler:(SNTSyncFCMMessageHandler)messageHandler NS_DESIGNATED_INITIALIZER;
/** A convenience initializer. Optional args will use their zero values. */
- (instancetype)initWithProject:(NSString *)project
entity:(NSString *)entity
apiKey:(NSString *)apiKey
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler;
messageHandler:(SNTSyncFCMMessageHandler)messageHandler;
/** A convenience initializer. Optional args will use their zero values. */
- (instancetype)initWithProject:(NSString *)project
entity:(NSString *)entity
apiKey:(NSString *)apiKey
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler;
messageHandler:(SNTSyncFCMMessageHandler)messageHandler;
/**
* Opens a connection to FCM and starts listening for messages.

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncFCM.h"
#import "Source/santasyncservice/SNTSyncFCM.h"
#import <SystemConfiguration/SystemConfiguration.h>
@@ -53,7 +53,7 @@ static const uint32_t kDefaultConnectDelayMaxSeconds = 10;
#pragma mark MOLFCMClient Extension
@interface SNTCommandSyncFCM () {
@interface SNTSyncFCM () {
/** URL components for client registration, receiving and acknowledging messages. */
NSURLComponents *_checkinComponents;
NSURLComponents *_registerComponents;
@@ -77,7 +77,7 @@ static const uint32_t kDefaultConnectDelayMaxSeconds = 10;
@property(nonatomic) MOLAuthenticatingURLSession *authSession;
/** The block to be called for every message. */
@property(copy, nonatomic) SNTCommandSyncFCMMessageHandler messageHandler;
@property(copy, nonatomic) SNTSyncFCMMessageHandler messageHandler;
/** Is used throughout the class to reconnect to FCM after a connection loss. */
@property SCNetworkReachabilityRef reachability;
@@ -104,7 +104,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
void *info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (flags & kSCNetworkReachabilityFlagsReachable) {
SNTCommandSyncFCM *FCMClient = (__bridge SNTCommandSyncFCM *)info;
SNTSyncFCM *FCMClient = (__bridge SNTSyncFCM *)info;
SEL s = @selector(reachabilityRestored);
[NSObject cancelPreviousPerformRequestsWithTarget:FCMClient selector:s object:nil];
[FCMClient performSelector:s withObject:nil afterDelay:1];
@@ -112,7 +112,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
});
}
@implementation SNTCommandSyncFCM
@implementation SNTSyncFCM
#pragma mark init/dealloc methods
@@ -123,7 +123,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
backoffMax:(uint32_t)backoffMax
fatalCodes:(NSArray<NSNumber *> *)fatalCodes
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
messageHandler:(SNTSyncFCMMessageHandler)messageHandler {
self = [super init];
if (self) {
_project = project;
@@ -159,7 +159,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
entity:(NSString *)entity
apiKey:(NSString *)apiKey
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
messageHandler:(SNTSyncFCMMessageHandler)messageHandler {
return [self initWithProject:project
entity:entity
apiKey:apiKey
@@ -173,7 +173,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
- (instancetype)initWithProject:(NSString *)project
entity:(NSString *)entity
apiKey:(NSString *)apiKey
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
messageHandler:(SNTSyncFCMMessageHandler)messageHandler {
return [self initWithProject:project
entity:entity
apiKey:apiKey

View File

@@ -21,7 +21,7 @@
///
/// Handles push notifications and periodic syncing with a sync server.
///
@interface SNTCommandSyncManager : NSObject <SNTSyncdXPC>
@interface SNTSyncManager : NSObject <SNTSyncdXPC>
@property(readonly, nonatomic) BOOL daemon;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncManager.h"
#import "Source/santasyncservice/SNTSyncManager.h"
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -25,20 +25,20 @@
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/common/SNTXPCSyncdInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncEventUpload.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncFCM.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncPostflight.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncPreflight.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncEventUpload.h"
#import "Source/santasyncservice/SNTSyncFCM.h"
#import "Source/santasyncservice/SNTSyncPostflight.h"
#import "Source/santasyncservice/SNTSyncPreflight.h"
#import "Source/santasyncservice/SNTSyncRuleDownload.h"
#import "Source/santasyncservice/SNTSyncState.h"
static NSString *const kFCMActionKey = @"action";
static NSString *const kFCMFileHashKey = @"file_hash";
static NSString *const kFCMFileNameKey = @"file_name";
static NSString *const kFCMTargetHostIDKey = @"target_host_id";
@interface SNTCommandSyncManager () {
@interface SNTSyncManager () {
SCNetworkReachabilityRef _reachability;
}
@@ -61,7 +61,7 @@ static NSString *const kFCMTargetHostIDKey = @"target_host_id";
@property NSUInteger FCMGlobalRuleSyncDeadline;
@property NSUInteger eventBatchSize;
@property SNTCommandSyncFCM *FCMClient;
@property SNTSyncFCM *FCMClient;
@property NSString *FCMToken;
@property(nonatomic) MOLXPCConnection *daemonConn;
@@ -77,7 +77,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
void *info) {
// Put this check and set on the main thread to ensure serial access.
dispatch_async(dispatch_get_main_queue(), ^{
SNTCommandSyncManager *commandSyncManager = (__bridge SNTCommandSyncManager *)info;
SNTSyncManager *commandSyncManager = (__bridge SNTSyncManager *)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)) {
@@ -86,7 +86,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
});
}
@implementation SNTCommandSyncManager
@implementation SNTSyncManager
#pragma mark init
@@ -107,11 +107,11 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
0);
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[self lockAction:kRuleSync];
SNTCommandSyncState *syncState = [self createSyncState];
SNTSyncState *syncState = [self createSyncState];
syncState.targetedRuleSync = self.targetedRuleSync;
syncState.allowlistNotifications = self.allowlistNotifications;
syncState.allowlistNotificationQueue = self.allowlistNotificationQueue;
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
SNTSyncRuleDownload *p = [[SNTSyncRuleDownload alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Rule download complete");
} else {
@@ -141,9 +141,9 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
#pragma mark SNTSyncdXPC protocol methods
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events isFromBundle:(BOOL)isFromBundle {
SNTCommandSyncState *syncState = [self createSyncState];
SNTSyncState *syncState = [self createSyncState];
if (isFromBundle) syncState.eventBatchSize = self.eventBatchSize;
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
SNTSyncEventUpload *p = [[SNTSyncEventUpload alloc] initWithState:syncState];
if (events && [p uploadEvents:events]) {
LOGD(@"Events upload complete");
} else {
@@ -159,8 +159,8 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
reply(SNTBundleEventActionDropEvents);
return;
}
SNTCommandSyncState *syncState = [self createSyncState];
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
SNTSyncState *syncState = [self createSyncState];
SNTSyncEventUpload *p = [[SNTSyncEventUpload alloc] initWithState:syncState];
if ([p uploadEvents:@[ event ]]) {
if ([syncState.bundleBinaryRequests containsObject:event.fileBundleHash]) {
reply(SNTBundleEventActionSendEvents);
@@ -185,7 +185,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
#pragma mark push notification methods
- (void)listenForPushNotificationsWithSyncState:(SNTCommandSyncState *)syncState {
- (void)listenForPushNotificationsWithSyncState:(SNTSyncState *)syncState {
if ([self.FCMToken isEqualToString:syncState.FCMToken]) {
LOGD(@"Already listening for push notifications");
return;
@@ -197,18 +197,17 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
[self.FCMClient disconnect];
NSString *machineID = syncState.machineID;
SNTConfigurator *config = [SNTConfigurator configurator];
self.FCMClient = [[SNTCommandSyncFCM alloc] initWithProject:config.fcmProject
entity:config.fcmEntity
apiKey:config.fcmAPIKey
sessionConfiguration:syncState.session.configuration.copy
messageHandler:^(NSDictionary *message) {
if (!message || message[@"noOp"]) return;
STRONGIFY(self);
LOGD(@"%@", message);
[self.FCMClient acknowledgeMessage:message];
[self processFCMMessage:message
withMachineID:machineID];
}];
self.FCMClient = [[SNTSyncFCM alloc] initWithProject:config.fcmProject
entity:config.fcmEntity
apiKey:config.fcmAPIKey
sessionConfiguration:syncState.session.configuration.copy
messageHandler:^(NSDictionary *message) {
if (!message || message[@"noOp"]) return;
STRONGIFY(self);
LOGD(@"%@", message);
[self.FCMClient acknowledgeMessage:message];
[self processFCMMessage:message withMachineID:machineID];
}];
self.FCMClient.tokenHandler = ^(NSString *t) {
STRONGIFY(self);
@@ -354,8 +353,8 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
- (void)preflightOnly:(BOOL)preflightOnly {
LOGD(@"Preflight starting");
SNTCommandSyncState *syncState = [self createSyncState];
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:syncState];
SNTSyncState *syncState = [self createSyncState];
SNTSyncPreflight *p = [[SNTSyncPreflight alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Preflight complete");
@@ -390,9 +389,9 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
}
}
- (void)eventUploadWithSyncState:(SNTCommandSyncState *)syncState {
- (void)eventUploadWithSyncState:(SNTSyncState *)syncState {
LOGD(@"Event upload starting");
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
SNTSyncEventUpload *p = [[SNTSyncEventUpload alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Event upload complete");
return [self ruleDownloadWithSyncState:syncState];
@@ -402,9 +401,9 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
}
}
- (void)ruleDownloadWithSyncState:(SNTCommandSyncState *)syncState {
- (void)ruleDownloadWithSyncState:(SNTSyncState *)syncState {
LOGD(@"Rule download starting");
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
SNTSyncRuleDownload *p = [[SNTSyncRuleDownload alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Rule download complete");
return [self postflightWithSyncState:syncState];
@@ -414,9 +413,9 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
}
}
- (void)postflightWithSyncState:(SNTCommandSyncState *)syncState {
- (void)postflightWithSyncState:(SNTSyncState *)syncState {
LOGD(@"Postflight starting");
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:syncState];
SNTSyncPostflight *p = [[SNTSyncPostflight alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Postflight complete");
LOGI(@"Sync completed successfully");
@@ -438,9 +437,9 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
return timerQueue;
}
- (SNTCommandSyncState *)createSyncState {
- (SNTSyncState *)createSyncState {
// Gather some data needed during some sync stages
SNTCommandSyncState *syncState = [[SNTCommandSyncState alloc] init];
SNTSyncState *syncState = [[SNTSyncState alloc] init];
SNTConfigurator *config = [SNTConfigurator configurator];
syncState.syncBaseURL = config.syncBaseURL;
@@ -470,7 +469,11 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
dispatch_group_leave(group);
}];
MOLAuthenticatingURLSession *authURLSession = [[MOLAuthenticatingURLSession alloc] init];
NSURLSessionConfiguration *sessConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessConfig.connectionProxyDictionary = [[SNTConfigurator configurator] syncProxyConfig];
MOLAuthenticatingURLSession *authURLSession =
[[MOLAuthenticatingURLSession alloc] initWithSessionConfiguration:sessConfig];
authURLSession.userAgent = @"santactl-sync/";
NSString *santactlVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
if (santactlVersion) {

View File

@@ -14,7 +14,7 @@
#import <Foundation/Foundation.h>
#import "SNTCommandSyncStage.h"
#import "SNTSyncStage.h"
@interface SNTCommandSyncPreflight : SNTCommandSyncStage
@interface SNTSyncPostflight : SNTSyncStage
@end

View File

@@ -12,16 +12,16 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncPostflight.h"
#import "Source/santasyncservice/SNTSyncPostflight.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTCommandSyncPostflight
@implementation SNTSyncPostflight
- (NSURL *)stageURL {
NSString *stageName = [@"postflight" stringByAppendingFormat:@"/%@", self.syncState.machineID];
@@ -60,6 +60,17 @@
reply:replyBlock];
}
if (self.syncState.blockUSBMount) {
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] setBlockUSBMount:self.syncState.blockUSBMount
reply:replyBlock];
}
if (self.syncState.remountUSBMode) {
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] setRemountUSBMode:self.syncState.remountUSBMode
reply:replyBlock];
}
// Update last sync success
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] setFullSyncLastSuccess:[NSDate date] reply:replyBlock];

View File

@@ -14,7 +14,7 @@
#import <Foundation/Foundation.h>
#import "SNTCommandSyncStage.h"
#import "SNTSyncStage.h"
@interface SNTCommandSyncPostflight : SNTCommandSyncStage
@interface SNTSyncPreflight : SNTSyncStage
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncPreflight.h"
#import "Source/santasyncservice/SNTSyncPreflight.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -21,10 +21,10 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTSystemInfo.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTCommandSyncPreflight
@implementation SNTSyncPreflight
- (NSURL *)stageURL {
NSString *stageName = [@"preflight" stringByAppendingFormat:@"/%@", self.syncState.machineID];
@@ -134,6 +134,14 @@
self.syncState.blocklistRegex = resp[kBlockedPathRegexDeprecated];
}
if ([resp[kBlockUSBMount] boolValue]) {
self.syncState.blockUSBMount = YES;
}
if ([resp[kRemountUSBMode] isKindOfClass:[NSArray class]]) {
self.syncState.remountUSBMode = resp[kRemountUSBMode];
}
if ([resp[kCleanSync] boolValue]) {
LOGD(@"Clean sync requested by server");
self.syncState.cleanSync = YES;

View File

@@ -14,10 +14,10 @@
#import <Foundation/Foundation.h>
#import "Source/santactl/Commands/sync/SNTCommandSyncStage.h"
#import "Source/santasyncservice/SNTSyncStage.h"
@class SNTRule;
@interface SNTCommandSyncRuleDownload : SNTCommandSyncStage
@interface SNTSyncRuleDownload : SNTSyncStage
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict;
@end

View File

@@ -12,17 +12,17 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.h"
#import "Source/santasyncservice/SNTSyncRuleDownload.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTRule.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTCommandSyncRuleDownload
@implementation SNTSyncRuleDownload
- (NSURL *)stageURL {
NSString *stageName = [@"ruledownload" stringByAppendingFormat:@"/%@", self.syncState.machineID];
@@ -45,7 +45,10 @@
error = e;
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 300 * NSEC_PER_SEC));
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 300 * NSEC_PER_SEC))) {
LOGE(@"Failed to add rule(s) to database: timeout sending rules to daemon");
return NO;
}
if (error) {
LOGE(@"Failed to add rule(s) to database: %@", error.localizedDescription);
@@ -136,7 +139,10 @@
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.identifier = dict[kRuleSHA256];
newRule.identifier = dict[kRuleIdentifier];
if (newRule.identifier == nil) {
newRule.identifier = dict[kRuleSHA256];
}
NSString *policyString = dict[kRulePolicy];
if ([policyString isEqual:kRulePolicyAllowlist] ||
@@ -162,6 +168,8 @@
newRule.type = SNTRuleTypeBinary;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
newRule.type = SNTRuleTypeCertificate;
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
newRule.type = SNTRuleTypeTeamID;
} else {
return nil;
}

View File

@@ -14,13 +14,13 @@
#import <Foundation/Foundation.h>
@class SNTCommandSyncState;
@class SNTSyncState;
@class MOLXPCConnection;
@interface SNTCommandSyncStage : NSObject
@interface SNTSyncStage : NSObject
@property(readonly, nonnull) NSURLSession *urlSession;
@property(readonly, nonnull) SNTCommandSyncState *syncState;
@property(readonly, nonnull) SNTSyncState *syncState;
@property(readonly, nonnull) MOLXPCConnection *daemonConn;
/**
@@ -28,8 +28,7 @@
@param state A holder for state used across requests
*/
- (nullable instancetype)initWithState:(nonnull SNTCommandSyncState *)state
NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithState:(nonnull SNTSyncState *)state NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)init NS_UNAVAILABLE;

View File

@@ -12,28 +12,28 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santactl/Commands/sync/SNTCommandSyncStage.h"
#import "Source/santasyncservice/SNTSyncStage.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/NSData+Zlib.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/NSData+Zlib.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@interface SNTCommandSyncStage ()
@interface SNTSyncStage ()
@property(readwrite) NSURLSession *urlSession;
@property(readwrite) SNTCommandSyncState *syncState;
@property(readwrite) SNTSyncState *syncState;
@property(readwrite) MOLXPCConnection *daemonConn;
@property BOOL xsrfFetched;
@end
@implementation SNTCommandSyncStage
@implementation SNTSyncStage
- (nullable instancetype)initWithState:(nonnull SNTCommandSyncState *)syncState {
- (nullable instancetype)initWithState:(nonnull SNTSyncState *)syncState {
self = [super init];
if (self) {
_syncState = syncState;

View File

@@ -16,12 +16,12 @@
#import "Source/common/SNTCommonEnums.h"
@class SNTCommandSyncManager;
@class SNTSyncManager;
@class MOLXPCConnection;
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@interface SNTCommandSyncState : NSObject
@interface SNTSyncState : NSObject
/// Configured session to use for requests.
@property NSURLSession *session;
@@ -56,6 +56,9 @@
@property SNTClientMode clientMode;
@property NSString *allowlistRegex;
@property NSString *blocklistRegex;
@property BOOL blockUSBMount;
// Array of mount args for the forced remounting feature.
@property NSArray *remountUSBMode;
/// Clean sync flag, if True, all existing rules should be deleted before inserting any new rules.
@property BOOL cleanSync;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTCommandSyncState
@implementation SNTSyncState
@end

View File

@@ -21,13 +21,13 @@
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncEventUpload.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncPostflight.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncPreflight.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncStage.h"
#import "Source/santactl/Commands/sync/SNTCommandSyncState.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncEventUpload.h"
#import "Source/santasyncservice/SNTSyncPostflight.h"
#import "Source/santasyncservice/SNTSyncPreflight.h"
#import "Source/santasyncservice/SNTSyncRuleDownload.h"
#import "Source/santasyncservice/SNTSyncStage.h"
#import "Source/santasyncservice/SNTSyncState.h"
// Prevent Zlib compression during testing
@implementation NSData (Zlib)
@@ -39,17 +39,17 @@
}
@end
@interface SNTCommandSyncTest : XCTestCase
@property SNTCommandSyncState *syncState;
@interface SNTSyncTest : XCTestCase
@property SNTSyncState *syncState;
@property id<SNTDaemonControlXPC> daemonConnRop;
@end
@implementation SNTCommandSyncTest
@implementation SNTSyncTest
- (void)setUp {
[super setUp];
self.syncState = [[SNTCommandSyncState alloc] init];
self.syncState = [[SNTSyncState alloc] init];
self.syncState.daemonConn = OCMClassMock([MOLXPCConnection class]);
self.daemonConnRop = OCMProtocolMock(@protocol(SNTDaemonControlXPC));
OCMStub([self.syncState.daemonConn remoteObjectProxy]).andReturn(self.daemonConnRop);
@@ -140,7 +140,7 @@
return [NSData dataWithContentsOfFile:path];
}
#pragma mark - SNTCommandSyncStage Tests
#pragma mark - SNTSyncStage Tests
- (void)testBaseFetchXSRFTokenSuccess {
// NOTE: This test only works if the other tests don't return a 403 and run before this test.
@@ -177,16 +177,16 @@
NSString *stageName = [@"a" stringByAppendingFormat:@"/%@", self.syncState.machineID];
NSURL *u1 = [NSURL URLWithString:stageName relativeToURL:self.syncState.syncBaseURL];
SNTCommandSyncStage *sut = [[SNTCommandSyncStage alloc] initWithState:self.syncState];
SNTSyncStage *sut = [[SNTSyncStage alloc] initWithState:self.syncState];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:u1];
XCTAssertTrue([sut performRequest:req]);
XCTAssertEqualObjects(self.syncState.xsrfToken, @"my-xsrf-token");
}
#pragma mark - SNTCommandSyncPreflight Tests
#pragma mark - SNTSyncPreflight Tests
- (void)testPreflightBasicResponse {
SNTCommandSyncPreflight *sut = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_basic.json"];
[self stubRequestBody:respData response:nil error:nil validateBlock:nil];
@@ -198,8 +198,20 @@
XCTAssertNil(self.syncState.blocklistRegex);
}
- (void)testPreflightBlockUSBMount {
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_toggle_blockusb.json"];
[self stubRequestBody:respData response:nil error:nil validateBlock:nil];
XCTAssertTrue([sut sync]);
XCTAssertEqual(self.syncState.blockUSBMount, true);
NSArray<NSString *> *wantRemountUSBMode = @[ @"rdonly", @"noexec" ];
XCTAssertEqualObjects(self.syncState.remountUSBMode, wantRemountUSBMode);
}
- (void)testPreflightDatabaseCounts {
SNTCommandSyncPreflight *sut = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
int64_t bin = 5, cert = 8, compiler = 2, transitive = 19, teamID = 3;
OCMStub([self.daemonConnRop
@@ -225,7 +237,7 @@
}
- (void)testPreflightCleanSync {
SNTCommandSyncPreflight *sut = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
id processInfoMock = OCMClassMock([NSProcessInfo class]);
OCMStub([processInfoMock processInfo]).andReturn(processInfoMock);
@@ -247,7 +259,7 @@
}
- (void)testPreflightLockdown {
SNTCommandSyncPreflight *sut = [[SNTCommandSyncPreflight alloc] initWithState:self.syncState];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_lockdown.json"];
[self stubRequestBody:respData response:nil error:nil validateBlock:nil];
@@ -257,10 +269,10 @@
XCTAssertEqual(self.syncState.clientMode, SNTClientModeLockdown);
}
#pragma mark - SNTCommandSyncEventUpload Tests
#pragma mark - SNTSyncEventUpload Tests
- (void)testEventUploadBasic {
SNTCommandSyncEventUpload *sut = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
SNTSyncEventUpload *sut = [[SNTSyncEventUpload alloc] initWithState:self.syncState];
self.syncState.eventBatchSize = 50;
NSData *eventData = [self dataFromFixture:@"sync_eventupload_input_basic.plist"];
@@ -318,7 +330,7 @@
}
- (void)testEventUploadBundleAndQuarantineData {
SNTCommandSyncEventUpload *sut = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
SNTSyncEventUpload *sut = [[SNTSyncEventUpload alloc] initWithState:self.syncState];
sut = OCMPartialMock(sut);
NSData *eventData = [self dataFromFixture:@"sync_eventupload_input_quarantine.plist"];
@@ -353,7 +365,7 @@
}
- (void)testEventUploadBatching {
SNTCommandSyncEventUpload *sut = [[SNTCommandSyncEventUpload alloc] initWithState:self.syncState];
SNTSyncEventUpload *sut = [[SNTSyncEventUpload alloc] initWithState:self.syncState];
self.syncState.eventBatchSize = 1;
sut = OCMPartialMock(sut);
@@ -376,11 +388,10 @@
XCTAssertEqual(requestCount, 2);
}
#pragma mark - SNTCommandSyncRuleDownload Tests
#pragma mark - SNTSyncRuleDownload Tests
- (void)testRuleDownload {
SNTCommandSyncRuleDownload *sut =
[[SNTCommandSyncRuleDownload alloc] initWithState:self.syncState];
SNTSyncRuleDownload *sut = [[SNTSyncRuleDownload alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_ruledownload_batch1.json"];
[self stubRequestBody:respData
@@ -423,6 +434,10 @@
state:SNTRuleStateBlock
type:SNTRuleTypeCertificate
customMsg:@"Hi There"],
[[SNTRule alloc] initWithIdentifier:@"AAAAAAAAAA"
state:SNTRuleStateBlock
type:SNTRuleTypeTeamID
customMsg:@"Banned team ID"],
];
OCMVerify([self.daemonConnRop databaseRuleAddRules:rules cleanSlate:NO reply:OCMOCK_ANY]);

View File

@@ -0,0 +1 @@
{"allowed_path_regex": null, "client_mode": "LOCKDOWN", "blocked_path_regex": null, "batch_size": 100, "block_usb_mount":true, "remount_usb_mode": ["rdonly", "noexec"]}

View File

@@ -0,0 +1 @@
{"rules": [{"rule_type": "CERTIFICATE", "policy": "BLACKLIST", "sha256": "7846698e47ef41be80b83fb9e2b98fa6dc46c9188b068bff323c302955a00142", "custom_msg": "Hi There"},{"rule_type":"TEAMID", "policy":"BLOCKLIST", "identifier": "AAAAAAAAAA", "custom_msg": "Banned team ID"}]}

View File

@@ -0,0 +1,15 @@
GIT_ROOT=$(git rev-parse --show-toplevel)
ANALYZE_PATH="$GIT_ROOT/Testing/clang_analyzer"
TITLE="Santa Clang Analysis"
EXECUTION_ROOT=`bazel info execution_root`
function main() {
bazel clean
bazel run @hedron_compile_commands//:refresh_all
analyze-build --cdb $GIT_ROOT/compile_commands.json -o $ANALYZE_PATH/analysis --html-title "$TITLE" --use-analyzer=$(which clang)
}
main $@
exit $?

View File

@@ -16,6 +16,13 @@
<dict>
<key>BannedBlockMessage</key>
<string>This application has been blocked from executing because it has been banned.</string>
<key>BlockUSBMount</key>
<true/>
<key>RemountUSBMode</key>
<array>
<string>noexec</string>
<string>rdonly</string>
</array>
<key>EnableForkAndExitLogging</key>
<true/>
<key>EnablePageZeroProtection</key>

View File

@@ -20,6 +20,20 @@ load("@build_bazel_apple_support//lib:repositories.bzl", "apple_support_dependen
apple_support_dependencies()
# Hedron Bazel Compile Commands Extractor
# Allows integrating with clangd
# https://github.com/hedronvision/bazel-compile-commands-extractor
git_repository(
name = "hedron_compile_commands",
commit = "92db741ee6dee0c4a83a5c58be7747df7b89ed10",
remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
shallow_since = "1638167585 -0800",
)
load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup")
hedron_compile_commands_setup()
# Macops MOL* dependencies
git_repository(

View File

@@ -37,6 +37,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| ModeNotificationMonitor | String | The notification text to display when the client goes into Monitor mode. Defaults to "Switching into Monitor mode". |
| ModeNotificationLockdown | String | The notification text to display when the client goes into Lockdown mode. Defaults to "Switching into Lockdown mode". |
| SyncBaseURL | String | The base URL of the sync server. |
| SyncProxyConfiguration | Dictionary | The proxy configuration to use when syncing. See the [Apple Documentation](https://developer.apple.com/documentation/cfnetwork/global_proxy_settings_constants) for details on the keys that can be used in this dictionary. |
| ClientAuthCertificateFile | String | If set, this contains the location of a PKCS#12 certificate to be used for sync authentication. |
| ClientAuthCertificatePassword | String | Contains the password for the PKCS#12 certificate. |
| ClientAuthCertificateCN | String | If set, this is the Common Name of a certificate in the System keychain to be used for sync authentication. The corresponding private key must also be in the keychain. |
@@ -52,10 +53,10 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| EventLogType | String | Defines how event logs are stored. Options are 1) syslog: Sent to ASL or ULS (if built with the 10.12 SDK or later). 2) filelog: Sent to a file on disk. Use EventLogPath to specify a path. Defaults to filelog |
| EventLogPath | String | If EventLogType is set to filelog, EventLogPath will provide the path to save logs. Defaults to /var/db/santa/santa.log. If you change this value ensure you also update com.google.santa.newsyslog.conf with the new path. |
| EnableMachineIDDecoration | Bool | If YES, this appends the MachineID to the end of each log line. Defaults to NO. |
| MetricFormat | String | Format to export metrics as, supported formats are "rawjson" for a single JSON blob and "monarchjson" for a format consumable by Google's Monarch tooling. Defaults to "". |
| MetricURL | String | URL describing where monitoring metrics should be exported. |
| MetricExportInterval | Integer | Number of seconds to wait between exporting metrics. Defaults to 30.
| MetricExtraLabels | Dictionary | A map of key value pairs to add to all metric root labels. (e.g. a=b,c=d) defaults to @{}). If a previously set key (e.g. host_name is set to "" then the key is remove from the metric root labels. Alternatively if a value is set for an existing key then the new value will override the old. |
| MetricFormat | String | Format to export metrics as, supported formats are "rawjson" for a single JSON blob and "monarchjson" for a format consumable by Google's Monarch tooling. Defaults to "". |
| MetricURL | String | URL describing where monitoring metrics should be exported. |
| MetricExportInterval | Integer | Number of seconds to wait between exporting metrics. Defaults to 30.
| MetricExtraLabels | Dictionary | A map of key value pairs to add to all metric root labels. (e.g. a=b,c=d) defaults to @{}). If a previously set key (e.g. host_name is set to "" then the key is remove from the metric root labels. Alternatively if a value is set for an existing key then the new value will override the old. |
*overridable by the sync server: run `santactl status` to check the current
@@ -187,20 +188,23 @@ ways to install configuration profiles:
## Sync Server Provided Configuration
| Key | Value Type | Description |
| ------------------------------ | ---------- | ---------------------------------------- |
| client_mode | String | MONITOR or LOCKDOWN, defaults to MONITOR. |
| clean_sync** | Bool | If set to `True` Santa will clear all local rules and download a fresh copy from the sync-server. Defaults to `False`. |
| batch_size | Integer | The number of rules to download or events to upload per request. Multiple requests will be made if there is more work than can fit in single request. Defaults to 50. |
| upload_logs_url** | String | If set, the endpoint to send Santa's current logs. No default. |
| allowed_path_regex | String | Same as the "Local Configuration" AllowedPathRegex. No default. |
| blocked_path_regex | String | Same as the "Local Configuration" BlockedPathRegex. No default. |
| full_sync_interval* | Integer | The max time to wait before performing a full sync with the server. Defaults to 600 secs (10 minutes) if not set. |
| fcm_token* | String | The FCM token used by Santa to listen for FCM messages. Unique for every machine. No default. |
| fcm_full_sync_interval* | Integer | The full sync interval if a fcm_token is set. Defaults to 14400 secs (4 hours). |
| fcm_global_rule_sync_deadline* | Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). |
| enable_bundles* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. |
| enable_transitive_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
| Key | Value Type | Description |
| ----------------------------------- | ---------- | ---------------------------------------- |
| client\_mode | String | MONITOR or LOCKDOWN, defaults to MONITOR. |
| clean\_sync\*\* | Bool | If set to `True` Santa will clear all local rules and download a fresh copy from the sync-server. Defaults to `False`. |
| batch\_size | Integer | The number of rules to download or events to upload per request. Multiple requests will be made if there is more work than can fit in single request. Defaults to 50. |
| upload\_logs\_url\*\* | String | If set, the endpoint to send Santa's current logs. No default. |
| allowed\_path\_regex | String | Same as the "Local Configuration" AllowedPathRegex. No default. |
| blocked\_path\_regex | String | Same as the "Local Configuration" BlockedPathRegex. No default. |
| full\_sync\_interval\* | Integer | The max time to wait before performing a full sync with the server. Defaults to 600 secs (10 minutes) if not set. |
| fcm\_token\* | String | The FCM token used by Santa to listen for FCM messages. Unique for every machine. No default. |
| fcm\_full\_sync\_interval\* | Integer | The full sync interval if a fcm\_token is set. Defaults to 14400 secs (4 hours). |
| fcm\_global\_rule\_sync\_deadline\* | Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). |
| enable\_bundles\* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. |
| enable\_transitive\_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
| block\_usb\_mass\_storage | Bool | If set to 'True' blocking USB Mass storage feature is enabled. Defaults to `False`. |
| remount\_usb\_mode | Array | Array of strings for arguments to pass to mount -o (any of "rdonly", "noexec", "nosuid", "nobrowse", "noowners", "nodev", "async", "-j"). when forcibly remounting devices. No default. |
*Held only in memory. Not persistent upon process restart.
@@ -225,4 +229,4 @@ and for allowing the [system extension](https://github.com/google/santa/blob/mai
Please note that for release package installers that included the kernel extension
as part of the payload (prior to 2021.8) the end user to be prompted to allow it
unless explicitly allowed with another MDM-delivered configuration profile to the
supervised system.
supervised system.

1
external Symbolic link
View File

@@ -0,0 +1 @@
bazel-out/../../../external

View File

@@ -1,3 +1,3 @@
"""The version for all Santa components."""
SANTA_VERSION = "2021.8"
SANTA_VERSION = "2022.1"