mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9e5bf09a7 | ||
|
|
4ee3f281c3 | ||
|
|
462ce89d42 | ||
|
|
44117833c0 | ||
|
|
8b6e029da2 | ||
|
|
f183e246df | ||
|
|
c60a35f280 | ||
|
|
4f65965277 | ||
|
|
01e4e15b81 | ||
|
|
532cb37e0b | ||
|
|
9d379d3884 | ||
|
|
3e7a191bf7 | ||
|
|
c5a048f4d9 | ||
|
|
f4769bad90 | ||
|
|
254497ad15 | ||
|
|
0a83445838 | ||
|
|
eff287259e | ||
|
|
6f2c0e3457 | ||
|
|
38769f7cd1 | ||
|
|
fa785ad3c2 | ||
|
|
5dae0cabdd | ||
|
|
a8b4f4ea7e | ||
|
|
2221c93bbc | ||
|
|
d1c33baf35 | ||
|
|
d2bbdff373 | ||
|
|
db1d65f944 | ||
|
|
d17aeac2f4 | ||
|
|
7840270dd0 | ||
|
|
dcf44c9872 | ||
|
|
fc365c888f | ||
|
|
85f0782399 | ||
|
|
64bc34c302 | ||
|
|
e2fc4c735d |
25
.github/workflows/ci.yml
vendored
Normal file
25
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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
|
||||
15
.travis.yml
15
.travis.yml
@@ -1,15 +0,0 @@
|
||||
---
|
||||
os: osx
|
||||
osx_image: xcode11
|
||||
language: objective-c
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
homebrew:
|
||||
taps: bazelbuild/tap
|
||||
packages: bazelbuild/tap/bazel
|
||||
update: true
|
||||
|
||||
script:
|
||||
- bazel build :release --show_progress_rate_limit=30.0 -c opt --apple_generate_dsym --color=no --verbose_failures --sandbox_debug
|
||||
- bazel test :unit_tests --show_progress_rate_limit=30.0 --test_output=errors --color=no --verbose_failures --sandbox_debug
|
||||
64
BUILD
64
BUILD
@@ -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
|
||||
@@ -78,7 +78,6 @@ genrule(
|
||||
name = "release",
|
||||
srcs = [
|
||||
"//Source/santa:Santa",
|
||||
"//Source/santa_driver",
|
||||
"Conf/install.sh",
|
||||
"Conf/uninstall.sh",
|
||||
"Conf/com.google.santa.bundleservice.plist",
|
||||
@@ -97,9 +96,9 @@ genrule(
|
||||
echo "Please add '-c opt' flag to bazel invocation"
|
||||
""",
|
||||
":opt_build": """
|
||||
# Extract santa_driver.zip and Santa.zip
|
||||
# Extract Santa.zip
|
||||
for SRC in $(SRCS); do
|
||||
if [ "$$(basename $${SRC})" == "santa_driver.zip" -o "$$(basename $${SRC})" == "Santa.zip" ]; then
|
||||
if [ "$$(basename $${SRC})" == "Santa.zip" ]; then
|
||||
mkdir -p $(@D)/binaries
|
||||
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
|
||||
fi
|
||||
@@ -116,10 +115,6 @@ genrule(
|
||||
# Gather together the dSYMs. Throw an error if no dSYMs were found
|
||||
for SRC in $(SRCS); do
|
||||
case $${SRC} in
|
||||
*santa-driver.kext.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
|
||||
;;
|
||||
*santad.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santad.dSYM
|
||||
@@ -162,12 +157,61 @@ genrule(
|
||||
heuristic_label_expansion = 0,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "release_driver",
|
||||
srcs = [
|
||||
"//Source/santa_driver",
|
||||
],
|
||||
outs = ["santa-driver-" + SANTA_VERSION + ".tar.gz"],
|
||||
cmd = select({
|
||||
"//conditions:default": """
|
||||
echo "ERROR: Trying to create a release tarball without optimization."
|
||||
echo "Please add '-c opt' flag to bazel invocation"
|
||||
""",
|
||||
":opt_build": """
|
||||
# Extract santa_driver.zip
|
||||
for SRC in $(SRCS); do
|
||||
if [ "$$(basename $${SRC})" == "santa_driver.zip" ]; then
|
||||
mkdir -p $(@D)/binaries
|
||||
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
# Gather together the dSYMs. Throw an error if no dSYMs were found
|
||||
for SRC in $(SRCS); do
|
||||
case $${SRC} in
|
||||
*santa-driver.kext.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Cause a build failure if the dSYMs are missing.
|
||||
if [[ ! -d "$(@D)/dsym" ]]; then
|
||||
echo "dsym dir missing: Did you forget to use --apple_generate_dsym?"
|
||||
echo "This flag is required for the 'release' target."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update all the timestamps to now. Bazel avoids timestamps to allow
|
||||
# builds to be hermetic and cacheable but for releases we want the
|
||||
# timestamps to be more-or-less correct.
|
||||
find $(@D)/{binaries,dsym} -exec touch {} \\;
|
||||
|
||||
# Create final output tar
|
||||
tar -C $(@D) -czpf $(@) binaries dsym
|
||||
""",
|
||||
}),
|
||||
heuristic_label_expansion = 0,
|
||||
)
|
||||
|
||||
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",
|
||||
|
||||
@@ -13,18 +13,15 @@ approved it, but you must do it before we can put your code into our codebase.
|
||||
|
||||
Before you start working on a larger contribution, you should get in touch with
|
||||
us first through the [issue tracker](https://github.com/google/santa/issues)
|
||||
with your idea so that we can help out and possibly guide you. Coordinating up
|
||||
front makes it much easier to avoid frustration later on.
|
||||
with your idea so that we can help out and possibly guide you. Co-ordinating
|
||||
large changes ahead of time can avoid frustration later on.
|
||||
|
||||
### Code reviews
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. It's also a good idea to run the
|
||||
tests beforehand, which you can do with the following commands:
|
||||
All submissions - including submissions by project members - require review. We
|
||||
use GitHub pull requests for this purpose. GitHub will automatically run the
|
||||
tests when you mail your pull request and a proper review won't be started until
|
||||
the tests are complete and passing.
|
||||
|
||||
```sh
|
||||
rake tests:logic
|
||||
rake tests:kernel # only necessary if you're changing the kext code
|
||||
```
|
||||
### Code Style
|
||||
|
||||
All code submissions should try to match the surrounding code. Wherever possible,
|
||||
|
||||
@@ -23,7 +23,7 @@ DEPENDENCIES:
|
||||
- OCMock
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
@@ -43,4 +43,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: d03767a9915896232523962c98d9ff7294aec2b7
|
||||
|
||||
COCOAPODS: 1.7.5
|
||||
COCOAPODS: 1.10.0
|
||||
|
||||
14
README.md
14
README.md
@@ -1,9 +1,4 @@
|
||||
# Santa [![Build Status][build-status-img]][build-status-link] [![Documentation Status][doc-status-img]][doc-status-link]
|
||||
|
||||
[build-status-img]: https://travis-ci.org/google/santa.png?branch=master
|
||||
[build-status-link]: https://travis-ci.org/google/santa
|
||||
[doc-status-img]: https://readthedocs.org/projects/santa/badge/?version=latest
|
||||
[doc-status-link]: https://santa.readthedocs.io/en/latest/?badge=latest
|
||||
# Santa 
|
||||
|
||||
<p align="center">
|
||||
<img src="./Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
@@ -145,6 +140,9 @@ protect hosts in whatever other ways you see fit.
|
||||
* [Zentral](https://github.com/zentralopensource/zentral/wiki) - A
|
||||
centralized service that pulls data from multiple sources and deploy
|
||||
configurations to multiple services.
|
||||
* [Zercurity](https://github.com/zercurity/zercurity) - A dockerized service
|
||||
for managing and monitoring applications across a large fleet utilizing
|
||||
Santa + Osquery.
|
||||
|
||||
* Alternatively, `santactl` can configure rules locally (without a sync
|
||||
server).
|
||||
@@ -154,8 +152,8 @@ protect hosts in whatever other ways you see fit.
|
||||
A tool like Santa doesn't really lend itself to screenshots, so here's a video
|
||||
instead.
|
||||
|
||||
<p align="center"> <img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif"
|
||||
alt="Santa Block Video" /> </p>
|
||||
|
||||
<p align="center"> <img src="https://thumbs.gfycat.com/MadFatalAmphiuma-small.gif" alt="Santa Block Video" /> </p>
|
||||
|
||||
# Kext Signing
|
||||
Kernel extensions on macOS 10.9 and later must be signed using an Apple-provided
|
||||
|
||||
@@ -115,6 +115,8 @@
|
||||
C7FA384824B6169400D192F9 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD82322B84F00F36578 /* SNTLogging.m */; };
|
||||
C7FA384924B6169400D192F9 /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ADC2322B84F00F36578 /* SNTStoredEvent.m */; };
|
||||
C7FA384A24B6169400D192F9 /* SNTXPCSyncServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FA383724B60FF800D192F9 /* SNTXPCSyncServiceInterface.m */; };
|
||||
C7FACBFA25646FE500CCB198 /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACD2322B84F00F36578 /* SNTConfigurator.m */; };
|
||||
C7FACC0225646FEB00CCB198 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD12322B84F00F36578 /* SNTSystemInfo.m */; };
|
||||
D28CA4C618C62392319BB642 /* libPods-santactl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7714ABC7F247685608DACE7 /* libPods-santactl.a */; };
|
||||
D698C8C9E47554577ED4939F /* libPods-com.google.santa.daemon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C05F6AD95EB704B20828BDA1 /* libPods-com.google.santa.daemon.a */; };
|
||||
F5F5D1EF2AF051FEA97A3A59 /* libPods-sysx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 91FF0B4E62F1E90A88478993 /* libPods-sysx.a */; };
|
||||
@@ -1341,7 +1343,9 @@
|
||||
C7CDA6FC233E73D80013622B /* SNTFileInfo.m in Sources */,
|
||||
C7CDA6FF233E80160013622B /* SNTLogging.m in Sources */,
|
||||
C7CDA6FD233E73E40013622B /* SNTStoredEvent.m in Sources */,
|
||||
C7FACBFA25646FE500CCB198 /* SNTConfigurator.m in Sources */,
|
||||
C7CDA6FE233E73ED0013622B /* SNTXPCNotifierInterface.m in Sources */,
|
||||
C7FACC0225646FEB00CCB198 /* SNTSystemInfo.m in Sources */,
|
||||
C7F5C1AE233E72CC00A3F7FD /* main.m in Sources */,
|
||||
C7F5C1AF233E72CF00A3F7FD /* SNTBundleService.m in Sources */,
|
||||
);
|
||||
@@ -1453,7 +1457,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
|
||||
package(default_visibility = ["//:santa_package_group"])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
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",
|
||||
@@ -10,6 +25,7 @@ objc_library(
|
||||
hdrs = ["SNTBlockMessage.h"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTLogging",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
@@ -21,6 +37,7 @@ objc_library(
|
||||
defines = ["SANTAGUI"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTLogging",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
@@ -35,7 +52,7 @@ objc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
objc_library(
|
||||
name = "SNTCommonEnums",
|
||||
hdrs = ["SNTCommonEnums.h"],
|
||||
)
|
||||
@@ -46,7 +63,6 @@ objc_library(
|
||||
hdrs = ["SNTConfigurator.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTLogging",
|
||||
":SNTStrengthify",
|
||||
":SNTSystemInfo",
|
||||
],
|
||||
@@ -76,12 +92,18 @@ cc_library(
|
||||
cc_library(
|
||||
name = "SNTLoggingKernel",
|
||||
hdrs = ["SNTLogging.h"],
|
||||
copts = [
|
||||
"-mkernel",
|
||||
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
|
||||
],
|
||||
defines = ["KERNEL"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTLogging",
|
||||
srcs = ["SNTLogging.m"],
|
||||
hdrs = ["SNTLogging.h"],
|
||||
deps = [":SNTConfigurator"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -207,6 +229,7 @@ santa_unit_test(
|
||||
resources = [
|
||||
"testdata/bad_pagezero",
|
||||
"testdata/missing_pagezero",
|
||||
"testdata/32bitplist",
|
||||
],
|
||||
structured_resources = glob([
|
||||
"testdata/BundleExample.app/**",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@implementation SNTBlockMessage
|
||||
|
||||
@@ -106,6 +107,9 @@
|
||||
+ (NSURL *)eventDetailURLForEvent:(SNTStoredEvent *)event {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
NSString *hostname = [SNTSystemInfo longHostname];
|
||||
NSString *uuid = [SNTSystemInfo hardwareUUID];
|
||||
NSString *serial = [SNTSystemInfo serialNumber];
|
||||
NSString *formatStr = config.eventDetailURL;
|
||||
if (!formatStr.length) return nil;
|
||||
|
||||
@@ -122,6 +126,15 @@
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%machine_id%"
|
||||
withString:config.machineID];
|
||||
}
|
||||
if (hostname.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%hostname%" withString:hostname];
|
||||
}
|
||||
if (uuid.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%uuid%" withString:uuid];
|
||||
}
|
||||
if (serial.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%serial%" withString:serial];
|
||||
}
|
||||
|
||||
return [NSURL URLWithString:formatStr];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
///
|
||||
@@ -191,6 +200,9 @@
|
||||
/// %file_sha% -- SHA-256 of the file that was blocked.
|
||||
/// %machine_id% -- ID of the machine.
|
||||
/// %username% -- executing user.
|
||||
/// %serial% -- System's serial number.
|
||||
/// %uuid% -- System's UUID.
|
||||
/// %hostname% -- System's full hostname.
|
||||
///
|
||||
/// @note: This is not an NSURL because the format-string parsing is done elsewhere.
|
||||
///
|
||||
@@ -317,6 +329,26 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableForkAndExitLogging;
|
||||
|
||||
///
|
||||
/// If true, ignore actions from other endpoint security clients. Defaults to false. This only
|
||||
/// applies when running as a sysx.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL ignoreOtherEndpointSecurityClients;
|
||||
|
||||
///
|
||||
/// If true, debug logging will be enabled for all Santa components. Defaults to false.
|
||||
/// Passing --debug as an executable argument will enable debug logging for that specific component.
|
||||
///
|
||||
@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.
|
||||
///
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@@ -24,13 +23,16 @@
|
||||
/// A NSUserDefaults object set to use the com.google.santa suite.
|
||||
@property(readonly, nonatomic) NSUserDefaults *defaults;
|
||||
|
||||
// Keys and expected value types.
|
||||
/// Keys and expected value types.
|
||||
@property(readonly, nonatomic) NSDictionary *syncServerKeyTypes;
|
||||
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
|
||||
|
||||
/// Holds the configurations from a sync server and mobileconfig.
|
||||
@property NSMutableDictionary *syncState;
|
||||
@property NSMutableDictionary *configState;
|
||||
|
||||
/// Was --debug passed as an argument to this process?
|
||||
@property(readonly, nonatomic) BOOL debugFlag;
|
||||
@end
|
||||
|
||||
@implementation SNTConfigurator
|
||||
@@ -77,8 +79,13 @@ 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";
|
||||
@@ -126,7 +133,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kBlockedPathRegexKey : re,
|
||||
kBlockedPathRegexKeyDeprecated : re,
|
||||
kEnablePageZeroProtectionKey : number,
|
||||
kEnableBadSignatureProtectionKey: number,
|
||||
kEnableBadSignatureProtectionKey : number,
|
||||
kMoreInfoURLKey : string,
|
||||
kEventDetailURLKey : string,
|
||||
kEventDetailTextKey : string,
|
||||
@@ -139,7 +146,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kClientAuthCertificatePasswordKey : string,
|
||||
kClientAuthCertificateCNKey : string,
|
||||
kClientAuthCertificateIssuerKey : string,
|
||||
kServerAuthRootsDataKey : data,
|
||||
kServerAuthRootsDataKey : data,
|
||||
kServerAuthRootsFileKey : string,
|
||||
kMachineOwnerKey : string,
|
||||
kMachineIDKey : string,
|
||||
@@ -151,12 +158,17 @@ 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"];
|
||||
_configState = [self readForcedConfig];
|
||||
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
|
||||
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
|
||||
[self startWatchingDefaults];
|
||||
}
|
||||
return self;
|
||||
@@ -322,10 +334,26 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingIgnoreOtherEndpointSecurityClients {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableDebugLogging {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
@@ -345,8 +373,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
- (void)setSyncServerClientMode:(SNTClientMode)newMode {
|
||||
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
|
||||
[self updateSyncStateForKey:kClientModeKey value:@(newMode)];
|
||||
} else {
|
||||
LOGW(@"Ignoring request to change client mode to %ld", newMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +435,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
NSArray *filters = self.configState[kFileChangesPrefixFiltersKey];
|
||||
for (id filter in filters) {
|
||||
if (![filter isKindOfClass:[NSString class]]) {
|
||||
LOGE(@"Ignoring FileChangesPrefixFilters: array contains a non-string %@", filter);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -420,7 +445,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
NSString *urlString = self.configState[kSyncBaseURLKey];
|
||||
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
if (urlString && !url) LOGW(@"SyncBaseURL is not a valid URL!");
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -565,11 +589,31 @@ 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;
|
||||
}
|
||||
|
||||
- (BOOL)ignoreOtherEndpointSecurityClients {
|
||||
NSNumber *number = self.configState[kIgnoreOtherEndpointSecurityClients];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableDebugLogging {
|
||||
NSNumber *number = self.configState[kEnableDebugLogging];
|
||||
return [number boolValue] || self.debugFlag;
|
||||
}
|
||||
|
||||
- (BOOL)enableBackwardsCompatibleContentEncoding {
|
||||
NSNumber *number = self.configState[kEnableBackwardsCompatibleContentEncoding];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
///
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
/// Common defines between kernel <-> userspace
|
||||
///
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifndef SANTA__COMMON__KERNELCOMMON_H
|
||||
#define SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
// Defines the name of the userclient class and the driver bundle ID.
|
||||
#define USERCLIENT_CLASS "com_google_SantaDriver"
|
||||
#define USERCLIENT_ID "com.google.santa-driver"
|
||||
@@ -118,6 +119,7 @@ typedef struct {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
int pidversion;
|
||||
pid_t ppid;
|
||||
char path[MAXPATHLEN];
|
||||
char newpath[MAXPATHLEN];
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
|
||||
#import <asl.h>
|
||||
#import <pthread.h>
|
||||
|
||||
@@ -39,8 +41,7 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
dispatch_once(&pred, ^{
|
||||
binaryName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
// If debug logging is enabled, the process must be restarted.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
|
||||
if ([SNTConfigurator configurator].enableDebugLogging) {
|
||||
logLevel = LOG_LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
BIN
Source/common/testdata/32bitplist
vendored
Executable file
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
exports_files([
|
||||
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
|
||||
])
|
||||
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
|
||||
objc_library(
|
||||
name = "SantaGUI_lib",
|
||||
srcs = [
|
||||
@@ -35,6 +35,7 @@ objc_library(
|
||||
deps = [
|
||||
"//Source/common:SNTBlockMessage_SantaGUI",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"@MOLCodesignChecker",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@build_bazel_rules_apple//apple:macos.bzl",
|
||||
@@ -11,7 +11,6 @@ load("//:version.bzl", "SANTA_VERSION")
|
||||
cc_library(
|
||||
name = "santa_driver_lib",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaDecisionManager.cc",
|
||||
"SantaDecisionManager.h",
|
||||
"SantaDriver.cc",
|
||||
@@ -23,6 +22,7 @@ cc_library(
|
||||
copts = [
|
||||
"-mkernel",
|
||||
"-fapple-kext",
|
||||
"-Wno-ossharedptr-misuse",
|
||||
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
|
||||
],
|
||||
defines = [
|
||||
@@ -32,6 +32,7 @@ cc_library(
|
||||
"SANTA_VERSION=" + SANTA_VERSION,
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SNTKernelCommon",
|
||||
"//Source/common:SNTLoggingKernel",
|
||||
"//Source/common:SNTPrefixTreeKernel",
|
||||
@@ -39,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",
|
||||
|
||||
@@ -616,7 +616,7 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
pid_t pid = proc_pid(proc);
|
||||
pid_t ppid = proc_ppid(proc);
|
||||
// pid_t is 32-bit; pid is in upper 32 bits, ppid in lower.
|
||||
uint64_t val = ((uint64_t)pid << 32) | (ppid & 0xFFFFFFFF);
|
||||
uint64_t val = ((uint64_t)pid << 32) | ((uint64_t)ppid & 0xFFFFFFFF);
|
||||
vnode_pid_map_->set(vnode_id, val);
|
||||
if (returnedAction == ACTION_RESPOND_ALLOW_COMPILER && ppid != 0) {
|
||||
// Do some additional bookkeeping for compilers:
|
||||
@@ -674,8 +674,8 @@ void SantaDecisionManager::FileOpCallback(
|
||||
uint64_t val = vnode_pid_map_->get(vnode_id);
|
||||
if (val) {
|
||||
// pid_t is 32-bit, so pid is in upper 32 bits, ppid in lower.
|
||||
message->pid = (val >> 32);
|
||||
message->ppid = (val & ~0xFFFFFFFF00000000);
|
||||
message->pid = (pid_t)(val >> 32);
|
||||
message->ppid = (pid_t)(val & ~0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
PostToLogQueue(message);
|
||||
@@ -816,7 +816,7 @@ extern "C" int vnode_scope_callback(
|
||||
// We only care about regular files.
|
||||
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
|
||||
|
||||
if ((action & KAUTH_VNODE_EXECUTE) && !(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
if ((action & (int)KAUTH_VNODE_EXECUTE) && !(action & (int)KAUTH_VNODE_ACCESS)) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
int result = sdm->VnodeCallback(credential,
|
||||
reinterpret_cast<vfs_context_t>(arg0),
|
||||
@@ -824,9 +824,9 @@ extern "C" int vnode_scope_callback(
|
||||
reinterpret_cast<int *>(arg3));
|
||||
sdm->DecrementListenerInvocations();
|
||||
return result;
|
||||
} else if (action & KAUTH_VNODE_WRITE_DATA || action & KAUTH_VNODE_APPEND_DATA) {
|
||||
} else if (action & (int)KAUTH_VNODE_WRITE_DATA || action & (int)KAUTH_VNODE_APPEND_DATA) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
if (!(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
if (!(action & (int)KAUTH_VNODE_ACCESS)) {
|
||||
auto vnode_id = sdm->GetVnodeIDForVnode(reinterpret_cast<vfs_context_t>(arg0), vp);
|
||||
sdm->RemoveFromCache(vnode_id);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -96,13 +96,13 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
for (NSUInteger i = 0; i < arguments.count; ++i) {
|
||||
NSString *arg = arguments[i];
|
||||
|
||||
if ([arg caseInsensitiveCompare:@"--allowlist"] == NSOrderedSame ||
|
||||
if ([arg caseInsensitiveCompare:@"--allow"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateAllow;
|
||||
} else if ([arg caseInsensitiveCompare:@"--blocklist"] == NSOrderedSame ||
|
||||
} else if ([arg caseInsensitiveCompare:@"--block"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateBlock;
|
||||
} else if ([arg caseInsensitiveCompare:@"--silent-blocklist"] == NSOrderedSame ||
|
||||
} else if ([arg caseInsensitiveCompare:@"--silent-block"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateSilentBlock;
|
||||
} else if ([arg caseInsensitiveCompare:@"--compiler"] == NSOrderedSame) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ extern NSString *const kBinaryRuleCount;
|
||||
extern NSString *const kCertificateRuleCount;
|
||||
extern NSString *const kCompilerRuleCount;
|
||||
extern NSString *const kTransitiveRuleCount;
|
||||
extern NSString *const kFullSyncInterval;
|
||||
extern NSString *const kFCMToken;
|
||||
extern NSString *const kFCMFullSyncInterval;
|
||||
extern NSString *const kFCMGlobalRuleSyncDeadline;
|
||||
|
||||
@@ -37,6 +37,7 @@ NSString *const kBinaryRuleCount = @"binary_rule_count";
|
||||
NSString *const kCertificateRuleCount = @"certificate_rule_count";
|
||||
NSString *const kCompilerRuleCount = @"compiler_rule_count";
|
||||
NSString *const kTransitiveRuleCount = @"transitive_rule_count";
|
||||
NSString *const kFullSyncInterval = @"full_sync_interval";
|
||||
NSString *const kFCMToken = @"fcm_token";
|
||||
NSString *const kFCMFullSyncInterval = @"fcm_full_sync_interval";
|
||||
NSString *const kFCMGlobalRuleSyncDeadline = @"fcm_global_rule_sync_deadline";
|
||||
|
||||
@@ -55,6 +55,8 @@ static NSString *const kFCMTargetHostIDKey = @"target_host_id";
|
||||
// allowlistNotificationQueue is used to serialize access to the allowlistNotifications dictionary.
|
||||
@property(nonatomic) NSOperationQueue *allowlistNotificationQueue;
|
||||
|
||||
@property NSUInteger fullSyncInterval;
|
||||
|
||||
@property NSUInteger FCMFullSyncInterval;
|
||||
@property NSUInteger FCMGlobalRuleSyncDeadline;
|
||||
@property NSUInteger eventBatchSize;
|
||||
@@ -122,6 +124,7 @@ static void reachabilityHandler(
|
||||
_allowlistNotificationQueue = [[NSOperationQueue alloc] init];
|
||||
_allowlistNotificationQueue.maxConcurrentOperationCount = 1; // make this a serial queue
|
||||
|
||||
_fullSyncInterval = kDefaultFullSyncInterval;
|
||||
_eventBatchSize = kDefaultEventBatchSize;
|
||||
_FCMFullSyncInterval = kDefaultFCMFullSyncInterval;
|
||||
_FCMGlobalRuleSyncDeadline = kDefaultFCMGlobalRuleSyncDeadline;
|
||||
@@ -350,10 +353,11 @@ static void reachabilityHandler(
|
||||
self.FCMGlobalRuleSyncDeadline = syncState.FCMGlobalRuleSyncDeadline;
|
||||
[self listenForPushNotificationsWithSyncState:syncState];
|
||||
} else if (syncState.daemon) {
|
||||
LOGD(@"FCMToken not provided. Sync every %lu min.", kDefaultFullSyncInterval / 60);
|
||||
LOGD(@"FCMToken not provided. Sync every %lu min.", syncState.fullSyncInterval / 60);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kDefaultFullSyncInterval];
|
||||
self.fullSyncInterval = syncState.fullSyncInterval;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:self.fullSyncInterval];
|
||||
}
|
||||
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
@@ -478,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;
|
||||
}
|
||||
|
||||
@@ -105,12 +105,17 @@
|
||||
self.syncState.FCMToken = resp[kFCMToken];
|
||||
|
||||
// Don't let these go too low
|
||||
NSUInteger value = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.FCMFullSyncInterval =
|
||||
(value < kDefaultFullSyncInterval) ? kDefaultFCMFullSyncInterval : value;
|
||||
value = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
|
||||
NSUInteger FCMIntervalValue = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.FCMFullSyncInterval = (FCMIntervalValue < kDefaultFullSyncInterval)
|
||||
? kDefaultFCMFullSyncInterval
|
||||
: FCMIntervalValue;
|
||||
FCMIntervalValue = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
|
||||
self.syncState.FCMGlobalRuleSyncDeadline =
|
||||
(value < 60) ? kDefaultFCMGlobalRuleSyncDeadline : value;
|
||||
(FCMIntervalValue < 60) ? kDefaultFCMGlobalRuleSyncDeadline : FCMIntervalValue;
|
||||
|
||||
// Check if our sync interval has changed
|
||||
NSUInteger intervalValue = [resp[kFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.fullSyncInterval = (intervalValue < 60) ? kDefaultFullSyncInterval : intervalValue;
|
||||
|
||||
if ([resp[kClientMode] isEqual:kClientModeMonitor]) {
|
||||
self.syncState.clientMode = SNTClientModeMonitor;
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
NSData *compressed = [requestBody zlibCompressed];
|
||||
if (compressed) {
|
||||
requestBody = compressed;
|
||||
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[req setValue:self.syncState.compressedContentEncoding forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
/// An XSRF token to send in the headers with each request.
|
||||
@property NSString *xsrfToken;
|
||||
|
||||
/// Full sync interval in seconds without FCM to update kDefaultFullSyncInterval => when FCM
|
||||
/// is not used, defaults to 10m.
|
||||
@property NSUInteger fullSyncInterval;
|
||||
|
||||
/// An FCM token to subscribe to push notifications.
|
||||
@property(copy) NSString *FCMToken;
|
||||
|
||||
@@ -74,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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
@@ -77,10 +73,23 @@
|
||||
|
||||
es_client_t *client = NULL;
|
||||
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
|
||||
pid_t pid = audit_token_to_pid(m->process->audit_token);
|
||||
int pidversion = audit_token_to_pidversion(m->process->audit_token);
|
||||
|
||||
// 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, false);
|
||||
}
|
||||
if (self.selfPID != pid) {
|
||||
LOGD(@"Skipping event type: 0x%x from es_client pid: %d", m->event_type, pid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the following checks on this serial queue.
|
||||
// Some checks are simple filters that avoid copying m.
|
||||
// However, the bulk of the work done here is to support transitive whitelisting.
|
||||
pid_t pid = audit_token_to_pid(m->process->audit_token);
|
||||
switch (m->event_type) {
|
||||
case ES_EVENT_TYPE_NOTIFY_EXEC: {
|
||||
// Deny results are currently logged when ES_EVENT_TYPE_AUTH_EXEC posts a deny.
|
||||
@@ -94,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 = {};
|
||||
@@ -107,6 +119,7 @@
|
||||
}
|
||||
sm.action = ACTION_NOTIFY_WHITELIST;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
LOGI(@"CLOSE: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
|
||||
self.decisionCallback(sm);
|
||||
}
|
||||
@@ -128,6 +141,7 @@
|
||||
}
|
||||
sm.action = ACTION_NOTIFY_WHITELIST;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
LOGI(@"RENAME: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
|
||||
self.decisionCallback(sm);
|
||||
}
|
||||
@@ -139,11 +153,12 @@
|
||||
// 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;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
sm.ppid = m->process->original_ppid;
|
||||
audit_token_t at = m->process->audit_token;
|
||||
sm.uid = audit_token_to_ruid(at);
|
||||
@@ -154,13 +169,14 @@
|
||||
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;
|
||||
sm.ppid = m->event.fork.child->original_ppid;
|
||||
audit_token_t at = m->event.fork.child->audit_token;
|
||||
sm.pid = audit_token_to_pid(at);
|
||||
sm.pidversion = audit_token_to_pidversion(at);
|
||||
sm.uid = audit_token_to_ruid(at);
|
||||
sm.gid = audit_token_to_rgid(at);
|
||||
dispatch_async(self.esNotifyQueue, ^{
|
||||
@@ -219,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");
|
||||
@@ -234,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;
|
||||
@@ -244,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;
|
||||
@@ -279,6 +303,7 @@
|
||||
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
|
||||
LOGW(@"Preventing attempt to delete Santa databases!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
@@ -294,6 +319,20 @@
|
||||
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
|
||||
LOGW(@"Preventing attempt to rename Santa databases!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
}
|
||||
case ES_EVENT_TYPE_AUTH_KEXTLOAD: {
|
||||
es_string_token_t identifier = m->event.kextload.identifier;
|
||||
NSString *ident = [[NSString alloc] initWithBytes:identifier.data
|
||||
length:identifier.length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if ([ident isEqualToString:@"com.google.santa-driver"]) {
|
||||
LOGW(@"Preventing attempt to load Santa kext!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
@@ -360,6 +399,7 @@
|
||||
sm.uid = audit_token_to_ruid(targetProcess->audit_token);
|
||||
sm.gid = audit_token_to_rgid(targetProcess->audit_token);
|
||||
sm.pid = audit_token_to_pid(targetProcess->audit_token);
|
||||
sm.pidversion = audit_token_to_pidversion(targetProcess->audit_token);
|
||||
sm.ppid = targetProcess->original_ppid;
|
||||
proc_name((m->event_type == ES_EVENT_TYPE_AUTH_EXEC) ? sm.ppid : sm.pid, sm.pname, 1024);
|
||||
callback(sm);
|
||||
@@ -370,13 +410,17 @@
|
||||
|
||||
self.decisionCallback = callback;
|
||||
es_event_type_t events[] = {
|
||||
ES_EVENT_TYPE_AUTH_EXEC,
|
||||
ES_EVENT_TYPE_AUTH_UNLINK,
|
||||
ES_EVENT_TYPE_AUTH_RENAME,
|
||||
ES_EVENT_TYPE_NOTIFY_EXIT,
|
||||
ES_EVENT_TYPE_AUTH_EXEC,
|
||||
ES_EVENT_TYPE_AUTH_UNLINK,
|
||||
ES_EVENT_TYPE_AUTH_RENAME,
|
||||
ES_EVENT_TYPE_AUTH_KEXTLOAD,
|
||||
|
||||
// This is in the decision callback because it's used for detecting
|
||||
// the exit of a 'compiler' used by transitive whitelisting.
|
||||
ES_EVENT_TYPE_NOTIFY_EXIT,
|
||||
};
|
||||
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_AUTH_EXEC: %d", sret);
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe to auth events: %d", sret);
|
||||
|
||||
// There's a gap between creating a client and subscribing to events. Creating the client
|
||||
// triggers a cache flush automatically but any events that happen in this gap could be allowed
|
||||
@@ -397,7 +441,7 @@
|
||||
ES_EVENT_TYPE_NOTIFY_FORK,
|
||||
};
|
||||
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_NOTIFY_EXEC: %d", sret);
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe to notify events: %d", sret);
|
||||
}
|
||||
|
||||
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
|
||||
@@ -516,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
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
char ppath[PATH_MAX] = "(null)";
|
||||
proc_pidpath(message.pid, ppath, PATH_MAX);
|
||||
|
||||
[outStr appendFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|user=%@|gid=%d|group=%@",
|
||||
message.pid, message.ppid, message.pname, ppath,
|
||||
[outStr appendFormat:@"|pid=%d|pidversion=%d|ppid=%d|process=%s|processpath=%s|uid=%d|user=%@|gid=%d|group=%@",
|
||||
message.pid, message.pidversion, message.ppid, message.pname, ppath,
|
||||
message.uid, [self nameForUID:message.uid],
|
||||
message.gid, [self nameForGID:message.gid]];
|
||||
|
||||
@@ -169,8 +169,8 @@
|
||||
mode = @"U"; break;
|
||||
}
|
||||
|
||||
[outLog appendFormat:@"|pid=%d|ppid=%d|uid=%d|user=%@|gid=%d|group=%@|mode=%@|path=%@",
|
||||
message.pid, message.ppid,
|
||||
[outLog appendFormat:@"|pid=%d|pidversion=%d|ppid=%d|uid=%d|user=%@|gid=%d|group=%@|mode=%@|path=%@",
|
||||
message.pid, message.pidversion, message.ppid,
|
||||
message.uid, [self nameForUID:message.uid],
|
||||
message.gid, [self nameForGID:message.gid],
|
||||
mode, [self sanitizeString:@(message.path)]];
|
||||
@@ -269,15 +269,15 @@
|
||||
}
|
||||
|
||||
- (void)logFork:(santa_message_t)message {
|
||||
NSString *s = [NSString stringWithFormat:@"action=FORK|pid=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.ppid, message.uid, message.gid];
|
||||
NSString *s = [NSString stringWithFormat:@"action=FORK|pid=%d|pidversion=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.pidversion, message.ppid, message.uid, message.gid];
|
||||
[self writeLog:s];
|
||||
|
||||
}
|
||||
|
||||
- (void)logExit:(santa_message_t)message {
|
||||
NSString *s = [NSString stringWithFormat:@"action=EXIT|pid=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.ppid, message.uid, message.gid];
|
||||
NSString *s = [NSString stringWithFormat:@"action=EXIT|pid=%d|pidversion=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.pidversion, message.ppid, message.uid, message.gid];
|
||||
[self writeLog:s];
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
LOGE(@"unable to add new transitive rule to database: %@", err.localizedDescription);
|
||||
} else {
|
||||
[self.eventLog
|
||||
writeLog:[NSString stringWithFormat:@"action=ALLOWLIST|pid=%d|path=%s|sha256=%@",
|
||||
message.pid, target, fi.SHA256]];
|
||||
writeLog:[NSString stringWithFormat:@"action=ALLOWLIST|pid=%d|pidversion=%d|path=%s|sha256=%@",
|
||||
message.pid, message.pidversion, target, fi.SHA256]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl",
|
||||
git_repository(
|
||||
name = "build_bazel_rules_apple",
|
||||
remote = "https://github.com/bazelbuild/rules_apple.git",
|
||||
tag = "0.19.0",
|
||||
tag = "0.21.2",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")
|
||||
@@ -19,7 +19,7 @@ apple_rules_dependencies()
|
||||
git_repository(
|
||||
name = "MOLAuthenticatingURLSession",
|
||||
remote = "https://github.com/google/macops-molauthenticatingurlsession.git",
|
||||
tag = "v2.8",
|
||||
tag = "v2.9",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
|
||||
@@ -52,6 +52,9 @@ This property contains a kind of format string to be turned into the URL to send
|
||||
| %file_sha% | SHA-256 of the file that was blocked |
|
||||
| %machine_id% | ID of the machine |
|
||||
| %username% | The executing user |
|
||||
| %serial% | System's serial number |
|
||||
| %uuid% | System's UUID |
|
||||
| %hostname% | System's full hostname |
|
||||
|
||||
For example: `https://sync-server-hostname/%machine_id%/%file_sha%`
|
||||
|
||||
@@ -167,6 +170,7 @@ Configuration profiles have a `.mobileconfig` file extension. There are many way
|
||||
| upload_logs_url** | String | If set, the endpoint to send Santa's current logs. No default. |
|
||||
| allowed_path_regex | String | Same as the "Local Configuration" AllowedPathRegex. No default. |
|
||||
| blocked_path_regex | String | Same as the "Local Configuration" BlockedPathRegex. No default. |
|
||||
| full_sync_interval* | Integer | The max time to wait before performing a full sync with the server. Defaults to 600 secs (10 minutes) if not set. |
|
||||
| fcm_token* | String | The FCM token used by Santa to listen for FCM messages. Unique for every machine. No default. |
|
||||
| fcm_full_sync_interval* | Integer | The full sync interval if a fcm_token is set. Defaults to 14400 secs (4 hours). |
|
||||
| fcm_global_rule_sync_deadline* | Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""The version for all Santa components."""
|
||||
|
||||
SANTA_VERSION = "1.14"
|
||||
SANTA_VERSION = "2021.1"
|
||||
|
||||
Reference in New Issue
Block a user