Compare commits

...

28 Commits

Author SHA1 Message Date
Russell Hancox
a67801d5ed santactl/status: Remove driver connected, re-org USB blocking status (#826) 2022-06-22 14:59:46 -04:00
Russell Hancox
3d37a3a5ae santad: Update assert usage to avoid a string-to-bool conversion (#825) 2022-06-22 12:55:57 -04:00
Russell Hancox
bfae5dc828 fix some style issues (#824) 2022-06-22 10:41:05 -04:00
Pete Markowsky
fde5f52a11 Added handling for Remount events to USB mass storage blocking (#818)
* Added handling for Remount events to USB mass storage blocking.
2022-06-22 09:39:20 -04:00
Russell Hancox
01bd1bfdca santad: Use multiple semaphores to avoid freeing ES message before use of it has ended. (#822)
This slightly complex solution is necessary because while on macOS 11+ there are retain/release methods that can be used on ES messages, on 10.15 the only option is a copy which is comparatively expensive (and on 11+ the copy/free methods are aliases for retain/release)

Fixes #821
2022-06-08 11:21:40 -04:00
Matt W
ae13900676 Mute self to reduce message volume. Remove noisy log message. (#820)
* Mute self to reduce message volume. Remove noisy log message.

* Bail if self muting failed. Remove selfPid.

* Fix tests by mocking es_mute_process
2022-05-31 11:36:35 -04:00
Matt W
a65c91874b Copy new PrinterProxy file instead of overwriting (#819)
* Copy new PrinterProxy file instead of overwriting

* Update log type for error message

* Update log message severity
2022-05-27 13:08:25 -04:00
Matt W
6a3fda069c Remove unused testing scripts (#816)
* Remvoe unused testing scripts

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2022-05-27 11:03:10 -04:00
Khalid Jamal Abdulnasser
4d34099142 santad: log decision when failing to read file (#817) 2022-05-27 09:52:06 -04:00
Russell Hancox
e639574973 Project: Fix layering for tests (#813) 2022-05-12 16:52:11 -04:00
Russell Hancox
636f9ea873 Project: Layering, missed a dependency (#812) 2022-05-12 14:49:18 -04:00
Russell Hancox
9099409915 Project: Enable layering check, fix all dependency violations (#811) 2022-05-12 14:26:08 -04:00
Russell Hancox
976f483a99 syncservice: Fix SNTSyncTest (#810)
Failing preflight early if the daemonConn doesn't return a response the tests. This fix is a bit awkward, I tried to add the defaults in setUp but then you can't overwrite the stubs in methods that need to do it
2022-05-12 09:54:00 -04:00
Tom Burgin
8a32b7a56b preflight sync: fix dispatch_group_wait return polarity (#809)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:55:42 -04:00
Tom Burgin
7eeb06b406 preflight sync: stop the sync if we cannot communicate with the daemon (#808)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 18:45:58 +00:00
Tom Burgin
4540a1c656 SNTConfigurator: remove mutability from sync state dict (#807)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:35:43 -04:00
Russell Hancox
acc7b32b24 GUI: Switch to UserNotification.framework notifications (#806) 2022-05-11 12:32:08 -04:00
Russell Hancox
b92d513f5d GUI: Fix message queuing (#805) 2022-05-11 09:59:38 -04:00
Tom Burgin
3458fccd4e santasyncservice: handle loading and unloading of the service in the pkg (#804)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-10 14:59:34 -04:00
Russell Hancox
fdfb00368c GUI: Update keys for EventDetailURL. (#802)
The previous change here (#797) was not backward compatible and would be difficult to roll out. This change restores the previously used key and adds 2 new ones for migration. The previous key is marked deprecated and will be removed in the future.
2022-05-09 13:46:13 -04:00
Tom Burgin
6bd369cfb2 santad: remove sema from sync service queue (#803)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-09 13:32:28 -04:00
Pete Markowsky
0df26c6214 Fix ES Mock Client Subscription issues (#801)
Fixes an issue with the ES mock where it was deleting all clients on an unsubscribe.
2022-05-06 14:34:42 -04:00
Russell Hancox
6e22da1d97 santad: Add 'null' event logger. Fixes #754 (#799) 2022-05-06 12:22:04 -04:00
Russell Hancox
1725809335 Add config to allow uploading all events (#800)
* Add config to allow uploading all events

This config can be enabled locally or by a sync server and causes the
client to upload all events, not just those for binaries that are or
would be blocked.

Fixes #689
2022-05-06 11:45:53 -04:00
Pete Markowsky
3eff49feda Added macos-12 to the build matrix. (#798) 2022-05-03 21:14:15 -04:00
Pete Markowsky
5caedebb06 Created a profiles package so provisioning profiles only need to be in one place. (#794) 2022-05-03 17:14:02 -04:00
Russell Hancox
d823028b72 Sync: Add option to enable event upload despite clean sync. (#796)
Related to #789
2022-05-03 15:15:42 -04:00
Russell Hancox
49b2d6e22a GUI: Add %bundle_or_file_sha% translation key (#797)
* GUI: Add %bundle_or_file_sha% translation key

This mimics the current behavior that %file_sha% previously had and
moves %file_sha% to the expected behavior or just showing the file's
SHA.

Related to #795
2022-05-03 14:59:01 -04:00
45 changed files with 669 additions and 413 deletions

View File

@@ -53,7 +53,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11]
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
@@ -66,7 +66,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11]
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'

View File

@@ -26,6 +26,9 @@ mkdir -p /usr/local/bin
# Load com.google.santa.metricservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.metricservice.plist
# Load com.google.santa.syncservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.syncservice.plist
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "${GUI_USER}" ]] && exit 0

View File

@@ -9,6 +9,7 @@
/bin/launchctl remove com.google.santad || true
/bin/launchctl remove com.google.santa.bundleservice || true
/bin/launchctl remove com.google.santa.metricservice || true
/bin/launchctl remove com.google.santa.syncservice || true
/bin/sleep 1

View File

@@ -3,7 +3,6 @@ load("@rules_proto_grpc//objc:defs.bzl", "objc_proto_library")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -11,7 +10,6 @@ licenses(["notice"])
proto_library(
name = "santa_proto",
srcs = ["santa.proto"],
features = ["layering_check"],
deps = [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:timestamp_proto",
@@ -28,7 +26,6 @@ objc_proto_library(
cc_library(
name = "SantaCache",
hdrs = ["SantaCache.h"],
features = ["layering_check"],
deps = ["//Source/common:SNTCommon"],
)
@@ -49,15 +46,7 @@ objc_library(
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
":SNTSystemInfo",
],
)
@@ -71,6 +60,7 @@ objc_library(
":SNTDeviceEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)
@@ -84,6 +74,15 @@ objc_library(
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
],
)
objc_library(
name = "SNTAllowlistInfo",
srcs = ["SNTAllowlistInfo.m"],
@@ -129,7 +128,6 @@ cc_library(
"TARGET_OS_OSX",
"TARGET_OS_MAC",
],
features = ["layering_check"],
)
objc_library(
@@ -144,7 +142,6 @@ cc_library(
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
features = ["layering_check"],
deps = [":SNTLogging"],
)
@@ -168,7 +165,6 @@ objc_library(
cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
features = ["layering_check"],
)
objc_library(
@@ -202,9 +198,12 @@ objc_library(
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
deps = [
":SNTCommonEnums",
":SNTConfigurator",
":SNTRule",
":SNTStoredEvent",
":SNTXPCUnprivilegedControlInterface",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
@@ -231,6 +230,7 @@ objc_library(
srcs = ["SNTXPCSyncServiceInterface.m"],
hdrs = ["SNTXPCSyncServiceInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
"@MOLXPCConnection",
],

View File

@@ -119,9 +119,16 @@
if (!formatStr.length) return nil;
if (event.fileSHA256) {
// This key is deprecated, use %file_identifier% or %bundle_or_file_identifier%
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
withString:event.fileBundleHash ?: event.fileSHA256];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_identifier%"
withString:event.fileSHA256];
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%bundle_or_file_identifier%"
withString:event.fileBundleHash ?: event.fileSHA256];
}
if (event.executingUser) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"

View File

@@ -93,6 +93,7 @@ typedef NS_ENUM(NSInteger, SNTEventLogType) {
SNTEventLogTypeSyslog,
SNTEventLogTypeFilelog,
SNTEventLogTypeProtobuf,
SNTEventLogTypeNull,
};
// The return status of a sync.

View File

@@ -151,9 +151,10 @@
///
/// Defines how event logs are stored. Options are:
/// SNTEventLogTypeSyslog: Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog: Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeProtobuf: (BETA) Sent to a file on disk, using maildir format. Use
/// SNTEventLogTypeSyslog "syslog": Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog "file": Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeNull "null": Logs nothing
/// SNTEventLogTypeProtobuf "protobuf": (BETA) Sent to a file on disk, using maildir format. Use
/// mailDirectory to specify a path. Use mailDirectoryFileSizeThresholdKB,
/// mailDirectorySizeThresholdMB and mailDirectoryEventMaxFlushTimeSec to configure
/// additional maildir format settings.
@@ -411,6 +412,17 @@
///
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// If true, syncs will upload events when a clean sync is requested. Defaults to false.
///
@property(readonly, nonatomic) BOOL enableCleanSyncEventUpload;
///
/// If true, events will be uploaded for all executions, even those that are allowed.
/// Use with caution, this generates a lot of events. Defaults to false.
///
@property(nonatomic) BOOL enableAllEventUpload;
///
/// If true, forks and exits will be logged. Defaults to false.
///

View File

@@ -28,7 +28,7 @@
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
/// Holds the configurations from a sync server and mobileconfig.
@property NSMutableDictionary *syncState;
@property NSDictionary *syncState;
@property NSMutableDictionary *configState;
/// Was --debug passed as an argument to this process?
@@ -46,6 +46,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 kSyncEnableCleanSyncEventUpload = @"SyncEnableCleanSyncEventUpload";
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString *const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -111,6 +112,7 @@ static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
static NSString *const kEnableAllEventUploadKey = @"EnableAllEventUpload";
// TODO(markowsky): move these to sync server only.
static NSString *const kMetricFormat = @"MetricFormat";
@@ -146,7 +148,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kFullSyncLastSuccess : date,
kRuleSyncLastSuccess : date,
kSyncCleanRequired : number
kSyncCleanRequired : number,
kEnableAllEventUploadKey : number,
};
_forcedConfigKeyTypes = @{
kClientModeKey : number,
@@ -207,6 +210,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kMetricExportInterval : number,
kMetricExportTimeout : number,
kMetricExtraLabels : dictionary,
kEnableAllEventUploadKey : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
@@ -394,6 +398,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableAllEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
@@ -693,6 +701,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return SNTEventLogTypeProtobuf;
} else if ([logType isEqualToString:@"syslog"]) {
return SNTEventLogTypeSyslog;
} else if ([logType isEqualToString:@"null"]) {
return SNTEventLogTypeNull;
} else if ([logType isEqualToString:@"file"]) {
return SNTEventLogTypeFilelog;
} else {
return SNTEventLogTypeFilelog;
}
@@ -734,6 +746,22 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : YES;
}
- (BOOL)enableCleanSyncEventUpload {
NSNumber *number = self.configState[kSyncEnableCleanSyncEventUpload];
return number ? [number boolValue] : NO;
}
- (BOOL)enableAllEventUpload {
NSNumber *n = self.syncState[kEnableAllEventUploadKey];
if (n) return [n boolValue];
return [self.configState[kEnableAllEventUploadKey] boolValue];
}
- (void)setEnableAllEventUpload:(BOOL)enabled {
[self updateSyncStateForKey:kEnableAllEventUploadKey value:@(enabled)];
}
- (BOOL)enableForkAndExitLogging {
NSNumber *number = self.configState[kEnableForkAndExitLogging];
return number ? [number boolValue] : NO;

View File

@@ -51,6 +51,7 @@
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply;
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
///
/// Syncd Ops

View File

@@ -28,12 +28,11 @@
@protocol SNTUnprivilegedDaemonControlXPC
///
/// Kernel ops
/// Cache Ops
///
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
///
/// Database ops

View File

@@ -2,6 +2,10 @@ load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
exports_files([
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
])
@@ -36,13 +40,18 @@ objc_library(
"IOKit",
"SecurityInterface",
"SystemExtensions",
"UserNotifications",
],
deps = [
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCNotifierInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -70,7 +79,7 @@ macos_application(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -28,7 +28,8 @@
}
- (NSString *)messageHash {
return @"";
[self doesNotRecognizeSelector:_cmd];
return nil;
}
@end

View File

@@ -15,6 +15,7 @@
#import "Source/santa/SNTNotificationManager.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <UserNotifications/UserNotifications.h>
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
@@ -58,7 +59,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self.pendingNotifications removeObject:self.currentWindowController];
self.currentWindowController = nil;
if ([self.pendingNotifications count]) {
if (self.pendingNotifications.count) {
[self showQueuedWindow];
} else {
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
@@ -83,9 +84,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
- (BOOL)notificationAlreadyQueued:(SNTMessageWindowController *)pendingMsg {
for (SNTMessageWindowController *msg in self.pendingNotifications) {
if ([msg messageHash] == [pendingMsg messageHash]) {
return YES;
}
if ([[msg messageHash] isEqual:[pendingMsg messageHash]]) return YES;
}
return NO;
}
@@ -209,28 +208,57 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
#pragma mark SNTNotifierXPC protocol methods
- (void)postClientModeNotification:(SNTClientMode)clientmode {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
NSString *customMsg;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
switch (clientmode) {
case SNTClientModeMonitor:
un.informativeText = @"Switching into Monitor mode";
customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
case SNTClientModeMonitor: {
content.body = @"Switching into Monitor mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
case SNTClientModeLockdown:
un.informativeText = @"Switching into Lockdown mode";
customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
}
case SNTClientModeLockdown: {
content.body = @"Switching into Lockdown mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
}
default: return;
}
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
UNNotificationRequest *req =
[UNNotificationRequest requestWithIdentifier:@"clientModeNotification"
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
content.body = message ?: @"Requested application can now be run";
NSString *identifier = [NSString stringWithFormat:@"ruleSyncNotification_%@", content.body];
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:identifier
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
@@ -245,14 +273,6 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self queueMessage:pendingMsg];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
un.informativeText = message ?: @"Requested application can now be run";
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
}
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event withCustomMessage:(NSString *)message {
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");

View File

@@ -2,6 +2,10 @@ load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_applicatio
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
objc_library(
name = "santabs_lib",
srcs = [
@@ -12,6 +16,7 @@ objc_library(
deps = [
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"@FMDB",
@@ -32,7 +37,7 @@ macos_command_line_application(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -5,7 +5,6 @@ licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
objc_library(
@@ -53,6 +52,7 @@ objc_library(
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/santasyncservice:sync_lib",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -70,7 +70,7 @@ macos_command_line_application(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
deps = [":santactl_lib"],
@@ -90,6 +90,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTXPCControlInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
@@ -100,13 +101,23 @@ santa_unit_test(
santa_unit_test(
name = "SNTCommandMetricsTest",
srcs = ["Commands/SNTCommandMetricsTest.m"],
srcs = [
"SNTCommand.h",
"SNTCommandController.h",
"Commands/SNTCommandMetrics.h",
"Commands/SNTCommandMetricsTest.m",
],
structured_resources = glob(["Commands/testdata/*"]),
visibility = ["//:santa_package_group"],
deps = [
":santactl_lib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/common:SNTXPCControlInterface",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@OCMock",
"@MOLXPCConnection",
],
)

View File

@@ -48,16 +48,10 @@ REGISTER_COMMAND_NAME(@"status")
dispatch_group_t group = dispatch_group_create();
// Daemon status
__block BOOL driverConnected;
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
__block double cpuPeak, ramPeak;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] driverConnectionEstablished:^(BOOL connected) {
driverConnected = connected;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] clientMode:^(SNTClientMode cm) {
switch (cm) {
case SNTClientModeMonitor: clientMode = @"Monitor"; break;
@@ -181,7 +175,7 @@ REGISTER_COMMAND_NAME(@"status")
if ([arguments containsObject:@"--json"]) {
NSMutableDictionary *stats = [@{
@"daemon" : @{
@"driver_connected" : @(driverConnected),
@"driver_connected" : @(YES),
@"mode" : clientMode ?: @"null",
@"file_logging" : @(fileLogging),
@"watchdog_cpu_events" : @(cpuEvents),
@@ -223,16 +217,15 @@ REGISTER_COMMAND_NAME(@"status")
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Driver Connected", driverConnected ? "Yes" : "No");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
printf(" %-25s | %s\n", "USB Blocking", (configurator.blockUSBMount ? "Yes" : "No"));
if (configurator.blockUSBMount && configurator.remountUSBMode.count > 0) {
printf(" %-25s | %s\n", "USB Remounting Mode:",
[[configurator.remountUSBMode componentsJoinedByString:@", "] UTF8String]);
}
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
if (cachingEnabled) {
printf(">>> Cache Info\n");

View File

@@ -3,7 +3,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -22,11 +21,14 @@ objc_library(
],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
],
)
@@ -52,6 +54,8 @@ objc_library(
deps = [
":SNTEventProvider",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
],
@@ -69,15 +73,20 @@ objc_library(
"Logs/SNTSyslogEventLog.h",
"SNTDatabaseController.h",
],
hdrs = [
"Logs/SNTEventLog.h",
],
deps = [
":database_controller",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"@FMDB",
],
)
@@ -92,7 +101,13 @@ objc_library(
],
deps = [
":event_logs_common",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTStoredEvent",
"//Source/common:santa_objc_proto",
],
)
@@ -108,6 +123,12 @@ objc_library(
deps = [
":endpoint_security_manager",
":event_logs_common",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTLogging",
],
)
@@ -116,9 +137,14 @@ objc_library(
srcs = [
"Logs/SNTFileEventLog.h",
"Logs/SNTFileEventLog.m",
"Logs/SNTSyslogEventLog.h",
],
deps = [
":event_logs_common",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTStrengthify",
],
)
@@ -128,7 +154,9 @@ objc_library(
":file_event_logs",
":protobuf_event_logs",
":syslog_event_logs",
"//Source/common:SNTCommon",
],
hdrs = ["Logs/SNTEventLog.h"],
)
objc_library(
@@ -172,21 +200,31 @@ objc_library(
":database_controller",
":endpoint_security_manager",
":event_logs",
":SNTApplicationCoreMetrics",
"//Source/common:SantaCache",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCMetricServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"//Source/santad:SNTApplicationCoreMetrics",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
@@ -210,6 +248,9 @@ objc_library(
"EventProviders/EndpointSecurityTestUtil.h",
"EventProviders/EndpointSecurityTestUtil.mm",
],
hdrs = [
"EventProviders/EndpointSecurityTestUtil.h",
],
sdk_dylibs = [
"EndpointSecurity",
"bsm",
@@ -251,7 +292,7 @@ macos_bundle(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Daemon_Dev.provisionprofile",
"//conditions:default": "//profiles:daemon_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
@@ -261,6 +302,11 @@ macos_bundle(
santa_unit_test(
name = "SNTExecutionControllerTest",
srcs = [
"SNTExecutionController.h",
"DataLayer/SNTDatabaseTable.h",
"DataLayer/SNTEventTable.h",
"DataLayer/SNTRuleTable.h",
"EventProviders/SNTEventProvider.h",
"SNTExecutionControllerTest.m",
],
sdk_dylibs = [
@@ -273,6 +319,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
@@ -281,6 +328,8 @@ santa_unit_test(
"//Source/common:SNTRule",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SantaCache",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
"@OCMock",
@@ -317,6 +366,7 @@ santa_unit_test(
],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
@@ -345,7 +395,9 @@ santa_unit_test(
],
deps = [
":EndpointSecurityTestLib",
"//Source/common:SNTConfigurator",
"//Source/common:SNTCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
],
@@ -354,6 +406,8 @@ santa_unit_test(
santa_unit_test(
name = "SNTDeviceManagerTest",
srcs = [
"EventProviders/DiskArbitrationTestUtil.h",
"EventProviders/SNTDeviceManager.h",
"EventProviders/SNTDeviceManagerTest.mm",
],
minimum_os_version = "10.15",
@@ -366,6 +420,8 @@ santa_unit_test(
":EndpointSecurityTestLib",
":santad_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
"@OCMock",
@@ -375,7 +431,9 @@ santa_unit_test(
santa_unit_test(
name = "SNTApplicationTest",
srcs = [
"SNTApplication.h",
"SNTApplicationTest.m",
"SNTDatabaseController.h",
],
data = [
"//Source/santad/testdata:binaryrules_testdata",
@@ -388,10 +446,14 @@ santa_unit_test(
deps = [
":EndpointSecurityTestLib",
":santad_lib",
"//Source/common:SNTConfigurator",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
"@OCMock",
],
tags = ["exclusive"],
)
santa_unit_test(
@@ -425,6 +487,7 @@ santa_unit_test(
deps = [
":SNTApplicationCoreMetrics",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/common:SNTSystemInfo",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
@@ -435,13 +498,21 @@ santa_unit_test(
santa_unit_test(
name = "SNTProtobufEventLogTest",
srcs = [
"Logs/SNTLogOutput.h",
"Logs/SNTProtobufEventLog.h",
"Logs/SNTProtobufEventLogTest.m",
"Logs/SNTSimpleMaildir.h",
],
minimum_os_version = "10.15",
deps = [
":EndpointSecurityTestLib",
":event_logs",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:santa_objc_proto",
"@OCMock",
],

View File

@@ -21,7 +21,8 @@ es_string_token_t MakeStringToken(const NSString *_Nonnull s);
es_file_t MakeESFile(const char *_Nonnull path);
es_process_t MakeESProcess(es_file_t *_Nonnull esFile);
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *_Nonnull instigator, struct timespec ts);
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *_Nonnull instigator,
struct timespec ts);
CF_EXTERN_C_END
@class ESMessage;
@@ -68,6 +69,21 @@ API_UNAVAILABLE(ios, tvos, watchos)
es_new_client_result_t es_new_client(es_client_t *_Nullable *_Nonnull client,
es_handler_block_t _Nonnull handler);
API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_mute_process(es_client_t * _Nonnull client,
const audit_token_t * _Nonnull audit_token);
#if defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_muted_paths_events(es_client_t *_Nonnull client,
es_muted_paths_t *_Nonnull *_Nullable muted_paths);
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
void es_release_muted_paths(es_muted_paths_t *_Nonnull muted_paths);
#endif
API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos)
es_respond_result_t es_respond_auth_result(es_client_t *_Nonnull client,

View File

@@ -45,7 +45,8 @@ es_process_t MakeESProcess(es_file_t *esFile) {
return esProc;
}
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *instigator, struct timespec ts) {
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *instigator,
struct timespec ts) {
es_message_t esMsg = {};
esMsg.time = ts;
@@ -205,6 +206,18 @@ CF_EXTERN_C_END
[self.clients addObject:mockClient];
}
- (BOOL)removeClient:(es_client_t *_Nonnull)client {
MockESClient *clientToRemove = [self findClient:client];
if (!clientToRemove) {
NSLog(@"Attempted to remove unknown mock es client.");
return NO;
}
[self.clients removeObject:clientToRemove];
return YES;
}
- (void)triggerHandler:(es_message_t *_Nonnull)msg {
for (MockESClient *client in self.clients) {
if (client.subscriptions[msg->event_type]) {
@@ -233,17 +246,24 @@ CF_EXTERN_C_END
return ES_RESPOND_RESULT_SUCCESS;
};
- (MockESClient *)findClient:(es_client_t *)client {
for (MockESClient *c in self.clients) {
// Since we're mocking out a C interface and using this exact pointer as our
// client identifier, only check for pointer equality.
if (client == (__bridge es_client_t *)c) {
return c;
}
}
return nil;
}
- (void)setSubscriptions:(const es_event_type_t *_Nonnull)events
event_count:(uint32_t)event_count
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;
}
}
MockESClient *toUpdate = [self findClient:client];
if (toUpdate == nil) {
NSLog(@"setting subscription for unknown client");
return;
@@ -281,9 +301,36 @@ es_new_client_result_t es_new_client(es_client_t *_Nullable *_Nonnull client,
return ES_NEW_CLIENT_RESULT_SUCCESS;
};
es_return_t es_mute_process(es_client_t * _Nonnull client,
const audit_token_t * _Nonnull audit_token) {
return ES_RETURN_SUCCESS;
}
#if defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_muted_paths_events(es_client_t *_Nonnull client,
es_muted_paths_t *_Nonnull *_Nullable muted_paths) {
es_muted_paths_t *tmp = (es_muted_paths_t *)malloc(sizeof(es_muted_paths_t));
tmp->count = 0;
*muted_paths = (es_muted_paths_t *_Nullable)tmp;
return ES_RETURN_SUCCESS;
};
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
void es_release_muted_paths(es_muted_paths_t *_Nonnull muted_paths) {
free(muted_paths);
}
#endif
API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos) es_return_t es_delete_client(es_client_t *_Nullable client) {
[[MockEndpointSecurity mockEndpointSecurity] reset];
if (![[MockEndpointSecurity mockEndpointSecurity] removeClient:client]) {
return ES_RETURN_ERROR;
}
return ES_RETURN_SUCCESS;
};

View File

@@ -170,6 +170,7 @@ NS_ASSUME_NONNULL_BEGIN
es_event_type_t events[] = {
ES_EVENT_TYPE_AUTH_MOUNT,
ES_EVENT_TYPE_AUTH_REMOUNT,
};
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
@@ -199,44 +200,77 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
long mountMode = m->event.mount.statfs->f_flags;
struct statfs *eventStatFS;
BOOL isRemount = NO;
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_MOUNT: eventStatFS = m->event.mount.statfs; break;
case ES_EVENT_TYPE_AUTH_REMOUNT:
eventStatFS = m->event.remount.statfs;
isRemount = YES;
break;
default:
LOGE(@"Unexpected Event Type passed to DeviceManager handleAuthMount: %d", m->event_type);
// Fail closed.
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
assert(0 && "SNTDeviceManager: unexpected event type");
return;
}
long mountMode = eventStatFS->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",
LOGD(@"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);
DADiskRef disk = DADiskCreateFromBSDName(NULL, self.diskArbSession, eventStatFS->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 isInternal = [diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceInternalKey] boolValue];
BOOL isRemovable = [diskInfo[(__bridge NSString *)kDADiskDescriptionMediaRemovableKey] boolValue];
BOOL isUSB =
[diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey] isEqualTo:@"USB"];
BOOL isEjectable = [diskInfo[(__bridge NSString *)kDADiskDescriptionMediaEjectableKey] boolValue];
NSString *protocol = diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey];
BOOL isUSB = [protocol isEqualToString:@"USB"];
BOOL isVirtual = [protocol isEqualToString: @"Virtual Interface"];
if (!isRemovable || !isUSB) {
NSString *kind = diskInfo[(__bridge NSString *)kDADiskDescriptionMediaKindKey];
// TODO: check kind and protocol for banned things (e.g. MTP).
LOGD(@"SNTDeviceManager: DiskInfo Protocol: %@ Kind: %@ isInternal: %d isRemovable: %d "
@"isEjectable: %d",
protocol, kind, isInternal, isRemovable, isEjectable);
// If the device is internal or virtual we are okay with the operation. We
// also are okay with operations for devices that are non-removal as long as
// they are NOT a USB device.
if (isInternal || isVirtual || (!isRemovable && !isEjectable && !isUSB)) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
SNTDeviceEvent *event = [[SNTDeviceEvent alloc]
initWithOnName:[NSString stringWithUTF8String:m->event.mount.statfs->f_mntonname]
fromName:[NSString stringWithUTF8String:m->event.mount.statfs->f_mntfromname]];
initWithOnName:[NSString stringWithUTF8String:eventStatFS->f_mntonname]
fromName:[NSString stringWithUTF8String:eventStatFS->f_mntfromname]];
BOOL shouldRemount = self.remountArgs != nil && [self.remountArgs count] > 0;
if (shouldRemount) {
event.remountArgs = self.remountArgs;
long remountOpts = mountArgsToMask(self.remountArgs);
if (mountMode & remountOpts) {
LOGD(@"SNTDeviceManager: mountMode: %@", maskToMountArgs(mountMode));
LOGD(@"SNTDeviceManager: remountOpts: %@", maskToMountArgs(remountOpts));
if ((mountMode & remountOpts) == remountOpts && !isRemount) {
LOGD(@"SNTDeviceManager: Allowing as mount as flags match 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);
eventStatFS->f_mntfromname, eventStatFS->f_mntonname, mountMode, newMode);
[self remount:disk mountMode:newMode];
}
@@ -270,26 +304,34 @@ NS_ASSUME_NONNULL_BEGIN
// 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;
es_message_t *mc = es_copy_message(m);
dispatch_semaphore_t processingSema = dispatch_semaphore_create(0);
// Add 1 to the processing semaphore. We're not creating it with a starting
// value of 1 because that requires that the semaphore is not deallocated
// until its value matches the starting value, which we don't need.
dispatch_semaphore_signal(processingSema);
dispatch_semaphore_t deadlineExpiredSema = dispatch_semaphore_create(0);
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;
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Handler already responded, nothing to do.
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));
dispatch_semaphore_signal(deadlineExpiredSema);
});
}
// 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);
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Deadline expired, wait for deadline block to finish.
dispatch_semaphore_wait(deadlineExpiredSema, DISPATCH_TIME_FOREVER);
}
es_free_message(mc);
});
}
@@ -297,16 +339,17 @@ NS_ASSUME_NONNULL_BEGIN
- (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
case ES_EVENT_TYPE_AUTH_REMOUNT: {
[[fallthrough]];
}
// TODO(tnek): log any extra data here about mounts.
case ES_EVENT_TYPE_NOTIFY_MOUNT: {
case ES_EVENT_TYPE_AUTH_MOUNT: {
[self handleAuthMount:m withClient:c];
break;
}
default: LOGE(@"SNTDeviceManager: unexpected event type: %d", m->event_type);
default:
LOGE(@"SNTDeviceManager: unexpected event type: %d", m->event_type);
break;
}
}

View File

@@ -40,9 +40,11 @@
fclose(stdout);
}
- (ESResponse *)triggerTestMount:(SNTDeviceManager *)deviceManager
mockES:(MockEndpointSecurity *)mockES
mockDA:(MockDiskArbitration *)mockDA {
- (ESResponse *)triggerTestMountEvent:(SNTDeviceManager *)deviceManager
mockES:(MockEndpointSecurity *)mockES
mockDA:(MockDiskArbitration *)mockDA
eventType:(es_event_type_t)eventType
diskInfoOverrides:(NSDictionary *)diskInfo {
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
@@ -84,26 +86,39 @@
@"DAMediaBSDName" : test_mntfromname,
};
if (diskInfo != nil) {
NSMutableDictionary *mergedDiskDescription = [disk.diskDescription mutableCopy];
for (NSString *key in diskInfo) {
mergedDiskDescription[key] = diskInfo[key];
}
disk.diskDescription = (NSDictionary *)mergedDiskDescription;
}
[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}};
m.message->event_type = eventType;
if (eventType == ES_EVENT_TYPE_AUTH_MOUNT) {
m.message->event = (es_events_t){.mount = {.statfs = fs}};
} else {
m.message->event = (es_events_t){.remount = {.statfs = fs}};
}
}];
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
XCTestExpectation *mountExpectation =
[self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_MOUNT
[mockES registerResponseCallback:eventType
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
[mountExpectation fulfill];
}];
[mockES triggerHandler:m.message];
[self waitForExpectations:@[ expectation ] timeout:60.0];
[self waitForExpectations:@[ mountExpectation ] timeout:60.0];
free(fs);
return got;
@@ -118,7 +133,12 @@
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = NO;
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
}
@@ -145,7 +165,11 @@
[expectation fulfill];
};
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, YES);
@@ -179,7 +203,11 @@
[expectation fulfill];
};
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
@@ -190,4 +218,74 @@
XCTAssertEqualObjects(gotmntfromname, @"/dev/disk2s1");
}
- (void)testEnsureRemountsCannotChangePerms {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
deviceManager.remountArgs = @[ @"noexec", @"rdonly" ];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Wait for SNTDeviceManager's blockCallback to trigger"];
__block NSString *gotmntonname, *gotmntfromname;
__block NSArray<NSString *> *gotRemountedArgs;
deviceManager.deviceBlockCallback = ^(SNTDeviceEvent *event) {
gotRemountedArgs = event.remountArgs;
gotmntonname = event.mntonname;
gotmntfromname = event.mntfromname;
[expectation fulfill];
};
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_REMOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, YES);
[self waitForExpectations:@[ expectation ] timeout:10.0];
XCTAssertEqualObjects(gotRemountedArgs, deviceManager.remountArgs);
XCTAssertEqualObjects(gotmntonname, @"/Volumes/KATE'S 4G");
XCTAssertEqualObjects(gotmntfromname, @"/dev/disk2s1");
}
- (void)testEnsureDMGsDoNotPrompt {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
deviceManager.remountArgs = @[ @"noexec", @"rdonly" ];
deviceManager.deviceBlockCallback = ^(SNTDeviceEvent *event) {
XCTFail(@"Should not be called");
};
NSDictionary *diskInfo = @{
(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey: @"Virtual Interface",
(__bridge NSString *)kDADiskDescriptionDeviceModelKey: @"Disk Image",
(__bridge NSString *)kDADiskDescriptionMediaNameKey: @"disk image",
};
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:diskInfo];
XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
XCTAssertEqual(mockDA.wasRemounted, NO);
}
@end

View File

@@ -34,7 +34,6 @@ static const pid_t PID_MAX = 99999;
@property(nonatomic) SNTPrefixTree *prefixTree;
@property(nonatomic, readonly) dispatch_queue_t esAuthQueue;
@property(nonatomic, readonly) dispatch_queue_t esNotifyQueue;
@property(nonatomic, readonly) pid_t selfPID;
@end
@@ -48,6 +47,7 @@ static const pid_t PID_MAX = 99999;
_decisionCallback = ^(santa_message_t) {};
_logCallback = ^(santa_message_t) {};
[self establishClient];
[self muteSelf];
_prefixTree = new SNTPrefixTree();
_esAuthQueue =
dispatch_queue_create("com.google.santa.daemon.es_auth", DISPATCH_QUEUE_CONCURRENT);
@@ -56,7 +56,6 @@ static const pid_t PID_MAX = 99999;
_esNotifyQueue =
dispatch_queue_create("com.google.santa.daemon.es_notify", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(_esNotifyQueue, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0));
_selfPID = getpid();
}
return self;
@@ -70,6 +69,24 @@ static const pid_t PID_MAX = 99999;
if (_prefixTree) delete _prefixTree;
}
- (void)muteSelf {
audit_token_t myAuditToken;
mach_msg_type_number_t count = TASK_AUDIT_TOKEN_COUNT;
if (task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)&myAuditToken, &count) ==
KERN_SUCCESS) {
if (es_mute_process(self.client, &myAuditToken) == ES_RETURN_SUCCESS) {
return;
} else {
LOGE(@"Failed to mute this client's process, its events will not be muted.");
}
} else {
LOGE(@"Failed to fetch this client's audit token. Its events will not be muted.");
}
// If we get here, Santa was unable to mute itself. Assume transitory and bail.
exit(EXIT_FAILURE);
}
- (void)establishClient API_AVAILABLE(macos(10.15)) {
while (!self.client) {
SNTConfigurator *config = [SNTConfigurator configurator];
@@ -84,9 +101,7 @@ static const pid_t PID_MAX = 99999;
if (m->action_type == ES_ACTION_TYPE_AUTH) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
}
if (self.selfPID != pid) {
LOGD(@"Skipping event type: 0x%x from es_client pid: %d", m->event_type, pid);
}
return;
}
@@ -214,30 +229,42 @@ static const pid_t PID_MAX = 99999;
switch (m->action_type) {
case ES_ACTION_TYPE_AUTH: {
// Copy the message
es_message_t *mc = es_copy_message(m);
dispatch_semaphore_t processingSema = dispatch_semaphore_create(0);
// Add 1 to the processing semaphore. We're not creating it with a starting
// value of 1 because that requires that the semaphore is not deallocated
// until its value matches the starting value, which we don't need.
dispatch_semaphore_signal(processingSema);
dispatch_semaphore_t deadlineExpiredSema = dispatch_semaphore_create(0);
// Create a timer to deny the execution 5 seconds before the deadline,
// if a response hasn't already been sent. This block will still be enqueued if
// the the deadline - 5 secs is < DISPATCH_TIME_NOW.
// As of 10.15.5, a typical deadline is 60 seconds.
auto responded = std::make_shared<std::atomic<bool>>(false);
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -5), self.esAuthQueue, ^(void) {
if (responded->load()) return;
LOGE(@"Deadline reached: deny pid=%d ret=%d", pid,
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false));
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Handler has already responded, nothing to do.
return;
}
LOGE(@"SNTEndpointSecurityManager: deadline reached: deny pid=%d ret=%d", pid,
es_respond_auth_result(self.client, mc, ES_AUTH_RESULT_DENY, false));
dispatch_semaphore_signal(deadlineExpiredSema);
});
// Copy the message and return control back to ES
es_message_t *mc = es_copy_message(m);
// Dispatch off to the handler and return control to ES.
dispatch_async(self.esAuthQueue, ^{
[self messageHandler:mc];
responded->store(true);
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Deadline expired, wait for deadline block to finish.
dispatch_semaphore_wait(deadlineExpiredSema, DISPATCH_TIME_FOREVER);
}
es_free_message(mc);
});
break;
}
case ES_ACTION_TYPE_NOTIFY: {
// Don't log fileop events from com.google.santa.daemon
if (self.selfPID == pid && m->event_type != ES_EVENT_TYPE_NOTIFY_EXEC) return;
// Copy the message and return control back to ES
es_message_t *mc = es_copy_message(m);
dispatch_async(self.esNotifyQueue, ^{
@@ -322,8 +349,7 @@ static const pid_t PID_MAX = 99999;
NSString *path = [[NSString alloc] initWithBytes:pathToken.data
length:pathToken.length
encoding:NSUTF8StringEncoding];
if ([self isDatabasePath:path] &&
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
if ([self isDatabasePath:path]) {
LOGW(@"Preventing attempt to delete Santa databases!");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
return;
@@ -337,8 +363,7 @@ static const pid_t PID_MAX = 99999;
length:pathToken.length
encoding:NSUTF8StringEncoding];
if ([self isDatabasePath:path] &&
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
if ([self isDatabasePath:path]) {
LOGW(@"Preventing attempt to rename Santa databases!");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
return;
@@ -348,8 +373,7 @@ static const pid_t PID_MAX = 99999;
NSString *destPath = [[NSString alloc] initWithBytes:destToken.data
length:destToken.length
encoding:NSUTF8StringEncoding];
if ([self isDatabasePath:destPath] &&
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
if ([self isDatabasePath:destPath]) {
LOGW(@"Preventing attempt to overwrite Santa databases!");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
return;

View File

@@ -447,7 +447,11 @@
logger = [[SNTProtobufEventLog alloc] init];
break;
}
default: logger = nil;
case SNTEventLogTypeNull: {
// Messages sent to nil objects do nothing, which is perfect for a null logger.
logger = nil;
break;
}
}
});
return logger;

View File

@@ -85,10 +85,6 @@ double watchdogRAMPeak = 0;
reply([self.eventProvider checkCache:vnodeID]);
}
- (void)driverConnectionEstablished:(void (^)(BOOL))reply {
reply(self.eventProvider.connectionEstablished);
}
#pragma mark Database ops
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate, int64_t compiler,
@@ -252,6 +248,15 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)enableAllEventUpload:(void (^)(BOOL))reply {
reply([SNTConfigurator configurator].enableAllEventUpload);
}
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
[[SNTConfigurator configurator] setEnableAllEventUpload:enabled];
reply();
}
#pragma mark Metrics Ops
- (void)metrics:(void (^)(NSDictionary *))reply {

View File

@@ -14,6 +14,7 @@
#import "Source/santad/SNTExecutionController.h"
#include <copyfile.h>
#include <libproc.h>
#include <pwd.h>
#include <utmpx.h>
@@ -135,11 +136,14 @@ static NSString *const kPrinterProxyPostMonterey =
NSError *fileInfoError;
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@(message.path) error:&fileInfoError];
if (unlikely(!binInfo)) {
LOGE(@"Failed to read file %@: %@", @(message.path), fileInfoError.localizedDescription);
if (config.failClosed && config.clientMode == SNTClientModeLockdown) {
LOGE(@"Failed to read file %@: %@ and denying action", @(message.path),
fileInfoError.localizedDescription);
[self.eventProvider postAction:ACTION_RESPOND_DENY forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kDenyNoFileInfo ]];
} else {
LOGE(@"Failed to read file %@: %@ but allowing action", @(message.path),
fileInfoError.localizedDescription);
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kAllowNoFileInfo ]];
}
@@ -191,9 +195,11 @@ static NSString *const kPrinterProxyPostMonterey =
[self incrementEventCounters:cd.decision];
// Log to database if necessary.
if (cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler &&
cd.decision != SNTEventStateAllowTransitive && cd.decision != SNTEventStateAllowCertificate &&
cd.decision != SNTEventStateAllowTeamID && cd.decision != SNTEventStateAllowScope) {
if ([SNTConfigurator configurator].enableAllEventUpload ||
(cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler &&
cd.decision != SNTEventStateAllowTransitive &&
cd.decision != SNTEventStateAllowCertificate && cd.decision != SNTEventStateAllowTeamID &&
cd.decision != SNTEventStateAllowScope)) {
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.occurrenceDate = [[NSDate alloc] init];
se.fileSHA256 = cd.sha256;
@@ -300,15 +306,12 @@ static NSString *const kPrinterProxyPostMonterey =
SNTFileInfo *proxyFi = [self printerProxyFileInfo];
if ([proxyFi.SHA256 isEqual:fi.SHA256]) return NO;
NSFileHandle *inFh = [NSFileHandle fileHandleForReadingAtPath:proxyFi.path];
NSFileHandle *outFh = [NSFileHandle fileHandleForWritingAtPath:fi.path];
[outFh writeData:[inFh readDataToEndOfFile]];
[inFh closeFile];
[outFh truncateFileAtOffset:[outFh offsetInFile]];
[outFh synchronizeFile];
[outFh closeFile];
LOGW(@"PrinterProxy workaround applied to %@", fi.path);
copyfile_flags_t copyflags = COPYFILE_ALL | COPYFILE_UNLINK;
if (copyfile(proxyFi.path.UTF8String, fi.path.UTF8String, NULL, copyflags) != 0) {
LOGE(@"Failed to apply PrinterProxy workaround for %@", fi.path);
} else {
LOGI(@"PrinterProxy workaround applied to: %@", fi.path);
}
return YES;
}

View File

@@ -25,7 +25,5 @@
- (void)addEvents:(NSArray<SNTStoredEvent *> *)events isFromBundle:(BOOL)isFromBundle;
- (void)addBundleEvent:(SNTStoredEvent *)event reply:(void (^)(SNTBundleEventAction))reply;
- (void)startSyncingEvents;
- (void)stopSyncingEvents;
@end

View File

@@ -23,7 +23,6 @@
@interface SNTSyncdQueue ()
@property NSCache<NSString *, NSDate *> *uploadBackoff;
@property dispatch_queue_t syncdQueue;
@property dispatch_semaphore_t sema;
@end
@implementation SNTSyncdQueue
@@ -34,7 +33,6 @@
_uploadBackoff = [[NSCache alloc] init];
_uploadBackoff.countLimit = 128;
_syncdQueue = dispatch_queue_create("com.google.syncd_queue", DISPATCH_QUEUE_SERIAL);
_sema = dispatch_semaphore_create(0);
}
return self;
}
@@ -66,25 +64,10 @@
}];
}
- (void)startSyncingEvents {
dispatch_semaphore_signal(self.sema);
}
- (void)stopSyncingEvents {
self.sema = dispatch_semaphore_create(0);
}
// Hold events for a few seconds to allow santad and santactl to establish connections.
// If the connections are not established in time drop the event from the queue.
// They will be uploaded during a full sync.
- (void)dispatchBlockOnSyncdQueue:(void (^)(void))block {
if (!block) return;
dispatch_async(self.syncdQueue, ^{
if (!dispatch_semaphore_wait(self.sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
if (block) block();
dispatch_semaphore_signal(self.sema);
} else {
LOGD(@"Dropping block %@ from com.google.syncd_queue", block);
}
block();
});
}

View File

@@ -3,7 +3,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -15,6 +14,9 @@ objc_library(
"SNTMetricService.m",
"main.m",
],
hdrs = [
"SNTMetricService.h",
],
deps = [
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
@@ -36,8 +38,12 @@ santa_unit_test(
structured_resources = ["//Source/santametricservice/Formats:testdata"],
deps = [
":SNTMetricServiceLib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@OCMock",
"@MOLAuthenticatingURLSession",
],
)
@@ -62,7 +68,7 @@ macos_command_line_application(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -2,7 +2,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -30,6 +29,9 @@ objc_library(
"SNTMetricRawJSONFormat.h",
"SNTMetricRawJSONFormat.m",
],
hdrs = [
"SNTMetricRawJSONFormat.h",
],
deps = [
":SNTMetricFormat",
"//Source/common:SNTLogging",
@@ -43,6 +45,9 @@ objc_library(
"SNTMetricMonarchJSONFormat.h",
"SNTMetricMonarchJSONFormat.m",
],
hdrs = [
"SNTMetricMonarchJSONFormat.h",
],
deps = [
":SNTMetricFormat",
"//Source/common:SNTLogging",

View File

@@ -2,7 +2,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -18,6 +17,9 @@ objc_library(
"SNTMetricFileWriter.h",
"SNTMetricFileWriter.m",
],
hdrs = [
"SNTMetricFileWriter.h",
],
deps = [
":SNTMetricWriter",
"//Source/common:SNTLogging",
@@ -31,6 +33,7 @@ santa_unit_test(
],
deps = [
":SNTMetricFileWriter",
"//Source/common:SNTConfigurator",
],
)
@@ -40,6 +43,9 @@ objc_library(
"SNTMetricHTTPWriter.h",
"SNTMetricHTTPWriter.m",
],
hdrs = [
"SNTMetricHTTPWriter.h",
],
deps = [
":SNTMetricWriter",
"//Source/common:SNTConfigurator",
@@ -55,7 +61,9 @@ santa_unit_test(
],
deps = [
":SNTMetricHTTPWriter",
"//Source/common:SNTConfigurator",
"@OCMock",
"@MOLAuthenticatingURLSession",
],
)

View File

@@ -5,7 +5,6 @@ licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
objc_library(
@@ -50,11 +49,19 @@ objc_library(
deps = [
":FCM_lib",
":broadcaster_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"@MOLAuthenticatingURLSession",
"@MOLCertificate",
"@MOLXPCConnection",
],
)
@@ -97,6 +104,7 @@ santa_unit_test(
deps = [
":FCM_lib",
":broadcaster_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
@@ -104,8 +112,11 @@ santa_unit_test(
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"@MOLAuthenticatingURLSession",
"@MOLCertificate",
"@MOLXPCConnection",
"@OCMock",
],
@@ -129,6 +140,7 @@ objc_library(
"main.m",
],
deps = [
":broadcaster_lib",
":sync_lib",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTLogging",
@@ -151,7 +163,7 @@ macos_command_line_application(
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -50,6 +50,7 @@ extern NSString *const kEnableBundlesDeprecated;
extern NSString *const kEnableTransitiveRules;
extern NSString *const kEnableTransitiveRulesDeprecated;
extern NSString *const kEnableTransitiveRulesSuperDeprecated;
extern NSString *const kEnableAllEventUpload;
extern NSString *const kEvents;
extern NSString *const kFileSHA256;

View File

@@ -51,6 +51,7 @@ NSString *const kEnableBundlesDeprecated = @"bundles_enabled";
NSString *const kEnableTransitiveRules = @"enable_transitive_rules";
NSString *const kEnableTransitiveRulesDeprecated = @"enabled_transitive_whitelisting";
NSString *const kEnableTransitiveRulesSuperDeprecated = @"transitive_whitelisting_enabled";
NSString *const kEnableAllEventUpload = @"enable_all_event_upload";
NSString *const kEvents = @"events";
NSString *const kFileSHA256 = @"file_sha256";

View File

@@ -17,6 +17,7 @@
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
@@ -54,7 +55,7 @@
if (uploadEvents.count >= self.syncState.eventBatchSize) break;
}
if (!self.syncState.cleanSync) {
if (!self.syncState.cleanSync || [[SNTConfigurator configurator] enableCleanSyncEventUpload]) {
NSDictionary *r = [self performRequest:[self requestWithDictionary:@{kEvents : uploadEvents}]];
if (!r) return NO;

View File

@@ -266,8 +266,8 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
return [self eventUploadWithSyncState:syncState];
}
SLOGE(@"Preflight failed, will try again once %@ is reachable",
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
LOGE(@"Preflight failed, will try again once %@ is reachable",
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
[self startReachability];
return SNTSyncStatusTypePreflightFailed;
}

View File

@@ -75,7 +75,11 @@
dispatch_group_leave(group);
}];
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
// Stop the sync if we are unable to communicate with daemon.
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
SLOGE(@"Unable to communicate with daemon.");
return NO;
}
// If user requested it or we've never had a successful sync, try from a clean slate.
if (syncClean) {
@@ -106,6 +110,13 @@
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
NSNumber *enableAllEventUpload = resp[kEnableAllEventUpload];
[[self.daemonConn remoteObjectProxy] setEnableAllEventUpload:[enableAllEventUpload boolValue]
reply:^{
dispatch_group_leave(group);
}];
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
// Don't let these go too low

View File

@@ -140,6 +140,19 @@
return [NSData dataWithContentsOfFile:path];
}
- (void)setupDefaultDaemonConnResponses {
OCMStub([self.daemonConnRop
databaseRuleCounts:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE(0), // binary
OCMOCK_VALUE(0), // cert
OCMOCK_VALUE(0), // compiler
OCMOCK_VALUE(0), // transitive
OCMOCK_VALUE(0), // teamID
nil])]);
OCMStub([self.daemonConnRop syncCleanRequired:([OCMArg invokeBlockWithArgs:@NO, nil])]);
OCMStub([self.daemonConnRop
clientMode:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE(SNTClientModeMonitor), nil])]);
}
#pragma mark - SNTSyncStage Tests
- (void)testBaseFetchXSRFTokenSuccess {
@@ -186,6 +199,7 @@
#pragma mark - SNTSyncPreflight Tests
- (void)testPreflightBasicResponse {
[self setupDefaultDaemonConnResponses];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_basic.json"];
@@ -199,6 +213,7 @@
}
- (void)testPreflightBlockUSBMount {
[self setupDefaultDaemonConnResponses];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_toggle_blockusb.json"];
@@ -239,6 +254,15 @@
- (void)testPreflightCleanSync {
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
OCMStub([self.daemonConnRop
databaseRuleCounts:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE(0), // binary
OCMOCK_VALUE(0), // cert
OCMOCK_VALUE(0), // compiler
OCMOCK_VALUE(0), // transitive
OCMOCK_VALUE(0), // teamID
nil])]);
OCMStub([self.daemonConnRop
clientMode:([OCMArg invokeBlockWithArgs:OCMOCK_VALUE(SNTClientModeMonitor), nil])]);
OCMStub([self.daemonConnRop syncCleanRequired:([OCMArg invokeBlockWithArgs:@YES, nil])]);
NSData *respData = [self dataFromDict:@{kCleanSync : @YES}];
@@ -257,6 +281,7 @@
}
- (void)testPreflightLockdown {
[self setupDefaultDaemonConnResponses];
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
NSData *respData = [self dataFromFixture:@"sync_preflight_lockdown.json"];

View File

@@ -1,76 +0,0 @@
#!/bin/sh
set -e
GIT_ROOT=$(git rev-parse --show-toplevel)
TMP_DIR=$(mktemp -d)
function cleanup() {
# Reset randomize_version if we used it
if [ -f "$TMP_DIR/version.bzl" ]; then
mv "$TMP_DIR/version.bzl" $VERSION_FILE
fi
rm -rf $TMP_DIR
rm -f $GIT_ROOT/bazel-bin/santa-*.tar.gz
}
trap cleanup EXIT
function build_custom_signed() {
SANTAD_PATH=Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon
SANTA_BIN_PATH=Santa.app/Contents/MacOS
KEYCHAIN="santa-dev-test.keychain"
SANTAD_ENTITLEMENTS="$GIT_ROOT/Source/santad/com.google.santa.daemon.systemextension.entitlements"
SIGNING_IDENTITY="localhost"
bazel build \
--apple_generate_dsym \
-c opt \
--define=SANTA_BUILD_TYPE=ci \
--define=apple.propagate_embedded_extra_outputs=yes \
--macos_cpus=x86_64,arm64 \
--embed_label="santa_${RANDOM}.${RANDOM}.${RANDOM}"
//:release
echo "> Build complete, installing santa"
tar xvf $GIT_ROOT/bazel-bin/santa-*.tar.gz -C $TMP_DIR
CS_ARGS="--prefix=EQHXZ8M8AV -fs $SIGNING_IDENTITY --timestamp --options library,kill,runtime"
for bin in $TMP_DIR/binaries/$SANTA_BIN_PATH/*; do
codesign --keychain $KEYCHAIN --preserve-metadata=entitlements ${CS_ARGS} $bin
done
codesign ${CS_ARGS} --keychain $KEYCHAIN --entitlements $SANTAD_ENTITLEMENTS $TMP_DIR/binaries/$SANTAD_PATH
}
function build_provisionprofile_signed() {
bazel build --apple_generate_dsym -c opt --define=SANTA_BUILD_TYPE=release --define=apple.propagate_embedded_extra_outputs=yes --macos_cpus=x86_64,arm64 //:release
tar xvf $GIT_ROOT/bazel-bin/santa-*.tar.gz -C $TMP_DIR
}
function build() {
SANTA_DAEMON_PROVPROFILE=$GIT_ROOT/Source/santad/Santa_Daemon_Dev.provisionprofile
SANTA_PROVPROFILE=$GIT_ROOT/Source/santa/Santa_Dev.provisionprofile
if [[ -f $SANTA_DAEMON_PROVPROFILE && -f $SANTA_PROVPROFILE ]]; then
echo "Using provisionprofiles in $SANTA_DAEMON_PROVPROFILE and $SANTA_PROVPROFILE"
build_provisionprofile_signed
else
echo "No provisionprofiles detected, creating self-signed certs"
build_custom_signed
fi
}
function install() {
echo "> Running install.sh"
(
cd $TMP_DIR
sudo ./conf/install.sh
)
}
function main() {
build
install
}
main $@
exit $?

View File

@@ -1,41 +0,0 @@
#!/bin/sh
set -e
GIT_ROOT=$(git rev-parse --show-toplevel)
CNF_PATH=$GIT_ROOT/Testing/openssl.cnf
KEYCHAIN="santa-dev-test.keychain"
function init() {
openssl genrsa -out ./santa.key 2048
openssl rsa -in ./santa.key -out ./santa.key
openssl req -new -key ./santa.key -out ./santa.csr -config $CNF_PATH
openssl x509 -req -days 10 -in ./santa.csr -signkey ./santa.key -out ./santa.crt -extfile $CNF_PATH -extensions codesign
openssl pkcs12 -export -out santa.p12 -inkey santa.key -in santa.crt -password pass:santa
security create-keychain -p santa $KEYCHAIN
security import ./santa.p12 -k $KEYCHAIN -A -P santa
security add-trusted-cert -d -r trustRoot -k $KEYCHAIN santa.crt
}
function cleanup() {
security delete-keychain $KEYCHAIN
rm santa.key
rm santa.csr
rm santa.p12
}
function main() {
case $1 in
init)
init
;;
cleanup)
cleanup
;;
*)
echo "$0 [init|cleanup]"
;;
esac
}
main $@
exit $?

View File

@@ -1,25 +0,0 @@
#!/bin/bash
set -e
set -x
GIT_ROOT=$(git rev-parse --show-toplevel)
run_tests() {
(
local -a TEST_FLAGS=( --strategy=TestRunner=standalone --test_output=all )
cd $GIT_ROOT/Testing/integration
time bazel test "${TEST_FLAGS[@]}" -- ...
)
}
setup() {
$GIT_ROOT/Testing/start_env.sh
sudo santactl sync --debug
}
main() {
setup
run_tests
}
main "$@"

View File

@@ -1,22 +0,0 @@
[ ca ]
default_ca = CA_default
[ req ]
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
commonName = localhost
countryName = US
organizationName = Google LLC
OU=EQHXZ8M8AV
name = santa
[ codesign ]
keyUsage = digitalSignature
extendedKeyUsage = codeSigning
[ v3_ca ]
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always

View File

@@ -1,6 +0,0 @@
#!/bin/sh
killall moroz
security delete-identity -c "localhost"
rm -rf /Applications/Santa.app
systemextensionsctl reset
security delete-keychain santa-dev-test.keychain

View File

@@ -1,47 +0,0 @@
#!/bin/sh
set -e
GIT_ROOT=$(git rev-parse --show-toplevel)
SANTA_BIN_PATH=Santa.app/Contents/MacOS
SIGNING_IDENTITY="localhost"
function setup_certs() {
echo "> Creating codesigning certs and keys"
$GIT_ROOT/Testing/init_dev_certs.sh init
}
function run_moroz() {
echo "> Running moroz in the background"
go get github.com/groob/moroz/cmd/moroz
~/go/bin/moroz -configs="$GIT_ROOT/Testing/global.toml" -tls-key santa.key -tls-cert santa.crt &
}
function install_profile() {
echo "> Installing mobileconfig"
# The `profiles` tool has been deprecated as of Big Sur. Ugly workaround instead:
sudo open /System/Library/PreferencePanes/Profiles.prefPane "$GIT_ROOT/Testing/com.google.santa.mobileconfig"
}
function build_install_santa() {
echo "> Building and signing Santa"
$GIT_ROOT/Testing/build_and_sign.sh
systemextensionsctl list
# install.sh _should_ already start the system extension, but we want to
# explicitly call `--load-system-extension` again to actually log loading
# failures.
echo "> Install complete, attempting to explicitly start the santa systemextension"
/Applications/$SANTA_BIN_PATH/Santa --load-system-extension
systemextensionsctl list
}
function main() {
install_profile
setup_certs
run_moroz
build_install_santa
}
main $@
exit $?

View File

@@ -39,6 +39,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| 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. |
| SyncEnableCleanSyncEventUpload | Bool | If true, events will be uploaded to the sync server even if a clean sync is requested. Defaults to false. |
| 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. |
@@ -51,7 +52,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| MachineOwnerKey | String | The key to use on MachineOwnerPlist. |
| MachineIDPlist | String | The path to a plist that contains the MachineOwnerKey / value pair. |
| MachineIDKey | String | The key to use on MachineIDPlist. |
| 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. 3) protobuf (BETA): Sent to file on disk using maildir format. Defaults to filelog. |
| 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. 3) protobuf (BETA): Sent to file on disk using maildir format. 4) null: Don't output any event logs. 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. |
| MailDirectory | String | If EventLogType is set to protobuf, MailDirectory will provide the the base directory used to save files according to the maildir format. Defaults to /var/db/santa/mail. |
| MailDirectoryFileSizeThresholdKB | Integer | If EventLogType is set to protobuf, MailDirectoryFileSizeThresholdKB defines the per-file size limit for files stored in the mail directory. Events are buffered in memory until this threshold would be exceeded (or MailDirectoryEventMaxFlushTimeSec is exceeded). Defaults to 100. |
@@ -63,9 +64,10 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| MetricExportInterval | Integer | Number of seconds to wait between exporting metrics. Defaults to 30. |
| MetricExportTimeout | Integer | Number of seconds to wait before a timeout occurs when 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. |
| EnableAllEventUpload | Bool | If YES, the client will upload all execution events to the sync server, including those that were explicitly allowed. |
*overridable by the sync server: run `santactl status` to check the current
\*overridable by the sync server: run `santactl status` to check the current
running config
##### EventDetailURL
@@ -76,14 +78,16 @@ take them to a web page with more information about that event.
This property contains a kind of format string to be turned into the URL to send
them to. The following sequences will be replaced in the final URL:
| Key | Description |
| ------------ | ---------------------------------------- |
| %file_sha% | SHA-256 of the file that was blocked |
| %machine_id% | ID of the machine |
| %username% | The executing user |
| %serial% | System's serial number |
| %uuid% | System's UUID |
| %hostname% | System's full hostname |
| Key | Description |
| ------------------------------ | ------------------------------------------------------------------------------ |
| %file_identifier% | SHA-256 of the file that was blocked |
| %bundle\_or\_file\_identifier% | SHA-256 of the file that was blocked or the bundle containing it, if available |
| %file_sha% | Deprecated, acts like bundle\_or\_file\_identifier |
| %machine\_id% | ID of the machine |
| %username% | The executing user |
| %serial% | System's serial number |
| %uuid% | System's UUID |
| %hostname% | System's full hostname |
For example: `https://sync-server-hostname/%machine_id%/%file_sha%`
@@ -208,6 +212,7 @@ ways to install configuration profiles:
| 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`. |
| enable\_all\_event\_upload | Bool | If set to `True` the client will upload events for all executions, including those that are explicitly allowed. |
| 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. |

15
profiles/BUILD Normal file
View File

@@ -0,0 +1,15 @@
package(
default_visibility = ["//:santa_package_group"],
)
licenses(["notice"])
filegroup(
name="santa_dev",
srcs=["Santa_Dev.provisionprofile"]
)
filegroup(
name="daemon_dev",
srcs=["Santa_Daemon_Dev.provisionprofile"],
)