Compare commits

...

42 Commits

Author SHA1 Message Date
Russell Hancox
6093118ba1 sync: Improve logging when connection restored, avoid retries. (#1408)
* sync: Improve logging when connection restored, avoid retries.

Don't retry requests if the error is that the machine is offline
2024-07-31 13:29:25 -04:00
Russell Hancox
6719d4c32a sync: Upgrade from SCNetworkReachability -> nw_path_monitor (#1406) 2024-07-31 10:53:16 -04:00
Russell Hancox
1ce4756771 santad: Synchronize access to metric callback array (#1405) 2024-07-29 12:09:03 -04:00
Russell Hancox
9a7dcefb92 sync: Fix serial_num field name (#1404)
Disable the preserve_proto_field_names option when marshalling JSON requests as this prevents the json_name attribute on fields from working properly. Add that attribute to all fields so that they marshal as expected. Stop setting the always_print_enums_as_ints field as the value we're setting to is the default anyway.

Also add a test that preflight request data looks as expected.
2024-07-29 12:08:21 -04:00
Russell Hancox
59382bc3ac sync: Handle missing error string for abnormal statuses (#1402) 2024-07-25 10:48:16 -04:00
Russell Hancox
0725fccc7f Docs: Add DismissText key to configuration.md (#1400) 2024-07-24 15:35:59 -04:00
Russell Hancox
166c0420e5 GUI: Make dismiss button configurable, change default text back to dismiss (#1399) 2024-07-23 13:33:29 -04:00
Matt W
f4ec2d51ab Fix check for deprecated clean sync key (#1397) 2024-07-17 20:10:38 -04:00
Russell Hancox
d54ec98bd5 GUI: Update activation policy for binary blocks (#1396)
Also fix threading issues with the `queueMessage:` method.
2024-07-17 10:29:35 -04:00
Matt W
bbeb653c77 Improve handling of sync response default values (#1395)
* Improve default value handling for sync proto processing

* Fix capitalization of new enum values in comments

* Fix/add tests, update some docs

* Update more docs

* Lint

* Remove comment. Add LEGACY_NAMES tag for the linter
2024-07-16 12:33:31 -04:00
Matt W
52ffe5fc50 sync: Allow empty data for 200 responses (#1394) 2024-07-10 16:50:53 -04:00
Russell Hancox
ffd77fef9d common: Remove debug log when signing ID is missing. (#1393)
This log line gets spit out in santactl fileinfo output in a way that makes the command harder to use for various things
2024-07-10 15:44:07 -04:00
Evangelos Mamalakis
47648d2d5c Handle non-200 HTTP responses in SNTSyncStage performRequest (#1392)
* Handle non-200 HTTP responses in SNTSyncStage performRequest

If we receive a non-200 HTTP response, we should return an error
instead of parsing the response to an empty protobuf message.

* Fix nil check

---------

Co-authored-by: Matt W <436037+mlw@users.noreply.github.com>
2024-07-10 15:43:26 -04:00
Pete Markowsky
208b4a6ebc sync: Add machine_id field to facilitate a GRPC version of the protocol (#1390) 2024-07-09 16:35:26 -04:00
Russell Hancox
7f86366672 sync: Parse response as proto when SyncEnableProtoTransfer enabled (#1391) 2024-07-09 12:24:34 -04:00
Russell Hancox
9e7847740f sync: Handle parse errors, add UNKNOWN_CLIENT_MODE to enum (#1389) 2024-07-09 11:55:06 -04:00
Tom Burgin
348ff8c006 fileinfo: add --filter-inclusive (#1388)
* filter inclusive

* remove print method

* update help
2024-07-09 13:29:37 +00:00
Matt W
476cd21653 Convert santa::santad::event_providers and santa::santad (#1387) 2024-07-05 14:48:22 -04:00
Matt W
7bf11abca0 Convert santa::santad::event_providers::endpoint_security (#1386) 2024-07-05 14:13:10 -04:00
Matt W
466546f548 Namespace simplification pt2 (#1385)
* Convert santa::santad::logs::endpoint_security::serializers::Utilities

* Convert santa::santad::logs::endpoint_security::writers

* Convert santa::santad::logs::endpoint_security::serializers

* Convert santa::santad::logs::endpoint_security and santatest

* Lint

* Change type alias names to not conflict with sysinfo.h
2024-07-05 12:21:01 -04:00
Matt W
73c18851f9 Adopt namespace naming guidelines - part 1 (#1384) 2024-07-04 22:19:32 -04:00
Tom Burgin
650f6fac97 cap q (#1383) 2024-07-03 16:58:47 -04:00
Matt W
9764f1bd69 Use class member access operator for underlying ES message (#1381) 2024-07-03 12:17:00 -04:00
Matt W
688d560b62 Add protobuf serialization for new login/logout events (#1380) 2024-07-03 09:35:25 -04:00
Matt W
b6af5ade60 Add string serialization for new login/logout events (#1379) 2024-07-02 22:40:05 -04:00
Matt W
08ce693096 Login/logout events (#1371)
* WIP Basic new enriched types, hooked up serializers

* WIP Expanded enriched types, finished basic string logging

* WIP Standardize instigator and event user strings.

* WIP Remove sudo event for now. Fix proto types.

* Update proto field names. Fix builds on older SDKs.

* Fix more issues with builds on older SDKs.

* Even more build fixes for older SDKs

* Fix basic string test build on older sdks

* More fixes for older SDKs

* WIP Started on proto encoding and tests

* WIP expanded proto support for new events

* Lint. Fix recorder tests for missing event types

* WIP continued expanding proto support for new events

* WIP finished proto support for all new event types

* WIP Comment all new messages and fields in santa.proto

* WIP Use different impl to set strings to sidestep internal absl issues

* Temporarily removing serializer impls and tests to reduce PR size

* Lint fixes

* PR feedback
2024-07-02 16:41:01 -04:00
Russell Hancox
85cfa641ce Project: Update several bazel modules (#1378) 2024-06-28 13:23:46 -04:00
Matt W
72ed5ee4f9 Drop macos 11 (#1377)
* Drop macOS 11 support

* More changes after rebase, add basic macOS 15 support
2024-06-28 12:58:07 -04:00
Matt W
ecf7040b87 Proto tests min version support (#1376)
* Protobuf event tests should only apply to applicable versions

* Address local/remote lint issues

* Address more local/remote lint issues
2024-06-28 10:52:17 -04:00
Matt W
cedbc0da19 Add tests to ensure EventTypeToString handles all subscriptions (#1374) 2024-06-26 15:46:29 -04:00
Russell Hancox
ef9348e6f5 santad: Fix metrics for AuthSignal events (#1373) 2024-06-26 12:35:30 -04:00
Russell Hancox
b23b528082 docs: Update references to SNTXPCConnection (#1372) 2024-06-21 09:39:12 -04:00
Evangelos Mamalakis
587ac2ddc8 Fix santd title in docs (#1368) 2024-06-18 19:49:16 +02:00
Matt W
14729210d3 Use new Apple docs link for global proxy settings constants (#1367)
* Use new Apple docs link for global proxy settings constants

* Missed a file...

* WIP test workflow change

* WIP Fix link

* Remove trailing whitespace
2024-06-18 13:15:36 -04:00
Toast
c3d29e3c4a docs: Add Identifier Conventions (#1366) 2024-06-18 09:48:03 -04:00
Pete Markowsky
4b0ad39413 Add a Signing ID Format Helper (#1365) 2024-06-11 14:51:23 -04:00
Matt W
e8b7fdff64 Modernize docs (Round 1) (#1363)
* WIP Major modernization effort for many of the Santa docs

* Update IPC concept doc and diagram

* WIP - Apply suggestions from code review

Only some of the comments are included in this first commit.

Co-authored-by: Kathryn May <44557882+kathancox@users.noreply.github.com>
Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>

* WIP - Part 2 - Apply suggestions from code review

Adding some more suggestions. Still more to go through.

Co-authored-by: Kathryn May <44557882+kathancox@users.noreply.github.com>
Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>

* WIP Adding more PR suggestions

* WIP - Apply suggestions from code review

More commits from reviewers

Co-authored-by: Kathryn May <44557882+kathancox@users.noreply.github.com>
Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>

* WIP - Apply suggestions from code review

More PR suggestions

Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>
Co-authored-by: Kathryn May <44557882+kathancox@users.noreply.github.com>

* WIP addressed more PR feedback

* WIP - More PR feedback

* WIP - More PR feedback on bundle identification. Link updates

* WIP - Clarify bundle events

* WIP - clarify how to request bundle binary events

* Update santad setup tasks

* Fix doc link

* Update docs/binaries/santa-gui.md

Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>

---------

Co-authored-by: Kathryn May <44557882+kathancox@users.noreply.github.com>
Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
Co-authored-by: Pete Markowsky <pmarkowsky@users.noreply.github.com>
2024-06-07 10:17:07 -04:00
Russell Hancox
35d42d0134 sync: Add option to sync using binary protos (#1364) 2024-06-04 13:53:01 -04:00
Russell Hancox
a42dd6e120 santad: Add signal auth to tamper resistence. (#1360)
Prior to this change, root users could kill the com.google.santa.daemon process. 
It would be immediately restarted by sysextd but this opens a very brief
window where protection is lost. Hooking AUTH_SIGNAL and blocking all
signals to the santad process except those sent by launchd lets us block
this without breaking upgrades, reboots, etc.

This leaves `launchctl kill` and friends as an avenue, so we're also
hooking for exec and blocking executions of launchctl that reference
com.google.santa.daemon except in known safe cases.
2024-06-03 13:41:25 -04:00
Russell Hancox
53a2bbdd1e docs: Document new EventDetailURL keys (#1361) 2024-05-30 12:29:00 -04:00
Pete Markowsky
e417d8847f Project: Update check-markdown workflow to use Lychee (#1362) 2024-05-30 10:10:36 -04:00
Russell Hancox
a23b67d5de sync: Add a protobuf for the existing sync protocol (#1359)
This PR is intended to have no impact on existing sync servers. The fields and enum values in the protobuf have been named such that their JSON equivalents match the existing constants we have in the codebase.

Adding this provides a few benefits:

1. The protobuf serves as canonical documentation of the protocol in a form that's much easier to read than the existing code.
2. Protobuf parsing of JSON is likely to be better than our hand-written version.
3. We can (in a later PR) add a configuration option to use binary encoding instead of JSON, saving network during syncs.
4. Servers written in other languages are easier to write and update as time goes on, especially as we extend the protocol.
2024-05-29 14:22:49 -04:00
198 changed files with 5541 additions and 3058 deletions

View File

@@ -10,8 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout Santa"
uses: actions/checkout@61b9e3751b92087fd0b06925ba6dd6314e06f089 # ratchet:actions/checkout@master
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # ratchet:actions/checkout@v4
- name: "Check for deadlinks"
uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # ratchet:gaurav-nelson/github-action-markdown-link-check@v1
uses: lycheeverse/lychee-action@2b973e86fc7b1f6b36a93795fe2c9c6ae1118621 # ratchet:lycheeverse/lychee-action@v1
with:
fail: true
- name: "Check for trailing whitespace and newlines"
if: '!cancelled()'
run: "! git grep -EIn $'[ \t]+$' -- ':(exclude)*.patch'"

View File

@@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-11, macos-12, macos-13, macos-14]
os: [macos-12, macos-13, macos-14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # ratchet:actions/checkout@v3
@@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [macos-11, macos-12, macos-13, macos-14]
os: [macos-12, macos-13, macos-14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # ratchet:actions/checkout@v3

View File

@@ -1,20 +1,16 @@
module(name = "santa")
bazel_dep(name = "apple_support", version = "1.15.1", repo_name = "build_bazel_apple_support")
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "com_google_absl")
bazel_dep(name = "rules_python", version = "0.31.0")
bazel_dep(name = "abseil-cpp", version = "20240116.2", repo_name = "com_google_absl")
bazel_dep(name = "rules_python", version = "0.33.2")
bazel_dep(name = "rules_cc", version = "0.0.9")
bazel_dep(name = "rules_apple", version = "3.5.0", repo_name = "build_bazel_rules_apple")
bazel_dep(name = "rules_swift", version = "1.18.0", repo_name = "build_bazel_rules_swift")
bazel_dep(name = "rules_fuzzing", version = "0.5.1")
bazel_dep(name = "protobuf", version = "main", repo_name = "com_google_protobuf")
git_override(
module_name = "protobuf",
commit = "21d75f861cdbc03b0a6b235a9ccf3ba0e1f09b32",
remote = "https://github.com/protocolbuffers/protobuf.git",
)
bazel_dep(name = "rules_apple", version = "3.6.0", repo_name = "build_bazel_rules_apple")
bazel_dep(name = "rules_swift", version = "2.0.0-rc1", repo_name = "build_bazel_rules_swift")
bazel_dep(name = "rules_fuzzing", version = "0.5.2")
bazel_dep(name = "protobuf", version = "27.2", repo_name = "com_google_protobuf")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1", repo_name = "com_google_googletest")
# MOLCertificate
bazel_dep(name = "molcertificate", version = "2.1", repo_name = "MOLCertificate")
git_override(
module_name = "molcertificate",
@@ -22,6 +18,7 @@ git_override(
remote = "https://github.com/google/macops-molcertificate.git",
)
# MOLAuthenticatingURLSession
bazel_dep(name = "molauthenticatingurlsession", version = "3.0", repo_name = "MOLAuthenticatingURLSession")
git_override(
module_name = "molauthenticatingurlsession",
@@ -29,6 +26,7 @@ git_override(
remote = "https://github.com/google/macops-molauthenticatingurlsession.git",
)
# MOLCodesignChecker
bazel_dep(name = "molcodesignchecker", version = "3.0", repo_name = "MOLCodesignChecker")
git_override(
module_name = "molcodesignchecker",
@@ -36,6 +34,7 @@ git_override(
remote = "https://github.com/google/macops-molcodesignchecker.git",
)
# MOLXPCConnection
bazel_dep(name = "molxpcconnection", version = "2.1", repo_name = "MOLXPCConnection")
git_override(
module_name = "molxpcconnection",
@@ -43,10 +42,12 @@ git_override(
remote = "https://github.com/russellhancox/macops-molxpcconnection.git",
)
# FMDB
non_module_deps = use_extension("//:non_module_deps.bzl", "non_module_deps")
use_repo(non_module_deps, "FMDB")
use_repo(non_module_deps, "OCMock")
# Hedron's Compile Commands Extractor
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override(
module_name = "hedron_compile_commands",

View File

@@ -150,6 +150,16 @@ objc_library(
],
)
objc_library(
name = "SigningIDHelpers",
srcs = ["SigningIDHelpers.m"],
hdrs = ["SigningIDHelpers.h"],
deps = [
":SNTLogging",
"@MOLCodesignChecker",
],
)
objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
@@ -477,6 +487,9 @@ santa_unit_test(
santa_unit_test(
name = "SNTBlockMessageTest",
srcs = ["SNTBlockMessageTest.m"],
sdk_frameworks = [
"AppKit",
],
deps = [
":SNTBlockMessage_SantaGUI",
":SNTConfigurator",
@@ -485,9 +498,6 @@ santa_unit_test(
":SNTSystemInfo",
"@OCMock",
],
sdk_frameworks = [
"AppKit",
],
)
santa_unit_test(
@@ -527,6 +537,7 @@ objc_library(
"bsm",
],
deps = [
":Platform",
":SystemResources",
"@OCMock",
"@com_google_googletest//:gtest",

View File

@@ -17,13 +17,6 @@
#include <Availability.h>
#if defined(MAC_OS_VERSION_12_0) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
#define HAVE_MACOS_12 1
#else
#define HAVE_MACOS_12 0
#endif
#if defined(MAC_OS_VERSION_13_0) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_0
#define HAVE_MACOS_13 1
@@ -31,4 +24,18 @@
#define HAVE_MACOS_13 0
#endif
#if defined(MAC_OS_VERSION_14_0) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0
#define HAVE_MACOS_14 1
#else
#define HAVE_MACOS_14 0
#endif
#if defined(MAC_OS_VERSION_15_0) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_15_0
#define HAVE_MACOS_15 1
#else
#define HAVE_MACOS_15 0
#endif
#endif

View File

@@ -28,7 +28,7 @@
#define DEBUG_LOG(format, ...) // NOP
#endif
namespace santa::common {
namespace santa {
template <typename ValueT>
class PrefixTree {
@@ -125,7 +125,10 @@ class PrefixTree {
return false;
}
cur_byte = (uint8_t) * ++p;
// Disabling clang format due to local/remote version differences.
// clang-format off
cur_byte = (uint8_t)*++p;
// clang-format on
} while (*p);
node->node_type_ = node_type;
@@ -297,6 +300,6 @@ class PrefixTree {
absl::Mutex lock_;
};
} // namespace santa::common
} // namespace santa
#endif

View File

@@ -17,7 +17,7 @@
#define SANTA_PREFIX_TREE_DEBUG 1
#include "Source/common/PrefixTree.h"
using santa::common::PrefixTree;
using santa::PrefixTree;
@interface PrefixTreeTest : XCTestCase
@end

View File

@@ -347,6 +347,11 @@
///
@property(readonly, nonatomic) NSString *eventDetailText;
///
/// This string represents the text to show on the "Dismiss" button in the UI instead of "Dismiss".
///
@property(readonly, nonatomic) NSString *dismissText;
///
/// In lockdown mode this is the message shown to the user when an unknown binary
/// is blocked. If this message is not configured, a reasonable default is provided.
@@ -393,11 +398,17 @@
///
@property(readonly, nonatomic) NSURL *syncBaseURL;
///
/// If enabled, syncing will use binary protobufs for transfer instead
/// of JSON. Defaults to NO.
///
@property(readonly, nonatomic) BOOL syncEnableProtoTransfer;
///
/// 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.
/// https://developer.apple.com/documentation/cfnetwork/global-proxy-settings-constants.
///
@property(readonly, nonatomic) NSDictionary *syncProxyConfig;

View File

@@ -73,6 +73,7 @@ static NSString *const kMobileConfigDomain = @"com.google.santa";
/// The keys managed by a mobileconfig.
static NSString *const kStaticRules = @"StaticRules";
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
static NSString *const kSyncEnableProtoTransfer = @"SyncEnableProtoTransfer";
static NSString *const kSyncProxyConfigKey = @"SyncProxyConfiguration";
static NSString *const kSyncExtraHeadersKey = @"SyncExtraHeaders";
static NSString *const kSyncEnableCleanSyncEventUpload = @"SyncEnableCleanSyncEventUpload";
@@ -96,6 +97,7 @@ static NSString *const kAboutTextKey = @"AboutText";
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
static NSString *const kEventDetailURLKey = @"EventDetailURL";
static NSString *const kEventDetailTextKey = @"EventDetailText";
static NSString *const kDismissTextKey = @"DismissText";
static NSString *const kUnknownBlockMessage = @"UnknownBlockMessage";
static NSString *const kBannedBlockMessage = @"BannedBlockMessage";
static NSString *const kBannedUSBBlockMessage = @"BannedUSBBlockMessage";
@@ -226,6 +228,7 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
kDismissTextKey : string,
kUnknownBlockMessage : string,
kBannedBlockMessage : string,
kBannedUSBBlockMessage : string,
@@ -234,6 +237,7 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
kModeNotificationLockdown : string,
kStaticRules : array,
kSyncBaseURLKey : string,
kSyncEnableProtoTransfer : number,
kSyncEnableCleanSyncEventUpload : number,
kSyncProxyConfigKey : dictionary,
kSyncExtraHeadersKey : dictionary,
@@ -371,6 +375,10 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncEnableProtoTransfer {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncExtraHeaders {
return [self configStateSet];
}
@@ -403,6 +411,10 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingDismissText {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingUnknownBlockMessage {
return [self configStateSet];
}
@@ -719,6 +731,11 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
return url;
}
- (BOOL)syncEnableProtoTransfer {
NSNumber *number = self.configState[kSyncEnableProtoTransfer];
return number ? [number boolValue] : NO;
}
- (NSDictionary *)syncProxyConfig {
return self.configState[kSyncProxyConfigKey];
}
@@ -763,6 +780,10 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
return self.configState[kEventDetailTextKey];
}
- (NSString *)dismissText {
return self.configState[kDismissTextKey];
}
- (NSString *)unknownBlockMessage {
return self.configState[kUnknownBlockMessage];
}
@@ -1048,7 +1069,8 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
}
}
if ([action isEqualToString:@"auditonly"]) {
// Note: `auditonly` without an underscore is a deprecated, but still accepted form.
if ([action isEqualToString:@"audit_only"] || [action isEqualToString:@"auditonly"]) {
return SNTOverrideFileAccessActionAuditOnly;
} else if ([action isEqualToString:@"disable"]) {
return SNTOverrideFileAccessActionDiable;

View File

@@ -605,10 +605,15 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
/** Export current state of the SNTMetricSet as an NSDictionary. */
- (NSDictionary *)export {
NSDictionary *exported = nil;
NSDictionary *exported;
NSArray *callbacks;
@synchronized(self) {
callbacks = [_callbacks mutableCopy];
}
// Invoke callbacks to ensure metrics are up to date.
for (void (^cb)(void) in _callbacks) {
for (void (^cb)(void) in callbacks) {
cb();
}
@@ -639,20 +644,9 @@ NSString *SNTMetricStringFromMetricFormatType(SNTMetricFormatType format) {
NSDictionary *SNTMetricConvertDatesToISO8601Strings(NSDictionary *metrics) {
NSMutableDictionary *mutableMetrics = [metrics mutableCopy];
id formatter;
if (@available(macOS 10.13, *)) {
NSISO8601DateFormatter *isoFormatter = [[NSISO8601DateFormatter alloc] init];
isoFormatter.formatOptions =
NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithFractionalSeconds;
formatter = isoFormatter;
} else {
NSDateFormatter *localFormatter = [[NSDateFormatter alloc] init];
[localFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[localFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
formatter = localFormatter;
}
NSISO8601DateFormatter *formatter = [[NSISO8601DateFormatter alloc] init];
formatter.formatOptions =
NSISO8601DateFormatWithInternetDateTime | NSISO8601DateFormatWithFractionalSeconds;
for (NSString *metricName in mutableMetrics[@"metrics"]) {
NSMutableDictionary *metric = mutableMetrics[@"metrics"][metricName];

View File

@@ -54,4 +54,19 @@
///
+ (NSString *)modelIdentifier;
///
/// @return The Santa product version, e.g. 2024.6
///
+ (NSString *)santaProductVersion;
///
/// @return The Santa build version, e.g. 655965194
///
+ (NSString *)santaBuildVersion;
///
/// @return The full Santa versoin, e.g. 2024.6.655965194
///
+ (NSString *)santaFullVersion;
@end

View File

@@ -74,6 +74,21 @@
return @(model);
}
+ (NSString *)santaProductVersion {
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
return info_dict[@"CFBundleShortVersionString"];
}
+ (NSString *)santaBuildVersion {
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
return [[info_dict[@"CFBundleVersion"] componentsSeparatedByString:@"."] lastObject];
}
+ (NSString *)santaFullVersion {
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
return info_dict[@"CFBundleVersion"];
}
#pragma mark - Internal
+ (NSDictionary *)_systemVersionDictionary {

View File

@@ -21,7 +21,7 @@
/// A block that takes the calculated bundle hash, associated events and hashing time in ms.
typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNumber *);
/// Protocol implemented by santabs and utilized by SantaGUI for bundle hashing
/// Protocol implemented by santabundleservice and utilized by SantaGUI for bundle hashing
@protocol SNTBundleServiceXPC
///

View File

@@ -19,11 +19,11 @@
#include "Source/common/ScopedTypeRef.h"
namespace santa::common {
namespace santa {
template <typename CFT>
using ScopedCFTypeRef = ScopedTypeRef<CFT, (CFT)NULL, CFRetain, CFRelease>;
} // namespace santa::common
} // namespace santa
#endif

View File

@@ -19,7 +19,7 @@
#include "Source/common/ScopedCFTypeRef.h"
using santa::common::ScopedCFTypeRef;
using santa::ScopedCFTypeRef;
@interface ScopedCFTypeRefTest : XCTestCase
@end

View File

@@ -19,7 +19,7 @@
#include "Source/common/ScopedTypeRef.h"
namespace santa::common {
namespace santa {
template <typename IOT>
using ScopedIOObjectRef =
@@ -27,4 +27,4 @@ using ScopedIOObjectRef =
}
#endif
#endif // namespace santa

View File

@@ -20,8 +20,8 @@
#include "Source/common/ScopedIOObjectRef.h"
#include "Source/santad/Logs/EndpointSecurity/Serializers/Utilities.h"
using santa::common::ScopedIOObjectRef;
using santa::santad::logs::endpoint_security::serializers::Utilities::GetDefaultIOKitCommsPort;
using santa::GetDefaultIOKitCommsPort;
using santa::ScopedIOObjectRef;
@interface ScopedIOObjectRefTest : XCTestCase
@end

View File

@@ -18,7 +18,7 @@
#include <CoreFoundation/CoreFoundation.h>
#include <assert.h>
namespace santa::common {
namespace santa {
template <typename ElementT, ElementT InvalidV, auto RetainFunc,
auto ReleaseFunc>
@@ -75,6 +75,6 @@ class ScopedTypeRef {
ElementT object_;
};
} // namespace santa::common
} // namespace santa
#endif

View File

@@ -0,0 +1,30 @@
/// Copyright 2024 Google LLC
///
/// 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
///
/// https://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>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
__BEGIN_DECLS
/**
Return a string representing normalized SigningID (prefixed with TeamID and a
colon).
@param csc A MOLCodesignChecker instance
@return An NSString formated as teamID:signingID or nil if there isn't a valid signing ID.
*/
NSString *FormatSigningID(MOLCodesignChecker *csc);
__END_DECLS

View File

@@ -0,0 +1,33 @@
/// Copyright 2024 Google LLC
///
/// 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
///
/// https://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/common/SigningIDHelpers.h"
#import "Source/common/SNTLogging.h"
NSString *FormatSigningID(MOLCodesignChecker *csc) {
if (!csc.signingID.length) {
return nil;
}
if (!csc.teamID.length) {
if (csc.platformBinary) {
return [NSString stringWithFormat:@"%@:%@", @"platform", csc.signingID];
} else {
LOGD(@"unable to format signing ID missing team ID for non-platform binary");
return nil;
}
}
return [NSString stringWithFormat:@"%@:%@", csc.teamID, csc.signingID];
}

View File

@@ -15,13 +15,14 @@
#ifndef SANTA__COMMON__STRING_H
#define SANTA__COMMON__STRING_H
#include <EndpointSecurity/ESTypes.h>
#include <Foundation/Foundation.h>
#include <optional>
#include <string>
#include <string_view>
namespace santa::common {
namespace santa {
static inline std::string_view NSStringToUTF8StringView(NSString *str) {
return std::string_view(str.UTF8String, [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
@@ -48,6 +49,10 @@ static inline NSString *OptionalStringToNSString(const std::optional<std::string
}
}
} // namespace santa::common
static inline std::string_view StringTokenToStringView(es_string_token_t es_str) {
return std::string_view(es_str.data, es_str.length);
}
} // namespace santa
#endif

View File

@@ -83,5 +83,6 @@ es_message_t MakeESMessage(es_event_type_t et, es_process_t *proc,
uint64_t future_deadline_ms = 100000);
uint32_t MaxSupportedESMessageVersionForCurrentOS();
uint32_t MinSupportedESMessageVersion(es_event_type_t event_type);
#endif

View File

@@ -19,6 +19,8 @@
#include <mach/mach_time.h>
#include <time.h>
#include <uuid/uuid.h>
#include "Source/common/Platform.h"
#include "Source/common/SystemResources.h"
NSString *RepeatedString(NSString *str, NSUInteger len) {
@@ -64,7 +66,7 @@ struct stat MakeStat(int offset) {
es_string_token_t MakeESStringToken(const char *s) {
return es_string_token_t{
.length = strlen(s),
.length = s ? strlen(s) : 0,
.data = s,
};
}
@@ -91,25 +93,6 @@ es_process_t MakeESProcess(es_file_t *file, audit_token_t tok, audit_token_t par
};
}
uint32_t MaxSupportedESMessageVersionForCurrentOS() {
// Notes:
// 1. ES message v3 was only in betas.
// 2. Message version 7 appeared in macOS 13.3, but features from that are
// not currently used. Leaving off support here so as to not require
// adding v7 test JSON files.
if (@available(macOS 13.0, *)) {
return 6;
} else if (@available(macOS 12.3, *)) {
return 5;
} else if (@available(macOS 11.0, *)) {
return 4;
} else if (@available(macOS 10.15.4, *)) {
return 2;
} else {
return 1;
}
}
es_message_t MakeESMessage(es_event_type_t et, es_process_t *proc, ActionType action_type,
uint64_t future_deadline_ms) {
es_message_t es_msg = {
@@ -134,3 +117,204 @@ void SleepMS(long ms) {
XCTAssertEqual(errno, EINTR);
}
}
uint32_t MaxSupportedESMessageVersionForCurrentOS() {
// Note 1: This function only returns a subset of versions. This is due to the
// minimum supported OS build version as well as features in latest versions
// not currently being used. Capping the max means unnecessary duuplicate test
// JSON files are not needed.
//
// Note 2: The following table maps ES message versions to lmin macOS version:
// ES Version | macOS Version
// 1 | 10.15.0
// 2 | 10.15.4
// 3 | Only in a beta
// 4 | 11.0
// 5 | 12.3
// 6 | 13.0
// 7 | 14.0
// 8 | 15.0
if (@available(macOS 13.0, *)) {
return 6;
} else if (@available(macOS 12.3, *)) {
return 5;
} else {
return 4;
}
}
uint32_t MinSupportedESMessageVersion(es_event_type_t event_type) {
switch (event_type) {
// The following events are available beginning in macOS 10.15
case ES_EVENT_TYPE_AUTH_EXEC:
case ES_EVENT_TYPE_AUTH_OPEN:
case ES_EVENT_TYPE_AUTH_KEXTLOAD:
case ES_EVENT_TYPE_AUTH_MMAP:
case ES_EVENT_TYPE_AUTH_MPROTECT:
case ES_EVENT_TYPE_AUTH_MOUNT:
case ES_EVENT_TYPE_AUTH_RENAME:
case ES_EVENT_TYPE_AUTH_SIGNAL:
case ES_EVENT_TYPE_AUTH_UNLINK:
case ES_EVENT_TYPE_NOTIFY_EXEC:
case ES_EVENT_TYPE_NOTIFY_OPEN:
case ES_EVENT_TYPE_NOTIFY_FORK:
case ES_EVENT_TYPE_NOTIFY_CLOSE:
case ES_EVENT_TYPE_NOTIFY_CREATE:
case ES_EVENT_TYPE_NOTIFY_EXCHANGEDATA:
case ES_EVENT_TYPE_NOTIFY_EXIT:
case ES_EVENT_TYPE_NOTIFY_GET_TASK:
case ES_EVENT_TYPE_NOTIFY_KEXTLOAD:
case ES_EVENT_TYPE_NOTIFY_KEXTUNLOAD:
case ES_EVENT_TYPE_NOTIFY_LINK:
case ES_EVENT_TYPE_NOTIFY_MMAP:
case ES_EVENT_TYPE_NOTIFY_MPROTECT:
case ES_EVENT_TYPE_NOTIFY_MOUNT:
case ES_EVENT_TYPE_NOTIFY_UNMOUNT:
case ES_EVENT_TYPE_NOTIFY_IOKIT_OPEN:
case ES_EVENT_TYPE_NOTIFY_RENAME:
case ES_EVENT_TYPE_NOTIFY_SETATTRLIST:
case ES_EVENT_TYPE_NOTIFY_SETEXTATTR:
case ES_EVENT_TYPE_NOTIFY_SETFLAGS:
case ES_EVENT_TYPE_NOTIFY_SETMODE:
case ES_EVENT_TYPE_NOTIFY_SETOWNER:
case ES_EVENT_TYPE_NOTIFY_SIGNAL:
case ES_EVENT_TYPE_NOTIFY_UNLINK:
case ES_EVENT_TYPE_NOTIFY_WRITE:
case ES_EVENT_TYPE_AUTH_FILE_PROVIDER_MATERIALIZE:
case ES_EVENT_TYPE_NOTIFY_FILE_PROVIDER_MATERIALIZE:
case ES_EVENT_TYPE_AUTH_FILE_PROVIDER_UPDATE:
case ES_EVENT_TYPE_NOTIFY_FILE_PROVIDER_UPDATE:
case ES_EVENT_TYPE_AUTH_READLINK:
case ES_EVENT_TYPE_NOTIFY_READLINK:
case ES_EVENT_TYPE_AUTH_TRUNCATE:
case ES_EVENT_TYPE_NOTIFY_TRUNCATE:
case ES_EVENT_TYPE_AUTH_LINK:
case ES_EVENT_TYPE_NOTIFY_LOOKUP:
case ES_EVENT_TYPE_AUTH_CREATE:
case ES_EVENT_TYPE_AUTH_SETATTRLIST:
case ES_EVENT_TYPE_AUTH_SETEXTATTR:
case ES_EVENT_TYPE_AUTH_SETFLAGS:
case ES_EVENT_TYPE_AUTH_SETMODE:
case ES_EVENT_TYPE_AUTH_SETOWNER: return 1;
// The following events are available beginning in macOS 10.15.1
case ES_EVENT_TYPE_AUTH_CHDIR:
case ES_EVENT_TYPE_NOTIFY_CHDIR:
case ES_EVENT_TYPE_AUTH_GETATTRLIST:
case ES_EVENT_TYPE_NOTIFY_GETATTRLIST:
case ES_EVENT_TYPE_NOTIFY_STAT:
case ES_EVENT_TYPE_NOTIFY_ACCESS:
case ES_EVENT_TYPE_AUTH_CHROOT:
case ES_EVENT_TYPE_NOTIFY_CHROOT:
case ES_EVENT_TYPE_AUTH_UTIMES:
case ES_EVENT_TYPE_NOTIFY_UTIMES:
case ES_EVENT_TYPE_AUTH_CLONE:
case ES_EVENT_TYPE_NOTIFY_CLONE:
case ES_EVENT_TYPE_NOTIFY_FCNTL:
case ES_EVENT_TYPE_AUTH_GETEXTATTR:
case ES_EVENT_TYPE_NOTIFY_GETEXTATTR:
case ES_EVENT_TYPE_AUTH_LISTEXTATTR:
case ES_EVENT_TYPE_NOTIFY_LISTEXTATTR:
case ES_EVENT_TYPE_AUTH_READDIR:
case ES_EVENT_TYPE_NOTIFY_READDIR:
case ES_EVENT_TYPE_AUTH_DELETEEXTATTR:
case ES_EVENT_TYPE_NOTIFY_DELETEEXTATTR:
case ES_EVENT_TYPE_AUTH_FSGETPATH:
case ES_EVENT_TYPE_NOTIFY_FSGETPATH:
case ES_EVENT_TYPE_NOTIFY_DUP:
case ES_EVENT_TYPE_AUTH_SETTIME:
case ES_EVENT_TYPE_NOTIFY_SETTIME:
case ES_EVENT_TYPE_NOTIFY_UIPC_BIND:
case ES_EVENT_TYPE_AUTH_UIPC_BIND:
case ES_EVENT_TYPE_NOTIFY_UIPC_CONNECT:
case ES_EVENT_TYPE_AUTH_UIPC_CONNECT:
case ES_EVENT_TYPE_AUTH_EXCHANGEDATA:
case ES_EVENT_TYPE_AUTH_SETACL:
case ES_EVENT_TYPE_NOTIFY_SETACL: return 1;
// The following events are available beginning in macOS 10.15.4
case ES_EVENT_TYPE_NOTIFY_PTY_GRANT:
case ES_EVENT_TYPE_NOTIFY_PTY_CLOSE:
case ES_EVENT_TYPE_AUTH_PROC_CHECK:
case ES_EVENT_TYPE_NOTIFY_PROC_CHECK:
case ES_EVENT_TYPE_AUTH_GET_TASK: return 2;
// The following events are available beginning in macOS 11.0
case ES_EVENT_TYPE_AUTH_SEARCHFS:
case ES_EVENT_TYPE_NOTIFY_SEARCHFS:
case ES_EVENT_TYPE_AUTH_FCNTL:
case ES_EVENT_TYPE_AUTH_IOKIT_OPEN:
case ES_EVENT_TYPE_AUTH_PROC_SUSPEND_RESUME:
case ES_EVENT_TYPE_NOTIFY_PROC_SUSPEND_RESUME:
case ES_EVENT_TYPE_NOTIFY_CS_INVALIDATED:
case ES_EVENT_TYPE_NOTIFY_GET_TASK_NAME:
case ES_EVENT_TYPE_NOTIFY_TRACE:
case ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE:
case ES_EVENT_TYPE_AUTH_REMOUNT:
case ES_EVENT_TYPE_NOTIFY_REMOUNT: return 4;
// The following events are available beginning in macOS 11.3
case ES_EVENT_TYPE_AUTH_GET_TASK_READ:
case ES_EVENT_TYPE_NOTIFY_GET_TASK_READ:
case ES_EVENT_TYPE_NOTIFY_GET_TASK_INSPECT: return 4;
// The following events are available beginning in macOS 12.0
case ES_EVENT_TYPE_NOTIFY_SETUID:
case ES_EVENT_TYPE_NOTIFY_SETGID:
case ES_EVENT_TYPE_NOTIFY_SETEUID:
case ES_EVENT_TYPE_NOTIFY_SETEGID:
case ES_EVENT_TYPE_NOTIFY_SETREUID:
case ES_EVENT_TYPE_NOTIFY_SETREGID:
case ES_EVENT_TYPE_AUTH_COPYFILE:
case ES_EVENT_TYPE_NOTIFY_COPYFILE: return 4;
#if HAVE_MACOS_13
// The following events are available beginning in macOS 13.0
case ES_EVENT_TYPE_NOTIFY_AUTHENTICATION:
case ES_EVENT_TYPE_NOTIFY_XP_MALWARE_DETECTED:
case ES_EVENT_TYPE_NOTIFY_XP_MALWARE_REMEDIATED:
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN:
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT:
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK:
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK:
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH:
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH:
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN:
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT:
case ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN:
case ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT:
case ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD:
case ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_REMOVE: return 6;
#endif
#if HAVE_MACOS_14
// The following events are available beginning in macOS 14.0
case ES_EVENT_TYPE_NOTIFY_PROFILE_ADD:
case ES_EVENT_TYPE_NOTIFY_PROFILE_REMOVE:
case ES_EVENT_TYPE_NOTIFY_SU:
case ES_EVENT_TYPE_NOTIFY_AUTHORIZATION_PETITION:
case ES_EVENT_TYPE_NOTIFY_AUTHORIZATION_JUDGEMENT:
case ES_EVENT_TYPE_NOTIFY_SUDO:
case ES_EVENT_TYPE_NOTIFY_OD_GROUP_ADD:
case ES_EVENT_TYPE_NOTIFY_OD_GROUP_REMOVE:
case ES_EVENT_TYPE_NOTIFY_OD_GROUP_SET:
case ES_EVENT_TYPE_NOTIFY_OD_MODIFY_PASSWORD:
case ES_EVENT_TYPE_NOTIFY_OD_DISABLE_USER:
case ES_EVENT_TYPE_NOTIFY_OD_ENABLE_USER:
case ES_EVENT_TYPE_NOTIFY_OD_ATTRIBUTE_VALUE_ADD:
case ES_EVENT_TYPE_NOTIFY_OD_ATTRIBUTE_VALUE_REMOVE:
case ES_EVENT_TYPE_NOTIFY_OD_ATTRIBUTE_SET:
case ES_EVENT_TYPE_NOTIFY_OD_CREATE_USER:
case ES_EVENT_TYPE_NOTIFY_OD_CREATE_GROUP:
case ES_EVENT_TYPE_NOTIFY_OD_DELETE_USER:
case ES_EVENT_TYPE_NOTIFY_OD_DELETE_GROUP:
case ES_EVENT_TYPE_NOTIFY_XPC_CONNECT: return 7;
#endif
#if HAVE_MACOS_15
case ES_EVENT_TYPE_NOTIFY_GATEKEEPER_USER_OVERRIDE: return 8;
#endif
default: return UINT32_MAX;
}
}

View File

@@ -15,10 +15,10 @@
#ifndef SANTA__COMMON__UNIT_H
#define SANTA__COMMON__UNIT_H
namespace santa::common {
namespace santa {
struct Unit {};
} // namespace santa::common
} // namespace santa
#endif

View File

@@ -538,6 +538,225 @@ message FileAccess {
optional PolicyDecision policy_decision = 6;
}
// Session identifier for a graphical session
// Note: Identifiers are opaque and have no meaning outside of correlating Santa
// events with the same identifier
message GraphicalSession {
optional uint32 id = 1;
}
// Information about a socket address and its type
message SocketAddress {
// The socket address
optional bytes address = 1;
enum Type {
TYPE_UNKNOWN = 0;
TYPE_NONE = 1;
TYPE_IPV4 = 2;
TYPE_IPV6 = 3;
TYPE_NAMED_SOCKET = 4;
}
// The type of the socket address
optional Type type = 2;
}
// Information about a user logging in via loginwindow
message LoginWindowSessionLogin {
// The process that emitted the login event
optional ProcessInfoLight instigator = 1;
// Name of the user logging in
optional UserInfo user = 2;
// Graphical session information for this session
optional GraphicalSession graphical_session = 3;
}
// Information about a user logging out via loginwindow
message LoginWindowSessionLogout {
// The process that emitted the logout event
optional ProcessInfoLight instigator = 1;
// Name of the user logging out
optional UserInfo user = 2;
// Graphical session information for this session
optional GraphicalSession graphical_session = 3;
}
// Information about a user locking their session via loginwindow
message LoginWindowSessionLock {
// The process that emitted the lock event
optional ProcessInfoLight instigator = 1;
// Name of the user locking their session
optional UserInfo user = 2;
// Graphical session information for this session
optional GraphicalSession graphical_session = 3;
}
// Information about a user unlocking their session via loginwindow
message LoginWindowSessionUnlock {
// The process that emitted the unlock event
optional ProcessInfoLight instigator = 1;
// Name of the user unlocking their session
optional UserInfo user = 2;
// Graphical session information for this session
optional GraphicalSession graphical_session = 3;
}
// Information about loginwindow events
message LoginWindowSession {
oneof event {
LoginWindowSessionLogin login = 1;
LoginWindowSessionLogout logout = 2;
LoginWindowSessionLock lock = 3;
LoginWindowSessionUnlock unlock = 4;
}
}
// Information about a login event from the `login(1)` utility
message Login {
// The process that emitted the login event
optional ProcessInfoLight instigator = 1;
// Whether or not the login was successful
optional bool success = 2;
// Login failure message, if applicable
optional bytes failure_message = 3;
// Information about the user that attempted to log in
// Note: `uid` data may not always exist on failed attempts
optional UserInfo user = 4;
}
// Information about a logout event from the `login(1)` utility
message Logout {
// The process that emitted the logout event
optional ProcessInfoLight instigator = 1;
// Information about the user that logged out
optional UserInfo user = 2;
}
// Information about login and logout events from the `login(1)` utility
message LoginLogout {
oneof event {
Login login = 1;
Logout logout = 2;
}
}
// Information related to Screen Sharing attaching to a graphical session
message ScreenSharingAttach {
// The process that emitted the attach event
optional ProcessInfoLight instigator = 1;
// Whether or not the attach was successful
optional bool success = 2;
// Source address information
optional SocketAddress source = 3;
// Apple ID of the viewer
optional bytes viewer = 4;
// Type of authentication used
optional bytes authentication_type = 5;
// User that attempted authentication, if applicable
optional UserInfo authentication_user = 6;
// Username of the loginwindow session, if available
optional UserInfo session_user = 7;
// Whether or not there was an existing session
optional bool existing_session = 8;
// Graphical session information for this session
optional GraphicalSession graphical_session = 9;
}
// Information related to Screen Sharing detaching from a graphical session
message ScreenSharingDetach {
// The process that emitted the detach event
optional ProcessInfoLight instigator = 1;
// Source address information
optional SocketAddress source = 2;
// Apple ID of the viewer
optional bytes viewer = 3;
// Graphical session information for this session
optional GraphicalSession graphical_session = 4;
}
// Information about Screen Sharing attach and detach events
message ScreenSharing {
oneof event {
ScreenSharingAttach attach = 1;
ScreenSharingDetach detach = 2;
}
}
// Information about SSH login events from the macOS OpenSSH implementation
message OpenSSHLogin {
// The process that emitted the login event
optional ProcessInfoLight instigator = 1;
enum Result {
RESULT_UNKNOWN = 0;
RESULT_LOGIN_EXCEED_MAXTRIES = 1;
RESULT_LOGIN_ROOT_DENIED = 2;
RESULT_AUTH_SUCCESS = 3;
RESULT_AUTH_FAIL_NONE = 4;
RESULT_AUTH_FAIL_PASSWD = 5;
RESULT_AUTH_FAIL_KBDINT = 6;
RESULT_AUTH_FAIL_PUBKEY = 7;
RESULT_AUTH_FAIL_HOSTBASED = 8;
RESULT_AUTH_FAIL_GSSAPI = 9;
RESULT_INVALID_USER = 10;
}
// The result of the login attempt
// Note: Successful if type == `RESULT_AUTH_SUCCESS`
optional Result result = 2;
// Source address of the connection
optional SocketAddress source = 3;
// Name of the user that attempted to login
// Note: `uid` data may not always exist on failed attempts
optional UserInfo user = 4;
}
// Information about SSH logout events from the macOS OpenSSH implementation
message OpenSSHLogout {
// The process that emitted the logout event
optional ProcessInfoLight instigator = 1;
// Source address of the connection
optional SocketAddress source = 2;
// Information about the user that logged out
optional UserInfo user = 3;
}
// Information about login/logout events from the macOS OpenSSH implementation
message OpenSSH {
oneof event {
OpenSSHLogin login = 1;
OpenSSHLogout logout = 2;
}
}
// A message encapsulating a single event
message SantaMessage {
// Machine ID of the host emitting this log
@@ -565,6 +784,10 @@ message SantaMessage {
Allowlist allowlist = 20;
FileAccess file_access = 21;
CodesigningInvalidated codesigning_invalidated = 22;
LoginWindowSession login_window_session = 23;
LoginLogout login_logout = 24;
ScreenSharing screen_sharing = 25;
OpenSSH open_ssh = 26;
};
}

View File

@@ -119,7 +119,7 @@ macos_application(
"//conditions:default": None,
}),
infoplists = ["Info.plist"],
minimum_os_version = "11.0",
minimum_os_version = "12.0",
provisioning_profile = select({
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",

View File

@@ -13,6 +13,7 @@
<outlet property="foundFileCountLabel" destination="LHV-gV-vyf" id="Sr0-T2-xGx"/>
<outlet property="hashingIndicator" destination="VyY-Yg-JOe" id="Yq4-tZ-9ep"/>
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
<outlet property="dismissEventButton" destination="BbV-3h-mmL" id="5s4-ZB-xlo"/>
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
</connections>
</customObject>
@@ -286,7 +287,7 @@ DQ
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL" userLabel="Dismiss Button">
<rect key="frame" x="271" y="28" width="124" height="34"/>
<buttonCell key="cell" type="push" title="Ignore" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<buttonCell key="cell" type="push" title="Dismiss" bezelStyle="rounded" alignment="center" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">

View File

@@ -54,6 +54,11 @@
///
@property(weak) IBOutlet NSButton *openEventButton;
///
/// Reference to the "Dismiss Event" button in the XIB. Used to update its title.
///
@property(weak) IBOutlet NSButton *dismissEventButton;
///
/// The execution event that this window is for
///

View File

@@ -99,6 +99,11 @@
}
}
NSString *dismissButtonText = [[SNTConfigurator configurator] dismissText];
if (dismissButtonText.length) {
[self.dismissEventButton setTitle:dismissButtonText];
}
if (!self.event.needsBundleHash) {
[self.bundleHashLabel removeFromSuperview];
[self.hashingIndicator removeFromSuperview];

View File

@@ -50,18 +50,9 @@ NS_ASSUME_NONNULL_BEGIN
customMsg:self.attributedCustomMessage];
self.window.delegate = self;
// Add app to Cmd+Tab and Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
[super showWindow:sender];
}
- (void)windowWillClose:(NSNotification *)notification {
// Remove app from Cmd+Tab and Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyAccessory;
[super windowWillClose:notification];
}
- (NSAttributedString *)attributedCustomMessage {
return [SNTBlockMessage formatMessage:self.customMessage];
}

View File

@@ -66,18 +66,9 @@
self.window.delegate = self;
// Make sure app doesn't appear in Cmd+Tab or Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyAccessory;
[super showWindow:sender];
}
- (void)windowWillClose:(NSNotification *)notification {
// Remove app from Cmd+Tab and Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyAccessory;
[super windowWillClose:notification];
}
- (NSAttributedString *)attributedCustomMessage {
return [SNTBlockMessage attributedBlockMessageForFileAccessEvent:self.event
customMessage:self.customMessage];

View File

@@ -71,6 +71,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[bc resume];
[[bc remoteObjectProxy] spindown];
[bc invalidate];
// Remove app from Cmd+Tab and Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyAccessory;
[NSApp hide:self];
}
}
@@ -101,32 +103,37 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
// If GUI is in silent mode or if there's already a notification queued for
// this message, don't do anything else.
if ([SNTConfigurator configurator].enableSilentMode) return;
if ([self notificationAlreadyQueued:pendingMsg]) return;
// See if this message has been user-silenced.
NSString *messageHash = [pendingMsg messageHash];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSDate *silenceDate = [ud objectForKey:silencedNotificationsKey][messageHash];
if ([silenceDate isKindOfClass:[NSDate class]]) {
NSDate *oneDayAgo = [NSDate dateWithTimeIntervalSinceNow:-86400];
if ([silenceDate compare:[NSDate date]] == NSOrderedDescending) {
LOGI(@"Notification silence: date is in the future, ignoring");
[self updateSilenceDate:nil forHash:messageHash];
} else if ([silenceDate compare:oneDayAgo] == NSOrderedAscending) {
LOGI(@"Notification silence: date is more than one day ago, ignoring");
[self updateSilenceDate:nil forHash:messageHash];
} else {
LOGI(@"Notification silence: dropping notification for %@", messageHash);
return;
dispatch_async(dispatch_get_main_queue(), ^{
if ([self notificationAlreadyQueued:pendingMsg]) return;
// See if this message has been user-silenced.
NSString *messageHash = [pendingMsg messageHash];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSDate *silenceDate = [ud objectForKey:silencedNotificationsKey][messageHash];
if ([silenceDate isKindOfClass:[NSDate class]]) {
NSDate *oneDayAgo = [NSDate dateWithTimeIntervalSinceNow:-86400];
if ([silenceDate compare:[NSDate date]] == NSOrderedDescending) {
LOGI(@"Notification silence: date is in the future, ignoring");
[self updateSilenceDate:nil forHash:messageHash];
} else if ([silenceDate compare:oneDayAgo] == NSOrderedAscending) {
LOGI(@"Notification silence: date is more than one day ago, ignoring");
[self updateSilenceDate:nil forHash:messageHash];
} else {
LOGI(@"Notification silence: dropping notification for %@", messageHash);
return;
}
}
}
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
if (!self.currentWindowController) {
[self showQueuedWindow];
}
if (!self.currentWindowController) {
// Add app to Cmd+Tab and Dock.
NSApp.activationPolicy = NSApplicationActivationPolicyRegular;
[self showQueuedWindow];
}
});
}
// For blocked execution notifications, post an NSDistributedNotificationCenter

View File

@@ -28,24 +28,22 @@
- (OSSystemExtensionReplacementAction)request:(OSSystemExtensionRequest *)request
actionForReplacingExtension:(OSSystemExtensionProperties *)old
withExtension:
(OSSystemExtensionProperties *)new API_AVAILABLE(macos(10.15)) {
withExtension:(OSSystemExtensionProperties *)new {
NSLog(@"SystemExtension \"%@\" request for replacement", request.identifier);
return OSSystemExtensionReplacementActionReplace;
}
- (void)requestNeedsUserApproval:(OSSystemExtensionRequest *)request API_AVAILABLE(macos(10.15)) {
- (void)requestNeedsUserApproval:(OSSystemExtensionRequest *)request {
NSLog(@"SystemExtension \"%@\" request needs user approval", request.identifier);
}
- (void)request:(OSSystemExtensionRequest *)request
didFailWithError:(NSError *)error API_AVAILABLE(macos(10.15)) {
- (void)request:(OSSystemExtensionRequest *)request didFailWithError:(NSError *)error {
NSLog(@"SystemExtension \"%@\" request did fail: %@", request.identifier, error);
exit((int)error.code);
}
- (void)request:(OSSystemExtensionRequest *)request
didFinishWithResult:(OSSystemExtensionRequestResult)result API_AVAILABLE(macos(10.15)) {
didFinishWithResult:(OSSystemExtensionRequestResult)result {
NSLog(@"SystemExtension \"%@\" request did finish: %ld", request.identifier, (long)result);
exit(0);
}

View File

@@ -19,6 +19,7 @@ objc_library(
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SigningIDHelpers",
"@FMDB",
"@MOLCodesignChecker",
"@MOLXPCConnection",
@@ -34,7 +35,7 @@ macos_command_line_application(
"--options library,kill,runtime",
],
infoplists = ["Info.plist"],
minimum_os_version = "11.0",
minimum_os_version = "12.0",
provisioning_profile = select({
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",

View File

@@ -25,6 +25,7 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/common/SigningIDHelpers.h"
@interface SNTBundleService ()
@property MOLXPCConnection *notifierConnection;
@@ -228,13 +229,7 @@
se.signingChain = cs.certificates;
se.cdhash = cs.cdhash;
se.teamID = cs.teamID;
if (cs.signingID) {
if (cs.teamID) {
se.signingID = [NSString stringWithFormat:@"%@:%@", cs.teamID, cs.signingID];
} else if (cs.platformBinary) {
se.signingID = [NSString stringWithFormat:@"platform:%@", cs.signingID];
}
}
se.signingID = FormatSigningID(cs);
dispatch_sync(dispatch_get_main_queue(), ^{
relatedEvents[se.fileSHA256] = se;

View File

@@ -76,6 +76,7 @@ objc_library(
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/common:SigningIDHelpers",
"//Source/santasyncservice:sync_lib",
"@FMDB",
"@MOLCertificate",
@@ -93,7 +94,7 @@ macos_command_line_application(
"--options library,kill,runtime",
],
infoplists = ["Info.plist"],
minimum_os_version = "11.0",
minimum_os_version = "12.0",
provisioning_profile = select({
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
@@ -121,6 +122,7 @@ santa_unit_test(
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SigningIDHelpers",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",

View File

@@ -26,6 +26,7 @@
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/common/SigningIDHelpers.h"
#import "Source/santactl/SNTCommand.h"
#import "Source/santactl/SNTCommandController.h"
@@ -84,6 +85,7 @@ NSString *formattedStringForKeyArray(NSArray<NSString *> *array) {
@property(nonatomic) BOOL recursive;
@property(nonatomic) BOOL jsonOutput;
@property(nonatomic) BOOL bundleInfo;
@property(nonatomic) BOOL filterInclusive;
@property(nonatomic) NSNumber *certIndex;
@property(nonatomic, copy) NSArray<NSString *> *outputKeyList;
@property(nonatomic, copy) NSDictionary<NSString *, NSRegularExpression *> *outputFilters;
@@ -188,6 +190,10 @@ REGISTER_COMMAND_NAME(@"fileinfo")
@" case-insensitive regular expression which must match anywhere in\n"
@" the keyed property value for the file's info to be displayed.\n"
@" You may specify multiple filters by repeating this flag.\n"
@" If multiple filters are specified, any match will display the\n"
@" file.\n"
@" --filter-inclusive: If multiple filters are specified, they must all match\n"
@" for the file to be displayed.\n"
@" --bundleinfo: If the file is part of a bundle, will also display bundle\n"
@" hash information and hashes of all bundle executables.\n"
@" Incompatible with --recursive and --cert-index.\n"
@@ -382,16 +388,7 @@ REGISTER_COMMAND_NAME(@"fileinfo")
NSString *cdhash = csc.cdhash;
NSString *teamID = csc.teamID;
NSString *identifier = csc.signingID;
NSString *signingID;
if (identifier) {
if (teamID) {
signingID = [NSString stringWithFormat:@"%@:%@", teamID, identifier];
} else if (csc.platformBinary) {
signingID = [NSString stringWithFormat:@"platform:%@", identifier];
}
}
NSString *signingID = FormatSigningID(csc);
struct RuleIdentifiers identifiers = {
.cdhash = cdhash,
@@ -523,15 +520,7 @@ REGISTER_COMMAND_NAME(@"fileinfo")
return ^id(SNTCommandFileInfo *cmd, SNTFileInfo *fileInfo) {
MOLCodesignChecker *csc = [fileInfo codesignCheckerWithError:NULL];
NSString *identifier = csc.signingID;
NSString *teamID = csc.teamID;
if (!identifier) return nil;
if (teamID) {
return [NSString stringWithFormat:@"%@:%@", teamID, identifier];
} else if (csc.platformBinary) {
return [NSString stringWithFormat:@"platform:%@", identifier];
}
return nil;
return FormatSigningID(csc);
};
}
@@ -616,6 +605,31 @@ REGISTER_COMMAND_NAME(@"fileinfo")
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.qualityOfService = NSQualityOfServiceUserInitiated;
// Limit the number of concurrent operations to 2. By default it is unlimited. Querying for the
// `Rule` results in an XPC message to the santa daemon. On an M1 Max we are
// seeing issues with dropped XPC messages when there are 64 or more in-flight messages. The
// number of in-flight requests to the santa daemon to will be capped to
// `maxConcurrentOperationCount`.
//
// Why 2? We are seeing diminishing wall-time improvements for anything over 2 Qs.
//
// 1 Q
// bazel run //Source/santactl -- fileinfo --recursive --key Path --key Rule /usr/libexec/
// 1.16s user 0.92s system 35% cpu 5.775 total
// 2 Qs
// bazel run //Source/santactl -- fileinfo --recursive --key Path --key Rule /usr/libexec/
// 1.22s user 1.07s system 62% cpu 3.675 total
// 4 Qs
// bazel run //Source/santactl -- fileinfo --recursive --key Path --key Rule /usr/libexec/
// 1.22s user 1.16s system 72% cpu 3.275 total
// 8 Qs
// bazel run //Source/santactl -- fileinfo --recursive --key Path --key Rule /usr/libexec/
// 1.25s user 1.26s system 75% cpu 3.304 total
operationQueue.maxConcurrentOperationCount = 2;
if (isDir && self.recursive) {
NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath:path];
NSString *file = [dirEnum nextObject];
@@ -645,8 +659,29 @@ REGISTER_COMMAND_NAME(@"fileinfo")
[operationQueue waitUntilAllOperationsAreFinished];
}
- (BOOL)shouldOutputValueToDictionary:(NSMutableDictionary *)outputDict
valueForKey:(NSString * (^)(NSString *key))valueForKey {
if (self.outputFilters.count == 0) return YES;
int matches = 0;
for (NSString *key in self.outputFilters) {
NSString *value = valueForKey(key);
NSRegularExpression *regex = self.outputFilters[key];
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) continue;
// If this is a value we want to show, store it in the output dictionary.
// This does a linear search on an array, but it's a small array.
if (outputDict && value.length && [self.outputKeyList containsObject:key]) {
outputDict[key] = value;
}
++matches;
}
return self.filterInclusive ? matches == self.outputFilters.count : matches > 0;
}
// Prints out the info for a single (non-directory) file. Which info is printed is controlled
// by the keys in self.outputKeyList.
// TODO: Refactor so this method is testable.
- (void)printInfoForFile:(NSString *)path {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
if (!fileInfo) {
@@ -680,41 +715,31 @@ REGISTER_COMMAND_NAME(@"fileinfo")
NSDictionary *cert = signingChain[index];
// Check if we should skip over this item based on outputFilters.
BOOL filterMatch = self.outputFilters.count == 0;
for (NSString *key in self.outputFilters) {
NSString *value = cert[key] ?: @"";
NSRegularExpression *regex = self.outputFilters[key];
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) continue;
filterMatch = YES;
break;
BOOL shouldOutput = [self shouldOutputValueToDictionary:nil
valueForKey:^NSString *(NSString *key) {
return cert[key] ?: @"";
}];
if (!shouldOutput) {
return;
}
if (!filterMatch) return;
// Filter out the info we want now, in case JSON output
for (NSString *key in self.outputKeyList) {
outputDict[key] = cert[key];
}
} else {
// Check if we should skip over this item based on outputFilters. We do this before collecting
// Check if we should skip over this item based on outputFilters. We do this before collecting
// output info because there's a chance that we can bail out early if a filter doesn't match.
// However we also don't want to recompute info, so we save any values that we plan to show.
BOOL filterMatch = self.outputFilters.count == 0;
for (NSString *key in self.outputFilters) {
NSString *value = self.propertyMap[key](self, fileInfo) ?: @"";
NSRegularExpression *regex = self.outputFilters[key];
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) continue;
// If this is a value we want to show, store it in the output dictionary.
// This does a linear search on an array, but it's a small array.
if (value.length && [self.outputKeyList containsObject:key]) {
outputDict[key] = value;
}
filterMatch = YES;
break;
BOOL shouldOutput =
[self shouldOutputValueToDictionary:outputDict
valueForKey:^NSString *(NSString *key) {
return self.propertyMap[key](self, fileInfo) ?: @"";
}];
if (!shouldOutput) {
return;
}
if (!filterMatch) return;
// Then fill the outputDict with the rest of the missing values.
for (NSString *key in self.outputKeyList) {
if (outputDict[key]) continue; // ignore keys that we've already set due to a filter
@@ -887,6 +912,8 @@ REGISTER_COMMAND_NAME(@"fileinfo")
@"\n--bundleinfo is incompatible with --recursive and --cert-index"];
}
self.bundleInfo = YES;
} else if ([arg caseInsensitiveCompare:@"--filter-inclusive"] == NSOrderedSame) {
self.filterInclusive = YES;
} else {
[paths addObject:arg];
}

View File

@@ -24,6 +24,7 @@
typedef id (^SNTAttributeBlock)(SNTCommandFileInfo *, SNTFileInfo *);
@property(nonatomic) BOOL recursive;
@property(nonatomic) BOOL jsonOutput;
@property(nonatomic) BOOL filterInclusive;
@property(nonatomic) NSNumber *certIndex;
@property(nonatomic, copy) NSArray<NSString *> *outputKeyList;
@property(nonatomic) NSDictionary<NSString *, SNTAttributeBlock> *propertyMap;
@@ -250,4 +251,10 @@ typedef id (^SNTAttributeBlock)(SNTCommandFileInfo *, SNTFileInfo *);
XCTAssertEqualObjects(self.cfi.codeSigned(self.cfi, self.fileInfo), expected);
}
- (void)testParseArgumentsFilterInclusiveTrue {
NSArray *filePaths = [self.cfi parseArguments:@[ @"--filter-inclusive", @"/usr/bin/yes" ]];
XCTAssertTrue(self.cfi.filterInclusive);
XCTAssertTrue([filePaths containsObject:@"/usr/bin/yes"]);
}
@end

View File

@@ -205,6 +205,7 @@ objc_library(
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTRuleIdentifiers",
"//Source/common:SigningIDHelpers",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
@@ -334,6 +335,7 @@ objc_library(
":SNTCompilerController",
":SNTEndpointSecurityEventHandler",
":SNTEndpointSecurityTreeAwareClient",
"//Source/common:Platform",
"//Source/common:PrefixTree",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
@@ -471,8 +473,10 @@ objc_library(
hdrs = ["EventProviders/EndpointSecurity/Enricher.h"],
deps = [
":EndpointSecurityEnrichedTypes",
"//Source/common:Platform",
"//Source/common:SNTLogging",
"//Source/common:SantaCache",
"//Source/common:String",
"//Source/santad/ProcessTree:SNTEndpointSecurityAdapter",
"//Source/santad/ProcessTree:process_tree",
],
@@ -551,6 +555,7 @@ objc_library(
":EndpointSecuritySerializer",
":EndpointSecuritySerializerUtilities",
":SNTDecisionCache",
"//Source/common:Platform",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
@@ -566,6 +571,7 @@ objc_library(
":EndpointSecuritySerializer",
":EndpointSecuritySerializerUtilities",
":SNTDecisionCache",
"//Source/common:Platform",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
@@ -721,6 +727,7 @@ objc_library(
deps = [
":EndpointSecurityMessage",
":SNTApplicationCoreMetrics",
"//Source/common:Platform",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
@@ -816,6 +823,7 @@ objc_library(
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SystemResources",
],
@@ -837,7 +845,7 @@ macos_bundle(
}),
infoplists = ["Info.plist"],
linkopts = ["-execute"],
minimum_os_version = "11.0",
minimum_os_version = "12.0",
provisioning_profile = select({
"//:adhoc_build": None,
"//conditions:default": "//profiles:daemon_dev",
@@ -928,7 +936,6 @@ santa_unit_test(
santa_unit_test(
name = "SantadTest",
srcs = ["SantadTest.mm"],
minimum_os_version = "11.0",
sdk_dylibs = [
"bsm",
"EndpointSecurity",
@@ -964,7 +971,6 @@ santa_unit_test(
srcs = [
"SNTApplicationCoreMetricsTest.mm",
],
minimum_os_version = "11.0",
deps = [
":SNTApplicationCoreMetrics",
"//Source/common:SNTCommonEnums",
@@ -1017,6 +1023,7 @@ santa_unit_test(
":EndpointSecuritySerializerBasicString",
":MockEndpointSecurityAPI",
":SNTDecisionCache",
"//Source/common:Platform",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
@@ -1041,6 +1048,7 @@ santa_unit_test(
":EndpointSecuritySerializerProtobuf",
":MockEndpointSecurityAPI",
":SNTDecisionCache",
"//Source/common:Platform",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
@@ -1353,6 +1361,7 @@ santa_unit_test(
":MockEndpointSecurityAPI",
":SNTCompilerController",
":SNTEndpointSecurityRecorder",
"//Source/common:Platform",
"//Source/common:PrefixTree",
"//Source/common:SNTConfigurator",
"//Source/common:TestUtils",

View File

@@ -33,13 +33,7 @@ static const int64_t kTransitiveRuleCullingThreshold = 500000;
// Consider transitive rules out of date if they haven't been used in six months.
static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABLE(macos(12.0)) {
// Note: This function uses API introduced in macOS 12, but we want to continue to support
// building in older environments. API Availability checks do not help for this use case,
// instead we use the following preprocessor macros to conditionally compile these API. The
// drawback here is that if a pre-macOS 12 SDK is used to build Santa and it is then deployed
// on macOS 12 or later, the dynamic mute set will not be computed.
#if HAVE_MACOS_12
static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) {
// Create a temporary ES client in order to grab the default set of muted paths.
// TODO(mlw): Reorganize this code so that a temporary ES client doesn't need to be created
es_client_t *client = NULL;
@@ -69,7 +63,6 @@ static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABL
es_release_muted_paths(mps);
es_delete_client(client);
#endif
}
@interface SNTRuleTable ()
@@ -125,10 +118,8 @@ static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABL
NSMutableSet *superSet = [NSMutableSet setWithSet:fallbackDefaultMuteSet];
[superSet unionSet:santaDefinedCriticalPaths];
if (@available(macOS 12.0, *)) {
// Attempt to add the real default mute set
addPathsFromDefaultMuteSet(superSet);
}
// Attempt to add the real default mute set
addPathsFromDefaultMuteSet(superSet);
criticalPaths = [superSet allObjects];
});

View File

@@ -23,7 +23,7 @@
#include <string_view>
#include <vector>
namespace santa::santad::data_layer {
namespace santa {
enum class WatchItemPathType {
kPrefix,
@@ -117,6 +117,6 @@ struct WatchItemPolicy {
std::string version = "temp_version";
};
} // namespace santa::santad::data_layer
} // namespace santa
#endif

View File

@@ -52,11 +52,11 @@ extern NSString *const kWatchItemConfigKeyProcessesCDHash;
extern NSString *const kWatchItemConfigKeyProcessesPlatformBinary;
// Forward declarations
namespace santa::santad::data_layer {
namespace santa {
class WatchItemsPeer;
}
namespace santa::santad::data_layer {
namespace santa {
struct WatchItemsState {
uint64_t rule_count;
@@ -69,7 +69,7 @@ class WatchItems : public std::enable_shared_from_this<WatchItems> {
public:
using VersionAndPolicies =
std::pair<std::string, std::vector<std::optional<std::shared_ptr<WatchItemPolicy>>>>;
using WatchItemsTree = santa::common::PrefixTree<std::shared_ptr<WatchItemPolicy>>;
using WatchItemsTree = santa::PrefixTree<std::shared_ptr<WatchItemPolicy>>;
// Factory
static std::shared_ptr<WatchItems> Create(NSString *config_path,
@@ -99,7 +99,7 @@ class WatchItems : public std::enable_shared_from_this<WatchItems> {
std::pair<NSString *, NSString *> EventDetailLinkInfo(
const std::shared_ptr<WatchItemPolicy> &watch_item);
friend class santa::santad::data_layer::WatchItemsPeer;
friend class santa::WatchItemsPeer;
private:
static std::shared_ptr<WatchItems> CreateInternal(NSString *config_path, NSDictionary *config,
@@ -135,6 +135,6 @@ class WatchItems : public std::enable_shared_from_this<WatchItems> {
NSString *policy_event_detail_text_ ABSL_GUARDED_BY(lock_);
};
} // namespace santa::santad::data_layer
} // namespace santa
#endif

View File

@@ -38,12 +38,12 @@
#import "Source/common/Unit.h"
#include "Source/santad/DataLayer/WatchItemPolicy.h"
using santa::common::NSStringToUTF8String;
using santa::common::NSStringToUTF8StringView;
using santa::common::PrefixTree;
using santa::common::Unit;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::data_layer::WatchItemPolicy;
using santa::NSStringToUTF8String;
using santa::NSStringToUTF8StringView;
using santa::PrefixTree;
using santa::Unit;
using santa::WatchItemPathType;
using santa::WatchItemPolicy;
NSString *const kWatchItemConfigKeyVersion = @"Version";
NSString *const kWatchItemConfigKeyEventDetailURL = @"EventDetailURL";
@@ -96,13 +96,15 @@ static constexpr NSUInteger kWatchItemConfigEventDetailTextMaxLength = 48;
// max is used here.
static constexpr NSUInteger kWatchItemConfigEventDetailURLMaxLength = 6000;
namespace santa::santad::data_layer {
namespace santa {
namespace {
// Type aliases
using ValidatorBlock = bool (^)(id, NSError **);
using PathAndTypePair = std::pair<std::string, WatchItemPathType>;
using PathList = std::vector<PathAndTypePair>;
using ProcessList = std::vector<WatchItemPolicy::Process>;
using PathAndTypeVec = std::vector<PathAndTypePair>;
using PolicyProcessVec = std::vector<WatchItemPolicy::Process>;
} // namespace
static void PopulateError(NSError **err, NSString *msg) {
if (err) {
@@ -265,8 +267,8 @@ bool VerifyConfigKeyArray(NSDictionary *dict, NSString *key, Class expected, NSE
/// <true/>
/// </dict>
/// </array>
std::variant<Unit, PathList> VerifyConfigWatchItemPaths(NSArray<id> *paths, NSError **err) {
PathList path_list;
std::variant<Unit, PathAndTypeVec> VerifyConfigWatchItemPaths(NSArray<id> *paths, NSError **err) {
PathAndTypeVec path_list;
for (id path in paths) {
if ([path isKindOfClass:[NSDictionary class]]) {
@@ -336,9 +338,9 @@ std::variant<Unit, PathList> VerifyConfigWatchItemPaths(NSArray<id> *paths, NSEr
/// <string>EEEE</string>
/// </dict>
/// </array>
std::variant<Unit, ProcessList> VerifyConfigWatchItemProcesses(NSDictionary *watch_item,
NSError **err) {
__block ProcessList proc_list;
std::variant<Unit, PolicyProcessVec> VerifyConfigWatchItemProcesses(NSDictionary *watch_item,
NSError **err) {
__block PolicyProcessVec proc_list;
if (!VerifyConfigKeyArray(
watch_item, kWatchItemConfigKeyProcesses, [NSDictionary class], err,
@@ -429,7 +431,7 @@ bool ParseConfigSingleWatchItem(NSString *name, NSDictionary *watch_item,
return false;
}
std::variant<Unit, PathList> path_list =
std::variant<Unit, PathAndTypeVec> path_list =
VerifyConfigWatchItemPaths(watch_item[kWatchItemConfigKeyPaths], err);
if (std::holds_alternative<Unit>(path_list)) {
@@ -485,19 +487,19 @@ bool ParseConfigSingleWatchItem(NSString *name, NSDictionary *watch_item,
bool enable_silent_tty_mode = GetBoolValue(options, kWatchItemConfigKeyOptionsEnableSilentTTYMode,
kWatchItemPolicyDefaultEnableSilentTTYMode);
std::variant<Unit, ProcessList> proc_list = VerifyConfigWatchItemProcesses(watch_item, err);
std::variant<Unit, PolicyProcessVec> proc_list = VerifyConfigWatchItemProcesses(watch_item, err);
if (std::holds_alternative<Unit>(proc_list)) {
return false;
}
for (const PathAndTypePair &path_type_pair : std::get<PathList>(path_list)) {
for (const PathAndTypePair &path_type_pair : std::get<PathAndTypeVec>(path_list)) {
policies.push_back(std::make_shared<WatchItemPolicy>(
NSStringToUTF8StringView(name), path_type_pair.first, path_type_pair.second,
allow_read_access, audit_only, invert_process_exceptions, enable_silent_mode,
enable_silent_tty_mode,
NSStringToUTF8StringView(options[kWatchItemConfigKeyOptionsCustomMessage]),
options[kWatchItemConfigKeyOptionsEventDetailURL],
options[kWatchItemConfigKeyOptionsEventDetailText], std::get<ProcessList>(proc_list)));
options[kWatchItemConfigKeyOptionsEventDetailText], std::get<PolicyProcessVec>(proc_list)));
}
return true;
@@ -897,4 +899,4 @@ std::pair<NSString *, NSString *> WatchItems::EventDetailLinkInfo(
return {url, text};
}
} // namespace santa::santad::data_layer
} // namespace santa

View File

@@ -32,23 +32,23 @@
#include "Source/santad/DataLayer/WatchItemPolicy.h"
#include "Source/santad/DataLayer/WatchItems.h"
using santa::common::Unit;
using santa::santad::data_layer::kWatchItemPolicyDefaultAllowReadAccess;
using santa::santad::data_layer::kWatchItemPolicyDefaultAuditOnly;
using santa::santad::data_layer::kWatchItemPolicyDefaultInvertProcessExceptions;
using santa::santad::data_layer::kWatchItemPolicyDefaultPathType;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::data_layer::WatchItemPolicy;
using santa::santad::data_layer::WatchItems;
using santa::santad::data_layer::WatchItemsState;
using santa::kWatchItemPolicyDefaultAllowReadAccess;
using santa::kWatchItemPolicyDefaultAuditOnly;
using santa::kWatchItemPolicyDefaultInvertProcessExceptions;
using santa::kWatchItemPolicyDefaultPathType;
using santa::Unit;
using santa::WatchItemPathType;
using santa::WatchItemPolicy;
using santa::WatchItems;
using santa::WatchItemsState;
namespace santatest {
namespace {
using PathAndTypePair = std::pair<std::string, WatchItemPathType>;
using PathList = std::vector<PathAndTypePair>;
using ProcessList = std::vector<WatchItemPolicy::Process>;
} // namespace santatest
using PathAndTypeVec = std::vector<PathAndTypePair>;
using PolicyProcessVec = std::vector<WatchItemPolicy::Process>;
} // namespace
namespace santa::santad::data_layer {
namespace santa {
extern bool ParseConfig(NSDictionary *config,
std::vector<std::shared_ptr<WatchItemPolicy>> &policies, NSError **err);
@@ -56,10 +56,10 @@ extern bool IsWatchItemNameValid(NSString *watch_item_name, NSError **err);
extern bool ParseConfigSingleWatchItem(NSString *name, NSDictionary *watch_item,
std::vector<std::shared_ptr<WatchItemPolicy>> &policies,
NSError **err);
extern std::variant<Unit, santatest::PathList> VerifyConfigWatchItemPaths(NSArray<id> *paths,
NSError **err);
extern std::variant<Unit, santatest::ProcessList> VerifyConfigWatchItemProcesses(
NSDictionary *watch_item, NSError **err);
extern std::variant<Unit, PathAndTypeVec> VerifyConfigWatchItemPaths(NSArray<id> *paths,
NSError **err);
extern std::variant<Unit, PolicyProcessVec> VerifyConfigWatchItemProcesses(NSDictionary *watch_item,
NSError **err);
class WatchItemsPeer : public WatchItems {
public:
using WatchItems::WatchItems;
@@ -72,14 +72,14 @@ class WatchItemsPeer : public WatchItems {
using WatchItems::embedded_config_;
};
} // namespace santa::santad::data_layer
} // namespace santa
using santa::santad::data_layer::IsWatchItemNameValid;
using santa::santad::data_layer::ParseConfig;
using santa::santad::data_layer::ParseConfigSingleWatchItem;
using santa::santad::data_layer::VerifyConfigWatchItemPaths;
using santa::santad::data_layer::VerifyConfigWatchItemProcesses;
using santa::santad::data_layer::WatchItemsPeer;
using santa::IsWatchItemNameValid;
using santa::ParseConfig;
using santa::ParseConfigSingleWatchItem;
using santa::VerifyConfigWatchItemPaths;
using santa::VerifyConfigWatchItemProcesses;
using santa::WatchItemsPeer;
static constexpr std::string_view kBadPolicyName("__BAD_NAME__");
static constexpr std::string_view kBadPolicyPath("__BAD_PATH__");
@@ -440,7 +440,7 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
}
- (void)testVerifyConfigWatchItemPaths {
std::variant<Unit, santatest::PathList> path_list;
std::variant<Unit, PathAndTypeVec> path_list;
NSError *err;
// Test no paths specified
@@ -466,29 +466,28 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
// Test path array dictionary with default path type
path_list = VerifyConfigWatchItemPaths(@[ @{kWatchItemConfigKeyPathsPath : @"A"} ], &err);
XCTAssertTrue(std::holds_alternative<santatest::PathList>(path_list));
XCTAssertEqual(std::get<santatest::PathList>(path_list).size(), 1);
XCTAssertCStringEqual(std::get<santatest::PathList>(path_list)[0].first.c_str(), "A");
XCTAssertEqual(std::get<santatest::PathList>(path_list)[0].second,
kWatchItemPolicyDefaultPathType);
XCTAssertTrue(std::holds_alternative<PathAndTypeVec>(path_list));
XCTAssertEqual(std::get<PathAndTypeVec>(path_list).size(), 1);
XCTAssertCStringEqual(std::get<PathAndTypeVec>(path_list)[0].first.c_str(), "A");
XCTAssertEqual(std::get<PathAndTypeVec>(path_list)[0].second, kWatchItemPolicyDefaultPathType);
// Test path array dictionary with custom path type
path_list = VerifyConfigWatchItemPaths(
@[ @{kWatchItemConfigKeyPathsPath : @"A", kWatchItemConfigKeyPathsIsPrefix : @(YES)} ], &err);
XCTAssertTrue(std::holds_alternative<santatest::PathList>(path_list));
XCTAssertEqual(std::get<santatest::PathList>(path_list).size(), 1);
XCTAssertCStringEqual(std::get<santatest::PathList>(path_list)[0].first.c_str(), "A");
XCTAssertEqual(std::get<santatest::PathList>(path_list)[0].second, WatchItemPathType::kPrefix);
XCTAssertTrue(std::holds_alternative<PathAndTypeVec>(path_list));
XCTAssertEqual(std::get<PathAndTypeVec>(path_list).size(), 1);
XCTAssertCStringEqual(std::get<PathAndTypeVec>(path_list)[0].first.c_str(), "A");
XCTAssertEqual(std::get<PathAndTypeVec>(path_list)[0].second, WatchItemPathType::kPrefix);
}
- (void)testVerifyConfigWatchItemProcesses {
std::variant<Unit, santatest::ProcessList> proc_list;
std::variant<Unit, PolicyProcessVec> proc_list;
NSError *err;
// Non-existent process list parses successfully, but has no items
proc_list = VerifyConfigWatchItemProcesses(@{}, &err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 0);
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 0);
// Process list fails to parse if contains non-array type
proc_list = VerifyConfigWatchItemProcesses(@{kWatchItemConfigKeyProcesses : @""}, &err);
@@ -498,7 +497,7 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
proc_list = VerifyConfigWatchItemProcesses(@{kWatchItemConfigKeyProcesses : @{}}, &err);
XCTAssertTrue(std::holds_alternative<Unit>(proc_list));
proc_list = VerifyConfigWatchItemProcesses(@{kWatchItemConfigKeyProcesses : @[]}, &err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
// Test a process dictionary with no valid attributes set
proc_list = VerifyConfigWatchItemProcesses(@{kWatchItemConfigKeyProcesses : @[ @{} ]}, &err);
@@ -516,9 +515,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
proc_list = VerifyConfigWatchItemProcesses(
@{kWatchItemConfigKeyProcesses : @[ @{kWatchItemConfigKeyProcessesBinaryPath : @"mypath"} ]},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("mypath", "", "", {}, "", std::nullopt));
// Test SigningID length limits
@@ -535,9 +534,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
@[ @{kWatchItemConfigKeyProcessesSigningID : @"com.google.test"} ]
},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("", "com.google.test", "", {}, "", std::nullopt));
// Test TeamID length limits
@@ -552,9 +551,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
proc_list = VerifyConfigWatchItemProcesses(
@{kWatchItemConfigKeyProcesses : @[ @{kWatchItemConfigKeyProcessesTeamID : @"myvalidtid"} ]},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("", "", "myvalidtid", {}, "", std::nullopt));
// Test CDHash length limits
@@ -579,9 +578,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
std::fill(cdhashBytes.begin(), cdhashBytes.end(), 0xAA);
proc_list = VerifyConfigWatchItemProcesses(
@{kWatchItemConfigKeyProcesses : @[ @{kWatchItemConfigKeyProcessesCDHash : cdhash} ]}, &err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("", "", "", cdhashBytes, "", std::nullopt));
// Test Cert Hash length limits
@@ -610,9 +609,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
kWatchItemConfigKeyProcesses : @[ @{kWatchItemConfigKeyProcessesCertificateSha256 : certHash} ]
},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("", "", "", {}, [certHash UTF8String], std::nullopt));
// Test valid invalid PlatformBinary type
@@ -625,9 +624,9 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
proc_list = VerifyConfigWatchItemProcesses(
@{kWatchItemConfigKeyProcesses : @[ @{kWatchItemConfigKeyProcessesPlatformBinary : @(YES)} ]},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 1);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 1);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("", "", "", {}, "", std::make_optional(true)));
// Test valid multiple attributes, multiple procs
@@ -652,12 +651,12 @@ static NSMutableDictionary *WrapWatchItemsConfig(NSDictionary *config) {
]
},
&err);
XCTAssertTrue(std::holds_alternative<santatest::ProcessList>(proc_list));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list).size(), 2);
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[0],
XCTAssertTrue(std::holds_alternative<PolicyProcessVec>(proc_list));
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list).size(), 2);
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[0],
WatchItemPolicy::Process("mypath1", "com.google.test1", "validtid_1", cdhashBytes,
[certHash UTF8String], std::make_optional(true)));
XCTAssertEqual(std::get<santatest::ProcessList>(proc_list)[1],
XCTAssertEqual(std::get<PolicyProcessVec>(proc_list)[1],
WatchItemPolicy::Process("mypath2", "com.google.test2", "validtid_2", cdhashBytes,
[certHash UTF8String], std::make_optional(false)));
}

View File

@@ -27,7 +27,7 @@
#import "Source/common/SantaVnode.h"
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
namespace santa::santad::event_providers {
namespace santa {
enum class FlushCacheMode {
kNonRootOnly,
@@ -52,13 +52,12 @@ class AuthResultCache {
// previously denied binary is allowed, it can be re-executed by the user in a
// timely manner. But the value should be high enough to allow the cache to be
// effective in the event the binary is executed in rapid succession.
static std::unique_ptr<AuthResultCache> Create(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTMetricSet *metric_set, uint64_t cache_deny_time_ms = 1500);
static std::unique_ptr<AuthResultCache> Create(std::shared_ptr<santa::EndpointSecurityAPI> esapi,
SNTMetricSet *metric_set,
uint64_t cache_deny_time_ms = 1500);
AuthResultCache(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTMetricCounter *flush_count, uint64_t cache_deny_time_ms = 1500);
AuthResultCache(std::shared_ptr<santa::EndpointSecurityAPI> esapi, SNTMetricCounter *flush_count,
uint64_t cache_deny_time_ms = 1500);
virtual ~AuthResultCache();
AuthResultCache(AuthResultCache &&other) = delete;
@@ -81,13 +80,13 @@ class AuthResultCache {
SantaCache<SantaVnode, uint64_t> *root_cache_;
SantaCache<SantaVnode, uint64_t> *nonroot_cache_;
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi_;
std::shared_ptr<santa::EndpointSecurityAPI> esapi_;
SNTMetricCounter *flush_count_;
uint64_t root_devno_;
uint64_t cache_deny_time_ns_;
dispatch_queue_t q_;
};
} // namespace santa::santad::event_providers
} // namespace santa
#endif

View File

@@ -22,8 +22,8 @@
#import "Source/common/SantaVnodeHash.h"
#include "Source/santad/EventProviders/EndpointSecurity/Client.h"
using santa::santad::event_providers::endpoint_security::Client;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::Client;
using santa::EndpointSecurityAPI;
static NSString *const kFlushCacheReasonClientModeChanged = @"ClientModeChanged";
static NSString *const kFlushCacheReasonPathRegexChanged = @"PathRegexChanged";
@@ -36,7 +36,7 @@ static NSString *const kFlushCacheReasonEntitlementsPrefixFilterChanged =
static NSString *const kFlushCacheReasonEntitlementsTeamIDFilterChanged =
@"EntitlementsTeamIDFilterChanged";
namespace santa::santad::event_providers {
namespace santa {
static inline uint64_t GetCurrentUptime() {
return clock_gettime_nsec_np(CLOCK_MONOTONIC);
@@ -185,4 +185,4 @@ NSArray<NSNumber *> *AuthResultCache::CacheCounts() {
return @[ @(root_cache_->count()), @(nonroot_cache_->count()) ];
}
} // namespace santa::santad::event_providers
} // namespace santa

View File

@@ -27,15 +27,15 @@
#include "Source/santad/EventProviders/AuthResultCache.h"
#include "Source/santad/EventProviders/EndpointSecurity/MockEndpointSecurityAPI.h"
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::FlushCacheMode;
using santa::santad::event_providers::FlushCacheReason;
using santa::AuthResultCache;
using santa::FlushCacheMode;
using santa::FlushCacheReason;
namespace santa::santad::event_providers {
namespace santa {
extern NSString *const FlushCacheReasonToString(FlushCacheReason reason);
}
} // namespace santa
using santa::santad::event_providers::FlushCacheReasonToString;
using santa::FlushCacheReasonToString;
// Grab the st_dev number of the root volume to match the root cache
static uint64_t RootDevno() {

View File

@@ -150,15 +150,18 @@ void DARegisterDiskAppearedCallback(DASessionRef session, CFDictionaryRef __null
}];
}
// Disabling clang format due to local/remote version differences.
// clang-format off
void DARegisterDiskDisappearedCallback(DASessionRef session, CFDictionaryRef __nullable match,
DADiskDisappearedCallback callback,
void *__nullable context){};
void *__nullable context) {}
void DARegisterDiskDescriptionChangedCallback(DASessionRef session,
CFDictionaryRef __nullable match,
CFArrayRef __nullable watch,
DADiskDescriptionChangedCallback callback,
void *__nullable context){};
void *__nullable context) {}
// clang-format on
void DASessionSetDispatchQueue(DASessionRef session, dispatch_queue_t __nullable queue) {
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];

View File

@@ -19,7 +19,7 @@
#include <cstddef>
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
class Client {
public:
@@ -64,6 +64,6 @@ class Client {
es_new_client_result_t result_;
};
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa
#endif

View File

@@ -20,7 +20,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/Client.h"
using santa::santad::event_providers::endpoint_security::Client;
using santa::Client;
// Global semaphore used for custom `es_delete_client` function
dispatch_semaphore_t gSema;

View File

@@ -25,7 +25,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/Client.h"
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
class EndpointSecurityAPI : public std::enable_shared_from_this<EndpointSecurityAPI> {
public:
@@ -43,9 +43,9 @@ class EndpointSecurityAPI : public std::enable_shared_from_this<EndpointSecurity
virtual bool InvertTargetPathMuting(const Client &client);
virtual bool MuteTargetPath(const Client &client, std::string_view path,
santa::santad::data_layer::WatchItemPathType path_type);
santa::WatchItemPathType path_type);
virtual bool UnmuteTargetPath(const Client &client, std::string_view path,
santa::santad::data_layer::WatchItemPathType path_type);
santa::WatchItemPathType path_type);
virtual void RetainMessage(const es_message_t *msg);
virtual void ReleaseMessage(const es_message_t *msg);
@@ -69,6 +69,6 @@ class EndpointSecurityAPI : public std::enable_shared_from_this<EndpointSecurity
virtual const es_fd_t *ExecFD(const es_event_exec_t *event, uint32_t index);
};
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa
#endif

View File

@@ -19,9 +19,9 @@
#include "Source/common/Platform.h"
using santa::santad::data_layer::WatchItemPathType;
using santa::WatchItemPathType;
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
Client EndpointSecurityAPI::NewClient(void (^message_handler)(es_client_t *, Message)) {
es_client_t *client = NULL;
@@ -156,19 +156,11 @@ es_string_token_t EndpointSecurityAPI::ExecEnv(const es_event_exec_t *event, uin
}
uint32_t EndpointSecurityAPI::ExecFDCount(const es_event_exec_t *event) {
if (@available(macOS 11.0, *)) {
return es_exec_fd_count(event);
} else {
return 0;
}
return es_exec_fd_count(event);
}
const es_fd_t *EndpointSecurityAPI::ExecFD(const es_event_exec_t *event, uint32_t index) {
if (@available(macOS 11.0, *)) {
return es_exec_fd(event, index);
} else {
return NULL;
}
return es_exec_fd(event, index);
}
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa

View File

@@ -28,7 +28,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/ProcessTree/process_tree.pb.h"
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
class EnrichedFile {
public:
@@ -151,7 +151,8 @@ class EnrichedEventType {
virtual ~EnrichedEventType() = default;
const es_message_t &es_msg() const { return *es_msg_; }
inline const es_message_t *operator->() const { return es_msg_.operator->(); }
const EnrichedProcess &instigator() const { return instigator_; }
struct timespec enrichment_time() const {
// No reason to return a reference
@@ -340,10 +341,108 @@ class EnrichedCSInvalidated : public EnrichedEventType {
EnrichedCSInvalidated(const EnrichedCSInvalidated &other) = delete;
};
using EnrichedType =
std::variant<EnrichedClose, EnrichedExchange, EnrichedExec, EnrichedExit,
EnrichedFork, EnrichedLink, EnrichedRename, EnrichedUnlink,
EnrichedCSInvalidated>;
// Note: All EnrichedLoginWindowSession* classes currently have the same
// data and implementation. To improve maintainability but still provide
// individual types, an internal EnrichedLoginWindowSession base class is
// defined that is derived by each desired types.
// EnrichedLoginWindowSession is wrapped in an `internal` namespace as it
// shouldn't be directly used outside of this file.
namespace internal {
class EnrichedLoginWindowSession : public EnrichedEventType {
public:
EnrichedLoginWindowSession(Message &&es_msg, EnrichedProcess instigator,
std::optional<uid_t> uid)
: EnrichedEventType(std::move(es_msg), std::move(instigator)),
uid_(std::move(uid)) {}
EnrichedLoginWindowSession(EnrichedLoginWindowSession &&) = default;
virtual ~EnrichedLoginWindowSession() = default;
inline std::optional<uid_t> UID() const { return uid_; }
private:
std::optional<uid_t> uid_;
};
} // namespace internal
class EnrichedLoginWindowSessionLogin
: public internal::EnrichedLoginWindowSession {
using EnrichedLoginWindowSession::EnrichedLoginWindowSession;
};
class EnrichedLoginWindowSessionLogout
: public internal::EnrichedLoginWindowSession {
using EnrichedLoginWindowSession::EnrichedLoginWindowSession;
};
class EnrichedLoginWindowSessionLock
: public internal::EnrichedLoginWindowSession {
using EnrichedLoginWindowSession::EnrichedLoginWindowSession;
};
class EnrichedLoginWindowSessionUnlock
: public internal::EnrichedLoginWindowSession {
using EnrichedLoginWindowSession::EnrichedLoginWindowSession;
};
class EnrichedScreenSharingAttach : public EnrichedEventType {
public:
EnrichedScreenSharingAttach(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedScreenSharingAttach(EnrichedScreenSharingAttach &&) = default;
};
class EnrichedScreenSharingDetach : public EnrichedEventType {
public:
EnrichedScreenSharingDetach(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedScreenSharingDetach(EnrichedScreenSharingDetach &&) = default;
};
class EnrichedOpenSSHLogin : public EnrichedEventType {
public:
EnrichedOpenSSHLogin(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedOpenSSHLogin(EnrichedOpenSSHLogin &&) = default;
};
class EnrichedOpenSSHLogout : public EnrichedEventType {
public:
EnrichedOpenSSHLogout(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedOpenSSHLogout(EnrichedOpenSSHLogout &&) = default;
};
class EnrichedLoginLogin : public EnrichedEventType {
public:
EnrichedLoginLogin(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedLoginLogin(EnrichedLoginLogin &&) = default;
};
class EnrichedLoginLogout : public EnrichedEventType {
public:
EnrichedLoginLogout(Message &&es_msg, EnrichedProcess instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)) {}
EnrichedLoginLogout(EnrichedLoginLogout &&) = default;
};
using EnrichedType = std::variant<
EnrichedClose, EnrichedExchange, EnrichedExec, EnrichedExit, EnrichedFork,
EnrichedLink, EnrichedRename, EnrichedUnlink, EnrichedCSInvalidated,
EnrichedLoginWindowSessionLogin, EnrichedLoginWindowSessionLogout,
EnrichedLoginWindowSessionLock, EnrichedLoginWindowSessionUnlock,
EnrichedScreenSharingAttach, EnrichedScreenSharingDetach,
EnrichedOpenSSHLogin, EnrichedOpenSSHLogout, EnrichedLoginLogin,
EnrichedLoginLogout>;
class EnrichedMessage {
public:
@@ -355,6 +454,6 @@ class EnrichedMessage {
EnrichedType msg_;
};
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa
#endif

View File

@@ -15,12 +15,13 @@
#define SANTA__SANTAD__EVENTPROVIDERS_ENDPOINTSECURITY_ENRICHER_H
#include <memory>
#include <string_view>
#include "Source/common/SantaCache.h"
#include "Source/santad/EventProviders/EndpointSecurity/EnrichedTypes.h"
#include "Source/santad/ProcessTree/process_tree.h"
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
enum class EnrichOptions {
// Specifies default enricher operation.
@@ -33,7 +34,8 @@ enum class EnrichOptions {
class Enricher {
public:
Enricher(std::shared_ptr<process_tree::ProcessTree> pt = nullptr);
Enricher(
std::shared_ptr<santa::santad::process_tree::ProcessTree> pt = nullptr);
virtual ~Enricher() = default;
virtual std::unique_ptr<EnrichedMessage> Enrich(Message &&msg);
virtual EnrichedProcess Enrich(
@@ -47,14 +49,19 @@ class Enricher {
virtual std::optional<std::shared_ptr<std::string>> UsernameForGID(
gid_t gid, EnrichOptions options = EnrichOptions::kDefault);
// This method does not chache. It should not be used on a hot path.
virtual std::optional<uid_t> UIDForUsername(
std::string_view username,
EnrichOptions options = EnrichOptions::kDefault);
private:
SantaCache<uid_t, std::optional<std::shared_ptr<std::string>>>
username_cache_;
SantaCache<gid_t, std::optional<std::shared_ptr<std::string>>>
groupname_cache_;
std::shared_ptr<process_tree::ProcessTree> process_tree_;
std::shared_ptr<santa::santad::process_tree::ProcessTree> process_tree_;
};
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa
#endif

View File

@@ -23,13 +23,17 @@
#include <memory>
#include <optional>
#include "Source/common/Platform.h"
#include "Source/common/SNTLogging.h"
#include "Source/common/String.h"
#include "Source/santad/EventProviders/EndpointSecurity/EnrichedTypes.h"
#include "Source/santad/ProcessTree/SNTEndpointSecurityAdapter.h"
#include "Source/santad/ProcessTree/process_tree.h"
#include "Source/santad/ProcessTree/process_tree_macos.h"
namespace santa::santad::event_providers::endpoint_security {
using santa::StringTokenToStringView;
namespace santa {
Enricher::Enricher(std::shared_ptr<::santa::santad::process_tree::ProcessTree> pt)
: username_cache_(256), groupname_cache_(256), process_tree_(std::move(pt)) {}
@@ -81,6 +85,42 @@ std::unique_ptr<EnrichedMessage> Enricher::Enrich(Message &&es_msg) {
case ES_EVENT_TYPE_NOTIFY_CS_INVALIDATED:
return std::make_unique<EnrichedMessage>(
EnrichedCSInvalidated(std::move(es_msg), Enrich(*es_msg->process)));
#if HAVE_MACOS_13
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN:
return std::make_unique<EnrichedMessage>(EnrichedLoginWindowSessionLogin(
std::move(es_msg), Enrich(*es_msg->process),
UIDForUsername(StringTokenToStringView(es_msg->event.lw_session_login->username))));
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT:
return std::make_unique<EnrichedMessage>(EnrichedLoginWindowSessionLogout(
std::move(es_msg), Enrich(*es_msg->process),
UIDForUsername(StringTokenToStringView(es_msg->event.lw_session_logout->username))));
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK:
return std::make_unique<EnrichedMessage>(EnrichedLoginWindowSessionLock(
std::move(es_msg), Enrich(*es_msg->process),
UIDForUsername(StringTokenToStringView(es_msg->event.lw_session_lock->username))));
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK:
return std::make_unique<EnrichedMessage>(EnrichedLoginWindowSessionUnlock(
std::move(es_msg), Enrich(*es_msg->process),
UIDForUsername(StringTokenToStringView(es_msg->event.lw_session_unlock->username))));
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH:
return std::make_unique<EnrichedMessage>(
EnrichedScreenSharingAttach(std::move(es_msg), Enrich(*es_msg->process)));
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH:
return std::make_unique<EnrichedMessage>(
EnrichedScreenSharingDetach(std::move(es_msg), Enrich(*es_msg->process)));
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN:
return std::make_unique<EnrichedMessage>(
EnrichedOpenSSHLogin(std::move(es_msg), Enrich(*es_msg->process)));
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT:
return std::make_unique<EnrichedMessage>(
EnrichedOpenSSHLogout(std::move(es_msg), Enrich(*es_msg->process)));
case ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN:
return std::make_unique<EnrichedMessage>(
EnrichedLoginLogin(std::move(es_msg), Enrich(*es_msg->process)));
case ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT:
return std::make_unique<EnrichedMessage>(
EnrichedLoginLogout(std::move(es_msg), Enrich(*es_msg->process)));
#endif
default:
// This is a programming error
LOGE(@"Attempting to enrich an unhandled event type: %d", es_msg->event_type);
@@ -94,9 +134,10 @@ EnrichedProcess Enricher::Enrich(const es_process_t &es_proc, EnrichOptions opti
UsernameForUID(audit_token_to_ruid(es_proc.audit_token), options),
UsernameForGID(audit_token_to_rgid(es_proc.audit_token), options),
Enrich(*es_proc.executable, options),
process_tree_ ? process_tree_->ExportAnnotations(
process_tree::PidFromAuditToken(es_proc.audit_token))
: std::nullopt);
process_tree_
? process_tree_->ExportAnnotations(
santa::santad::process_tree::PidFromAuditToken(es_proc.audit_token))
: std::nullopt);
}
EnrichedFile Enricher::Enrich(const es_file_t &es_file, EnrichOptions options) {
@@ -152,4 +193,14 @@ std::optional<std::shared_ptr<std::string>> Enricher::UsernameForGID(gid_t gid,
}
}
} // namespace santa::santad::event_providers::endpoint_security
std::optional<uid_t> Enricher::UIDForUsername(std::string_view username, EnrichOptions options) {
if (options == EnrichOptions::kLocalOnly) {
// If `kLocalOnly` option is set, do not attempt a lookup
return std::nullopt;
}
struct passwd *pw = getpwnam(username.data());
return pw ? std::make_optional(pw->pw_uid) : std::nullopt;
}
} // namespace santa

View File

@@ -18,7 +18,7 @@
#include "Source/common/TestUtils.h"
#include "Source/santad/EventProviders/EndpointSecurity/Enricher.h"
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::Enricher;
@interface EnricherTest : XCTestCase
@end

View File

@@ -23,7 +23,7 @@
#import "Source/common/SNTCommonEnums.h"
#include "Source/santad/ProcessTree/process_tree.h"
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
class EndpointSecurityAPI;
class MessagePeer;
@@ -41,11 +41,11 @@ class Message {
Message(const Message& other);
Message& operator=(const Message& other) = delete;
void SetProcessToken(process_tree::ProcessToken tok);
void SetProcessToken(santa::santad::process_tree::ProcessToken tok);
// Operators to access underlying es_message_t
const es_message_t* operator->() const { return es_msg_; }
const es_message_t& operator*() const { return *es_msg_; }
inline const es_message_t* operator->() const { return es_msg_; }
inline const es_message_t& operator*() const { return *es_msg_; }
// Helper to get the API associated with this message.
// Used for things like es_exec_arg_count.
@@ -60,12 +60,12 @@ class Message {
inline StatChangeStep StatChangeStep() const { return stat_change_step_; }
inline StatResult StatResult() const { return stat_result_; }
friend class santa::santad::event_providers::endpoint_security::MessagePeer;
friend class santa::MessagePeer;
private:
std::shared_ptr<EndpointSecurityAPI> esapi_;
const es_message_t* es_msg_;
std::optional<process_tree::ProcessToken> process_token_;
std::optional<santa::santad::process_tree::ProcessToken> process_token_;
std::string GetProcessName(pid_t pid) const;
@@ -73,6 +73,6 @@ class Message {
mutable enum StatResult stat_result_ = StatResult::kOK;
};
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa
#endif

View File

@@ -20,7 +20,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
namespace santa::santad::event_providers::endpoint_security {
namespace santa {
Message::Message(std::shared_ptr<EndpointSecurityAPI> esapi, const es_message_t *es_msg)
: esapi_(std::move(esapi)), es_msg_(es_msg), process_token_(std::nullopt) {
@@ -72,7 +72,7 @@ void Message::UpdateStatState(enum StatChangeStep step) const {
}
}
void Message::SetProcessToken(process_tree::ProcessToken tok) {
void Message::SetProcessToken(santa::santad::process_tree::ProcessToken tok) {
process_token_ = std::move(tok);
}
@@ -91,4 +91,4 @@ std::string Message::GetProcessName(pid_t pid) const {
}
}
} // namespace santa::santad::event_providers::endpoint_security
} // namespace santa

View File

@@ -23,7 +23,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/EventProviders/EndpointSecurity/MockEndpointSecurityAPI.h"
using santa::santad::event_providers::endpoint_security::Message;
using santa::Message;
bool IsPidInUse(pid_t pid) {
char pname[MAXCOMLEN * 2 + 1] = {};

View File

@@ -27,18 +27,13 @@
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
using santa::santad::event_providers::endpoint_security::Client;
using santa::Client;
class MockEndpointSecurityAPI
: public santa::santad::event_providers::endpoint_security::EndpointSecurityAPI {
class MockEndpointSecurityAPI : public santa::EndpointSecurityAPI {
public:
MOCK_METHOD(santa::santad::event_providers::endpoint_security::Client, NewClient,
(void (^message_handler)(
es_client_t *, santa::santad::event_providers::endpoint_security::Message)));
MOCK_METHOD(santa::Client, NewClient, (void (^message_handler)(es_client_t *, santa::Message)));
MOCK_METHOD(bool, Subscribe,
(const santa::santad::event_providers::endpoint_security::Client &,
const std::set<es_event_type_t> &));
MOCK_METHOD(bool, Subscribe, (const santa::Client &, const std::set<es_event_type_t> &));
MOCK_METHOD(bool, UnsubscribeAll, (const Client &client));
MOCK_METHOD(bool, UnmuteAllPaths, (const Client &client));
@@ -48,30 +43,23 @@ class MockEndpointSecurityAPI
MOCK_METHOD(bool, InvertTargetPathMuting, (const Client &client));
MOCK_METHOD(bool, MuteTargetPath,
(const Client &client, std::string_view path,
santa::santad::data_layer::WatchItemPathType path_type));
(const Client &client, std::string_view path, santa::WatchItemPathType path_type));
MOCK_METHOD(bool, UnmuteTargetPath,
(const Client &client, std::string_view path,
santa::santad::data_layer::WatchItemPathType path_type));
(const Client &client, std::string_view path, santa::WatchItemPathType path_type));
MOCK_METHOD(void, RetainMessage, (const es_message_t *msg));
MOCK_METHOD(void, ReleaseMessage, (const es_message_t *msg));
MOCK_METHOD(bool, RespondAuthResult,
(const santa::santad::event_providers::endpoint_security::Client &,
const santa::santad::event_providers::endpoint_security::Message &msg,
es_auth_result_t result, bool cache));
(const santa::Client &, const santa::Message &msg, es_auth_result_t result,
bool cache));
MOCK_METHOD(bool, RespondFlagsResult,
(const santa::santad::event_providers::endpoint_security::Client &client,
const santa::santad::event_providers::endpoint_security::Message &msg,
uint32_t allowed_flags, bool cache));
(const santa::Client &client, const santa::Message &msg, uint32_t allowed_flags,
bool cache));
MOCK_METHOD(bool, MuteProcess,
(const santa::santad::event_providers::endpoint_security::Client &,
const audit_token_t *tok));
MOCK_METHOD(bool, MuteProcess, (const santa::Client &, const audit_token_t *tok));
MOCK_METHOD(bool, ClearCache,
(const santa::santad::event_providers::endpoint_security::Client &));
MOCK_METHOD(bool, ClearCache, (const santa::Client &));
MOCK_METHOD(uint32_t, ExecArgCount, (const es_event_exec_t *event));
MOCK_METHOD(es_string_token_t, ExecArg, (const es_event_exec_t *event, uint32_t index));
@@ -84,8 +72,7 @@ class MockEndpointSecurityAPI
void SetExpectationsESNewClient() {
EXPECT_CALL(*this, NewClient)
.WillOnce(testing::Return(santa::santad::event_providers::endpoint_security::Client(
nullptr, ES_NEW_CLIENT_RESULT_SUCCESS)));
.WillOnce(testing::Return(santa::Client(nullptr, ES_NEW_CLIENT_RESULT_SUCCESS)));
EXPECT_CALL(*this, MuteProcess).WillOnce(testing::Return(true));
EXPECT_CALL(*this, ClearCache).WillRepeatedly(testing::Return(true));
EXPECT_CALL(*this, Subscribe).WillRepeatedly(testing::Return(true));

View File

@@ -23,11 +23,11 @@
#include "Source/santad/Metrics.h"
// Forward declarations
namespace santa::santad::event_providers {
namespace santa {
class RateLimiterPeer;
}
} // namespace santa
namespace santa::santad::event_providers {
namespace santa {
// Very basic rate limiting infrastructure.
// Currently only handles X events per duration.
@@ -39,12 +39,11 @@ class RateLimiter {
public:
// Factory
static std::shared_ptr<RateLimiter> Create(
std::shared_ptr<santa::santad::Metrics> metrics,
santa::santad::Processor processor, uint16_t max_qps,
NSTimeInterval reset_duration = kDefaultResetDuration);
std::shared_ptr<santa::Metrics> metrics, santa::Processor processor,
uint16_t max_qps, NSTimeInterval reset_duration = kDefaultResetDuration);
RateLimiter(std::shared_ptr<santa::santad::Metrics> metrics,
santa::santad::Processor processor, uint16_t max_qps,
RateLimiter(std::shared_ptr<santa::Metrics> metrics,
santa::Processor processor, uint16_t max_qps,
NSTimeInterval reset_duration);
enum class Decision {
@@ -54,7 +53,7 @@ class RateLimiter {
Decision Decide(uint64_t cur_mach_time);
friend class santa::santad::event_providers::RateLimiterPeer;
friend class santa::RateLimiterPeer;
private:
bool ShouldRateLimitLocked();
@@ -63,8 +62,8 @@ class RateLimiter {
static constexpr NSTimeInterval kDefaultResetDuration = 15.0;
std::shared_ptr<santa::santad::Metrics> metrics_;
santa::santad::Processor processor_;
std::shared_ptr<santa::Metrics> metrics_;
santa::Processor processor_;
size_t log_count_total_ = 0;
size_t max_log_count_total_;
uint64_t reset_mach_time_;
@@ -72,6 +71,6 @@ class RateLimiter {
dispatch_queue_t q_;
};
} // namespace santa::santad::event_providers
} // namespace santa
#endif

View File

@@ -17,10 +17,10 @@
#include "Source/common/BranchPrediction.h"
#include "Source/common/SystemResources.h"
using santa::santad::Metrics;
using santa::santad::Processor;
using santa::Metrics;
using santa::Processor;
namespace santa::santad::event_providers {
namespace santa {
std::shared_ptr<RateLimiter> RateLimiter::Create(std::shared_ptr<Metrics> metrics,
Processor processor, uint16_t max_qps,
@@ -82,4 +82,4 @@ RateLimiter::Decision RateLimiter::Decide(uint64_t cur_mach_time) {
return decision;
}
} // namespace santa::santad::event_providers
} // namespace santa

View File

@@ -20,12 +20,11 @@
#include "Source/common/SystemResources.h"
#include "Source/santad/Metrics.h"
using santa::santad::event_providers::RateLimiter;
using santa::RateLimiter;
static const santa::santad::Processor kDefaultProcessor =
santa::santad::Processor::kFileAccessAuthorizer;
static const santa::Processor kDefaultProcessor = santa::Processor::kFileAccessAuthorizer;
namespace santa::santad::event_providers {
namespace santa {
class RateLimiterPeer : public RateLimiter {
public:
@@ -39,9 +38,9 @@ class RateLimiterPeer : public RateLimiter {
using RateLimiter::reset_mach_time_;
};
} // namespace santa::santad::event_providers
} // namespace santa
using santa::santad::event_providers::RateLimiterPeer;
using santa::RateLimiterPeer;
@interface RateLimiterTest : XCTestCase
@end

View File

@@ -27,14 +27,10 @@
@interface SNTEndpointSecurityAuthorizer
: SNTEndpointSecurityClient <SNTEndpointSecurityEventHandler>
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)
esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
execController:(SNTExecutionController *)execController
compilerController:(SNTCompilerController *)compilerController
authResultCache:
(std::shared_ptr<santa::santad::event_providers::AuthResultCache>)authResultCache;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
execController:(SNTExecutionController *)execController
compilerController:(SNTCompilerController *)compilerController
authResultCache:(std::shared_ptr<santa::AuthResultCache>)authResultCache;
@end

View File

@@ -26,10 +26,10 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::Message;
using santa::AuthResultCache;
using santa::EndpointSecurityAPI;
using santa::EventDisposition;
using santa::Message;
@interface SNTEndpointSecurityAuthorizer ()
@property SNTCompilerController *compilerController;
@@ -41,13 +41,13 @@ using santa::santad::event_providers::endpoint_security::Message;
}
- (instancetype)initWithESAPI:(std::shared_ptr<EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
metrics:(std::shared_ptr<santa::Metrics>)metrics
execController:(SNTExecutionController *)execController
compilerController:(SNTCompilerController *)compilerController
authResultCache:(std::shared_ptr<AuthResultCache>)authResultCache {
self = [super initWithESAPI:std::move(esApi)
metrics:std::move(metrics)
processor:santa::santad::Processor::kAuthorizer];
processor:santa::Processor::kAuthorizer];
if (self) {
_execController = execController;
_compilerController = compilerController;

View File

@@ -33,9 +33,9 @@
#import "Source/santad/SNTCompilerController.h"
#import "Source/santad/SNTExecutionController.h"
using santa::santad::EventDisposition;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::endpoint_security::Message;
using santa::AuthResultCache;
using santa::EventDisposition;
using santa::Message;
class MockAuthResultCache : public AuthResultCache {
public:
@@ -72,7 +72,7 @@ class MockAuthResultCache : public AuthResultCache {
id authClient =
[[SNTEndpointSecurityAuthorizer alloc] initWithESAPI:mockESApi
metrics:nullptr
processor:santa::santad::Processor::kAuthorizer];
processor:santa::Processor::kAuthorizer];
EXPECT_CALL(*mockESApi, ClearCache)
.After(EXPECT_CALL(*mockESApi, Subscribe(testing::_, expectedEventSubs))
@@ -81,6 +81,10 @@ class MockAuthResultCache : public AuthResultCache {
[authClient enable];
for (const auto &event : expectedEventSubs) {
XCTAssertNoThrow(santa::EventTypeToString(event));
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
}

View File

@@ -37,14 +37,14 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::Metrics;
using santa::santad::Processor;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::event_providers::endpoint_security::Client;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::Message;
using santa::Client;
using santa::EndpointSecurityAPI;
using santa::EnrichedMessage;
using santa::EventDisposition;
using santa::Message;
using santa::Metrics;
using santa::Processor;
using santa::WatchItemPathType;
constexpr std::string_view kProtectedFiles[] = {"/private/var/db/santa/rules.db",
"/private/var/db/santa/events.db"};

View File

@@ -29,11 +29,9 @@
@protocol SNTEndpointSecurityClientBase
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
processor:(santa::santad::Processor)processor;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
processor:(santa::Processor)processor;
/// @note If this fails to establish a new ES client via `es_new_client`, an exception is raised
/// that should terminate the program.
@@ -52,9 +50,9 @@
- (bool)unmuteAllTargetPaths;
- (bool)enableTargetPathWatching;
- (bool)muteTargetPaths:
(const std::vector<std::pair<std::string, santa::santad::data_layer::WatchItemPathType>> &)paths;
(const std::vector<std::pair<std::string, santa::WatchItemPathType>> &)paths;
- (bool)unmuteTargetPaths:
(const std::vector<std::pair<std::string, santa::santad::data_layer::WatchItemPathType>> &)paths;
(const std::vector<std::pair<std::string, santa::WatchItemPathType>> &)paths;
/// Responds to the Message with the given auth result
///
@@ -66,27 +64,18 @@
/// @note If the msg event type requires a flags response, the correct ES API will automatically
/// be called. ALLOWED results will be translated to having all flags set, and DENIED results
/// will be translated to having all flags cleared.
- (bool)respondToMessage:(const santa::santad::event_providers::endpoint_security::Message &)msg
- (bool)respondToMessage:(const santa::Message &)msg
withAuthResult:(es_auth_result_t)result
cacheable:(bool)cacheable;
- (void)
processEnrichedMessage:
(std::unique_ptr<santa::santad::event_providers::endpoint_security::EnrichedMessage>)msg
handler:
(void (^)(std::unique_ptr<
santa::santad::event_providers::endpoint_security::EnrichedMessage>))
messageHandler;
- (void)processEnrichedMessage:(std::unique_ptr<santa::EnrichedMessage>)msg
handler:(void (^)(std::unique_ptr<santa::EnrichedMessage>))messageHandler;
- (void)asynchronouslyProcess:(santa::santad::event_providers::endpoint_security::Message)msg
handler:
(void (^)(santa::santad::event_providers::endpoint_security::Message &&))
messageHandler;
- (void)asynchronouslyProcess:(santa::Message)msg
handler:(void (^)(santa::Message &&))messageHandler;
- (void)processMessage:(santa::santad::event_providers::endpoint_security::Message &&)msg
handler:
(void (^)(const santa::santad::event_providers::endpoint_security::Message &))
messageHandler;
- (void)processMessage:(santa::Message &&)msg
handler:(void (^)(const santa::Message &))messageHandler;
- (bool)clearCache;

View File

@@ -34,21 +34,21 @@
#import "Source/santad/EventProviders/SNTEndpointSecurityClient.h"
#include "Source/santad/Metrics.h"
using santa::santad::Processor;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::event_providers::endpoint_security::Client;
using santa::santad::event_providers::endpoint_security::EnrichedClose;
using santa::santad::event_providers::endpoint_security::EnrichedFile;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::Message;
using santa::Client;
using santa::EnrichedClose;
using santa::EnrichedFile;
using santa::EnrichedMessage;
using santa::EnrichedProcess;
using santa::Message;
using santa::Processor;
using santa::WatchItemPathType;
@interface SNTEndpointSecurityClient (Testing)
- (void)establishClientOrDie;
- (bool)muteSelf;
- (NSString *)errorMessageForNewClientResult:(es_new_client_result_t)result;
- (void)handleMessage:(Message &&)esMsg
recordEventMetrics:(void (^)(santa::santad::EventDisposition disposition))recordEventMetrics;
recordEventMetrics:(void (^)(santa::EventDisposition disposition))recordEventMetrics;
- (BOOL)shouldHandleMessage:(const Message &)esMsg;
- (int64_t)computeBudgetForDeadline:(uint64_t)deadline currentTime:(uint64_t)currentTime;

View File

@@ -39,17 +39,13 @@ typedef void (^SNTDeviceBlockCallback)(SNTDeviceEvent *event);
@property(nonatomic, readwrite, nullable) NSArray<NSString *> *remountArgs;
@property(nonatomic, nullable) SNTDeviceBlockCallback deviceBlockCallback;
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)
esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
authResultCache:
(std::shared_ptr<santa::santad::event_providers::AuthResultCache>)authResultCache
blockUSBMount:(BOOL)blockUSBMount
remountUSBMode:(nullable NSArray<NSString *> *)remountUSBMode
startupPreferences:(SNTDeviceManagerStartupPreferences)startupPrefs;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<santa::Logger>)logger
authResultCache:(std::shared_ptr<santa::AuthResultCache>)authResultCache
blockUSBMount:(BOOL)blockUSBMount
remountUSBMode:(nullable NSArray<NSString *> *)remountUSBMode
startupPreferences:(SNTDeviceManagerStartupPreferences)startupPrefs;
@end

View File

@@ -33,13 +33,13 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::FlushCacheMode;
using santa::santad::event_providers::FlushCacheReason;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::AuthResultCache;
using santa::EndpointSecurityAPI;
using santa::EventDisposition;
using santa::FlushCacheMode;
using santa::FlushCacheReason;
using santa::Logger;
using santa::Message;
// Defined operations for startup metrics:
// Device shouldn't be operated on (e.g. not a mass storage device)
@@ -178,7 +178,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (instancetype)initWithESAPI:(std::shared_ptr<EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<Logger>)logger
authResultCache:(std::shared_ptr<AuthResultCache>)authResultCache
blockUSBMount:(BOOL)blockUSBMount
@@ -186,7 +186,7 @@ NS_ASSUME_NONNULL_BEGIN
startupPreferences:(SNTDeviceManagerStartupPreferences)startupPrefs {
self = [super initWithESAPI:std::move(esApi)
metrics:std::move(metrics)
processor:santa::santad::Processor::kDeviceManager];
processor:santa::Processor::kDeviceManager];
if (self) {
_logger = logger;
_authResultCache = authResultCache;

View File

@@ -38,11 +38,11 @@
#import "Source/santad/EventProviders/SNTEndpointSecurityDeviceManager.h"
#include "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::FlushCacheMode;
using santa::santad::event_providers::FlushCacheReason;
using santa::santad::event_providers::endpoint_security::Message;
using santa::AuthResultCache;
using santa::EventDisposition;
using santa::FlushCacheMode;
using santa::FlushCacheReason;
using santa::Message;
class MockAuthResultCache : public AuthResultCache {
public:
@@ -384,18 +384,21 @@ class MockAuthResultCache : public AuthResultCache {
on:@"v2"
flags:@(MNT_RDONLY | MNT_NOEXEC | MNT_JOURNALED)]];
// Disabling clang format due to local/remote version differences.
// clang-format off
// Create mock disks with desired args
MockDADisk * (^CreateMockDisk)(NSString *, NSString *) =
^MockDADisk *(NSString *mountOn, NSString *mountFrom) {
MockDADisk *mockDisk = [[MockDADisk alloc] init];
mockDisk.diskDescription = @{
@"DAVolumePath" : mountOn, // f_mntonname,
@"DADevicePath" : mountOn, // f_mntonname,
@"DAMediaBSDName" : mountFrom, // f_mntfromname,
};
MockDADisk *mockDisk = [[MockDADisk alloc] init];
mockDisk.diskDescription = @{
@"DAVolumePath" : mountOn, // f_mntonname,
@"DADevicePath" : mountOn, // f_mntonname,
@"DAMediaBSDName" : mountFrom, // f_mntfromname,
};
return mockDisk;
};
return mockDisk;
};
// clang-format on
// Reset the Mock DA property, setup disks and remount args, then trigger the test
void (^PerformStartupTest)(NSArray<MockDADisk *> *, NSArray<NSString *> *,
@@ -495,10 +498,10 @@ class MockAuthResultCache : public AuthResultCache {
};
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
id deviceClient = [[SNTEndpointSecurityDeviceManager alloc]
initWithESAPI:mockESApi
metrics:nullptr
processor:santa::santad::Processor::kDeviceManager];
id deviceClient =
[[SNTEndpointSecurityDeviceManager alloc] initWithESAPI:mockESApi
metrics:nullptr
processor:santa::Processor::kDeviceManager];
EXPECT_CALL(*mockESApi, ClearCache(testing::_))
.After(EXPECT_CALL(*mockESApi, Subscribe(testing::_, expectedEventSubs))
@@ -507,6 +510,10 @@ class MockAuthResultCache : public AuthResultCache {
[deviceClient enable];
for (const auto &event : expectedEventSubs) {
XCTAssertNoThrow(santa::EventTypeToString(event));
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
}

View File

@@ -26,8 +26,8 @@
// Called Synchronously and serially for each message provided by the
// EndpointSecurity framework.
- (void)handleMessage:(santa::santad::event_providers::endpoint_security::Message &&)esMsg
recordEventMetrics:(void (^)(santa::santad::EventDisposition))recordEventMetrics;
- (void)handleMessage:(santa::Message &&)esMsg
recordEventMetrics:(void (^)(santa::EventDisposition))recordEventMetrics;
// Called after Santa has finished initializing itself.
// This is an optimal place to subscribe to ES events
@@ -43,13 +43,10 @@
// Called when a client should no longer receive events.
- (void)disable;
- (void)
watchItemsCount:(size_t)count
newPaths:
(const std::vector<std::pair<std::string, santa::santad::data_layer::WatchItemPathType>>
&)newPaths
removedPaths:
(const std::vector<std::pair<std::string, santa::santad::data_layer::WatchItemPathType>> &)
removedPaths;
- (void)watchItemsCount:(size_t)count
newPaths:
(const std::vector<std::pair<std::string, santa::WatchItemPathType>> &)newPaths
removedPaths:
(const std::vector<std::pair<std::string, santa::WatchItemPathType>> &)removedPaths;
@end

View File

@@ -33,16 +33,13 @@ typedef void (^SNTFileAccessBlockCallback)(SNTFileAccessEvent *event, NSString *
@interface SNTEndpointSecurityFileAccessAuthorizer
: SNTEndpointSecurityClient <SNTEndpointSecurityDynamicEventHandler>
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
watchItems:(std::shared_ptr<santa::santad::data_layer::WatchItems>)watchItems
enricher:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::Enricher>)enricher
decisionCache:(SNTDecisionCache *)decisionCache
ttyWriter:(std::shared_ptr<santa::santad::TTYWriter>)ttyWriter;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<santa::Logger>)logger
watchItems:(std::shared_ptr<santa::WatchItems>)watchItems
enricher:(std::shared_ptr<santa::Enricher>)enricher
decisionCache:(SNTDecisionCache *)decisionCache
ttyWriter:(std::shared_ptr<santa::TTYWriter>)ttyWriter;
@property SNTFileAccessBlockCallback fileAccessBlockCallback;

View File

@@ -51,21 +51,21 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
using santa::common::OptionalStringToNSString;
using santa::common::StringToNSString;
using santa::santad::EventDisposition;
using santa::santad::FileAccessMetricStatus;
using santa::santad::Metrics;
using santa::santad::TTYWriter;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::data_layer::WatchItemPolicy;
using santa::santad::data_layer::WatchItems;
using santa::santad::event_providers::RateLimiter;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::santad::event_providers::endpoint_security::EnrichOptions;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::EndpointSecurityAPI;
using santa::Enricher;
using santa::EnrichOptions;
using santa::EventDisposition;
using santa::FileAccessMetricStatus;
using santa::Logger;
using santa::Message;
using santa::Metrics;
using santa::OptionalStringToNSString;
using santa::RateLimiter;
using santa::StringToNSString;
using santa::TTYWriter;
using santa::WatchItemPathType;
using santa::WatchItemPolicy;
using santa::WatchItems;
NSString *kBadCertHash = @"BAD_CERT_HASH";
@@ -396,19 +396,16 @@ bool ShouldMessageTTY(const std::shared_ptr<WatchItemPolicy> &policy, const Mess
std::shared_ptr<Metrics> _metrics;
}
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
watchItems:(std::shared_ptr<WatchItems>)watchItems
enricher:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::Enricher>)enricher
decisionCache:(SNTDecisionCache *)decisionCache
ttyWriter:(std::shared_ptr<santa::santad::TTYWriter>)ttyWriter {
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<Metrics>)metrics
logger:(std::shared_ptr<santa::Logger>)logger
watchItems:(std::shared_ptr<WatchItems>)watchItems
enricher:(std::shared_ptr<santa::Enricher>)enricher
decisionCache:(SNTDecisionCache *)decisionCache
ttyWriter:(std::shared_ptr<santa::TTYWriter>)ttyWriter {
self = [super initWithESAPI:std::move(esApi)
metrics:metrics
processor:santa::santad::Processor::kFileAccessAuthorizer];
processor:santa::Processor::kFileAccessAuthorizer];
if (self) {
_watchItems = std::move(watchItems);
_logger = std::move(logger);
@@ -419,8 +416,8 @@ bool ShouldMessageTTY(const std::shared_ptr<WatchItemPolicy> &policy, const Mess
_configurator = [SNTConfigurator configurator];
_rateLimiter = RateLimiter::Create(_metrics, santa::santad::Processor::kFileAccessAuthorizer,
kDefaultRateLimitQPS);
_rateLimiter =
RateLimiter::Create(_metrics, santa::Processor::kFileAccessAuthorizer, kDefaultRateLimitQPS);
SNTMetricBooleanGauge *famEnabled = [[SNTMetricSet sharedInstance]
booleanGaugeWithName:@"/santa/fam_enabled"
@@ -797,7 +794,7 @@ bool ShouldMessageTTY(const std::shared_ptr<WatchItemPolicy> &policy, const Mess
cacheable:(policyResult == ES_AUTH_RESULT_ALLOW && !allow_read_access)];
}
- (void)handleMessage:(santa::santad::event_providers::endpoint_security::Message &&)esMsg
- (void)handleMessage:(santa::Message &&)esMsg
recordEventMetrics:(void (^)(EventDisposition))recordEventMetrics {
SNTOverrideFileAccessAction overrideAction = [self.configurator overrideFileAccessAction];
@@ -834,17 +831,12 @@ bool ShouldMessageTTY(const std::shared_ptr<WatchItemPolicy> &policy, const Mess
- (void)enable {
std::set<es_event_type_t> events = {
ES_EVENT_TYPE_AUTH_CLONE, ES_EVENT_TYPE_AUTH_CREATE, ES_EVENT_TYPE_AUTH_EXCHANGEDATA,
ES_EVENT_TYPE_AUTH_LINK, ES_EVENT_TYPE_AUTH_OPEN, ES_EVENT_TYPE_AUTH_RENAME,
ES_EVENT_TYPE_AUTH_TRUNCATE, ES_EVENT_TYPE_AUTH_UNLINK, ES_EVENT_TYPE_NOTIFY_EXIT,
ES_EVENT_TYPE_AUTH_CLONE, ES_EVENT_TYPE_AUTH_COPYFILE, ES_EVENT_TYPE_AUTH_CREATE,
ES_EVENT_TYPE_AUTH_EXCHANGEDATA, ES_EVENT_TYPE_AUTH_LINK, ES_EVENT_TYPE_AUTH_OPEN,
ES_EVENT_TYPE_AUTH_RENAME, ES_EVENT_TYPE_AUTH_TRUNCATE, ES_EVENT_TYPE_AUTH_UNLINK,
ES_EVENT_TYPE_NOTIFY_EXIT,
};
#if HAVE_MACOS_12
if (@available(macOS 12.0, *)) {
events.insert(ES_EVENT_TYPE_AUTH_COPYFILE);
}
#endif
if (!self.isSubscribed) {
if ([super subscribe:events]) {
self.isSubscribed = true;

View File

@@ -44,8 +44,8 @@
#include "Source/santad/Logs/EndpointSecurity/MockLogger.h"
#include "Source/santad/SNTDecisionCache.h"
using santa::santad::data_layer::WatchItemPolicy;
using santa::santad::event_providers::endpoint_security::Message;
using santa::Message;
using santa::WatchItemPolicy;
extern NSString *kBadCertHash;
@@ -729,17 +729,12 @@ void ClearWatchItemPolicyProcess(WatchItemPolicy::Process &proc) {
- (void)testEnable {
std::set<es_event_type_t> expectedEventSubs = {
ES_EVENT_TYPE_AUTH_CLONE, ES_EVENT_TYPE_AUTH_CREATE, ES_EVENT_TYPE_AUTH_EXCHANGEDATA,
ES_EVENT_TYPE_AUTH_LINK, ES_EVENT_TYPE_AUTH_OPEN, ES_EVENT_TYPE_AUTH_RENAME,
ES_EVENT_TYPE_AUTH_TRUNCATE, ES_EVENT_TYPE_AUTH_UNLINK, ES_EVENT_TYPE_NOTIFY_EXIT,
ES_EVENT_TYPE_AUTH_CLONE, ES_EVENT_TYPE_AUTH_COPYFILE, ES_EVENT_TYPE_AUTH_CREATE,
ES_EVENT_TYPE_AUTH_EXCHANGEDATA, ES_EVENT_TYPE_AUTH_LINK, ES_EVENT_TYPE_AUTH_OPEN,
ES_EVENT_TYPE_AUTH_RENAME, ES_EVENT_TYPE_AUTH_TRUNCATE, ES_EVENT_TYPE_AUTH_UNLINK,
ES_EVENT_TYPE_NOTIFY_EXIT,
};
#if HAVE_MACOS_12
if (@available(macOS 12.0, *)) {
expectedEventSubs.insert(ES_EVENT_TYPE_AUTH_COPYFILE);
}
#endif
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
EXPECT_CALL(*mockESApi, ClearCache)
.After(EXPECT_CALL(*mockESApi, Subscribe(testing::_, expectedEventSubs))
@@ -749,10 +744,14 @@ void ClearWatchItemPolicyProcess(WatchItemPolicy::Process &proc) {
id fileAccessClient = [[SNTEndpointSecurityFileAccessAuthorizer alloc]
initWithESAPI:mockESApi
metrics:nullptr
processor:santa::santad::Processor::kFileAccessAuthorizer];
processor:santa::Processor::kFileAccessAuthorizer];
[fileAccessClient enable];
for (const auto &event : expectedEventSubs) {
XCTAssertNoThrow(santa::EventTypeToString(event));
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
}
@@ -940,42 +939,40 @@ void ClearWatchItemPolicyProcess(WatchItemPolicy::Process &proc) {
XCTAssertFalse(targets[0].devnoIno.has_value());
}
if (@available(macOS 12.0, *)) {
{
esMsg.event_type = ES_EVENT_TYPE_AUTH_COPYFILE;
esMsg.event.copyfile.source = &testFile1;
esMsg.event.copyfile.target_dir = &testDir;
esMsg.event.copyfile.target_name = testTok;
{
esMsg.event_type = ES_EVENT_TYPE_AUTH_COPYFILE;
esMsg.event.copyfile.source = &testFile1;
esMsg.event.copyfile.target_dir = &testDir;
esMsg.event.copyfile.target_name = testTok;
esMsg.event.copyfile.target_file = nullptr;
{
esMsg.event.copyfile.target_file = nullptr;
std::vector<PathTarget> targets;
PopulatePathTargets(msg, targets);
std::vector<PathTarget> targets;
PopulatePathTargets(msg, targets);
XCTAssertEqual(targets.size(), 2);
XCTAssertCStringEqual(targets[0].path.c_str(), testFile1.path.data);
XCTAssertTrue(targets[0].isReadable);
XCTAssertEqual(targets[0].devnoIno.value(), FileID(testFile1));
XCTAssertCppStringEqual(targets[1].path, dirTok);
XCTAssertFalse(targets[1].isReadable);
XCTAssertFalse(targets[1].devnoIno.has_value());
}
XCTAssertEqual(targets.size(), 2);
XCTAssertCStringEqual(targets[0].path.c_str(), testFile1.path.data);
XCTAssertTrue(targets[0].isReadable);
XCTAssertEqual(targets[0].devnoIno.value(), FileID(testFile1));
XCTAssertCppStringEqual(targets[1].path, dirTok);
XCTAssertFalse(targets[1].isReadable);
XCTAssertFalse(targets[1].devnoIno.has_value());
}
{
esMsg.event.copyfile.target_file = &testFile2;
{
esMsg.event.copyfile.target_file = &testFile2;
std::vector<PathTarget> targets;
PopulatePathTargets(msg, targets);
std::vector<PathTarget> targets;
PopulatePathTargets(msg, targets);
XCTAssertEqual(targets.size(), 2);
XCTAssertCStringEqual(targets[0].path.c_str(), testFile1.path.data);
XCTAssertTrue(targets[0].isReadable);
XCTAssertEqual(targets[0].devnoIno.value(), FileID(testFile1));
XCTAssertCStringEqual(targets[1].path.c_str(), testFile2.path.data);
XCTAssertFalse(targets[1].isReadable);
XCTAssertFalse(targets[1].devnoIno.has_value());
}
XCTAssertEqual(targets.size(), 2);
XCTAssertCStringEqual(targets[0].path.c_str(), testFile1.path.data);
XCTAssertTrue(targets[0].isReadable);
XCTAssertEqual(targets[0].devnoIno.value(), FileID(testFile1));
XCTAssertCStringEqual(targets[1].path.c_str(), testFile2.path.data);
XCTAssertFalse(targets[1].isReadable);
XCTAssertFalse(targets[1].devnoIno.has_value());
}
}
}

View File

@@ -29,18 +29,14 @@
@interface SNTEndpointSecurityRecorder
: SNTEndpointSecurityTreeAwareClient <SNTEndpointSecurityEventHandler>
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)
esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger
enricher:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::Enricher>)enricher
compilerController:(SNTCompilerController *)compilerController
authResultCache:
(std::shared_ptr<santa::santad::event_providers::AuthResultCache>)authResultCache
prefixTree:(std::shared_ptr<santa::common::PrefixTree<santa::common::Unit>>)prefixTree
processTree:(std::shared_ptr<santa::santad::process_tree::ProcessTree>)processTree;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<santa::Logger>)logger
enricher:(std::shared_ptr<santa::Enricher>)enricher
compilerController:(SNTCompilerController *)compilerController
authResultCache:(std::shared_ptr<santa::AuthResultCache>)authResultCache
prefixTree:(std::shared_ptr<santa::PrefixTree<santa::Unit>>)prefixTree
processTree:
(std::shared_ptr<santa::santad::process_tree::ProcessTree>)processTree;
@end

View File

@@ -17,6 +17,7 @@
#include <EndpointSecurity/EndpointSecurity.h>
#include "Source/common/Platform.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#include "Source/common/String.h"
@@ -26,15 +27,15 @@
#include "Source/santad/Metrics.h"
#include "Source/santad/ProcessTree/process_tree.h"
using santa::common::PrefixTree;
using santa::common::Unit;
using santa::santad::EventDisposition;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::AuthResultCache;
using santa::EndpointSecurityAPI;
using santa::EnrichedMessage;
using santa::Enricher;
using santa::EventDisposition;
using santa::Logger;
using santa::Message;
using santa::PrefixTree;
using santa::Unit;
using santa::santad::process_tree::ProcessTree;
es_file_t *GetTargetFileForPrefixTree(const es_message_t *msg) {
@@ -61,7 +62,7 @@ es_file_t *GetTargetFileForPrefixTree(const es_message_t *msg) {
}
- (instancetype)initWithESAPI:(std::shared_ptr<EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<Logger>)logger
enricher:(std::shared_ptr<Enricher>)enricher
compilerController:(SNTCompilerController *)compilerController
@@ -70,7 +71,7 @@ es_file_t *GetTargetFileForPrefixTree(const es_message_t *msg) {
processTree:(std::shared_ptr<ProcessTree>)processTree {
self = [super initWithESAPI:std::move(esApi)
metrics:std::move(metrics)
processor:santa::santad::Processor::kRecorder
processor:santa::Processor::kRecorder
processTree:std::move(processTree)];
if (self) {
_enricher = enricher;
@@ -141,7 +142,7 @@ es_file_t *GetTargetFileForPrefixTree(const es_message_t *msg) {
}
// Only log file changes that match the given regex
NSString *targetPath = santa::common::StringToNSString(targetFile->path.data);
NSString *targetPath = santa::StringToNSString(targetFile->path.data);
if (![[self.configurator fileChangesRegex]
numberOfMatchesInString:targetPath
options:0
@@ -186,16 +187,36 @@ es_file_t *GetTargetFileForPrefixTree(const es_message_t *msg) {
}
- (void)enable {
[super subscribe:{
ES_EVENT_TYPE_NOTIFY_CLOSE,
ES_EVENT_TYPE_NOTIFY_EXCHANGEDATA,
ES_EVENT_TYPE_NOTIFY_EXEC,
ES_EVENT_TYPE_NOTIFY_EXIT,
ES_EVENT_TYPE_NOTIFY_FORK,
ES_EVENT_TYPE_NOTIFY_LINK,
ES_EVENT_TYPE_NOTIFY_RENAME,
ES_EVENT_TYPE_NOTIFY_UNLINK,
}];
// clang-format off
std::set<es_event_type_t> events{
ES_EVENT_TYPE_NOTIFY_CLOSE,
ES_EVENT_TYPE_NOTIFY_EXCHANGEDATA,
ES_EVENT_TYPE_NOTIFY_EXEC,
ES_EVENT_TYPE_NOTIFY_EXIT,
ES_EVENT_TYPE_NOTIFY_FORK,
ES_EVENT_TYPE_NOTIFY_LINK,
ES_EVENT_TYPE_NOTIFY_RENAME,
ES_EVENT_TYPE_NOTIFY_UNLINK,
};
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
events.insert({
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN,
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK,
ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH,
ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH,
ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN,
ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT});
}
#endif
// clang-format on
[super subscribe:events];
}
@end

View File

@@ -23,6 +23,7 @@
#include <memory>
#include <set>
#include "Source/common/Platform.h"
#include "Source/common/PrefixTree.h"
#import "Source/common/SNTConfigurator.h"
#include "Source/common/TestUtils.h"
@@ -38,15 +39,15 @@
#include "Source/santad/Metrics.h"
#import "Source/santad/SNTCompilerController.h"
using santa::common::PrefixTree;
using santa::common::Unit;
using santa::santad::EventDisposition;
using santa::santad::Processor;
using santa::santad::event_providers::AuthResultCache;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::AuthResultCache;
using santa::EnrichedMessage;
using santa::Enricher;
using santa::EventDisposition;
using santa::Logger;
using santa::Message;
using santa::PrefixTree;
using santa::Processor;
using santa::Unit;
class MockEnricher : public Enricher {
public:
@@ -90,6 +91,24 @@ class MockLogger : public Logger {
ES_EVENT_TYPE_NOTIFY_FORK, ES_EVENT_TYPE_NOTIFY_EXIT, ES_EVENT_TYPE_NOTIFY_LINK,
ES_EVENT_TYPE_NOTIFY_RENAME, ES_EVENT_TYPE_NOTIFY_UNLINK,
};
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
expectedEventSubs.insert({
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK,
ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH,
ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH,
ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN,
ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN,
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT,
});
}
#endif
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
id recorderClient = [[SNTEndpointSecurityRecorder alloc] initWithESAPI:mockESApi
@@ -100,6 +119,10 @@ class MockLogger : public Logger {
[recorderClient enable];
for (const auto &event : expectedEventSubs) {
XCTAssertNoThrow(santa::EventTypeToString(event));
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
}
@@ -172,7 +195,7 @@ es_file_t targetFileMissesRegex = MakeESFile("/foo/misses");
[mockCC stopMocking];
}
- (void)testHandleMessageWithCloseMappedWriteable {
- (void)testHandleEventCloseMappedWritableMatchesRegex {
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
// CLOSE not modified, but was_mapped_writable, should remove from cache,
@@ -199,12 +222,12 @@ es_file_t targetFileMissesRegex = MakeESFile("/foo/misses");
XCTAssertSemaTrue(*sema, 5, "Log wasn't called within expected time window");
};
[self handleMessageWithMatchCalls:YES withMissCalls:NO withBlock:testBlock];
[self handleMessageShouldLog:YES shouldRemoveFromCache:YES withBlock:testBlock];
}
#endif
}
- (void)testHandleEventCloseNotModifiedWithWasMappedWritable {
- (void)testHandleEventCloseMappedWritableMissesRegex {
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
// CLOSE not modified, but was_mapped_writable, remove from cache, and does not match
@@ -220,13 +243,15 @@ es_file_t targetFileMissesRegex = MakeESFile("/foo/misses");
esMsg->event.close.target = &targetFileMissesRegex;
Message msg(mockESApi, esMsg);
OCMExpect([mockCC handleEvent:msg withLogger:nullptr]).ignoringNonObjectArgs();
XCTAssertNoThrow([recorderClient handleMessage:Message(mockESApi, esMsg)
recordEventMetrics:^(EventDisposition d) {
XCTFail("Metrics record callback should not be called here");
}]);
};
[self handleMessageWithMatchCalls:NO withMissCalls:YES withBlock:testBlock];
[self handleMessageShouldLog:NO shouldRemoveFromCache:YES withBlock:testBlock];
}
#endif
}

View File

@@ -26,10 +26,8 @@
@interface SNTEndpointSecurityTamperResistance
: SNTEndpointSecurityClient <SNTEndpointSecurityEventHandler>
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
logger:(std::shared_ptr<santa::santad::logs::endpoint_security::Logger>)logger;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<santa::Logger>)logger;
@end

View File

@@ -15,18 +15,20 @@
#import "Source/santad/EventProviders/SNTEndpointSecurityTamperResistance.h"
#include <EndpointSecurity/ESTypes.h>
#include <bsm/libbsm.h>
#include <string.h>
#include <algorithm>
#import "Source/common/SNTLogging.h"
#include "Source/santad/DataLayer/WatchItemPolicy.h"
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::EndpointSecurityAPI;
using santa::EventDisposition;
using santa::Logger;
using santa::Message;
using santa::WatchItemPathType;
static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-driver";
@@ -35,11 +37,11 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
}
- (instancetype)initWithESAPI:(std::shared_ptr<EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
metrics:(std::shared_ptr<santa::Metrics>)metrics
logger:(std::shared_ptr<Logger>)logger {
self = [super initWithESAPI:std::move(esApi)
metrics:std::move(metrics)
processor:santa::santad::Processor::kTamperResistance];
processor:santa::Processor::kTamperResistance];
if (self) {
_logger = logger;
@@ -85,6 +87,26 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
break;
}
case ES_EVENT_TYPE_AUTH_SIGNAL: {
// Only block signals sent to us and not from launchd.
if (audit_token_to_pid(esMsg->event.signal.target->audit_token) == getpid() &&
audit_token_to_pid(esMsg->process->audit_token) != 1) {
LOGW(@"Preventing attempt to kill Santa daemon");
result = ES_AUTH_RESULT_DENY;
}
break;
}
case ES_EVENT_TYPE_AUTH_EXEC: {
// When not running a debug build, prevent attempts to kill Santa
// by launchctl commands.
#ifndef DEBUG
result = ValidateLaunchctlExec(esMsg);
if (result == ES_AUTH_RESULT_DENY) LOGW(@"Preventing attempt to kill Santa daemon");
#endif
break;
}
case ES_EVENT_TYPE_AUTH_KEXTLOAD: {
// TODO(mlw): Since we don't package the kext anymore, we should consider removing this.
// TODO(mlw): Consider logging when kext loads are attempted.
@@ -120,15 +142,53 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
for (const auto &path : protectedPaths) {
watchPaths.push_back({path, WatchItemPathType::kLiteral});
}
watchPaths.push_back({"/Library/SystemExtensions", WatchItemPathType::kPrefix});
watchPaths.push_back({"/bin/launchctl", WatchItemPathType::kLiteral});
// Begin watching the protected set
[super muteTargetPaths:watchPaths];
[super subscribeAndClearCache:{
ES_EVENT_TYPE_AUTH_KEXTLOAD,
ES_EVENT_TYPE_AUTH_SIGNAL,
ES_EVENT_TYPE_AUTH_EXEC,
ES_EVENT_TYPE_AUTH_UNLINK,
ES_EVENT_TYPE_AUTH_RENAME,
}];
}
es_auth_result_t ValidateLaunchctlExec(const Message &esMsg) {
es_string_token_t exec_path = esMsg->event.exec.target->executable->path;
if (strncmp(exec_path.data, "/bin/launchctl", exec_path.length) != 0) {
return ES_AUTH_RESULT_ALLOW;
}
// Ensure there are at least 2 arguments after the command
std::shared_ptr<EndpointSecurityAPI> esApi = esMsg.ESAPI();
uint32_t argCount = esApi->ExecArgCount(&esMsg->event.exec);
if (argCount < 2) {
return ES_AUTH_RESULT_ALLOW;
}
// Check for some allowed subcommands
es_string_token_t arg = esApi->ExecArg(&esMsg->event.exec, 1);
static const std::unordered_set<std::string> safe_commands{
"blame", "help", "hostinfo", "list", "plist", "print", "procinfo",
};
if (safe_commands.find(std::string(arg.data, arg.length)) != safe_commands.end()) {
return ES_AUTH_RESULT_ALLOW;
}
// Check whether com.google.santa.daemon is in the argument list.
// launchctl no longer accepts PIDs to operate on.
for (int i = 2; i < argCount; i++) {
es_string_token_t arg = esApi->ExecArg(&esMsg->event.exec, i);
if (strnstr(arg.data, "com.google.santa.daemon", arg.length) != NULL) {
return ES_AUTH_RESULT_DENY;
}
}
return ES_AUTH_RESULT_ALLOW;
}
@end

View File

@@ -31,10 +31,10 @@
#import "Source/santad/EventProviders/SNTEndpointSecurityTamperResistance.h"
#import "Source/santad/Metrics.h"
using santa::santad::EventDisposition;
using santa::santad::data_layer::WatchItemPathType;
using santa::santad::event_providers::endpoint_security::Client;
using santa::santad::event_providers::endpoint_security::Message;
using santa::Client;
using santa::EventDisposition;
using santa::Message;
using santa::WatchItemPathType;
static constexpr std::string_view kEventsDBPath = "/private/var/db/santa/events.db";
static constexpr std::string_view kRulesDBPath = "/private/var/db/santa/rules.db";
@@ -49,9 +49,8 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
- (void)testEnable {
// Ensure the client subscribes to expected event types
std::set<es_event_type_t> expectedEventSubs{
ES_EVENT_TYPE_AUTH_KEXTLOAD,
ES_EVENT_TYPE_AUTH_UNLINK,
ES_EVENT_TYPE_AUTH_RENAME,
ES_EVENT_TYPE_AUTH_KEXTLOAD, ES_EVENT_TYPE_AUTH_SIGNAL, ES_EVENT_TYPE_AUTH_EXEC,
ES_EVENT_TYPE_AUTH_UNLINK, ES_EVENT_TYPE_AUTH_RENAME,
};
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
@@ -70,6 +69,8 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
// Setup mocks to handle muting the rules db and events db
EXPECT_CALL(*mockESApi, MuteTargetPath(testing::_, testing::_, WatchItemPathType::kLiteral))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mockESApi, MuteTargetPath(testing::_, testing::_, WatchItemPathType::kPrefix))
.WillRepeatedly(testing::Return(true));
SNTEndpointSecurityTamperResistance *tamperClient =
[[SNTEndpointSecurityTamperResistance alloc] initWithESAPI:mockESApi
@@ -79,6 +80,10 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
[mockTamperClient enable];
for (const auto &event : expectedEventSubs) {
XCTAssertNoThrow(santa::EventTypeToString(event));
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
[mockTamperClient stopMocking];
}
@@ -86,7 +91,7 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
- (void)testHandleMessage {
es_file_t file = MakeESFile("foo");
es_process_t proc = MakeESProcess(&file);
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_AUTH_EXEC, &proc, ActionType::Auth);
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_AUTH_LINK, &proc, ActionType::Auth);
es_file_t fileEventsDB = MakeESFile(kEventsDBPath.data());
es_file_t fileRulesDB = MakeESFile(kRulesDBPath.data());
@@ -106,6 +111,12 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
{&benignTok, ES_AUTH_RESULT_ALLOW},
};
std::map<std::pair<pid_t, pid_t>, es_auth_result_t> pidsToResult{
{{getpid(), 31838}, ES_AUTH_RESULT_DENY},
{{getpid(), 1}, ES_AUTH_RESULT_ALLOW},
{{435, 98381}, ES_AUTH_RESULT_ALLOW},
};
dispatch_semaphore_t semaMetrics = dispatch_semaphore_create(0);
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
@@ -232,6 +243,31 @@ static constexpr std::string_view kSantaKextIdentifier = "com.google.santa-drive
}
}
// Check SIGNAL tamper events
{
esMsg.event_type = ES_EVENT_TYPE_AUTH_SIGNAL;
for (const auto &kv : pidsToResult) {
Message msg(mockESApi, &esMsg);
es_process_t target_proc = MakeESProcess(&file);
target_proc.audit_token = MakeAuditToken(kv.first.first, 42);
esMsg.event.signal.target = &target_proc;
esMsg.process->audit_token = MakeAuditToken(kv.first.second, 42);
[mockTamperClient
handleMessage:std::move(msg)
recordEventMetrics:^(EventDisposition d) {
XCTAssertEqual(d, kv.second == ES_AUTH_RESULT_DENY ? EventDisposition::kProcessed
: EventDisposition::kDropped);
dispatch_semaphore_signal(semaMetrics);
}];
XCTAssertSemaTrue(semaMetrics, 5, "Metrics not recorded within expected window");
XCTAssertEqual(gotAuthResult, kv.second);
XCTAssertEqual(gotCachable, kv.second == ES_AUTH_RESULT_ALLOW);
}
}
XCTBubbleMockVerifyAndClearExpectations(mockESApi.get());
XCTAssertTrue(OCMVerifyAll(mockTamperClient));

View File

@@ -21,10 +21,9 @@
@interface SNTEndpointSecurityTreeAwareClient : SNTEndpointSecurityClient
@property std::shared_ptr<santa::santad::process_tree::ProcessTree> processTree;
- (instancetype)
initWithESAPI:
(std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::santad::Metrics>)metrics
processor:(santa::santad::Processor)processor
processTree:(std::shared_ptr<santa::santad::process_tree::ProcessTree>)processTree;
- (instancetype)initWithESAPI:(std::shared_ptr<santa::EndpointSecurityAPI>)esApi
metrics:(std::shared_ptr<santa::Metrics>)metrics
processor:(santa::Processor)processor
processTree:
(std::shared_ptr<santa::santad::process_tree::ProcessTree>)processTree;
@end

View File

@@ -23,11 +23,11 @@
#include "Source/santad/ProcessTree/process_tree.h"
#include "Source/santad/ProcessTree/process_tree_macos.h"
using santa::santad::EventDisposition;
using santa::santad::Metrics;
using santa::santad::Processor;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::Message;
using santa::EndpointSecurityAPI;
using santa::EventDisposition;
using santa::Message;
using santa::Metrics;
using santa::Processor;
@implementation SNTEndpointSecurityTreeAwareClient {
std::vector<bool> _addedEvents;

View File

@@ -30,51 +30,48 @@
// Forward declarations
@class SNTStoredEvent;
namespace santa::santad::logs::endpoint_security {
namespace santa {
class LoggerPeer;
}
namespace santa::santad::logs::endpoint_security {
namespace santa {
class Logger {
public:
static std::unique_ptr<Logger> Create(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTEventLogType log_type, SNTDecisionCache *decision_cache, NSString *event_log_path,
NSString *spool_log_path, size_t spool_dir_size_threshold, size_t spool_file_size_threshold,
uint64_t spool_flush_timeout_ms);
static std::unique_ptr<Logger> Create(std::shared_ptr<santa::EndpointSecurityAPI> esapi,
SNTEventLogType log_type, SNTDecisionCache *decision_cache,
NSString *event_log_path, NSString *spool_log_path,
size_t spool_dir_size_threshold,
size_t spool_file_size_threshold,
uint64_t spool_flush_timeout_ms);
Logger(std::shared_ptr<serializers::Serializer> serializer,
std::shared_ptr<writers::Writer> writer);
Logger(std::shared_ptr<santa::Serializer> serializer, std::shared_ptr<santa::Writer> writer);
virtual ~Logger() = default;
virtual void Log(
std::unique_ptr<santa::santad::event_providers::endpoint_security::EnrichedMessage> msg);
virtual void Log(std::unique_ptr<santa::EnrichedMessage> msg);
void LogAllowlist(const santa::santad::event_providers::endpoint_security::Message &msg,
const std::string_view hash);
void LogAllowlist(const santa::Message &msg, const std::string_view hash);
void LogBundleHashingEvents(NSArray<SNTStoredEvent *> *events);
void LogDiskAppeared(NSDictionary *props);
void LogDiskDisappeared(NSDictionary *props);
virtual void LogFileAccess(
const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision);
virtual void LogFileAccess(const std::string &policy_version, const std::string &policy_name,
const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision);
void Flush();
friend class santa::santad::logs::endpoint_security::LoggerPeer;
friend class santa::LoggerPeer;
private:
std::shared_ptr<serializers::Serializer> serializer_;
std::shared_ptr<writers::Writer> writer_;
std::shared_ptr<santa::Serializer> serializer_;
std::shared_ptr<santa::Writer> writer_;
};
} // namespace santa::santad::logs::endpoint_security
} // namespace santa
#endif

View File

@@ -27,19 +27,19 @@
#include "Source/santad/Logs/EndpointSecurity/Writers/Syslog.h"
#include "Source/santad/SNTDecisionCache.h"
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::serializers::BasicString;
using santa::santad::logs::endpoint_security::serializers::Empty;
using santa::santad::logs::endpoint_security::serializers::Protobuf;
using santa::santad::logs::endpoint_security::writers::File;
using santa::santad::logs::endpoint_security::writers::Null;
using santa::santad::logs::endpoint_security::writers::Spool;
using santa::santad::logs::endpoint_security::writers::Syslog;
using santa::BasicString;
using santa::Empty;
using santa::EndpointSecurityAPI;
using santa::EnrichedMessage;
using santa::EnrichedProcess;
using santa::File;
using santa::Message;
using santa::Null;
using santa::Protobuf;
using santa::Spool;
using santa::Syslog;
namespace santa::santad::logs::endpoint_security {
namespace santa {
// Flush the write buffer every 5 seconds
static const uint64_t kFlushBufferTimeoutMS = 10000;
@@ -81,8 +81,7 @@ std::unique_ptr<Logger> Logger::Create(std::shared_ptr<EndpointSecurityAPI> esap
}
}
Logger::Logger(std::shared_ptr<serializers::Serializer> serializer,
std::shared_ptr<writers::Writer> writer)
Logger::Logger(std::shared_ptr<santa::Serializer> serializer, std::shared_ptr<santa::Writer> writer)
: serializer_(std::move(serializer)), writer_(std::move(writer)) {}
void Logger::Log(std::unique_ptr<EnrichedMessage> msg) {
@@ -107,11 +106,10 @@ void Logger::LogDiskDisappeared(NSDictionary *props) {
writer_->Write(serializer_->SerializeDiskDisappeared(props));
}
void Logger::LogFileAccess(
const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision) {
void Logger::LogFileAccess(const std::string &policy_version, const std::string &policy_name,
const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision) {
writer_->Write(serializer_->SerializeFileAccess(policy_version, policy_name, msg,
enriched_process, target, decision));
}
@@ -120,4 +118,4 @@ void Logger::Flush() {
writer_->Flush();
}
} // namespace santa::santad::logs::endpoint_security
} // namespace santa

View File

@@ -39,21 +39,21 @@
#include "Source/santad/Logs/EndpointSecurity/Writers/Syslog.h"
#include "Source/santad/Logs/EndpointSecurity/Writers/Writer.h"
using santa::santad::event_providers::endpoint_security::EnrichedClose;
using santa::santad::event_providers::endpoint_security::EnrichedFile;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::Logger;
using santa::santad::logs::endpoint_security::serializers::BasicString;
using santa::santad::logs::endpoint_security::serializers::Empty;
using santa::santad::logs::endpoint_security::serializers::Protobuf;
using santa::santad::logs::endpoint_security::writers::File;
using santa::santad::logs::endpoint_security::writers::Null;
using santa::santad::logs::endpoint_security::writers::Spool;
using santa::santad::logs::endpoint_security::writers::Syslog;
using santa::BasicString;
using santa::Empty;
using santa::EnrichedClose;
using santa::EnrichedFile;
using santa::EnrichedMessage;
using santa::EnrichedProcess;
using santa::File;
using santa::Logger;
using santa::Message;
using santa::Null;
using santa::Protobuf;
using santa::Spool;
using santa::Syslog;
namespace santa::santad::logs::endpoint_security {
namespace santa {
class LoggerPeer : public Logger {
public:
@@ -62,14 +62,14 @@ class LoggerPeer : public Logger {
LoggerPeer(std::unique_ptr<Logger> l) : Logger(l->serializer_, l->writer_) {}
std::shared_ptr<serializers::Serializer> Serializer() { return serializer_; }
std::shared_ptr<santa::Serializer> Serializer() { return serializer_; }
std::shared_ptr<writers::Writer> Writer() { return writer_; }
std::shared_ptr<santa::Writer> Writer() { return writer_; }
};
} // namespace santa::santad::logs::endpoint_security
} // namespace santa
using santa::santad::logs::endpoint_security::LoggerPeer;
using santa::LoggerPeer;
class MockSerializer : public Empty {
public:
@@ -81,12 +81,10 @@ class MockSerializer : public Empty {
MOCK_METHOD(std::vector<uint8_t>, SerializeDiskAppeared, (NSDictionary *));
MOCK_METHOD(std::vector<uint8_t>, SerializeDiskDisappeared, (NSDictionary *));
MOCK_METHOD(
std::vector<uint8_t>, SerializeFileAccess,
(const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision));
MOCK_METHOD(std::vector<uint8_t>, SerializeFileAccess,
(const std::string &policy_version, const std::string &policy_name,
const santa::Message &msg, const santa::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision));
};
class MockWriter : public Null {

View File

@@ -21,19 +21,17 @@
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/Logs/EndpointSecurity/Logger.h"
class MockLogger : public santa::santad::logs::endpoint_security::Logger {
class MockLogger : public santa::Logger {
public:
using Logger::Logger;
MockLogger() : Logger(nullptr, nullptr) {}
MOCK_METHOD(
void, LogFileAccess,
(const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess
&enriched_process,
const std::string &target, FileAccessPolicyDecision decision));
MOCK_METHOD(void, LogFileAccess,
(const std::string &policy_version,
const std::string &policy_name, const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision));
};
#endif

View File

@@ -21,52 +21,53 @@
#include <sstream>
#include <vector>
#include "Source/common/Platform.h"
#import "Source/common/SNTCachedDecision.h"
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
#include "Source/santad/Logs/EndpointSecurity/Serializers/Serializer.h"
#import "Source/santad/SNTDecisionCache.h"
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
class BasicString : public Serializer {
public:
static std::shared_ptr<BasicString> Create(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache, bool prefix_time_name = true);
static std::shared_ptr<BasicString> Create(std::shared_ptr<santa::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache,
bool prefix_time_name = true);
BasicString(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache, bool prefix_time_name);
BasicString(std::shared_ptr<santa::EndpointSecurityAPI> esapi, SNTDecisionCache *decision_cache,
bool prefix_time_name);
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExec &,
SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExec &, SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedCSInvalidated &) override;
#if HAVE_MACOS_13
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionUnlock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingAttach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingDetach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogout &) override;
#endif
std::vector<uint8_t> SerializeFileAccess(
const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name,
const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target,
FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeAllowlist(
const santa::santad::event_providers::endpoint_security::Message &,
const std::string_view) override;
std::vector<uint8_t> SerializeAllowlist(const santa::Message &, const std::string_view) override;
std::vector<uint8_t> SerializeBundleHashingEvent(SNTStoredEvent *) override;
@@ -77,10 +78,10 @@ class BasicString : public Serializer {
std::string CreateDefaultString(size_t reserved_size = 512);
std::vector<uint8_t> FinalizeString(std::string &str);
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi_;
std::shared_ptr<santa::EndpointSecurityAPI> esapi_;
bool prefix_time_name_;
};
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa
#endif

View File

@@ -35,26 +35,37 @@
#include "Source/santad/Logs/EndpointSecurity/Serializers/Utilities.h"
#import "Source/santad/SNTDecisionCache.h"
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::EnrichedClose;
using santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated;
using santa::santad::event_providers::endpoint_security::EnrichedExchange;
using santa::santad::event_providers::endpoint_security::EnrichedExec;
using santa::santad::event_providers::endpoint_security::EnrichedExit;
using santa::santad::event_providers::endpoint_security::EnrichedFork;
using santa::santad::event_providers::endpoint_security::EnrichedLink;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::EnrichedRename;
using santa::santad::event_providers::endpoint_security::EnrichedUnlink;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::serializers::Utilities::MountFromName;
using santa::santad::logs::endpoint_security::serializers::Utilities::NonNull;
using santa::santad::logs::endpoint_security::serializers::Utilities::Pid;
using santa::santad::logs::endpoint_security::serializers::Utilities::Pidversion;
using santa::santad::logs::endpoint_security::serializers::Utilities::RealGroup;
using santa::santad::logs::endpoint_security::serializers::Utilities::RealUser;
using santa::EndpointSecurityAPI;
using santa::EnrichedClose;
using santa::EnrichedCSInvalidated;
using santa::EnrichedEventType;
using santa::EnrichedExchange;
using santa::EnrichedExec;
using santa::EnrichedExit;
using santa::EnrichedFork;
using santa::EnrichedLink;
using santa::EnrichedLoginLogin;
using santa::EnrichedLoginLogout;
using santa::EnrichedLoginWindowSessionLock;
using santa::EnrichedLoginWindowSessionLogin;
using santa::EnrichedLoginWindowSessionLogout;
using santa::EnrichedLoginWindowSessionUnlock;
using santa::EnrichedOpenSSHLogin;
using santa::EnrichedOpenSSHLogout;
using santa::EnrichedProcess;
using santa::EnrichedRename;
using santa::EnrichedScreenSharingAttach;
using santa::EnrichedScreenSharingDetach;
using santa::EnrichedUnlink;
using santa::Message;
using santa::MountFromName;
using santa::NonNull;
using santa::Pid;
using santa::Pidversion;
using santa::RealGroup;
using santa::RealUser;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
static inline SanitizableString FilePath(const es_file_t *file) {
return SanitizableString(file);
@@ -168,6 +179,68 @@ static inline void AppendUserGroup(std::string &str, const audit_token_t &tok,
str.append(group.has_value() ? group->get()->c_str() : "(null)");
}
static inline void AppendInstigator(std::string &str, const EnrichedEventType &event) {
AppendProcess(str, event->process);
AppendUserGroup(str, event->process->audit_token, event.instigator().real_user(),
event.instigator().real_group());
}
#if HAVE_MACOS_13
static inline void AppendEventUser(std::string &str, const es_string_token_t &user,
std::optional<uid_t> uid) {
if (user.length > 0) {
str.append("|event_user=");
str.append(user.data);
}
if (uid.has_value()) {
str.append("|event_uid=");
str.append(std::to_string(uid.value()));
}
}
static inline void AppendGraphicalSession(std::string &str, es_graphical_session_id_t session_id) {
str.append("|graphical_session_id=");
str.append(std::to_string(session_id));
}
static inline void AppendSocketAddress(std::string &str, es_address_type_t type,
es_string_token_t addr) {
str.append("|address_type=");
switch (type) {
case ES_ADDRESS_TYPE_NONE: str.append("none"); break;
case ES_ADDRESS_TYPE_IPV4: str.append("ipv4"); break;
case ES_ADDRESS_TYPE_IPV6: str.append("ipv6"); break;
case ES_ADDRESS_TYPE_NAMED_SOCKET: str.append("named_socket"); break;
default: str.append("unknown"); break;
}
if (addr.length > 0) {
str.append("|address=");
str.append(SanitizableString(addr).Sanitized());
}
}
static inline std::string GetOpenSSHLoginResult(std::string &str,
es_openssh_login_result_type_t result) {
switch (result) {
case ES_OPENSSH_LOGIN_EXCEED_MAXTRIES: return "LOGIN_EXCEED_MAXTRIES";
case ES_OPENSSH_LOGIN_ROOT_DENIED: return "LOGIN_ROOT_DENIED";
case ES_OPENSSH_AUTH_SUCCESS: return "AUTH_SUCCESS";
case ES_OPENSSH_AUTH_FAIL_NONE: return "AUTH_FAIL_NONE";
case ES_OPENSSH_AUTH_FAIL_PASSWD: return "AUTH_FAIL_PASSWD";
case ES_OPENSSH_AUTH_FAIL_KBDINT: return "AUTH_FAIL_KBDINT";
case ES_OPENSSH_AUTH_FAIL_PUBKEY: return "AUTH_FAIL_PUBKEY";
case ES_OPENSSH_AUTH_FAIL_HOSTBASED: return "AUTH_FAIL_HOSTBASED";
case ES_OPENSSH_AUTH_FAIL_GSSAPI: return "AUTH_FAIL_GSSAPI";
case ES_OPENSSH_INVALID_USER: return "INVALID_USER";
default: return "UNKNOWN";
}
}
#endif // HAVE_MACOS_13
static char *FormattedDateString(char *buf, size_t len) {
struct timeval tv;
struct tm tm;
@@ -219,37 +292,30 @@ std::vector<uint8_t> BasicString::FinalizeString(std::string &str) {
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedClose &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=WRITE|path=");
str.append(FilePath(esm.event.close.target).Sanitized());
str.append(FilePath(msg->event.close.target).Sanitized());
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExchange &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=EXCHANGE|path=");
str.append(FilePath(esm.event.exchangedata.file1).Sanitized());
str.append(FilePath(msg->event.exchangedata.file1).Sanitized());
str.append("|newpath=");
str.append(FilePath(esm.event.exchangedata.file2).Sanitized());
str.append(FilePath(msg->event.exchangedata.file2).Sanitized());
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExec &msg, SNTCachedDecision *cd) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString(1024); // EXECs tend to be bigger, reserve more space.
str.append("action=EXEC|decision=");
@@ -285,27 +351,27 @@ std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExec &msg, SNTC
}
str.append("|pid=");
str.append(std::to_string(Pid(esm.event.exec.target->audit_token)));
str.append(std::to_string(Pid(msg->event.exec.target->audit_token)));
str.append("|pidversion=");
str.append(std::to_string(Pidversion(esm.event.exec.target->audit_token)));
str.append(std::to_string(Pidversion(msg->event.exec.target->audit_token)));
str.append("|ppid=");
str.append(std::to_string(esm.event.exec.target->original_ppid));
str.append(std::to_string(msg->event.exec.target->original_ppid));
AppendUserGroup(str, esm.event.exec.target->audit_token, msg.instigator().real_user(),
AppendUserGroup(str, msg->event.exec.target->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
str.append("|mode=");
str.append(GetModeString(cd.decisionClientMode));
str.append("|path=");
str.append(FilePath(esm.event.exec.target->executable).Sanitized());
str.append(FilePath(msg->event.exec.target->executable).Sanitized());
NSString *origPath = Utilities::OriginalPathForTranslocation(esm.event.exec.target);
NSString *origPath = santa::OriginalPathForTranslocation(msg->event.exec.target);
if (origPath) {
str.append("|origpath=");
str.append(SanitizableString(origPath).Sanitized());
}
uint32_t argCount = esapi_->ExecArgCount(&esm.event.exec);
uint32_t argCount = esapi_->ExecArgCount(&msg->event.exec);
if (argCount > 0) {
str.append("|args=");
for (uint32_t i = 0; i < argCount; i++) {
@@ -313,7 +379,7 @@ std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExec &msg, SNTC
str.append(" ");
}
str.append(SanitizableString(esapi_->ExecArg(&esm.event.exec, i)).Sanitized());
str.append(SanitizableString(esapi_->ExecArg(&msg->event.exec, i)).Sanitized());
}
}
@@ -321,113 +387,269 @@ std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExec &msg, SNTC
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedExit &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=EXIT|pid=");
str.append(std::to_string(Pid(esm.process->audit_token)));
str.append(std::to_string(Pid(msg->process->audit_token)));
str.append("|pidversion=");
str.append(std::to_string(Pidversion(esm.process->audit_token)));
str.append(std::to_string(Pidversion(msg->process->audit_token)));
str.append("|ppid=");
str.append(std::to_string(esm.process->original_ppid));
str.append(std::to_string(msg->process->original_ppid));
str.append("|uid=");
str.append(std::to_string(RealUser(esm.process->audit_token)));
str.append(std::to_string(RealUser(msg->process->audit_token)));
str.append("|gid=");
str.append(std::to_string(RealGroup(esm.process->audit_token)));
str.append(std::to_string(RealGroup(msg->process->audit_token)));
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedFork &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=FORK|pid=");
str.append(std::to_string(Pid(esm.event.fork.child->audit_token)));
str.append(std::to_string(Pid(msg->event.fork.child->audit_token)));
str.append("|pidversion=");
str.append(std::to_string(Pidversion(esm.event.fork.child->audit_token)));
str.append(std::to_string(Pidversion(msg->event.fork.child->audit_token)));
str.append("|ppid=");
str.append(std::to_string(esm.event.fork.child->original_ppid));
str.append(std::to_string(msg->event.fork.child->original_ppid));
str.append("|uid=");
str.append(std::to_string(RealUser(esm.event.fork.child->audit_token)));
str.append(std::to_string(RealUser(msg->event.fork.child->audit_token)));
str.append("|gid=");
str.append(std::to_string(RealGroup(esm.event.fork.child->audit_token)));
str.append(std::to_string(RealGroup(msg->event.fork.child->audit_token)));
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLink &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=LINK|path=");
str.append(FilePath(esm.event.link.source).Sanitized());
str.append(FilePath(msg->event.link.source).Sanitized());
str.append("|newpath=");
str.append(FilePath(esm.event.link.target_dir).Sanitized());
str.append(FilePath(msg->event.link.target_dir).Sanitized());
str.append("/");
str.append(SanitizableString(esm.event.link.target_filename).Sanitized());
str.append(SanitizableString(msg->event.link.target_filename).Sanitized());
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedRename &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=RENAME|path=");
str.append(FilePath(esm.event.rename.source).Sanitized());
str.append(FilePath(msg->event.rename.source).Sanitized());
str.append("|newpath=");
switch (esm.event.rename.destination_type) {
switch (msg->event.rename.destination_type) {
case ES_DESTINATION_TYPE_EXISTING_FILE:
str.append(FilePath(esm.event.rename.destination.existing_file).Sanitized());
str.append(FilePath(msg->event.rename.destination.existing_file).Sanitized());
break;
case ES_DESTINATION_TYPE_NEW_PATH:
str.append(FilePath(esm.event.rename.destination.new_path.dir).Sanitized());
str.append(FilePath(msg->event.rename.destination.new_path.dir).Sanitized());
str.append("/");
str.append(SanitizableString(esm.event.rename.destination.new_path.filename).Sanitized());
str.append(SanitizableString(msg->event.rename.destination.new_path.filename).Sanitized());
break;
default: str.append("(null)"); break;
}
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedUnlink &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=DELETE|path=");
str.append(FilePath(esm.event.unlink.target).Sanitized());
str.append(FilePath(msg->event.unlink.target).Sanitized());
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedCSInvalidated &msg) {
const es_message_t &esm = msg.es_msg();
std::string str = CreateDefaultString();
str.append("action=CODESIGNING_INVALIDATED");
AppendProcess(str, esm.process);
AppendUserGroup(str, esm.process->audit_token, msg.instigator().real_user(),
msg.instigator().real_group());
AppendInstigator(str, msg);
str.append("|codesigning_flags=");
str.append([NSString stringWithFormat:@"0x%08x", esm.process->codesigning_flags].UTF8String);
str.append([NSString stringWithFormat:@"0x%08x", msg->process->codesigning_flags].UTF8String);
return FinalizeString(str);
}
#if HAVE_MACOS_13
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginWindowSessionLogin &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGIN_WINDOW_SESSION_LOGIN");
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.lw_session_login->username, msg.UID());
AppendGraphicalSession(str, msg->event.lw_session_login->graphical_session_id);
return FinalizeString(str);
};
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginWindowSessionLogout &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGIN_WINDOW_SESSION_LOGOUT");
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.lw_session_logout->username, msg.UID());
AppendGraphicalSession(str, msg->event.lw_session_logout->graphical_session_id);
return FinalizeString(str);
};
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginWindowSessionLock &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGIN_WINDOW_SESSION_LOCK");
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.lw_session_lock->username, msg.UID());
AppendGraphicalSession(str, msg->event.lw_session_lock->graphical_session_id);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginWindowSessionUnlock &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGIN_WINDOW_SESSION_UNLOCK");
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.lw_session_unlock->username, msg.UID());
AppendGraphicalSession(str, msg->event.lw_session_unlock->graphical_session_id);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedScreenSharingAttach &msg) {
std::string str = CreateDefaultString();
str.append("action=SCREEN_SHARING_ATTACH|success=");
str.append(msg->event.screensharing_attach->success ? "true" : "false");
AppendSocketAddress(str, msg->event.screensharing_attach->source_address_type,
msg->event.screensharing_attach->source_address);
if (msg->event.screensharing_attach->viewer_appleid.length > 0) {
str.append("|viewer=");
str.append(SanitizableString(msg->event.screensharing_attach->viewer_appleid).Sanitized());
}
if (msg->event.screensharing_attach->authentication_type.length > 0) {
str.append("|auth_type=");
str.append(SanitizableString(msg->event.screensharing_attach->authentication_type).Sanitized());
}
if (msg->event.screensharing_attach->authentication_username.length > 0) {
str.append("|auth_user=");
str.append(
SanitizableString(msg->event.screensharing_attach->authentication_username).Sanitized());
}
if (msg->event.screensharing_attach->session_username.length > 0) {
str.append("|session_user=");
str.append(SanitizableString(msg->event.screensharing_attach->session_username).Sanitized());
}
str.append("|existing_session=");
str.append(msg->event.screensharing_attach->existing_session ? "true" : "false");
AppendInstigator(str, msg);
AppendGraphicalSession(str, msg->event.screensharing_attach->graphical_session_id);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedScreenSharingDetach &msg) {
std::string str = CreateDefaultString();
str.append("action=SCREEN_SHARING_DETACH");
AppendSocketAddress(str, msg->event.screensharing_detach->source_address_type,
msg->event.screensharing_detach->source_address);
if (msg->event.screensharing_detach->viewer_appleid.length > 0) {
str.append("|viewer=");
str.append(SanitizableString(msg->event.screensharing_detach->viewer_appleid).Sanitized());
}
AppendInstigator(str, msg);
AppendGraphicalSession(str, msg->event.screensharing_detach->graphical_session_id);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedOpenSSHLogin &msg) {
std::string str = CreateDefaultString();
str.append("action=OPENSSH_LOGIN|success=");
str.append(msg->event.openssh_login->success ? "true" : "false");
str.append("|result_type=");
str.append(GetOpenSSHLoginResult(str, msg->event.openssh_login->result_type));
AppendSocketAddress(str, msg->event.openssh_login->source_address_type,
msg->event.openssh_login->source_address);
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.openssh_login->username,
msg->event.openssh_login->has_uid
? std::make_optional<uid_t>(msg->event.openssh_login->uid.uid)
: std::nullopt);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedOpenSSHLogout &msg) {
std::string str = CreateDefaultString();
str.append("action=OPENSSH_LOGOUT");
AppendSocketAddress(str, msg->event.openssh_logout->source_address_type,
msg->event.openssh_logout->source_address);
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.openssh_logout->username,
std::make_optional<uid_t>(msg->event.openssh_logout->uid));
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginLogin &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGIN|success=");
str.append(msg->event.login_login->success ? "true" : "false");
if (!msg->event.login_login->success) {
str.append("|failure=");
str.append(SanitizableString(msg->event.login_login->failure_message).Sanitized());
}
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.login_login->username,
msg->event.login_login->has_uid
? std::make_optional<uid_t>(msg->event.login_login->uid.uid)
: std::nullopt);
return FinalizeString(str);
}
std::vector<uint8_t> BasicString::SerializeMessage(const EnrichedLoginLogout &msg) {
std::string str = CreateDefaultString();
str.append("action=LOGOUT");
AppendInstigator(str, msg);
AppendEventUser(str, msg->event.login_logout->username,
std::make_optional<uid_t>(msg->event.login_logout->uid));
return FinalizeString(str);
}
#endif // HAVE_MACOS_13
std::vector<uint8_t> BasicString::SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name,
const Message &msg,
@@ -463,7 +685,7 @@ std::vector<uint8_t> BasicString::SerializeAllowlist(const Message &msg,
str.append("|pidversion=");
str.append(std::to_string(Pidversion(msg->process->audit_token)));
str.append("|path=");
str.append(FilePath(Utilities::GetAllowListTargetFile(msg)).Sanitized());
str.append(FilePath(santa::GetAllowListTargetFile(msg)).Sanitized());
str.append("|sha256=");
str.append(hash);
@@ -493,9 +715,9 @@ std::vector<uint8_t> BasicString::SerializeDiskAppeared(NSDictionary *props) {
NSString *dmg_path = nil;
NSString *serial = nil;
if ([props[@"DADeviceModel"] isEqual:@"Disk Image"]) {
dmg_path = Utilities::DiskImageForDevice(props[@"DADevicePath"]);
dmg_path = santa::DiskImageForDevice(props[@"DADevicePath"]);
} else {
serial = Utilities::SerialForDevice(props[@"DADevicePath"]);
serial = santa::SerialForDevice(props[@"DADevicePath"]);
}
NSString *model = [NSString
@@ -546,4 +768,4 @@ std::vector<uint8_t> BasicString::SerializeDiskDisappeared(NSDictionary *props)
return FinalizeString(str);
}
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa

View File

@@ -23,6 +23,7 @@
#include <map>
#include <string>
#include "Source/common/Platform.h"
#import "Source/common/SNTCachedDecision.h"
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTConfigurator.h"
@@ -36,24 +37,24 @@
#include "Source/santad/Logs/EndpointSecurity/Serializers/Serializer.h"
#import "Source/santad/SNTDecisionCache.h"
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::serializers::BasicString;
using santa::santad::logs::endpoint_security::serializers::Serializer;
using santa::BasicString;
using santa::Enricher;
using santa::Message;
using santa::Serializer;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
extern std::string GetDecisionString(SNTEventState event_state);
extern std::string GetReasonString(SNTEventState event_state);
extern std::string GetModeString(SNTClientMode mode);
extern std::string GetAccessTypeString(es_event_type_t event_type);
extern std::string GetFileAccessPolicyDecisionString(FileAccessPolicyDecision decision);
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa
using santa::santad::logs::endpoint_security::serializers::GetAccessTypeString;
using santa::santad::logs::endpoint_security::serializers::GetDecisionString;
using santa::santad::logs::endpoint_security::serializers::GetFileAccessPolicyDecisionString;
using santa::santad::logs::endpoint_security::serializers::GetModeString;
using santa::santad::logs::endpoint_security::serializers::GetReasonString;
using santa::GetAccessTypeString;
using santa::GetDecisionString;
using santa::GetFileAccessPolicyDecisionString;
using santa::GetModeString;
using santa::GetReasonString;
std::string BasicStringSerializeMessage(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg, SNTDecisionCache *decisionCache) {
@@ -265,6 +266,248 @@ std::string BasicStringSerializeMessage(es_message_t *esMsg) {
XCTAssertCppStringEqual(got, want);
}
#if HAVE_MACOS_13
- (void)testSerializeMessageLoginWindowSessionLogin {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN, &proc);
es_event_lw_session_login_t lwLogin = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
esMsg.event.lw_session_login = &lwLogin;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=LOGIN_WINDOW_SESSION_LOGIN|pid=12|ppid=56|process=foo|processpath=foo|"
"uid=-2|user=nobody|gid=-1|group=nogroup|event_user=daemon|event_uid=1|"
"graphical_session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageLoginWindowSessionLogout {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT, &proc);
es_event_lw_session_logout_t lwLogout = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
esMsg.event.lw_session_logout = &lwLogout;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=LOGIN_WINDOW_SESSION_LOGOUT|pid=12|ppid=56|process=foo|processpath="
"foo|uid=-2|user=nobody|gid=-1|group=nogroup|event_user=daemon|event_uid=1|"
"graphical_session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageLoginWindowSessionLock {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK, &proc);
es_event_lw_session_lock_t lwLock = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
esMsg.event.lw_session_lock = &lwLock;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=LOGIN_WINDOW_SESSION_LOCK|pid=12|ppid=56|process=foo|processpath=foo|"
"uid=-2|user=nobody|gid=-1|group=nogroup|event_user=daemon|event_uid=1|"
"graphical_session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageLoginWindowSessionUnlock {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK, &proc);
es_event_lw_session_unlock_t lwUnlock = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
esMsg.event.lw_session_unlock = &lwUnlock;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=LOGIN_WINDOW_SESSION_UNLOCK|pid=12|ppid=56|process=foo|processpath="
"foo|uid=-2|user=nobody|gid=-1|group=nogroup|event_user=daemon|event_uid=1|"
"graphical_session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageLoginLogin {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN, &proc);
es_event_login_login_t login = {
.success = false,
.failure_message = MakeESStringToken("my|failure"),
.username = MakeESStringToken("asdf"),
.has_uid = false,
};
esMsg.event.login_login = &login;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want =
"action=LOGIN|success=false|failure=my<pipe>failure|pid=12|ppid=56|process=foo|processpath=foo|"
"uid=-2|user=nobody|gid=-1|group=nogroup|event_user=asdf|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
login.success = true;
login.has_uid = true;
login.uid.uid = 123;
got = BasicStringSerializeMessage(&esMsg);
want = "action=LOGIN|success=true|pid=12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|"
"gid=-1|group=nogroup|event_user=asdf|event_uid=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageLoginLogout {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT, &proc);
es_event_login_logout_t logout{
.username = MakeESStringToken("asdf"),
.uid = 123,
};
esMsg.event.login_logout = &logout;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=LOGOUT|pid=12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|"
"gid=-1|group=nogroup|event_user=asdf|event_uid=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageScreenSharingAttach {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH, &proc);
es_event_screensharing_attach_t attach{
.success = true,
.source_address_type = ES_ADDRESS_TYPE_IPV6,
.source_address = MakeESStringToken("::1"),
.viewer_appleid = MakeESStringToken("foo@example.com"),
.authentication_type = MakeESStringToken("idk"),
.authentication_username = MakeESStringToken("my_auth_user"),
.session_username = MakeESStringToken("my_session_user"),
.existing_session = true,
.graphical_session_id = 123,
};
esMsg.event.screensharing_attach = &attach;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want =
"action=SCREEN_SHARING_ATTACH|success=true|address_type=ipv6|address=::1|viewer=foo@example."
"com|auth_type=idk|auth_user=my_auth_user|session_user=my_session_user|existing_session=true|"
"pid=12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|gid=-1|group=nogroup|graphical_"
"session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
attach.source_address_type = (es_address_type_t)1234; // Intentionally bad
attach.source_address = MakeESStringToken(NULL);
attach.viewer_appleid = MakeESStringToken(NULL);
attach.authentication_type = MakeESStringToken(NULL);
attach.authentication_username = MakeESStringToken(NULL);
attach.session_username = MakeESStringToken(NULL);
got = BasicStringSerializeMessage(&esMsg);
want = "action=SCREEN_SHARING_ATTACH|success=true|address_type=unknown|existing_session=true|pid="
"12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|gid=-1|group=nogroup|graphical_"
"session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageScreenSharingDetach {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH, &proc);
es_event_screensharing_detach_t detach{
.source_address_type = ES_ADDRESS_TYPE_IPV4,
.source_address = MakeESStringToken("1.2.3.4"),
.viewer_appleid = MakeESStringToken("foo@example.com"),
.graphical_session_id = 123,
};
esMsg.event.screensharing_detach = &detach;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=SCREEN_SHARING_DETACH|address_type=ipv4|address=1.2.3.4|viewer=foo@"
"example.com|pid=12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|"
"gid=-1|group=nogroup|graphical_session_id=123|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageOpenSSHLogin {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN, &proc);
es_event_openssh_login_t login{
.success = false,
.result_type = ES_OPENSSH_AUTH_FAIL_PASSWD,
.source_address_type = ES_ADDRESS_TYPE_NAMED_SOCKET,
.source_address = MakeESStringToken("foo"),
.username = MakeESStringToken("my_user"),
.has_uid = false,
};
esMsg.event.openssh_login = &login;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=OPENSSH_LOGIN|success=false|result_type=AUTH_FAIL_PASSWD|address_type="
"named_socket|address=foo|pid=12|ppid=56|process=foo|processpath=foo|uid=-2|"
"user=nobody|gid=-1|group=nogroup|event_user=my_user|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
login.success = true;
login.result_type = ES_OPENSSH_AUTH_SUCCESS;
login.has_uid = true;
login.uid.uid = 456;
got = BasicStringSerializeMessage(&esMsg);
want = "action=OPENSSH_LOGIN|success=true|result_type=AUTH_SUCCESS|address_type=named_socket|"
"address=foo|pid=12|ppid=56|process=foo|processpath=foo|uid=-2|user=nobody|gid=-1|group="
"nogroup|event_user=my_user|event_uid=456|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
- (void)testSerializeMessageOpenSSHLogout {
es_file_t procFile = MakeESFile("foo");
es_process_t proc = MakeESProcess(&procFile, MakeAuditToken(12, 34), MakeAuditToken(56, 78));
es_message_t esMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT, &proc);
es_event_openssh_logout_t logout{
.source_address_type = ES_ADDRESS_TYPE_IPV4,
.source_address = MakeESStringToken("5.6.7.8"),
.username = MakeESStringToken("my_user"),
.uid = 321,
};
esMsg.event.openssh_logout = &logout;
std::string got = BasicStringSerializeMessage(&esMsg);
std::string want = "action=OPENSSH_LOGOUT|address_type=ipv4|address=5.6.7.8|pid=12|ppid=56|"
"process=foo|processpath=foo|uid=-2|user=nobody|gid=-1|group=nogroup|event_"
"user=my_user|event_uid=321|machineid=my_id\n";
XCTAssertCppStringEqual(got, want);
}
#endif // HAVE_MACOS_13
- (void)testGetAccessTypeString {
std::map<es_event_type_t, std::string> accessTypeToString = {
{ES_EVENT_TYPE_AUTH_OPEN, "OPEN"}, {ES_EVENT_TYPE_AUTH_LINK, "LINK"},

View File

@@ -23,42 +23,41 @@
#include "Source/common/SNTCachedDecision.h"
#include "Source/santad/Logs/EndpointSecurity/Serializers/Serializer.h"
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
class Empty : public Serializer {
public:
static std::shared_ptr<Empty> Create();
Empty();
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExec &,
SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExec &, SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedCSInvalidated &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionUnlock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingAttach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingDetach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogout &) override;
std::vector<uint8_t> SerializeFileAccess(
const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name,
const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target,
FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeAllowlist(
const santa::santad::event_providers::endpoint_security::Message &,
const std::string_view) override;
std::vector<uint8_t> SerializeAllowlist(const santa::Message &, const std::string_view) override;
std::vector<uint8_t> SerializeBundleHashingEvent(SNTStoredEvent *) override;
@@ -66,6 +65,6 @@ class Empty : public Serializer {
std::vector<uint8_t> SerializeDiskDisappeared(NSDictionary *) override;
};
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa
#endif

View File

@@ -14,19 +14,29 @@
#include "Source/santad/Logs/EndpointSecurity/Serializers/Empty.h"
using santa::santad::event_providers::endpoint_security::EnrichedClose;
using santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated;
using santa::santad::event_providers::endpoint_security::EnrichedExchange;
using santa::santad::event_providers::endpoint_security::EnrichedExec;
using santa::santad::event_providers::endpoint_security::EnrichedExit;
using santa::santad::event_providers::endpoint_security::EnrichedFork;
using santa::santad::event_providers::endpoint_security::EnrichedLink;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::EnrichedRename;
using santa::santad::event_providers::endpoint_security::EnrichedUnlink;
using santa::santad::event_providers::endpoint_security::Message;
using santa::EnrichedClose;
using santa::EnrichedCSInvalidated;
using santa::EnrichedExchange;
using santa::EnrichedExec;
using santa::EnrichedExit;
using santa::EnrichedFork;
using santa::EnrichedLink;
using santa::EnrichedLoginLogin;
using santa::EnrichedLoginLogout;
using santa::EnrichedLoginWindowSessionLock;
using santa::EnrichedLoginWindowSessionLogin;
using santa::EnrichedLoginWindowSessionLogout;
using santa::EnrichedLoginWindowSessionUnlock;
using santa::EnrichedOpenSSHLogin;
using santa::EnrichedOpenSSHLogout;
using santa::EnrichedProcess;
using santa::EnrichedRename;
using santa::EnrichedScreenSharingAttach;
using santa::EnrichedScreenSharingDetach;
using santa::EnrichedUnlink;
using santa::Message;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
std::shared_ptr<Empty> Empty::Create() {
return std::make_shared<Empty>();
@@ -70,6 +80,46 @@ std::vector<uint8_t> Empty::SerializeMessage(const EnrichedCSInvalidated &msg) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginWindowSessionLogin &msg) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginWindowSessionLogout &msg) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginWindowSessionLock &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginWindowSessionUnlock &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedScreenSharingAttach &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedScreenSharingDetach &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedOpenSSHLogin &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedOpenSSHLogout &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginLogin &) {
return {};
}
std::vector<uint8_t> Empty::SerializeMessage(const EnrichedLoginLogout &) {
return {};
}
std::vector<uint8_t> Empty::SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name, const Message &msg,
const EnrichedProcess &enriched_process,
@@ -94,4 +144,4 @@ std::vector<uint8_t> Empty::SerializeDiskDisappeared(NSDictionary *props) {
return {};
}
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa

View File

@@ -19,9 +19,7 @@
#include "Source/santad/EventProviders/EndpointSecurity/EnrichedTypes.h"
#include "Source/santad/Logs/EndpointSecurity/Serializers/Empty.h"
using santa::santad::logs::endpoint_security::serializers::Empty;
namespace es = santa::santad::event_providers::endpoint_security;
using santa::Empty;
@interface EmptyTest : XCTestCase
@end
@@ -35,17 +33,17 @@ namespace es = santa::santad::event_providers::endpoint_security;
// instead of constructing real ones since the Empty class never touches the
// input parameter.
int fake;
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedClose *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedExchange *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedExec *)&fake, nil).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedExit *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedFork *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedLink *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedRename *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedUnlink *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(es::EnrichedCSInvalidated *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedClose *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedExchange *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedExec *)&fake, nil).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedExit *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedFork *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedLink *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedRename *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedUnlink *)&fake).size(), 0);
XCTAssertEqual(e->SerializeMessage(*(santa::EnrichedCSInvalidated *)&fake).size(), 0);
XCTAssertEqual(e->SerializeAllowlist(*(es::Message *)&fake, "").size(), 0);
XCTAssertEqual(e->SerializeAllowlist(*(santa::Message *)&fake, "").size(), 0);
XCTAssertEqual(e->SerializeBundleHashingEvent(nil).size(), 0);
XCTAssertEqual(e->SerializeDiskAppeared(nil).size(), 0);
XCTAssertEqual(e->SerializeDiskDisappeared(nil).size(), 0);

View File

@@ -21,53 +21,53 @@
#include <memory>
#include <vector>
#include "Source/common/Platform.h"
#import "Source/common/SNTCachedDecision.h"
#include "Source/common/santa_proto_include_wrapper.h"
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
#include "Source/santad/Logs/EndpointSecurity/Serializers/Serializer.h"
#import "Source/santad/SNTDecisionCache.h"
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
class Protobuf : public Serializer {
public:
static std::shared_ptr<Protobuf> Create(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache, bool json = false);
static std::shared_ptr<Protobuf> Create(std::shared_ptr<santa::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache, bool json = false);
Protobuf(
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
SNTDecisionCache *decision_cache, bool json = false);
Protobuf(std::shared_ptr<santa::EndpointSecurityAPI> esapi, SNTDecisionCache *decision_cache,
bool json = false);
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExec &,
SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(
const santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedClose &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExchange &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExec &, SNTCachedDecision *) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedExit &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedFork &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedRename &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedUnlink &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedCSInvalidated &) override;
#if HAVE_MACOS_13
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionLock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginWindowSessionUnlock &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingAttach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedScreenSharingDetach &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedOpenSSHLogout &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogin &) override;
std::vector<uint8_t> SerializeMessage(const santa::EnrichedLoginLogout &) override;
#endif
std::vector<uint8_t> SerializeFileAccess(
const std::string &policy_version, const std::string &policy_name,
const santa::santad::event_providers::endpoint_security::Message &msg,
const santa::santad::event_providers::endpoint_security::EnrichedProcess &enriched_process,
const std::string &target, FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name,
const santa::Message &msg,
const santa::EnrichedProcess &enriched_process,
const std::string &target,
FileAccessPolicyDecision decision) override;
std::vector<uint8_t> SerializeAllowlist(
const santa::santad::event_providers::endpoint_security::Message &,
const std::string_view) override;
std::vector<uint8_t> SerializeAllowlist(const santa::Message &, const std::string_view) override;
std::vector<uint8_t> SerializeBundleHashingEvent(SNTStoredEvent *) override;
@@ -76,24 +76,22 @@ class Protobuf : public Serializer {
private:
::santa::pb::v1::SantaMessage *CreateDefaultProto(google::protobuf::Arena *arena);
::santa::pb::v1::SantaMessage *CreateDefaultProto(
google::protobuf::Arena *arena,
const santa::santad::event_providers::endpoint_security::EnrichedEventType &msg);
::santa::pb::v1::SantaMessage *CreateDefaultProto(
google::protobuf::Arena *arena,
const santa::santad::event_providers::endpoint_security::Message &msg);
::santa::pb::v1::SantaMessage *CreateDefaultProto(google::protobuf::Arena *arena,
const santa::EnrichedEventType &msg);
::santa::pb::v1::SantaMessage *CreateDefaultProto(google::protobuf::Arena *arena,
const santa::Message &msg);
::santa::pb::v1::SantaMessage *CreateDefaultProto(google::protobuf::Arena *arena,
struct timespec event_time,
struct timespec processed_time);
std::vector<uint8_t> FinalizeProto(::santa::pb::v1::SantaMessage *santa_msg);
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi_;
std::shared_ptr<santa::EndpointSecurityAPI> esapi_;
// Toggle for transforming protobuf output to its JSON form.
// See https://protobuf.dev/programming-guides/proto3/#json
bool json_;
};
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa
#endif

View File

@@ -43,33 +43,44 @@ using google::protobuf::Timestamp;
using JsonPrintOptions = google::protobuf::json::PrintOptions;
using google::protobuf::json::MessageToJsonString;
using santa::common::NSStringToUTF8StringView;
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
using santa::santad::event_providers::endpoint_security::EnrichedClose;
using santa::santad::event_providers::endpoint_security::EnrichedCSInvalidated;
using santa::santad::event_providers::endpoint_security::EnrichedEventType;
using santa::santad::event_providers::endpoint_security::EnrichedExchange;
using santa::santad::event_providers::endpoint_security::EnrichedExec;
using santa::santad::event_providers::endpoint_security::EnrichedExit;
using santa::santad::event_providers::endpoint_security::EnrichedFile;
using santa::santad::event_providers::endpoint_security::EnrichedFork;
using santa::santad::event_providers::endpoint_security::EnrichedLink;
using santa::santad::event_providers::endpoint_security::EnrichedProcess;
using santa::santad::event_providers::endpoint_security::EnrichedRename;
using santa::santad::event_providers::endpoint_security::EnrichedUnlink;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::serializers::Utilities::EffectiveGroup;
using santa::santad::logs::endpoint_security::serializers::Utilities::EffectiveUser;
using santa::santad::logs::endpoint_security::serializers::Utilities::MountFromName;
using santa::santad::logs::endpoint_security::serializers::Utilities::NonNull;
using santa::santad::logs::endpoint_security::serializers::Utilities::Pid;
using santa::santad::logs::endpoint_security::serializers::Utilities::Pidversion;
using santa::santad::logs::endpoint_security::serializers::Utilities::RealGroup;
using santa::santad::logs::endpoint_security::serializers::Utilities::RealUser;
using santa::EffectiveGroup;
using santa::EffectiveUser;
using santa::EndpointSecurityAPI;
using santa::EnrichedClose;
using santa::EnrichedCSInvalidated;
using santa::EnrichedEventType;
using santa::EnrichedExchange;
using santa::EnrichedExec;
using santa::EnrichedExit;
using santa::EnrichedFile;
using santa::EnrichedFork;
using santa::EnrichedLink;
using santa::EnrichedLoginLogin;
using santa::EnrichedLoginLogout;
using santa::EnrichedLoginWindowSessionLock;
using santa::EnrichedLoginWindowSessionLogin;
using santa::EnrichedLoginWindowSessionLogout;
using santa::EnrichedLoginWindowSessionUnlock;
using santa::EnrichedOpenSSHLogin;
using santa::EnrichedOpenSSHLogout;
using santa::EnrichedProcess;
using santa::EnrichedRename;
using santa::EnrichedScreenSharingAttach;
using santa::EnrichedScreenSharingDetach;
using santa::EnrichedUnlink;
using santa::Message;
using santa::MountFromName;
using santa::NonNull;
using santa::NSStringToUTF8StringView;
using santa::Pid;
using santa::Pidversion;
using santa::RealGroup;
using santa::RealUser;
using santa::StringTokenToStringView;
namespace pbv1 = ::santa::pb::v1;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
static constexpr NSUInteger kMaxEncodeObjectEntries = 64;
static constexpr NSUInteger kMaxEncodeObjectLevels = 5;
@@ -128,6 +139,16 @@ static inline void EncodeUserInfo(::pbv1::UserInfo *pb_user_info, uid_t uid,
}
}
static inline void EncodeUserInfo(std::function<::pbv1::UserInfo *()> lazy_f,
std::optional<uid_t> uid, const es_string_token_t &name) {
if (uid.has_value()) {
lazy_f()->set_uid(uid.value());
}
if (name.length > 0) {
lazy_f()->set_name(name.data, name.length);
}
}
static inline void EncodeGroupInfo(::pbv1::GroupInfo *pb_group_info, gid_t gid,
const std::optional<std::shared_ptr<std::string>> &name) {
pb_group_info->set_gid(gid);
@@ -194,7 +215,7 @@ static inline void EncodeAnnotations(std::function<::pbv1::process_tree::Annotat
}
static inline void EncodeProcessInfoLight(::pbv1::ProcessInfoLight *pb_proc_info,
uint32_t message_version, const es_process_t *es_proc,
const es_process_t *es_proc,
const EnrichedProcess &enriched_proc) {
EncodeProcessID(pb_proc_info->mutable_id(), es_proc->audit_token);
EncodeProcessID(pb_proc_info->mutable_parent_id(), es_proc->parent_audit_token);
@@ -217,6 +238,11 @@ static inline void EncodeProcessInfoLight(::pbv1::ProcessInfoLight *pb_proc_info
EncodeAnnotations([pb_proc_info] { return pb_proc_info->mutable_annotations(); }, enriched_proc);
}
static inline void EncodeProcessInfoLight(::pbv1::ProcessInfoLight *pb_proc_info,
const EnrichedEventType &msg) {
return EncodeProcessInfoLight(pb_proc_info, msg->process, msg.instigator());
}
static inline void EncodeProcessInfo(::pbv1::ProcessInfo *pb_proc_info, uint32_t message_version,
const es_process_t *es_proc,
const EnrichedProcess &enriched_proc,
@@ -393,7 +419,7 @@ static inline void EncodeCertificateInfo(::pbv1::CertificateInfo *pb_cert_info,
}
::pbv1::SantaMessage *Protobuf::CreateDefaultProto(Arena *arena, const EnrichedEventType &msg) {
return CreateDefaultProto(arena, msg.es_msg().time, msg.enrichment_time());
return CreateDefaultProto(arena, msg->time, msg.enrichment_time());
}
::pbv1::SantaMessage *Protobuf::CreateDefaultProto(Arena *arena, const Message &msg) {
@@ -442,10 +468,9 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedClose &msg) {
::pbv1::Close *pb_close = santa_msg->mutable_close();
EncodeProcessInfoLight(pb_close->mutable_instigator(), msg.es_msg().version, msg.es_msg().process,
msg.instigator());
EncodeFileInfo(pb_close->mutable_target(), msg.es_msg().event.close.target, msg.target());
pb_close->set_modified(msg.es_msg().event.close.modified);
EncodeProcessInfoLight(pb_close->mutable_instigator(), msg);
EncodeFileInfo(pb_close->mutable_target(), msg->event.close.target, msg.target());
pb_close->set_modified(msg->event.close.modified);
return FinalizeProto(santa_msg);
}
@@ -456,12 +481,9 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExchange &msg) {
::pbv1::Exchangedata *pb_exchangedata = santa_msg->mutable_exchangedata();
EncodeProcessInfoLight(pb_exchangedata->mutable_instigator(), msg.es_msg().version,
msg.es_msg().process, msg.instigator());
EncodeFileInfo(pb_exchangedata->mutable_file1(), msg.es_msg().event.exchangedata.file1,
msg.file1());
EncodeFileInfo(pb_exchangedata->mutable_file2(), msg.es_msg().event.exchangedata.file2,
msg.file2());
EncodeProcessInfoLight(pb_exchangedata->mutable_instigator(), msg);
EncodeFileInfo(pb_exchangedata->mutable_file1(), msg->event.exchangedata.file1, msg.file1());
EncodeFileInfo(pb_exchangedata->mutable_file2(), msg->event.exchangedata.file2, msg.file2());
return FinalizeProto(santa_msg);
}
@@ -592,45 +614,44 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExec &msg, SNTCach
::pbv1::Execution *pb_exec = santa_msg->mutable_execution();
EncodeProcessInfoLight(pb_exec->mutable_instigator(), msg.es_msg().version, msg.es_msg().process,
msg.instigator());
EncodeProcessInfo(pb_exec->mutable_target(), msg.es_msg().version, msg.es_msg().event.exec.target,
msg.target(), cd);
EncodeProcessInfoLight(pb_exec->mutable_instigator(), msg);
EncodeProcessInfo(pb_exec->mutable_target(), msg->version, msg->event.exec.target, msg.target(),
cd);
if (msg.es_msg().version >= 2 && msg.script().has_value()) {
EncodeFileInfo(pb_exec->mutable_script(), msg.es_msg().event.exec.script, msg.script().value());
if (msg->version >= 2 && msg.script().has_value()) {
EncodeFileInfo(pb_exec->mutable_script(), msg->event.exec.script, msg.script().value());
}
if (msg.es_msg().version >= 3 && msg.working_dir().has_value()) {
EncodeFileInfo(pb_exec->mutable_working_directory(), msg.es_msg().event.exec.cwd,
if (msg->version >= 3 && msg.working_dir().has_value()) {
EncodeFileInfo(pb_exec->mutable_working_directory(), msg->event.exec.cwd,
msg.working_dir().value());
}
uint32_t arg_count = esapi_->ExecArgCount(&msg.es_msg().event.exec);
uint32_t arg_count = esapi_->ExecArgCount(&msg->event.exec);
if (arg_count > 0) {
pb_exec->mutable_args()->Reserve(arg_count);
for (uint32_t i = 0; i < arg_count; i++) {
es_string_token_t tok = esapi_->ExecArg(&msg.es_msg().event.exec, i);
es_string_token_t tok = esapi_->ExecArg(&msg->event.exec, i);
pb_exec->add_args(tok.data, tok.length);
}
}
uint32_t env_count = esapi_->ExecEnvCount(&msg.es_msg().event.exec);
uint32_t env_count = esapi_->ExecEnvCount(&msg->event.exec);
if (env_count > 0) {
pb_exec->mutable_envs()->Reserve(env_count);
for (uint32_t i = 0; i < env_count; i++) {
es_string_token_t tok = esapi_->ExecEnv(&msg.es_msg().event.exec, i);
es_string_token_t tok = esapi_->ExecEnv(&msg->event.exec, i);
pb_exec->add_envs(tok.data, tok.length);
}
}
if (msg.es_msg().version >= 4) {
if (msg->version >= 4) {
int32_t max_fd = -1;
uint32_t fd_count = esapi_->ExecFDCount(&msg.es_msg().event.exec);
uint32_t fd_count = esapi_->ExecFDCount(&msg->event.exec);
if (fd_count > 0) {
pb_exec->mutable_fds()->Reserve(fd_count);
for (uint32_t i = 0; i < fd_count; i++) {
const es_fd_t *fd = esapi_->ExecFD(&msg.es_msg().event.exec, i);
const es_fd_t *fd = esapi_->ExecFD(&msg->event.exec, i);
max_fd = std::max(max_fd, fd->fd);
::pbv1::FileDescriptor *pb_fd = pb_exec->add_fds();
pb_fd->set_fd(fd->fd);
@@ -643,7 +664,7 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExec &msg, SNTCach
// If the `max_fd` seen is less than `last_fd`, we know that ES truncated
// the set of returned file descriptors
pb_exec->set_fd_list_truncated(max_fd < msg.es_msg().event.exec.last_fd);
pb_exec->set_fd_list_truncated(max_fd < msg->event.exec.last_fd);
}
pb_exec->set_decision(GetDecisionEnum(cd.decision));
@@ -657,7 +678,7 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExec &msg, SNTCach
EncodeString([pb_exec] { return pb_exec->mutable_explain(); }, cd.decisionExtra);
EncodeString([pb_exec] { return pb_exec->mutable_quarantine_url(); }, cd.quarantineURL);
NSString *orig_path = Utilities::OriginalPathForTranslocation(msg.es_msg().event.exec.target);
NSString *orig_path = santa::OriginalPathForTranslocation(msg->event.exec.target);
EncodeString([pb_exec] { return pb_exec->mutable_original_path(); }, orig_path);
EncodeEntitlements(pb_exec, cd);
@@ -671,9 +692,8 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExit &msg) {
::pbv1::Exit *pb_exit = santa_msg->mutable_exit();
EncodeProcessInfoLight(pb_exit->mutable_instigator(), msg.es_msg().version, msg.es_msg().process,
msg.instigator());
EncodeExitStatus(pb_exit, msg.es_msg().event.exit.stat);
EncodeProcessInfoLight(pb_exit->mutable_instigator(), msg);
EncodeExitStatus(pb_exit, msg->event.exit.stat);
return FinalizeProto(santa_msg);
}
@@ -684,10 +704,8 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedFork &msg) {
::pbv1::Fork *pb_fork = santa_msg->mutable_fork();
EncodeProcessInfoLight(pb_fork->mutable_instigator(), msg.es_msg().version, msg.es_msg().process,
msg.instigator());
EncodeProcessInfoLight(pb_fork->mutable_child(), msg.es_msg().version,
msg.es_msg().event.fork.child, msg.child());
EncodeProcessInfoLight(pb_fork->mutable_instigator(), msg);
EncodeProcessInfoLight(pb_fork->mutable_child(), msg->event.fork.child, msg.child());
return FinalizeProto(santa_msg);
}
@@ -697,11 +715,10 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLink &msg) {
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::Link *pb_link = santa_msg->mutable_link();
EncodeProcessInfoLight(pb_link->mutable_instigator(), msg.es_msg().version, msg.es_msg().process,
msg.instigator());
EncodeFileInfo(pb_link->mutable_source(), msg.es_msg().event.link.source, msg.source());
EncodePath(pb_link->mutable_target(), msg.es_msg().event.link.target_dir,
msg.es_msg().event.link.target_filename);
EncodeProcessInfoLight(pb_link->mutable_instigator(), msg);
EncodeFileInfo(pb_link->mutable_source(), msg->event.link.source, msg.source());
EncodePath(pb_link->mutable_target(), msg->event.link.target_dir,
msg->event.link.target_filename);
return FinalizeProto(santa_msg);
}
@@ -711,15 +728,14 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedRename &msg) {
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::Rename *pb_rename = santa_msg->mutable_rename();
EncodeProcessInfoLight(pb_rename->mutable_instigator(), msg.es_msg().version,
msg.es_msg().process, msg.instigator());
EncodeFileInfo(pb_rename->mutable_source(), msg.es_msg().event.rename.source, msg.source());
if (msg.es_msg().event.rename.destination_type == ES_DESTINATION_TYPE_EXISTING_FILE) {
EncodePath(pb_rename->mutable_target(), msg.es_msg().event.rename.destination.existing_file);
EncodeProcessInfoLight(pb_rename->mutable_instigator(), msg);
EncodeFileInfo(pb_rename->mutable_source(), msg->event.rename.source, msg.source());
if (msg->event.rename.destination_type == ES_DESTINATION_TYPE_EXISTING_FILE) {
EncodePath(pb_rename->mutable_target(), msg->event.rename.destination.existing_file);
pb_rename->set_target_existed(true);
} else {
EncodePath(pb_rename->mutable_target(), msg.es_msg().event.rename.destination.new_path.dir,
msg.es_msg().event.rename.destination.new_path.filename);
EncodePath(pb_rename->mutable_target(), msg->event.rename.destination.new_path.dir,
msg->event.rename.destination.new_path.filename);
pb_rename->set_target_existed(false);
}
@@ -731,9 +747,8 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedUnlink &msg) {
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::Unlink *pb_unlink = santa_msg->mutable_unlink();
EncodeProcessInfoLight(pb_unlink->mutable_instigator(), msg.es_msg().version,
msg.es_msg().process, msg.instigator());
EncodeFileInfo(pb_unlink->mutable_target(), msg.es_msg().event.unlink.target, msg.target());
EncodeProcessInfoLight(pb_unlink->mutable_instigator(), msg);
EncodeFileInfo(pb_unlink->mutable_target(), msg->event.unlink.target, msg.target());
return FinalizeProto(santa_msg);
}
@@ -743,12 +758,230 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedCSInvalidated &msg
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::CodesigningInvalidated *pb_cs_invalidated = santa_msg->mutable_codesigning_invalidated();
EncodeProcessInfoLight(pb_cs_invalidated->mutable_instigator(), msg.es_msg().version,
msg.es_msg().process, msg.instigator());
EncodeProcessInfoLight(pb_cs_invalidated->mutable_instigator(), msg);
return FinalizeProto(santa_msg);
}
#if HAVE_MACOS_13
::pbv1::SocketAddress::Type GetSocketAddressType(es_address_type_t type) {
switch (type) {
case ES_ADDRESS_TYPE_NONE: return ::pbv1::SocketAddress::TYPE_NONE;
case ES_ADDRESS_TYPE_IPV4: return ::pbv1::SocketAddress::TYPE_IPV4;
case ES_ADDRESS_TYPE_IPV6: return ::pbv1::SocketAddress::TYPE_IPV6;
case ES_ADDRESS_TYPE_NAMED_SOCKET: return ::pbv1::SocketAddress::TYPE_NAMED_SOCKET;
default: return ::pbv1::SocketAddress::TYPE_UNKNOWN;
}
}
::pbv1::OpenSSHLogin::Result GetOpenSSHLoginResultType(es_openssh_login_result_type_t type) {
switch (type) {
case ES_OPENSSH_LOGIN_EXCEED_MAXTRIES:
return ::pbv1::OpenSSHLogin::RESULT_LOGIN_EXCEED_MAXTRIES;
case ES_OPENSSH_LOGIN_ROOT_DENIED: return ::pbv1::OpenSSHLogin::RESULT_LOGIN_ROOT_DENIED;
case ES_OPENSSH_AUTH_SUCCESS: return ::pbv1::OpenSSHLogin::RESULT_AUTH_SUCCESS;
case ES_OPENSSH_AUTH_FAIL_NONE: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_NONE;
case ES_OPENSSH_AUTH_FAIL_PASSWD: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_PASSWD;
case ES_OPENSSH_AUTH_FAIL_KBDINT: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_KBDINT;
case ES_OPENSSH_AUTH_FAIL_PUBKEY: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_PUBKEY;
case ES_OPENSSH_AUTH_FAIL_HOSTBASED: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_HOSTBASED;
case ES_OPENSSH_AUTH_FAIL_GSSAPI: return ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_GSSAPI;
case ES_OPENSSH_INVALID_USER: return ::pbv1::OpenSSHLogin::RESULT_INVALID_USER;
default: return ::pbv1::OpenSSHLogin::RESULT_UNKNOWN;
}
}
static inline void EncodeSocketAddress(::pbv1::SocketAddress *pb_socket_addr, std::string_view addr,
es_address_type_t type) {
EncodeString([pb_socket_addr] { return pb_socket_addr->mutable_address(); }, addr);
pb_socket_addr->set_type(GetSocketAddressType(type));
}
static inline void EncodeUserInfo(std::function<::pbv1::UserInfo *()> lazy_f,
const es_string_token_t &name) {
EncodeUserInfo(lazy_f, std::nullopt, name);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginWindowSessionLogin &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::LoginWindowSessionLogin *pb_lw_login =
santa_msg->mutable_login_window_session()->mutable_login();
EncodeProcessInfoLight(pb_lw_login->mutable_instigator(), msg);
EncodeUserInfo([pb_lw_login] { return pb_lw_login->mutable_user(); }, msg.UID(),
msg->event.lw_session_login->username);
pb_lw_login->mutable_graphical_session()->set_id(
msg->event.lw_session_login->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginWindowSessionLogout &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::LoginWindowSessionLogout *pb_lw_logout =
santa_msg->mutable_login_window_session()->mutable_logout();
EncodeProcessInfoLight(pb_lw_logout->mutable_instigator(), msg);
EncodeUserInfo([pb_lw_logout] { return pb_lw_logout->mutable_user(); }, msg.UID(),
msg->event.lw_session_logout->username);
pb_lw_logout->mutable_graphical_session()->set_id(
msg->event.lw_session_logout->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginWindowSessionLock &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::LoginWindowSessionLock *pb_lw_lock =
santa_msg->mutable_login_window_session()->mutable_lock();
EncodeProcessInfoLight(pb_lw_lock->mutable_instigator(), msg);
EncodeUserInfo([pb_lw_lock] { return pb_lw_lock->mutable_user(); }, msg.UID(),
msg->event.lw_session_lock->username);
pb_lw_lock->mutable_graphical_session()->set_id(msg->event.lw_session_lock->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginWindowSessionUnlock &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::LoginWindowSessionUnlock *pb_lw_unlock =
santa_msg->mutable_login_window_session()->mutable_unlock();
EncodeProcessInfoLight(pb_lw_unlock->mutable_instigator(), msg);
EncodeUserInfo([pb_lw_unlock] { return pb_lw_unlock->mutable_user(); }, msg.UID(),
msg->event.lw_session_unlock->username);
pb_lw_unlock->mutable_graphical_session()->set_id(
msg->event.lw_session_unlock->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedScreenSharingAttach &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::ScreenSharingAttach *pb_attach = santa_msg->mutable_screen_sharing()->mutable_attach();
EncodeProcessInfoLight(pb_attach->mutable_instigator(), msg);
pb_attach->set_success(msg->event.screensharing_attach->success);
EncodeSocketAddress(pb_attach->mutable_source(),
StringTokenToStringView(msg->event.screensharing_attach->source_address),
msg->event.screensharing_attach->source_address_type);
EncodeString([pb_attach] { return pb_attach->mutable_viewer(); },
StringTokenToStringView(msg->event.screensharing_attach->viewer_appleid));
EncodeString([pb_attach] { return pb_attach->mutable_authentication_type(); },
StringTokenToStringView(msg->event.screensharing_attach->authentication_type));
EncodeUserInfo([pb_attach] { return pb_attach->mutable_authentication_user(); },
msg->event.screensharing_attach->authentication_username);
EncodeUserInfo([pb_attach] { return pb_attach->mutable_session_user(); },
msg->event.screensharing_attach->session_username);
pb_attach->set_existing_session(msg->event.screensharing_attach->existing_session);
pb_attach->mutable_graphical_session()->set_id(
msg->event.screensharing_attach->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedScreenSharingDetach &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::ScreenSharingDetach *pb_detach = santa_msg->mutable_screen_sharing()->mutable_detach();
EncodeProcessInfoLight(pb_detach->mutable_instigator(), msg);
EncodeSocketAddress(pb_detach->mutable_source(),
StringTokenToStringView(msg->event.screensharing_detach->source_address),
msg->event.screensharing_detach->source_address_type);
EncodeString([pb_detach] { return pb_detach->mutable_viewer(); },
StringTokenToStringView(msg->event.screensharing_detach->viewer_appleid));
pb_detach->mutable_graphical_session()->set_id(
msg->event.screensharing_detach->graphical_session_id);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedOpenSSHLogin &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::OpenSSHLogin *pb_ssh_login = santa_msg->mutable_open_ssh()->mutable_login();
EncodeProcessInfoLight(pb_ssh_login->mutable_instigator(), msg);
pb_ssh_login->set_result(GetOpenSSHLoginResultType(msg->event.openssh_login->result_type));
EncodeSocketAddress(pb_ssh_login->mutable_source(),
StringTokenToStringView(msg->event.openssh_login->source_address),
msg->event.openssh_login->source_address_type);
EncodeUserInfo([pb_ssh_login] { return pb_ssh_login->mutable_user(); },
msg->event.openssh_login->has_uid
? std::make_optional<uid_t>(msg->event.openssh_login->uid.uid)
: std::nullopt,
msg->event.openssh_login->username);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedOpenSSHLogout &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::OpenSSHLogout *pb_ssh_logout = santa_msg->mutable_open_ssh()->mutable_logout();
EncodeProcessInfoLight(pb_ssh_logout->mutable_instigator(), msg);
EncodeSocketAddress(pb_ssh_logout->mutable_source(),
StringTokenToStringView(msg->event.openssh_logout->source_address),
msg->event.openssh_logout->source_address_type);
EncodeUserInfo([pb_ssh_logout] { return pb_ssh_logout->mutable_user(); },
msg->event.openssh_logout->uid, msg->event.openssh_logout->username);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginLogin &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::Login *pb_login = santa_msg->mutable_login_logout()->mutable_login();
EncodeProcessInfoLight(pb_login->mutable_instigator(), msg);
pb_login->set_success(msg->event.login_login->success);
EncodeString([pb_login] { return pb_login->mutable_failure_message(); },
StringTokenToStringView(msg->event.login_login->failure_message));
EncodeUserInfo([pb_login] { return pb_login->mutable_user(); },
msg->event.login_login->has_uid
? std::make_optional<uid_t>(msg->event.login_login->uid.uid)
: std::nullopt,
msg->event.login_login->username);
return FinalizeProto(santa_msg);
}
std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedLoginLogout &msg) {
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena, msg);
::pbv1::Logout *pb_logout = santa_msg->mutable_login_logout()->mutable_logout();
EncodeProcessInfoLight(pb_logout->mutable_instigator(), msg);
EncodeUserInfo([pb_logout] { return pb_logout->mutable_user(); }, msg->event.login_logout->uid,
msg->event.login_logout->username);
return FinalizeProto(santa_msg);
}
#endif // HAVE_MACOS_13
std::vector<uint8_t> Protobuf::SerializeFileAccess(const std::string &policy_version,
const std::string &policy_name,
const Message &msg,
@@ -776,14 +1009,13 @@ std::vector<uint8_t> Protobuf::SerializeAllowlist(const Message &msg, const std:
Arena arena;
::pbv1::SantaMessage *santa_msg = CreateDefaultProto(&arena);
const es_file_t *es_file = Utilities::GetAllowListTargetFile(msg);
const es_file_t *es_file = santa::GetAllowListTargetFile(msg);
EnrichedFile enriched_file(std::nullopt, std::nullopt, std::nullopt);
EnrichedProcess enriched_process;
::pbv1::Allowlist *pb_allowlist = santa_msg->mutable_allowlist();
EncodeProcessInfoLight(pb_allowlist->mutable_instigator(), msg->version, msg->process,
enriched_process);
EncodeProcessInfoLight(pb_allowlist->mutable_instigator(), msg->process, enriched_process);
EncodeFileInfo(pb_allowlist->mutable_target(), es_file, enriched_file,
[NSString stringWithFormat:@"%s", hash.data()]);
@@ -815,9 +1047,9 @@ static void EncodeDisk(::pbv1::Disk *pb_disk, ::pbv1::Disk_Action action, NSDict
NSString *dmg_path = nil;
NSString *serial = nil;
if ([props[@"DADeviceModel"] isEqual:@"Disk Image"]) {
dmg_path = Utilities::DiskImageForDevice(props[@"DADevicePath"]);
dmg_path = santa::DiskImageForDevice(props[@"DADevicePath"]);
} else {
serial = Utilities::SerialForDevice(props[@"DADevicePath"]);
serial = santa::SerialForDevice(props[@"DADevicePath"]);
}
NSString *model = [NSString
@@ -871,4 +1103,4 @@ std::vector<uint8_t> Protobuf::SerializeDiskDisappeared(NSDictionary *props) {
return FinalizeProto(santa_msg);
}
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa

View File

@@ -27,6 +27,7 @@
#include <uuid/uuid.h>
#include <cstring>
#include "Source/common/Platform.h"
#import "Source/common/SNTCachedDecision.h"
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTConfigurator.h"
@@ -49,16 +50,16 @@ using JsonPrintOptions = google::protobuf::json::PrintOptions;
using JsonParseOptions = ::google::protobuf::json::ParseOptions;
using google::protobuf::json::JsonStringToMessage;
using google::protobuf::json::MessageToJsonString;
using santa::santad::event_providers::endpoint_security::EnrichedEventType;
using santa::santad::event_providers::endpoint_security::EnrichedMessage;
using santa::santad::event_providers::endpoint_security::Enricher;
using santa::santad::event_providers::endpoint_security::Message;
using santa::santad::logs::endpoint_security::serializers::Protobuf;
using santa::santad::logs::endpoint_security::serializers::Serializer;
using santa::EnrichedEventType;
using santa::EnrichedMessage;
using santa::Enricher;
using santa::Message;
using santa::Protobuf;
using santa::Serializer;
namespace pbv1 = ::santa::pb::v1;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
extern void EncodeExitStatus(::pbv1::Exit *pbExit, int exitStatus);
extern void EncodeEntitlements(::pbv1::Execution *pb_exec, SNTCachedDecision *cd);
extern ::pbv1::Execution::Decision GetDecisionEnum(SNTEventState event_state);
@@ -67,16 +68,24 @@ extern ::pbv1::Execution::Mode GetModeEnum(SNTClientMode mode);
extern ::pbv1::FileDescriptor::FDType GetFileDescriptorType(uint32_t fdtype);
extern ::pbv1::FileAccess::AccessType GetAccessType(es_event_type_t event_type);
extern ::pbv1::FileAccess::PolicyDecision GetPolicyDecision(FileAccessPolicyDecision decision);
} // namespace santa::santad::logs::endpoint_security::serializers
#if HAVE_MACOS_13
extern ::pbv1::SocketAddress::Type GetSocketAddressType(es_address_type_t type);
extern ::pbv1::OpenSSHLogin::Result GetOpenSSHLoginResultType(es_openssh_login_result_type_t type);
#endif // HAVE_MACOS_13
} // namespace santa
using santa::santad::logs::endpoint_security::serializers::EncodeEntitlements;
using santa::santad::logs::endpoint_security::serializers::EncodeExitStatus;
using santa::santad::logs::endpoint_security::serializers::GetAccessType;
using santa::santad::logs::endpoint_security::serializers::GetDecisionEnum;
using santa::santad::logs::endpoint_security::serializers::GetFileDescriptorType;
using santa::santad::logs::endpoint_security::serializers::GetModeEnum;
using santa::santad::logs::endpoint_security::serializers::GetPolicyDecision;
using santa::santad::logs::endpoint_security::serializers::GetReasonEnum;
using santa::EncodeEntitlements;
using santa::EncodeExitStatus;
using santa::GetAccessType;
using santa::GetDecisionEnum;
using santa::GetFileDescriptorType;
using santa::GetModeEnum;
using santa::GetPolicyDecision;
using santa::GetReasonEnum;
#if HAVE_MACOS_13
using santa::GetOpenSSHLoginResultType;
using santa::GetSocketAddressType;
#endif // HAVE_MACOS_13
@interface ProtobufTest : XCTestCase
@property id mockConfigurator;
@@ -93,34 +102,51 @@ JsonPrintOptions DefaultJsonPrintOptions() {
return options;
}
NSString *EventTypeToFilename(es_event_type_t eventType) {
NSString *ConstructFilename(es_event_type_t eventType, NSString *variant = nil) {
NSString *name;
switch (eventType) {
case ES_EVENT_TYPE_NOTIFY_CLOSE: return @"close.json";
case ES_EVENT_TYPE_NOTIFY_EXCHANGEDATA: return @"exchangedata.json";
case ES_EVENT_TYPE_NOTIFY_EXEC: return @"exec.json";
case ES_EVENT_TYPE_NOTIFY_EXIT: return @"exit.json";
case ES_EVENT_TYPE_NOTIFY_FORK: return @"fork.json";
case ES_EVENT_TYPE_NOTIFY_LINK: return @"link.json";
case ES_EVENT_TYPE_NOTIFY_RENAME: return @"rename.json";
case ES_EVENT_TYPE_NOTIFY_UNLINK: return @"unlink.json";
case ES_EVENT_TYPE_NOTIFY_CS_INVALIDATED: return @"cs_invalidated.json";
case ES_EVENT_TYPE_NOTIFY_CLOSE: name = @"close"; break;
case ES_EVENT_TYPE_NOTIFY_EXCHANGEDATA: name = @"exchangedata"; break;
case ES_EVENT_TYPE_NOTIFY_EXEC: name = @"exec"; break;
case ES_EVENT_TYPE_NOTIFY_EXIT: name = @"exit"; break;
case ES_EVENT_TYPE_NOTIFY_FORK: name = @"fork"; break;
case ES_EVENT_TYPE_NOTIFY_LINK: name = @"link"; break;
case ES_EVENT_TYPE_NOTIFY_RENAME: name = @"rename"; break;
case ES_EVENT_TYPE_NOTIFY_UNLINK: name = @"unlink"; break;
case ES_EVENT_TYPE_NOTIFY_CS_INVALIDATED: name = @"cs_invalidated"; break;
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN: name = @"lw_session_login"; break;
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT: name = @"lw_session_logout"; break;
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK: name = @"lw_session_lock"; break;
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK: name = @"lw_session_unlock"; break;
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH: name = @"screensharing_attach"; break;
case ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH: name = @"screensharing_detach"; break;
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN: name = @"openssh_login"; break;
case ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT: name = @"openssh_logout"; break;
case ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN: name = @"login_login"; break;
default: XCTFail(@"Unhandled event type: %d", eventType); return nil;
}
if (variant) {
return [NSString stringWithFormat:@"%@_%@.json", name, variant];
} else {
return [NSString stringWithFormat:@"%@.json", name];
}
}
NSString *TestJsonPath(NSString *jsonFileName, uint32_t version) {
NSString *p = [NSString pathWithComponents:@[
NSString *LoadTestJson(NSString *jsonFileName, uint32_t version) {
if (!jsonFileName) {
return nil;
}
NSString *path = [NSString pathWithComponents:@[
[[NSBundle bundleForClass:[ProtobufTest class]] resourcePath],
@"protobuf",
[NSString stringWithFormat:@"v%u", version],
jsonFileName,
]];
return p;
}
NSString *LoadTestJson(NSString *jsonFileName, uint32_t version) {
NSError *err = nil;
NSString *jsonData = [NSString stringWithContentsOfFile:TestJsonPath(jsonFileName, version)
NSString *jsonData = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&err];
@@ -150,6 +176,10 @@ const google::protobuf::Message &SantaMessageEvent(const ::pbv1::SantaMessage &s
case ::pbv1::SantaMessage::kAllowlist: return santaMsg.allowlist();
case ::pbv1::SantaMessage::kFileAccess: return santaMsg.file_access();
case ::pbv1::SantaMessage::kCodesigningInvalidated: return santaMsg.codesigning_invalidated();
case ::pbv1::SantaMessage::kLoginWindowSession: return santaMsg.login_window_session();
case ::pbv1::SantaMessage::kScreenSharing: return santaMsg.screen_sharing();
case ::pbv1::SantaMessage::kOpenSsh: return santaMsg.open_ssh();
case ::pbv1::SantaMessage::kLoginLogout: return santaMsg.login_logout();
case ::pbv1::SantaMessage::EVENT_NOT_SET:
XCTFail(@"Protobuf message SantaMessage did not set an 'event' field");
OS_FALLTHROUGH;
@@ -203,11 +233,11 @@ NSDictionary *FindDelta(NSDictionary *want, NSDictionary *got) {
void SerializeAndCheck(es_event_type_t eventType,
void (^messageSetup)(std::shared_ptr<MockEndpointSecurityAPI>,
es_message_t *),
SNTDecisionCache *decisionCache, bool json = false) {
SNTDecisionCache *decisionCache, bool json, NSString *variant) {
std::shared_ptr<MockEndpointSecurityAPI> mockESApi = std::make_shared<MockEndpointSecurityAPI>();
for (uint32_t cur_version = 1; cur_version <= MaxSupportedESMessageVersionForCurrentOS();
cur_version++) {
for (uint32_t cur_version = MinSupportedESMessageVersion(eventType);
cur_version <= MaxSupportedESMessageVersionForCurrentOS(); cur_version++) {
if (cur_version == 3) {
// Note: Version 3 was only in a macOS beta.
continue;
@@ -231,26 +261,23 @@ void SerializeAndCheck(es_event_type_t eventType,
struct timespec enrichmentTime;
struct timespec msgTime;
NSString *wantData = std::visit(
[&msgTime, &enrichmentTime](const EnrichedEventType &enrichedEvent) {
msgTime = enrichedEvent.es_msg().time;
[&msgTime, &enrichmentTime, variant](const EnrichedEventType &enrichedEvent) {
msgTime = enrichedEvent->time;
enrichmentTime = enrichedEvent.enrichment_time();
return LoadTestJson(EventTypeToFilename(enrichedEvent.es_msg().event_type),
enrichedEvent.es_msg().version);
return LoadTestJson(ConstructFilename(enrichedEvent->event_type, variant),
enrichedEvent->version);
},
enrichedMsg->GetEnrichedMessage());
std::vector<uint8_t> vec = bs->SerializeMessage(std::move(enrichedMsg));
std::string protoStr(vec.begin(), vec.end());
// if we're checking against JSON then we should already have a jsonified string and just need
// to
::pbv1::SantaMessage santaMsg;
std::string gotData;
if (json) {
// Parse the jsonified string into the protobuf
// gotData = protoStr;
JsonParseOptions options;
options.ignore_unknown_fields = true;
absl::Status status = JsonStringToMessage(protoStr, &santaMsg, options);
@@ -287,7 +314,7 @@ void SerializeAndCheck(es_event_type_t eventType,
}
void SerializeAndCheckNonESEvents(
es_event_type_t eventType, NSString *filename,
uint32_t minAssociatedESVersion, es_event_type_t eventType, NSString *filename,
void (^messageSetup)(std::shared_ptr<MockEndpointSecurityAPI>, es_message_t *),
std::vector<uint8_t> (^RunSerializer)(std::shared_ptr<Serializer> serializer,
const Message &msg)) {
@@ -295,8 +322,8 @@ void SerializeAndCheckNonESEvents(
mockESApi->SetExpectationsRetainReleaseMessage();
std::shared_ptr<Serializer> bs = Protobuf::Create(mockESApi, nil);
for (uint32_t cur_version = 1; cur_version <= MaxSupportedESMessageVersionForCurrentOS();
cur_version++) {
for (uint32_t cur_version = minAssociatedESVersion;
cur_version <= MaxSupportedESMessageVersionForCurrentOS(); cur_version++) {
if (cur_version == 3) {
// Note: Version 3 was only in a macOS beta.
continue;
@@ -375,7 +402,20 @@ void SerializeAndCheckNonESEvents(
messageSetup:(void (^)(std::shared_ptr<MockEndpointSecurityAPI>,
es_message_t *))messageSetup
json:(BOOL)json {
SerializeAndCheck(eventType, messageSetup, self.mockDecisionCache, (bool)json);
SerializeAndCheck(eventType, messageSetup, self.mockDecisionCache, (bool)json, nil);
}
- (void)serializeAndCheckEvent:(es_event_type_t)eventType
messageSetup:(void (^)(std::shared_ptr<MockEndpointSecurityAPI>,
es_message_t *))messageSetup {
SerializeAndCheck(eventType, messageSetup, self.mockDecisionCache, false, nil);
}
- (void)serializeAndCheckEvent:(es_event_type_t)eventType
messageSetup:(void (^)(std::shared_ptr<MockEndpointSecurityAPI>,
es_message_t *))messageSetup
variant:(NSString *)variant {
SerializeAndCheck(eventType, messageSetup, self.mockDecisionCache, false, variant);
}
- (void)testSerializeMessageClose {
@@ -386,8 +426,7 @@ void SerializeAndCheckNonESEvents(
es_message_t *esMsg) {
esMsg->event.close.modified = true;
esMsg->event.close.target = &file;
}
json:NO];
}];
}
- (void)testSerializeMessageExchange {
@@ -399,8 +438,7 @@ void SerializeAndCheckNonESEvents(
es_message_t *esMsg) {
esMsg->event.exchangedata.file1 = &file1;
esMsg->event.exchangedata.file2 = &file2;
}
json:NO];
}];
}
- (void)testGetDecisionEnum {
@@ -539,8 +577,7 @@ void SerializeAndCheckNonESEvents(
.WillOnce(testing::Return(&fd2))
.WillOnce(testing::Return(&fd3));
}
}
json:NO];
}];
}
- (void)testSerializeMessageExecJSON {
@@ -664,8 +701,7 @@ void SerializeAndCheckNonESEvents(
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.exit.stat = W_EXITCODE(1, 0);
}
json:NO];
}];
}
- (void)testEncodeExitStatus {
@@ -702,8 +738,7 @@ void SerializeAndCheckNonESEvents(
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.fork.child = &procChild;
}
json:NO];
}];
}
- (void)testSerializeMessageLink {
@@ -717,8 +752,7 @@ void SerializeAndCheckNonESEvents(
esMsg->event.link.source = &fileSource;
esMsg->event.link.target_dir = &fileTargetDir;
esMsg->event.link.target_filename = targetTok;
}
json:NO];
}];
}
- (void)testSerializeMessageRename {
@@ -739,8 +773,7 @@ void SerializeAndCheckNonESEvents(
esMsg->event.rename.destination.new_path.filename = targetTok;
esMsg->event.rename.destination_type = ES_DESTINATION_TYPE_NEW_PATH;
}
}
json:NO];
}];
}
- (void)testSerializeMessageUnlink {
@@ -752,8 +785,7 @@ void SerializeAndCheckNonESEvents(
es_message_t *esMsg) {
esMsg->event.unlink.target = &fileTarget;
esMsg->event.unlink.parent_dir = &fileTargetParent;
}
json:NO];
}];
}
- (void)testSerializeMessageCodesigningInvalidated {
@@ -764,6 +796,219 @@ void SerializeAndCheckNonESEvents(
json:NO];
}
#if HAVE_MACOS_13
- (void)testSerializeMessageLoginWindowSessionLogin {
__block es_event_lw_session_login_t lwLogin = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.lw_session_login = &lwLogin;
}];
}
- (void)testSerializeMessageLoginWindowSessionLogout {
__block es_event_lw_session_logout_t lwLogout = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.lw_session_logout = &lwLogout;
}];
}
- (void)testSerializeMessageLoginWindowSessionLock {
__block es_event_lw_session_lock_t lwLock = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.lw_session_lock = &lwLock;
}];
}
- (void)testSerializeMessageLoginWindowSessionUnlock {
__block es_event_lw_session_unlock_t lwUnlock = {
.username = MakeESStringToken("daemon"),
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LW_SESSION_UNLOCK
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.lw_session_unlock = &lwUnlock;
}];
}
- (void)testSerializeMessageScreensharingAttach {
__block es_event_screensharing_attach_t attach = {
.success = true,
.source_address_type = ES_ADDRESS_TYPE_IPV6,
.source_address = MakeESStringToken("::1"),
.viewer_appleid = MakeESStringToken("foo@example.com"),
.authentication_type = MakeESStringToken("idk"),
.authentication_username = MakeESStringToken("my_auth_user"),
.session_username = MakeESStringToken("my_session_user"),
.existing_session = true,
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.screensharing_attach = &attach;
}];
attach.source_address_type = (es_address_type_t)1234;
attach.source_address = MakeESStringToken(NULL);
attach.viewer_appleid = MakeESStringToken(NULL);
attach.authentication_type = MakeESStringToken(NULL);
attach.authentication_username = MakeESStringToken(NULL);
attach.session_username = MakeESStringToken(NULL);
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_SCREENSHARING_ATTACH
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.screensharing_attach = &attach;
}
variant:@"unset_fields"];
}
- (void)testSerializeMessageScreensharingDetach {
__block es_event_screensharing_detach_t detach = {
.source_address_type = ES_ADDRESS_TYPE_IPV4,
.source_address = MakeESStringToken("1.2.3.4"),
.viewer_appleid = MakeESStringToken("foo@example.com"),
.graphical_session_id = 123,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_SCREENSHARING_DETACH
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.screensharing_detach = &detach;
}];
}
- (void)testSerializeMessageOpenSSHLogin {
__block es_event_openssh_login_t sshLogin = {.success = true,
.result_type = ES_OPENSSH_AUTH_SUCCESS,
.source_address_type = ES_ADDRESS_TYPE_IPV4,
.source_address = MakeESStringToken("1.2.3.4"),
.username = MakeESStringToken("foo_user"),
.has_uid = true,
.uid = {
.uid = 12345,
}};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.openssh_login = &sshLogin;
}];
sshLogin.success = false;
sshLogin.result_type = ES_OPENSSH_AUTH_FAIL_HOSTBASED;
sshLogin.source_address_type = ES_ADDRESS_TYPE_IPV6;
sshLogin.source_address = MakeESStringToken("::1");
sshLogin.has_uid = false;
sshLogin.username = MakeESStringToken(NULL);
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGIN
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.openssh_login = &sshLogin;
}
variant:@"failed_attempt"];
}
- (void)testSerializeMessageOpenSSHLogout {
__block es_event_openssh_logout_t sshLogout = {
.source_address_type = ES_ADDRESS_TYPE_IPV4,
.source_address = MakeESStringToken("1.2.3.4"),
.username = MakeESStringToken("foo_user"),
.uid = 12345,
};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_OPENSSH_LOGOUT
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.openssh_logout = &sshLogout;
}];
}
- (void)testSerializeMessageLoginLogin {
__block es_event_login_login_t login = {.success = true,
.failure_message = MakeESStringToken(NULL),
.username = MakeESStringToken("asdf"),
.has_uid = true,
.uid = {
.uid = 321,
}};
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.login_login = &login;
}];
login.success = false;
login.failure_message = MakeESStringToken("my|failure");
login.has_uid = false;
[self serializeAndCheckEvent:ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN
messageSetup:^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi,
es_message_t *esMsg) {
esMsg->event.login_login = &login;
}
variant:@"failed_attempt"];
}
- (void)testGetSocketAddressType {
std::map<es_address_type_t, ::pbv1::SocketAddress::Type> esToSantaAddrType = {
{ES_ADDRESS_TYPE_NONE, ::pbv1::SocketAddress::TYPE_NONE},
{ES_ADDRESS_TYPE_IPV4, ::pbv1::SocketAddress::TYPE_IPV4},
{ES_ADDRESS_TYPE_IPV6, ::pbv1::SocketAddress::TYPE_IPV6},
{ES_ADDRESS_TYPE_NAMED_SOCKET, ::pbv1::SocketAddress::TYPE_NAMED_SOCKET},
{(es_address_type_t)1234, ::pbv1::SocketAddress::TYPE_UNKNOWN},
};
for (const auto &kv : esToSantaAddrType) {
XCTAssertEqual(GetSocketAddressType(kv.first), kv.second);
}
}
- (void)testGetOpenSSHLoginResultType {
std::map<es_openssh_login_result_type_t, ::pbv1::OpenSSHLogin::Result> esToSantaOpenSSHResultType{
{ES_OPENSSH_LOGIN_EXCEED_MAXTRIES, ::pbv1::OpenSSHLogin::RESULT_LOGIN_EXCEED_MAXTRIES},
{ES_OPENSSH_LOGIN_ROOT_DENIED, ::pbv1::OpenSSHLogin::RESULT_LOGIN_ROOT_DENIED},
{ES_OPENSSH_AUTH_SUCCESS, ::pbv1::OpenSSHLogin::RESULT_AUTH_SUCCESS},
{ES_OPENSSH_AUTH_FAIL_NONE, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_NONE},
{ES_OPENSSH_AUTH_FAIL_PASSWD, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_PASSWD},
{ES_OPENSSH_AUTH_FAIL_KBDINT, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_KBDINT},
{ES_OPENSSH_AUTH_FAIL_PUBKEY, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_PUBKEY},
{ES_OPENSSH_AUTH_FAIL_HOSTBASED, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_HOSTBASED},
{ES_OPENSSH_AUTH_FAIL_GSSAPI, ::pbv1::OpenSSHLogin::RESULT_AUTH_FAIL_GSSAPI},
{ES_OPENSSH_INVALID_USER, ::pbv1::OpenSSHLogin::RESULT_INVALID_USER},
{(es_openssh_login_result_type_t)1234, ::pbv1::OpenSSHLogin::RESULT_UNKNOWN},
};
for (const auto &kv : esToSantaOpenSSHResultType) {
XCTAssertEqual(GetOpenSSHLoginResultType(kv.first), kv.second);
}
}
#endif // HAVE_MACOS_13
- (void)testGetAccessType {
std::map<es_event_type_t, ::pbv1::FileAccess::AccessType> eventTypeToAccessType = {
{ES_EVENT_TYPE_AUTH_CLONE, ::pbv1::FileAccess::ACCESS_TYPE_CLONE},
@@ -805,7 +1050,7 @@ void SerializeAndCheckNonESEvents(
- (void)testSerializeFileAccess {
__block es_file_t openFile = MakeESFile("open_file", MakeStat(300));
SerializeAndCheckNonESEvents(
ES_EVENT_TYPE_AUTH_OPEN, @"file_access.json",
6, ES_EVENT_TYPE_AUTH_OPEN, @"file_access.json",
^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi, es_message_t *esMsg) {
esMsg->event.open.file = &openFile;
},
@@ -819,7 +1064,7 @@ void SerializeAndCheckNonESEvents(
- (void)testSerializeAllowlist {
__block es_file_t closeFile = MakeESFile("close_file", MakeStat(300));
SerializeAndCheckNonESEvents(
ES_EVENT_TYPE_NOTIFY_CLOSE, @"allowlist.json",
1, ES_EVENT_TYPE_NOTIFY_CLOSE, @"allowlist.json",
^(std::shared_ptr<MockEndpointSecurityAPI> mockESApi, es_message_t *esMsg) {
esMsg->event.close.target = &closeFile;
},

View File

@@ -22,7 +22,7 @@
#include <sstream>
#include <string>
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
// Small helper class that will sanitize a given string, but will only use new
// memory if the string required sanitization. If the string is already
@@ -56,6 +56,6 @@ class SanitizableString {
mutable std::optional<std::string> sanitized_string_;
};
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa
#endif

View File

@@ -16,9 +16,9 @@
#include "Source/common/String.h"
using santa::common::NSStringToUTF8StringView;
using santa::NSStringToUTF8StringView;
namespace santa::santad::logs::endpoint_security::serializers {
namespace santa {
SanitizableString::SanitizableString(const es_file_t *file)
: data_(file->path.data, file->path.length) {}
@@ -108,4 +108,4 @@ std::optional<std::string> SanitizableString::SanitizeString(const char *str, si
return std::nullopt;
}
} // namespace santa::santad::logs::endpoint_security::serializers
} // namespace santa

View File

@@ -22,7 +22,7 @@
#include "Source/common/TestUtils.h"
using santa::santad::logs::endpoint_security::serializers::SanitizableString;
using santa::SanitizableString;
@interface SanitizableStringTest : XCTestCase
@end

Some files were not shown because too many files have changed in this diff Show More