Compare commits

...

33 Commits
1.15 ... 2021.3

Author SHA1 Message Date
Russell Hancox
ad1868a50f santad: Fix transitive rules when using the sysx cache feature (#540)
This fixes transitive allowlisting when `EnableSysxCache` is turned on, reduces the deadline timer to fire 5s before the ES deadline, remaps our DEBUG logs to NOTICE so they can be more easily seen in Console and prevents transitive rules being created for paths under /dev/.
2021-03-04 09:47:32 -05:00
Russell Hancox
78643d3c49 fileinfo: Don't use non-bundle dirs as possible ancestors (#537) 2021-02-01 11:09:32 -05:00
Russell Hancox
8b22c85a64 Project: run buildifier on BUILD files (#534) 2021-01-28 10:31:07 -05:00
Russell Hancox
58fe5d3d76 santad: Use OS_FALLTHROUGH (#535) 2021-01-28 10:30:47 -05:00
Russell Hancox
8b2227967e santad: Fix caching of deny decisions (#533) 2021-01-28 10:12:20 -05:00
Russell Hancox
65693acea1 Docs: fix syncing-overview link in santactl doc (#531) 2021-01-27 12:35:02 -05:00
Russell Hancox
7cea383930 Docs: the docs build can't use symlinks ref. out of the docs dir (#530) 2021-01-27 12:25:50 -05:00
headmin
5ae2376158 Docs: Add example .mobileconfig profile to enable Notifications settings (#529) 2021-01-27 11:00:34 -05:00
Russell Hancox
e851337eac Docs: Fix some broken links in the index (#528) 2021-01-27 10:32:30 -05:00
Russell Hancox
2e53834980 santactl/sync: retry individual requests during a sync (#526)
Each request is retried up to 5 times with gaps of 2s, 4s, 6s, 8s
2021-01-26 15:58:52 -05:00
Hugh Neale
aef139e93c The configuration key "enabled_transitive_rules" should be "enable_transitive_rules" (#525) 2021-01-26 14:20:15 -05:00
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
Tom Burgin
9d379d3884 release: split out the kext into a separate release label (#520)
* fix SNTLoggingKernel BUILD rule (#518)

* release: split out santa-driver.kext

* release: update ci

* remove ipa script rule

* update ci
2020-12-19 18:23:54 -05:00
Tom Burgin
3e7a191bf7 fix SNTLoggingKernel BUILD rule (#518) 2020-12-17 16:35:13 -05:00
Ryan Diers
c5a048f4d9 santactl/sync: Use deflate as Content-Encoding instead of zlib
The latter was not standards-compliant.
2020-12-14 16:19:48 -05:00
Hugh Neale
f4769bad90 Added Zercurity to list of available sync servers (#511) 2020-12-08 20:29:28 -05:00
Russell Hancox
254497ad15 Project: don't reference obsolete rake commands in CONTRIBUTING (#513) 2020-12-08 09:57:42 -05:00
avanzini
0a83445838 Log pidversion along with pid. (#512) 2020-12-08 09:46:34 -05:00
Tom Burgin
eff287259e project: update Xcode project to build universal binaries (#509) 2020-11-17 16:18:16 -05:00
Russell Hancox
6f2c0e3457 Project: remove Travis, update CI status in README (#508) 2020-11-02 09:59:35 -05:00
Russell Hancox
38769f7cd1 Project: Add GitHub Actions CI workflow (#507) 2020-10-30 12:23:01 -04:00
Russell Hancox
fa785ad3c2 Kernel: fix some header imports (#505) 2020-10-26 10:05:25 -04:00
Russell Hancox
5dae0cabdd Project: fix some lint (#504) 2020-10-22 14:01:32 -04:00
Russell Hancox
a8b4f4ea7e Project: move travis to xcode12 (#503) 2020-10-22 13:50:32 -04:00
41 changed files with 708 additions and 198 deletions

25
.github/workflows/ci.yml vendored Normal file
View 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

View File

@@ -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
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
@@ -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",

View File

@@ -1,37 +0,0 @@
Want to contribute? Great! First, read this page (including the small print at the end).
### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
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.
### 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:
```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,
code should adhere to either the
[Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.xml)
or the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the [Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).

1
CONTRIBUTING.md Symbolic link
View File

@@ -0,0 +1 @@
docs/development/contributing.md

View File

@@ -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

View File

@@ -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 ![CI](https://github.com/google/santa/workflows/CI/badge.svg?branch=main)
<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).

View File

@@ -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;

View File

@@ -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",
@@ -37,7 +52,7 @@ objc_library(
],
)
cc_library(
objc_library(
name = "SNTCommonEnums",
hdrs = ["SNTCommonEnums.h"],
)
@@ -77,6 +92,11 @@ 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(
@@ -209,6 +229,7 @@ santa_unit_test(
resources = [
"testdata/bad_pagezero",
"testdata/missing_pagezero",
"testdata/32bitplist",
],
structured_resources = glob([
"testdata/BundleExample.app/**",

View File

@@ -127,16 +127,13 @@
withString:config.machineID];
}
if (hostname.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%hostname%"
withString:hostname];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%hostname%" withString:hostname];
}
if (uuid.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%uuid%"
withString:uuid];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%uuid%" withString:uuid];
}
if (serial.length) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%serial%"
withString:serial];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%serial%" withString:serial];
}
return [NSURL URLWithString:formatStr];

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
///
@@ -321,7 +330,8 @@
@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.
/// 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;
@@ -331,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,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";
@@ -131,7 +133,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kBlockedPathRegexKey : re,
kBlockedPathRegexKeyDeprecated : re,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey: number,
kEnableBadSignatureProtectionKey : number,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
@@ -144,7 +146,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kClientAuthCertificatePasswordKey : string,
kClientAuthCertificateCNKey : string,
kClientAuthCertificateIssuerKey : string,
kServerAuthRootsDataKey : data,
kServerAuthRootsDataKey : data,
kServerAuthRootsFileKey : string,
kMachineOwnerKey : string,
kMachineIDKey : string,
@@ -156,9 +158,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kEventLogPath : string,
kEnableMachineIDDecoration : number,
kEnableSystemExtension : number,
kEnableSysxCache : number,
kEnableForkAndExitLogging : number,
kIgnoreOtherEndpointSecurityClients : number,
kEnableDebugLogging: number,
kEnableDebugLogging : number,
kEnableBackwardsCompatibleContentEncoding : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
@@ -330,6 +334,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
return [self configStateSet];
}
@@ -342,6 +350,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
return [self configStateSet];
}
#pragma mark Public Interface
- (SNTClientMode)clientMode {
@@ -577,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;
@@ -592,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

@@ -362,7 +362,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
while (pathComponents.count > 1) {
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
if ([bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
if (!ancestor ||
if ((!ancestor && bndl.bundlePath.pathExtension.length) ||
[[self allowedAncestorExtensions] containsObject:bndl.bundlePath.pathExtension]) {
bundle = bndl;
}
@@ -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

@@ -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];

View File

@@ -88,7 +88,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
break;
case LOG_LEVEL_DEBUG:
levelName = "D";
syslogLevel = ASL_LEVEL_DEBUG;
// Log debug messages at the same ASL level as INFO.
// While it would make sense to use DEBUG, watching debug-level logs
// in Console means enabling all debug logs, which is absurdly noisy.
syslogLevel = ASL_LEVEL_NOTICE;
break;
}

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

@@ -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 = [

View File

@@ -1,17 +1,16 @@
licenses(["notice"]) # Apache 2.0
load(
"@build_bazel_rules_apple//apple:macos.bzl",
"macos_command_line_application",
"macos_kernel_extension",
)
load("//:helper.bzl", "run_command", "santa_unit_test")
load("//:helper.bzl", "run_command")
load("//:version.bzl", "SANTA_VERSION")
licenses(["notice"])
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

@@ -337,6 +337,7 @@ static void reachabilityHandler(
#pragma mark syncing chain
- (void)preflight {
LOGD(@"Preflight starting");
SNTCommandSyncState *syncState = [self createSyncState];
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:syncState];
if ([p sync]) {
@@ -373,6 +374,7 @@ static void reachabilityHandler(
}
- (void)eventUploadWithSyncState:(SNTCommandSyncState *)syncState {
LOGD(@"Event upload starting");
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Event upload complete");
@@ -384,6 +386,7 @@ static void reachabilityHandler(
}
- (void)ruleDownloadWithSyncState:(SNTCommandSyncState *)syncState {
LOGD(@"Rule download starting");
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Rule download complete");
@@ -395,6 +398,7 @@ static void reachabilityHandler(
}
- (void)postflightWithSyncState:(SNTCommandSyncState *)syncState {
LOGD(@"Postflight starting");
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:syncState];
if ([p sync]) {
LOGD(@"Postflight complete");
@@ -482,6 +486,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

@@ -106,16 +106,16 @@
// Don't let these go too low
NSUInteger FCMIntervalValue = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
self.syncState.FCMFullSyncInterval =
(FCMIntervalValue < kDefaultFullSyncInterval) ? kDefaultFCMFullSyncInterval : FCMIntervalValue;
self.syncState.FCMFullSyncInterval = (FCMIntervalValue < kDefaultFullSyncInterval)
? kDefaultFCMFullSyncInterval
: FCMIntervalValue;
FCMIntervalValue = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
self.syncState.FCMGlobalRuleSyncDeadline =
(FCMIntervalValue < 60) ? kDefaultFCMGlobalRuleSyncDeadline : FCMIntervalValue;
(FCMIntervalValue < 60) ? kDefaultFCMGlobalRuleSyncDeadline : FCMIntervalValue;
// Check if our sync interval has changed
NSUInteger intervalValue = [resp[kFullSyncInterval] unsignedIntegerValue];
self.syncState.fullSyncInterval =
(intervalValue < 60) ? kDefaultFullSyncInterval : intervalValue;
self.syncState.fullSyncInterval = (intervalValue < 60) ? kDefaultFullSyncInterval : intervalValue;
if ([resp[kClientMode] isEqual:kClientModeMonitor]) {
self.syncState.clientMode = SNTClientModeMonitor;

View File

@@ -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];
@@ -83,20 +83,33 @@
- (NSDictionary *)performRequest:(NSURLRequest *)request timeout:(NSTimeInterval)timeout {
NSHTTPURLResponse *response;
NSError *error;
NSData *data = [self performRequest:request timeout:timeout response:&response error:&error];
NSData *data;
// If the original request failed, attempt to get a new XSRF token and try again.
// Unfortunately some servers cause NSURLSession to return 'client cert required' or
// 'could not parse response' when a 403 occurs and SSL cert auth is enabled.
if ((response.statusCode == 403 ||
error.code == NSURLErrorClientCertificateRequired ||
error.code == NSURLErrorCannotParseResponse) &&
[self fetchXSRFToken]) {
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest setValue:self.syncState.xsrfToken forHTTPHeaderField:kXSRFToken];
return [self performRequest:mutableRequest timeout:timeout];
for (int attempt = 1; attempt < 6; ++attempt) {
if (attempt > 1) {
struct timespec ts = {.tv_sec = (attempt * 2)};
nanosleep(&ts, NULL);
}
LOGD(@"Performing request, attempt %d", attempt);
data = [self performRequest:request timeout:timeout response:&response error:&error];
if (response.statusCode == 200) break;
// If the original request failed because of an auth error, attempt to get a new XSRF token and
// try again. Unfortunately some servers cause NSURLSession to return 'client cert required' or
// 'could not parse response' when a 403 occurs and SSL cert auth is enabled.
if ((response.statusCode == 403 ||
error.code == NSURLErrorClientCertificateRequired ||
error.code == NSURLErrorCannotParseResponse) &&
[self fetchXSRFToken]) {
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[mutableRequest setValue:self.syncState.xsrfToken forHTTPHeaderField:kXSRFToken];
request = mutableRequest;
continue;
}
}
// If the final attempt resulted in an error, log the error and return nil.
if (response.statusCode != 200) {
long code;
NSString *errStr;

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,200 @@
/// 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 <bsm/libbsm.h>
#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 {
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)) {
switch (return_action) {
case ACTION_RESPOND_ALLOW:
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_ALLOW_COMPILER: {
pid_t pid = audit_token_to_pid(m->process->audit_token);
[self setIsCompilerPID:pid];
// Don't let ES cache compilers
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
break;
}
default:
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
break;
}
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 currentTicks: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:
[self setIsCompilerPID:sm.pid];
// Allow the exec and cache in our internal cache but don't let ES cache, because then
// we won't see future execs of the compiler in order to record the PID.
[self addToCache:sm.vnode_id
decision:ACTION_RESPOND_ALLOW_COMPILER
currentTicks:GetCurrentUptime()];
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 currentTicks:GetCurrentUptime()];
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_DENY:
[self addToCache:sm.vnode_id decision:ACTION_RESPOND_DENY currentTicks:GetCurrentUptime()];
OS_FALLTHROUGH;
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
currentTicks:(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,18 @@
#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;
- (BOOL)isCompilerPID:(pid_t)pid;
- (void)setIsCompilerPID:(pid_t)pid;
- (void)setNotCompilerPID:(pid_t)pid;
@property(readonly, nonatomic) es_client_t *client;
@end

View File

@@ -16,21 +16,19 @@
#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 () {
std::atomic<bool> _compilerPIDs[PID_MAX];
}
@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);
@@ -40,9 +38,7 @@
@end
@implementation SNTEndpointSecurityManager {
std::atomic<bool> _compilerPIDs[PID_MAX];
}
@implementation SNTEndpointSecurityManager
- (instancetype)init API_AVAILABLE(macos(10.15)) {
self = [super init];
@@ -78,13 +74,16 @@
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, 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);
}
LOGD(@"Skipping action from es_client pid: %d", pid);
return;
}
@@ -104,8 +103,11 @@
// 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()) {
if ([self isCompilerPID:pid]) {
santa_message_t sm = {};
BOOL truncated = [self populateBufferFromESFile:m->event.close.target
buffer:sm.path
@@ -115,8 +117,12 @@
sm.path, pid);
break;
}
if ([@(sm.path) hasPrefix:@"/dev/"]) {
break;
}
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);
}
@@ -126,7 +132,7 @@
}
case ES_EVENT_TYPE_NOTIFY_RENAME: {
// Create a transitive rule if the file was renamed by a running compiler
if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) {
if ([self isCompilerPID:pid]) {
santa_message_t sm = {};
BOOL truncated = [self populateRenamedNewPathFromESMessage:m->event.rename
buffer:sm.path
@@ -136,8 +142,12 @@
sm.path, pid);
break;
}
if ([@(sm.path) hasPrefix:@"/dev/"]) {
break;
}
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);
}
@@ -147,13 +157,14 @@
}
case ES_EVENT_TYPE_NOTIFY_EXIT: {
// Update the set of running compiler PIDs
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
[self setNotCompilerPID:pid];
// 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);
@@ -164,13 +175,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, ^{
@@ -185,15 +197,12 @@
switch (m->action_type) {
case ES_ACTION_TYPE_AUTH: {
// Create a timer to deny the execution 2 seconds before the deadline,
// Create a timer to deny the execution 5 seconds before the deadline,
// if a response hasn't already been sent. This block will still be enqueued if
// the the deadline - 2 secs is < DISPATCH_TIME_NOW.
// As of 10.15.2, a typical deadline is 60 seconds.
// TODO(bur/rah): Possibly cache decisions made after the deadline. Currently a
// large enough binary will never be allowed to execute. This should be a rare edge case;
// it's probably not worth adding a caching layer just for this.
// the the deadline - 5 secs is < DISPATCH_TIME_NOW.
// As of 10.15.5, a typical deadline is 60 seconds.
auto responded = std::make_shared<std::atomic<bool>>(false);
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -2), self.esAuthQueue, ^(void) {
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -5), self.esAuthQueue, ^(void) {
if (responded->load()) return;
LOGE(@"Deadline reached: deny pid=%d ret=%d",
pid, es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false));
@@ -229,7 +238,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");
@@ -244,6 +253,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;
@@ -254,6 +267,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;
@@ -385,6 +402,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);
@@ -395,14 +413,14 @@
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_AUTH_KEXTLOAD,
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,
// 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 to auth events: %d", sret);
@@ -434,12 +452,8 @@
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);
}
[self setIsCompilerPID:sm.pid];
// 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,
@@ -545,4 +559,30 @@
return truncated;
}
- (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file {
return {
.fsid = (uint64_t)file->stat.st_dev,
.fileid = file->stat.st_ino,
};
}
- (BOOL)isCompilerPID:(pid_t)pid {
return (pid && pid < PID_MAX && self->_compilerPIDs[pid].load());
}
- (void)setIsCompilerPID:(pid_t)pid {
if (pid < 1) {
LOGE(@"Unable to watch compiler pid=%d", pid);
} else if (pid >= PID_MAX) {
LOGE(@"Unable to watch compiler pid=%d >= PID_MAX(%d)", pid, PID_MAX);
} else {
self->_compilerPIDs[pid].store(true);
LOGD(@"Watching compiler pid=%d", pid);
}
}
- (void)setNotCompilerPID:(pid_t)pid {
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
}
@end

View File

@@ -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];
}

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

@@ -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]];
}
}
}

View File

@@ -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.20.0",
tag = "0.21.2",
)
load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")

View File

@@ -175,7 +175,7 @@ Configuration profiles have a `.mobileconfig` file extension. There are many way
| fcm_full_sync_interval* | Integer | The full sync interval if a fcm_token is set. Defaults to 14400 secs (4 hours). |
| fcm_global_rule_sync_deadline* | Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). |
| enable_bundles* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. |
| enabled_transitive_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
| enable_transitive_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
*Held only in memory. Not persistent upon process restart.

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>NotificationSettings</key>
<array>
<dict>
<key>AlertType</key>
<integer>1</integer>
<key>BadgesEnabled</key>
<true/>
<key>BundleIdentifier</key>
<string>com.google.santa</string>
<key>CriticalAlertEnabled</key>
<true/>
<key>NotificationsEnabled</key>
<true/>
<key>ShowInLockScreen</key>
<true/>
<key>ShowInNotificationCenter</key>
<true/>
<key>SoundsEnabled</key>
<false/>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>Notifications Payload</string>
<key>PayloadIdentifier</key>
<string>com.google.santa.notificationsettings.F1817DA0-0044-43DD-9540-36EBC60FDA8F</string>
<key>PayloadOrganization</key>
<string></string>
<key>PayloadType</key>
<string>com.apple.notificationsettings</string>
<key>PayloadUUID</key>
<string>510236AE-D7F8-4131-A4CA-5CC930C51866</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</array>
<key>PayloadDescription</key>
<string>Configures your Mac to automatically enable Notifications settings for Santa</string>
<key>PayloadDisplayName</key>
<string>Santa Notifications settings</string>
<key>PayloadEnabled</key>
<true/>
<key>PayloadIdentifier</key>
<string>com.google.santa.notificationsettings.069CA123-6129-46A5-8FD1-49322E5A5755</string>
<key>PayloadOrganization</key>
<string></string>
<key>PayloadRemovalDisallowed</key>
<true/>
<key>PayloadScope</key>
<string>System</string>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>069CA123-6129-46A5-8FD1-49322E5A5755</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>

View File

@@ -8,8 +8,8 @@ This may be the most complex part of Santa. It does two types of work:
can also inspect individual files. When running without a sync server it
also a supported method of managing the rules database.
The details of santactl's syncing functionality are covered in the syncing.md
document. This document will cover the status work that santactl performs.
The details of santactl's syncing functionality are covered in [introduction/syncing-overview.md](syncing-overview.md).
This document will cover the status work that santactl performs.
##### status

View File

@@ -0,0 +1,34 @@
Want to contribute? Great! First, read this page (including the small print at the end).
### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
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. 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. 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.
### Code Style
All code submissions should try to match the surrounding code. Wherever possible,
code should adhere to either the
[Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.xml)
or the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the [Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).

View File

@@ -9,7 +9,7 @@ contribute.
The following documents give an overview of how Santa accomplishes binary
authorization at the enterprise scale.
- [Binary Authorization](introduction/binary-authorization.md): How Santa makes allow or deny decisions for any `execve()` taking place.
- [Binary Authorization](introduction/binary-authorization-overview.md): How Santa makes allow or deny decisions for any `execve()` taking place.
- [Syncing](introduction/syncing-overview.md): How configuration and rules are applied from a sync server.
#### Deployment
@@ -19,7 +19,7 @@ authorization at the enterprise scale.
#### Development
* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine.
* [Contributing](../CONTRIBUTING.md): How to contribute a bug fix or new feature to Santa.
* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa.
#### Details
@@ -43,7 +43,6 @@ Additional documentation on the concepts that support the operation of the main
* [events](details/events.md): Represents an `execve()` that was blocked, or would have been blocked, depending on the mode.
* [rules](details/rules.md): Represents allow or deny decisions for a given `execve()`. Can either be a binary's SHA-256 hash or a leaf code-signing certificate's SHA-256 hash.
* [scopes](details/scopes.md): The level at which an `execve()` was allowed or denied from taking place.
* [syncing](introduction/syncing-overview.md): How Santa communicates with a TLS server for configuration, rules and event uploading.
* [ipc](details/ipc.md): How all the components of Santa communicate.
duction/syncing-overview.
* [logs](details/logs.md): What and where Santa logs.

View File

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