Compare commits

...

10 Commits
1.16 ... 2021.1

Author SHA1 Message Date
Russell Hancox
a9e5bf09a7 santad: Add some TODOs related to cache 2021-01-11 13:16:38 -05:00
Russell Hancox
4ee3f281c3 santactl/status: Output cache details for sysx 2021-01-11 13:16:38 -05:00
Russell Hancox
462ce89d42 Project: Fix test locations 2021-01-11 13:16:38 -05:00
Russell Hancox
44117833c0 Project: Fix build rule 2021-01-11 13:16:38 -05:00
Russell Hancox
8b6e029da2 Project: bump version to 2021.1
This is a new versioning scheme.
2021-01-11 13:16:38 -05:00
Russell Hancox
f183e246df santad: Make use of caching endpoint security optional 2021-01-11 13:16:38 -05:00
Russell Hancox
c60a35f280 santad: Add caching layer to EndpointSecurity
This first commit is very rough, just adding the caching as simply as
possible. Refactoring is needed.
2021-01-11 13:16:38 -05:00
Russell Hancox
4f65965277 santactl/fileinfo: Fix fileinfo tests on BigSur + multiarch plists (#523)
The fileinfo tests didn't work on BigSur because of some path and binary changes.

Also, the embeddedPlist method didn't work on fat binaries, of which there are now
many, because of M1 machines. I think we didn't notice this before because we pull
the embedded plist from the first arch listed in the headers dict which generally
seemed to pick x86_64 first but with the arm64/arm64e option being added
that now appears first.

Also fixed some errors handling 32-bit segment/sections and added a test for this.
2021-01-07 19:46:48 -05:00
Tom Burgin
01e4e15b81 santactl sync: add config option to enable legacy zlib content encoding (#522) 2020-12-23 10:36:39 -05:00
Russell Hancox
532cb37e0b CI: split out driver and userspace builds (#521) 2020-12-23 08:38:39 -05:00
23 changed files with 382 additions and 50 deletions

View File

@@ -17,7 +17,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Build
run: bazel build --apple_generate_dsym -c opt :release && bazel build --apple_generate_dsym -c opt :release_driver
- name: Build Userspace
run: bazel build --apple_generate_dsym -c opt :release
- name: Build Driver
run: bazel build --apple_generate_dsym -c opt :release_driver
- name: Test
run: bazel test :unit_tests

6
BUILD
View File

@@ -60,9 +60,9 @@ set -e
rm -rf /tmp/bazel_santa_reload
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa_driver/santa_driver.zip >/dev/null
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*/bin/Source/santa_driver/santa_driver.zip >/dev/null
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa/Santa.zip >/dev/null
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*/bin/Source/santa/Santa.zip >/dev/null
echo "You may be asked for your password for sudo"
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
@@ -209,9 +209,9 @@ genrule(
test_suite(
name = "unit_tests",
tests = [
"//Source/common:SantaCacheTest",
"//Source/common:SNTFileInfoTest",
"//Source/common:SNTPrefixTreeTest",
"//Source/santa_driver:SantaCacheTest",
"//Source/santactl:SNTCommandFileInfoTest",
"//Source/santactl:SNTCommandSyncTest",
"//Source/santad:SNTEventTableTest",

View File

@@ -4,6 +4,21 @@ package(default_visibility = ["//:santa_package_group"])
licenses(["notice"]) # Apache 2.0
cc_library(
name = "SantaCache",
srcs = ["SantaCache.h"],
deps = ["//Source/common:SNTKernelCommon"],
)
santa_unit_test(
name = "SantaCacheTest",
srcs = [
"SantaCache.h",
"SantaCacheTest.mm",
],
deps = ["//Source/common:SNTKernelCommon"],
)
objc_library(
name = "SNTBlockMessage",
srcs = ["SNTBlockMessage.m"],
@@ -214,6 +229,7 @@ santa_unit_test(
resources = [
"testdata/bad_pagezero",
"testdata/missing_pagezero",
"testdata/32bitplist",
],
structured_resources = glob([
"testdata/BundleExample.app/**",

View File

@@ -173,6 +173,15 @@
///
@property(readonly, nonatomic) BOOL enableSystemExtension;
///
/// Use an internal cache for decisions instead of relying on the caching
/// mechanism built-in to the EndpointSecurity framework. This may increase
/// performance, particularly when Santa is run alongside other system
/// extensions.
/// Has no effect if the system extension is not being used. Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableSysxCache;
#pragma mark - GUI Settings
///
@@ -332,6 +341,14 @@
///
@property(readonly, nonatomic) BOOL enableDebugLogging;
///
/// If true, compressed requests from "santactl sync" will set "Content-Encoding" to "zlib"
/// instead of the new default "deflate". If syncing with Upvote deployed at commit 0b4477d
/// or below, set this option to true.
/// Defaults to false.
///
@property(readonly, nonatomic) BOOL enableBackwardsCompatibleContentEncoding;
///
/// Retrieve an initialized singleton configurator object using the default file path.
///

View File

@@ -79,11 +79,14 @@ static NSString *const kEventLogPath = @"EventLogPath";
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
static NSString *const kEnableSystemExtension = @"EnableSystemExtension";
static NSString *const kEnableSysxCache = @"EnableSysxCache";
static NSString *const kEnableForkAndExitLogging = @"EnableForkAndExitLogging";
static NSString *const kIgnoreOtherEndpointSecurityClients = @"IgnoreOtherEndpointSecurityClients";
static NSString *const kEnableDebugLogging = @"EnableDebugLogging";
static NSString *const kEnableBackwardsCompatibleContentEncoding = @"EnableBackwardsCompatibleContentEncoding";
// The keys managed by a sync server or mobileconfig.
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kEnableTransitiveRulesKey = @"EnableTransitiveRules";
@@ -155,9 +158,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kEventLogPath : string,
kEnableMachineIDDecoration : number,
kEnableSystemExtension : number,
kEnableSysxCache : number,
kEnableForkAndExitLogging : number,
kIgnoreOtherEndpointSecurityClients : number,
kEnableDebugLogging : number,
kEnableBackwardsCompatibleContentEncoding : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
@@ -329,6 +334,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
return [self configStateSet];
}
@@ -341,6 +350,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
return [self configStateSet];
}
#pragma mark Public Interface
- (SNTClientMode)clientMode {
@@ -576,6 +589,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
}
}
- (BOOL)enableSysxCache {
NSNumber *number = self.configState[kEnableSysxCache];
return number ? [number boolValue] : NO;
}
- (BOOL)enableForkAndExitLogging {
NSNumber *number = self.configState[kEnableForkAndExitLogging];
return number ? [number boolValue] : NO;
@@ -591,6 +609,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [number boolValue] || self.debugFlag;
}
- (BOOL)enableBackwardsCompatibleContentEncoding {
NSNumber *number = self.configState[kEnableBackwardsCompatibleContentEncoding];
return number ? [number boolValue] : NO;
}
#pragma mark Private
///

View File

@@ -551,24 +551,51 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
for (uint32_t i = 0; i < ncmds; ++i) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (memcmp(lc->segname, "__TEXT", 6) == 0) {
if (is64) {
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT_64 && memcmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
offset += lc->cmdsize;
} else {
struct segment_command *lc = (struct segment_command *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT && memcmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
offset += lc->cmdsize;
}
offset += lc->cmdsize;
}
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; ++i) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
uint64_t sectoffset, sectsize = 0;
BOOL found = NO;
if (is64) {
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
sectoffset = sect->offset;
sectsize = sect->size;
found = YES;
}
} else {
struct section *sect = (struct section *)[sectData bytes];
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
sectoffset = sect->offset;
sectsize = sect->size;
found = YES;
}
}
if (found) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(mhwo.offset + sectoffset,
sectsize)];
if (!plistData) return nil;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData

View File

@@ -39,9 +39,8 @@
sut = [[SNTFileInfo alloc] initWithPath:@"../../../../../../../../../../../../../../../bin/ls"];
XCTAssertEqualObjects(sut.path, @"/bin/ls");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/AppleFileServer"];
XCTAssertEqualObjects(sut.path, @"/System/Library/CoreServices/AppleFileServer.app/"
@"Contents/MacOS/AppleFileServer");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/DirectoryService"];
XCTAssertEqualObjects(sut.path, @"/usr/libexec/dspluginhelperd");
}
- (void)testSHA1 {
@@ -72,7 +71,6 @@
XCTAssertTrue(sut.isExecutable);
XCTAssertFalse(sut.isDylib);
XCTAssertFalse(sut.isFat);
XCTAssertFalse(sut.isKext);
XCTAssertFalse(sut.isScript);
}
@@ -106,7 +104,7 @@
}
- (void)testDylibs {
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/libsqlite3.dylib"];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/system/libsystem_platform.dylib"];
XCTAssertTrue(sut.isMachO);
XCTAssertTrue(sut.isDylib);
@@ -231,10 +229,16 @@
}
- (void)testEmbeddedInfoPlist {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"32bitplist"
ofType:@""];
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
XCTAssertNotNil([sut infoPlist]);
XCTAssertEqualObjects([sut infoPlist][@"CFBundleShortVersionString"], @"1.0");
XCTAssertEqualObjects([sut infoPlist][@"CFBundleIdentifier"], @"com.google.i386plist");
// csreq is installed on all machines with Xcode installed. If you're running these tests,
// it should be available..
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
XCTAssertNotNil([sut infoPlist]);
}

View File

@@ -18,7 +18,7 @@
#include <string>
#include <vector>
#include "Source/santa_driver/SantaCache.h"
#include "Source/common/SantaCache.h"
@interface SantaCacheTest : XCTestCase
@end

BIN
Source/common/testdata/32bitplist vendored Executable file

Binary file not shown.

View File

@@ -11,7 +11,6 @@ load("//:version.bzl", "SANTA_VERSION")
cc_library(
name = "santa_driver_lib",
srcs = [
"SantaCache.h",
"SantaDecisionManager.cc",
"SantaDecisionManager.h",
"SantaDriver.cc",
@@ -33,6 +32,7 @@ cc_library(
"SANTA_VERSION=" + SANTA_VERSION,
],
deps = [
"//Source/common:SantaCache",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLoggingKernel",
"//Source/common:SNTPrefixTreeKernel",
@@ -40,15 +40,6 @@ cc_library(
alwayslink = 1,
)
santa_unit_test(
name = "SantaCacheTest",
srcs = [
"SantaCache.h",
"SantaCacheTest.mm",
],
deps = ["//Source/common:SNTKernelCommon"],
)
macos_kernel_extension(
name = "santa_driver",
bundle_id = "com.google.santa-driver",

View File

@@ -23,10 +23,10 @@
#include <sys/proc.h>
#include <sys/vnode.h>
#include "Source/common/SantaCache.h"
#include "Source/common/SNTKernelCommon.h"
#include "Source/common/SNTLogging.h"
#include "Source/common/SNTPrefixTree.h"
#include "Source/santa_driver/SantaCache.h"
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions

View File

@@ -86,9 +86,11 @@ REGISTER_COMMAND_NAME(@"status")
SNTConfigurator *configurator = [SNTConfigurator configurator];
BOOL cachingEnabled = (![configurator enableSystemExtension] || [configurator enableSysxCache]);
// Kext status
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
if (![configurator enableSystemExtension]) {
if (cachingEnabled) {
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
rootCacheCount = rootCache;
@@ -206,8 +208,8 @@ REGISTER_COMMAND_NAME(@"status")
@"transitive_rules" : @(enableTransitiveRules),
},
} mutableCopy];
if (![configurator enableSystemExtension]) {
stats[@"kernel"] = @{
if (cachingEnabled) {
stats[@"cache"] = @{
@"root_cache_count" : @(rootCacheCount),
@"non_root_cache_count": @(nonRootCacheCount),
};
@@ -224,8 +226,8 @@ REGISTER_COMMAND_NAME(@"status")
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
if (![configurator enableSystemExtension]) {
printf(">>> Kernel Info\n");
if (cachingEnabled) {
printf(">>> Cache Info\n");
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
}

View File

@@ -482,6 +482,9 @@ static void reachabilityHandler(
syncState.daemonConn = self.daemonConn;
syncState.daemon = self.daemon;
syncState.compressedContentEncoding =
config.enableBackwardsCompatibleContentEncoding ? @"zlib" : @"deflate";
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
return syncState;
}

View File

@@ -70,7 +70,7 @@
NSData *compressed = [requestBody zlibCompressed];
if (compressed) {
requestBody = compressed;
[req setValue:@"deflate" forHTTPHeaderField:@"Content-Encoding"];
[req setValue:self.syncState.compressedContentEncoding forHTTPHeaderField:@"Content-Encoding"];
}
[req setHTTPBody:requestBody];

View File

@@ -78,4 +78,8 @@
/// Reference to the serial operation queue used for accessing allowlistNotifications.
@property(weak) NSOperationQueue *allowlistNotificationQueue;
/// The header value for ContentEncoding when sending compressed content.
/// Either "deflate" (default) or "zlib".
@property(copy) NSString *compressedContentEncoding;
@end

View File

@@ -16,6 +16,8 @@ objc_library(
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
"EventProviders/SNTEndpointSecurityManager.mm",
"EventProviders/SNTCachingEndpointSecurityManager.h",
"EventProviders/SNTCachingEndpointSecurityManager.mm",
"EventProviders/SNTEventProvider.h",
"Logs/SNTEventLog.h",
"Logs/SNTEventLog.m",
@@ -50,6 +52,7 @@ objc_library(
"IOKit",
],
deps = [
"//Source/common:SantaCache",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
@@ -115,6 +118,7 @@ santa_unit_test(
"bsm",
],
deps = [
"//Source/common:SantaCache",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",

View File

@@ -0,0 +1,20 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <Foundation/Foundation.h>
#include "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
@interface SNTCachingEndpointSecurityManager : SNTEndpointSecurityManager
@end

View File

@@ -0,0 +1,190 @@
/// Copyright 2021 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santad/EventProviders/SNTCachingEndpointSecurityManager.h"
#import "Source/common/SantaCache.h"
#import "Source/common/SNTLogging.h"
#include <atomic>
#include <EndpointSecurity/EndpointSecurity.h>
uint64_t GetCurrentUptime() {
return clock_gettime_nsec_np(CLOCK_MONOTONIC);
}
template<> uint64_t SantaCacheHasher<santa_vnode_id_t>(santa_vnode_id_t const& t) {
return (SantaCacheHasher<uint64_t>(t.fsid) << 1) ^ SantaCacheHasher<uint64_t>(t.fileid);
}
@implementation SNTCachingEndpointSecurityManager {
std::atomic<bool> _compilerPIDs[PID_MAX];
SantaCache<santa_vnode_id_t, uint64_t> *_decisionCache;
}
- (instancetype)init {
self = [super init];
if (self) {
// TODO(rah): Consider splitting into root/non-root cache
_decisionCache = new SantaCache<santa_vnode_id_t, uint64_t>();
}
return self;
}
- (void)dealloc {
if (_decisionCache) delete _decisionCache;
}
- (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
auto vnode_id = [self vnodeIDForFile:m->event.exec.target->executable];
while (true) {
// Check to see if item is in cache
auto return_action = [self checkCache:vnode_id];
// If item was in cache with a valid response, return it.
// If item is in cache but hasn't received a response yet, sleep for a bit.
// If item is not in cache, break out of loop and forward request to callback.
if (RESPONSE_VALID(return_action)) {
if (return_action == ACTION_RESPOND_ALLOW) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
} else {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
}
return YES;
} else if (return_action == ACTION_REQUEST_BINARY || return_action == ACTION_RESPOND_ACK) {
// TODO(rah): Look at a replacement for msleep(), maybe NSCondition
usleep(5000);
} else {
break;
}
}
[self addToCache:vnode_id decision:ACTION_REQUEST_BINARY timeout:0];
return NO;
}
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
API_AVAILABLE(macos(10.15)) {
es_respond_result_t ret;
switch (action) {
case ACTION_RESPOND_ALLOW_COMPILER:
if (sm.pid >= PID_MAX) {
LOGE(@"Unable to watch compiler pid=%d >= pid_max=%d", sm.pid, PID_MAX);
} else {
LOGD(@"Watching compiler pid=%d path=%s", sm.pid, sm.path);
self->_compilerPIDs[sm.pid].store(true);
}
// Allow the exec, but don't cache the decision so subsequent execs of the compiler get
// marked appropriately.
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, false);
break;
case ACTION_RESPOND_ALLOW:
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE:
[self addToCache:sm.vnode_id decision:ACTION_RESPOND_ALLOW timeout:GetCurrentUptime()];
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_DENY:
case ACTION_RESPOND_TOOLONG:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_DENY, false);
break;
case ACTION_RESPOND_ACK:
return ES_RESPOND_RESULT_SUCCESS;
default:
ret = ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT;
}
return ret;
}
- (void)addToCache:(santa_vnode_id_t)identifier
decision:(santa_action_t)decision
timeout:(uint64_t)microsecs {
switch (decision) {
case ACTION_REQUEST_BINARY:
_decisionCache->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
break;
case ACTION_RESPOND_ACK:
_decisionCache->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
((uint64_t)ACTION_REQUEST_BINARY << 56));
break;
case ACTION_RESPOND_ALLOW:
case ACTION_RESPOND_ALLOW_COMPILER:
case ACTION_RESPOND_DENY: {
// Decision is stored in upper 8 bits, timestamp in remaining 56.
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
if (!_decisionCache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
_decisionCache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
}
break;
}
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: {
// Decision is stored in upper 8 bits, timestamp in remaining 56.
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
_decisionCache->set(identifier, val, 0);
break;
}
default:
break;
}
// TODO(rah): Look at a replacement for wakeup(), maybe NSCondition
}
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly API_AVAILABLE(macos(10.15)) {
_decisionCache->clear();
if (!self.connectionEstablished) return YES; // if not connected, there's nothing to flush.
return es_clear_cache(self.client) == ES_CLEAR_CACHE_RESULT_SUCCESS;
}
- (NSArray<NSNumber *> *)cacheCounts {
return @[@(_decisionCache->count()), @(0)];
}
- (NSArray<NSNumber *> *)cacheBucketCount {
// TODO: add this, maybe.
return nil;
}
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
auto result = ACTION_UNSET;
uint64_t decision_time = 0;
uint64_t cache_val = _decisionCache->get(vnodeID);
if (cache_val == 0) return result;
// Decision is stored in upper 8 bits, timestamp in remaining 56.
result = (santa_action_t)(cache_val >> 56);
decision_time = (cache_val & ~(0xFF00000000000000));
if (RESPONSE_VALID(result)) {
if (result == ACTION_RESPOND_DENY) {
auto expiry_time = decision_time + (500 * 100000); // kMaxCacheDenyTimeMilliseconds
if (expiry_time < GetCurrentUptime()) {
_decisionCache->remove(vnodeID);
return ACTION_UNSET;
}
}
}
return result;
}
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeID {
_decisionCache->remove(vnodeID);
// TODO(rah): Look at a replacement for wakeup(), maybe NSCondition
return 0;
}
@end

View File

@@ -17,5 +17,14 @@
#include "Source/common/SNTKernelCommon.h"
#include "Source/santad/EventProviders/SNTEventProvider.h"
#include <EndpointSecurity/EndpointSecurity.h>
// Gleaned from https://opensource.apple.com/source/xnu/xnu-4903.241.1/bsd/sys/proc_internal.h
const pid_t PID_MAX = 99999;
@interface SNTEndpointSecurityManager : NSObject<SNTEventProvider>
- (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file;
@property(readonly, nonatomic) es_client_t *client;
@end

View File

@@ -16,21 +16,17 @@
#include "Source/common/SNTPrefixTree.h"
#import "Source/common/SantaCache.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#include <EndpointSecurity/EndpointSecurity.h>
#include <atomic>
#include <bsm/libbsm.h>
#include <libproc.h>
#include <atomic>
// Gleaned from https://opensource.apple.com/source/xnu/xnu-4903.241.1/bsd/sys/proc_internal.h
#define PID_MAX 99999
@interface SNTEndpointSecurityManager ()
@property(nonatomic) es_client_t *client;
@property(nonatomic) SNTPrefixTree *prefixTree;
@property(nonatomic, copy) void (^decisionCallback)(santa_message_t);
@property(nonatomic, copy) void (^logCallback)(santa_message_t);
@@ -83,7 +79,7 @@
// If enabled, skip any action generated from another endpoint security client.
if (m->process->is_es_client && config.ignoreOtherEndpointSecurityClients) {
if (m->action_type == ES_ACTION_TYPE_AUTH) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
}
if (self.selfPID != pid) {
LOGD(@"Skipping event type: 0x%x from es_client pid: %d", m->event_type, pid);
@@ -107,6 +103,9 @@
// Ignore unmodified files
if (!m->event.close.modified) return;
// Remove from decision cache in case this is invalidating a cached binary.
[self removeCacheEntryForVnodeID:[self vnodeIDForFile:m->event.close.target]];
// Create a transitive rule if the file was modified by a running compiler
if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) {
santa_message_t sm = {};
@@ -154,7 +153,7 @@
// Update the set of running compiler PIDs
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
// Skip the standard pipline and just log.
// Skip the standard pipeline and just log.
if (![config enableForkAndExitLogging]) return;
santa_message_t sm = {};
sm.action = ACTION_NOTIFY_EXIT;
@@ -170,7 +169,7 @@
return;
}
case ES_EVENT_TYPE_NOTIFY_FORK: {
// Skip the standard pipline and just log.
// Skip the standard pipeline and just log.
if (![config enableForkAndExitLogging]) return;
santa_message_t sm = {};
sm.action = ACTION_NOTIFY_FORK;
@@ -236,7 +235,7 @@
switch (ret) {
case ES_NEW_CLIENT_RESULT_SUCCESS:
LOGI(@"Connected to EndpointSecurity");
self.client = client;
_client = client;
return;
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
@@ -251,6 +250,10 @@
}
}
- (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
return NO;
}
- (void)messageHandler:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
santa_message_t sm = {};
sm.es_message = (void *)m;
@@ -261,6 +264,10 @@
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_EXEC: {
if ([self respondFromCache:m]) {
return;
}
sm.action = ACTION_REQUEST_BINARY;
targetFile = m->event.exec.target->executable;
targetProcess = m->event.exec.target;
@@ -553,4 +560,11 @@
return truncated;
}
- (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file {
return {
.fsid = (uint64_t)file->stat.st_dev,
.fileid = file->stat.st_ino,
};
}
@end

View File

@@ -33,6 +33,7 @@
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/EventProviders/SNTDriverManager.h"
#import "Source/santad/EventProviders/SNTCachingEndpointSecurityManager.h"
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
#import "Source/santad/Logs/SNTFileEventLog.h"
@@ -59,8 +60,13 @@
// Choose an event logger.
// Locate and connect to driver / SystemExtension
if ([configurator enableSystemExtension]) {
LOGI(@"Using EndpointSecurity as event provider.");
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
if ([configurator enableSysxCache]) {
LOGI(@"Using CachingEndpointSecurity as event provider.");
_eventProvider = [[SNTCachingEndpointSecurityManager alloc] init];
} else {
LOGI(@"Using EndpointSecurity as event provider.");
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
}
} else {
LOGI(@"Using Kauth as event provider.");
_eventProvider = [[SNTDriverManager alloc] init];

View File

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