Compare commits

...

29 Commits

Author SHA1 Message Date
Russell Hancox
cc3177502c Tests: Fix un-needed expectation in SNTExecutionControllerTest.allEventUpload (#857) 2022-07-15 18:03:34 -04:00
Kathryn Hancox
a49a59b109 Docs: Add sync server list (#856) 2022-07-15 16:19:17 -04:00
Kathryn Hancox
2c06c39c82 Added quick getting started page for deployments (#855) 2022-07-15 15:23:33 -04:00
Pete Markowsky
234f81ea7c Ensure KVO works for USB config options (#853)
Ensure KVO works for USB config options.
2022-07-15 15:13:55 -04:00
Russell Hancox
743c567bf8 santad: Log team ID in execution logs, where available (#850) 2022-07-15 12:41:56 -04:00
Russell Hancox
21220f1499 santad: Add DisableUnknownEventUpload option. (#852) 2022-07-15 12:30:20 -04:00
Russell Hancox
39f3ffe8fc santactl/status: Fix printing of static rules (#848) 2022-07-15 11:53:38 -04:00
Russell Hancox
fdb01928a0 santad: Fix re-establishment of syncservice connection (#849)
* santad: Fix re-establishment of syncservice connection

The previous version could lead to santad having lots of threads stuck waiting for connections
2022-07-15 11:53:17 -04:00
Russell Hancox
fbefbc5910 santasyncservice: Keep XSRF token in memory, don't send to daemon (#851) 2022-07-15 11:52:43 -04:00
Russell Hancox
9db00d143d santad: Improve caching of static rules (#847)
In #846 I forgot that  is only a count of the entries so if the config changes but the number of rules remains the same we would never update the cache. This PR moves the processing of the raw config into the KVO handler code so it is not at all in the hot-path.
2022-07-14 10:50:30 -04:00
Russell Hancox
1cc40d59d8 santad: Allow configuring a static set of rules via configuration profile (#846) 2022-07-13 17:58:13 -04:00
Russell Hancox
ba1ace56f0 Project: Delete tulsiproj, add basic doc about hedron (#845) 2022-07-12 13:53:57 -04:00
Russell Hancox
6d911e9d6e CI: Make CI workflow only run on source changes (#843) 2022-07-08 16:03:30 -04:00
Kathryn Hancox
7e2b291122 Docs: Updated home page with README files & nav changes (#841) 2022-07-08 15:53:16 -04:00
Tom Burgin
64096f5d08 adhoc build and run santa (#840)
* adhoc build and run santa

* fold ci into adhoc

* review updates

Co-authored-by: Tom Burgin <bur@chromium.org>
2022-07-07 17:09:53 -04:00
Matt W
aec1c74fab Use the message copy in the dispatch blocks (#839) 2022-07-06 21:51:02 -04:00
Russell Hancox
d4a0d77cb9 Docs: Add gemfile for running jekyll locally. (#834)
This lets us test docs site changes by running `bundle exec jekyll serve` from inside the docs folder.
2022-07-01 11:06:16 -04:00
Russell Hancox
7df209ed3f Project: Upgrade bazel rules_apple to 1.0.1 release (#830) 2022-06-28 14:23:47 -04:00
np5
b7421e4499 Add team ID to synced events (#827) 2022-06-24 20:00:55 +00:00
Eric Case
e044fe3601 Readme: http -> https link (#829) 2022-06-24 14:34:32 -04:00
Russell Hancox
a67801d5ed santactl/status: Remove driver connected, re-org USB blocking status (#826) 2022-06-22 14:59:46 -04:00
Russell Hancox
3d37a3a5ae santad: Update assert usage to avoid a string-to-bool conversion (#825) 2022-06-22 12:55:57 -04:00
Russell Hancox
bfae5dc828 fix some style issues (#824) 2022-06-22 10:41:05 -04:00
Pete Markowsky
fde5f52a11 Added handling for Remount events to USB mass storage blocking (#818)
* Added handling for Remount events to USB mass storage blocking.
2022-06-22 09:39:20 -04:00
Russell Hancox
01bd1bfdca santad: Use multiple semaphores to avoid freeing ES message before use of it has ended. (#822)
This slightly complex solution is necessary because while on macOS 11+ there are retain/release methods that can be used on ES messages, on 10.15 the only option is a copy which is comparatively expensive (and on 11+ the copy/free methods are aliases for retain/release)

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

* Bail if self muting failed. Remove selfPid.

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

* Update log type for error message

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

Co-authored-by: Russell Hancox <russellhancox@users.noreply.github.com>
2022-05-27 11:03:10 -04:00
Khalid Jamal Abdulnasser
4d34099142 santad: log decision when failing to read file (#817) 2022-05-27 09:52:06 -04:00
93 changed files with 1012 additions and 816 deletions

View File

@@ -1,49 +1,20 @@
name: CI
on:
push:
branches:
- '*'
paths:
- 'Source/**'
pull_request:
branches:
- main
paths:
- 'Source/**'
jobs:
preqs:
runs-on: ubuntu-latest
outputs:
run_build_and_tests: ${{ steps.step1.outputs.run_build_and_tests }}
steps:
- uses: actions/checkout@v2
- name: Check If We Need to Run Build/Test
id: step1
run: |
git remote add mainline https://github.com/google/santa.git
git fetch mainline main
git diff --name-only mainline/main HEAD > files.txt
echo "FILES CHANGED: $(wc -l ./files.txt)\n"
cat files.txt
build_and_run_tests=0
for file in `cat files.txt`; do
if [[ $file = Source/* ]]; then
build_and_run_test=1;
fi
done
if [[ $build_and_run_test != 0 ]]; then
echo "NEED TO RUN BUILD AND TESTS"
echo "::set-output name=run_build_and_tests::true"
else
echo "::set-output name=run_build_and_tests::false"
fi
lint:
runs-on: ubuntu-latest
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run linters
@@ -55,12 +26,10 @@ jobs:
matrix:
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Build Userspace
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=ci
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=adhoc
unit_tests:
strategy:
@@ -68,17 +37,13 @@ jobs:
matrix:
os: [macos-10.15, macos-11, macos-12]
runs-on: ${{ matrix.os }}
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run All Tests
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci --test_output=errors
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors
test_coverage:
runs-on: macos-11
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Generate test coverage
@@ -92,8 +57,6 @@ jobs:
benchmark:
runs-on: macos-11
needs: [preqs]
if: needs.preqs.outputs.run_build_and_tests == 'true'
steps:
- uses: actions/checkout@v2
- name: Run All Tests

View File

@@ -10,4 +10,4 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Checks for flaky tests
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=ci
run: bazel test --test_strategy=exclusive --test_output=errors --runs_per_test 50 -t- :unit_tests --define=SANTA_BUILD_TYPE=adhoc

7
BUILD
View File

@@ -27,10 +27,11 @@ config_setting(
visibility = [":santa_package_group"],
)
# Used to detect CI builds
# Adhoc signed - provisioning profiles are not used.
# Used for CI runs and dev builds when SIP is disabled.
config_setting(
name = "ci_build",
values = {"define": "SANTA_BUILD_TYPE=ci"},
name = "adhoc_build",
values = {"define": "SANTA_BUILD_TYPE=adhoc"},
visibility = [":santa_package_group"],
)

View File

@@ -16,7 +16,7 @@ It is named Santa because it keeps track of binaries that are naughty or nice.
The Santa docs are stored in the
[Docs](https://github.com/google/santa/blob/main/docs) directory and published
at http://santa.dev.
at https://santa.dev.
The docs include deployment options, details on how parts of Santa work and
instructions for developing Santa itself.

View File

@@ -100,6 +100,7 @@ objc_library(
hdrs = ["SNTConfigurator.h"],
deps = [
":SNTCommonEnums",
":SNTRule",
":SNTStrengthify",
":SNTSystemInfo",
],
@@ -149,7 +150,16 @@ objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
hdrs = ["SNTRule.h"],
deps = [":SNTCommonEnums"],
deps = [
":SNTCommonEnums",
":SNTSyncConstants",
],
)
santa_unit_test(
name = "SNTRuleTest",
srcs = ["SNTRuleTest.m"],
deps = [":SNTRule"],
)
objc_library(
@@ -167,6 +177,12 @@ cc_library(
hdrs = ["SNTStrengthify.h"],
)
objc_library(
name = "SNTSyncConstants",
srcs = ["SNTSyncConstants.m"],
hdrs = ["SNTSyncConstants.h"],
)
objc_library(
name = "SNTSystemInfo",
srcs = ["SNTSystemInfo.m"],
@@ -197,6 +213,10 @@ objc_library(
name = "SNTXPCControlInterface",
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
defines = select({
"//:adhoc_build": ["SANTAADHOC"],
"//conditions:default": None,
}),
deps = [
":SNTCommonEnums",
":SNTConfigurator",

View File

@@ -16,6 +16,8 @@
#import "Source/common/SNTCommonEnums.h"
@class SNTRule;
///
/// Singleton that provides an interface for managing configuration values on disk
/// @note This class is designed as a singleton but that is not strictly enforced.
@@ -46,6 +48,32 @@
///
@property(readonly, nonatomic) BOOL failClosed;
///
/// A set of static rules that should always apply. These can be used as a
/// fallback set of rules for management tools that should always be allowed to
/// run even if a sync server does something unexpected. It can also be used
/// as the sole source of rules, distributed with an MDM.
///
/// The value of this key should be an array containing dictionaries. Each
/// dictionary should contain the same keys used for syncing, e.g:
///
/// <key>StaticRules</key>
/// <array>
/// <dict>
/// <key>identifier</key>
/// <string>binary sha256, certificate sha256, team ID</string>
/// <key>rule_type</key>
/// <string>BINARY</string> (one of BINARY, CERTIFICATE or TEAMID)
/// <key>policy</key>
/// <string>BLOCKLIST</string> (one of ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST)
/// </dict>
/// </array>
///
/// The return of this property is a dictionary where the keys are the
/// identifiers of each rule, with the SNTRule as a value
///
@property(readonly, nonatomic) NSDictionary<NSString *, SNTRule *> *staticRules;
///
/// The regex of allowed paths. Regexes are specified in ICU format.
///
@@ -423,6 +451,11 @@
///
@property(nonatomic) BOOL enableAllEventUpload;
///
/// If true, events will *not* be uploaded for ALLOW_UNKNOWN events for clients in Monitor mode.
///
@property(nonatomic) BOOL disableUnknownEventUpload;
///
/// If true, forks and exits will be logged. Defaults to false.
///

View File

@@ -16,6 +16,7 @@
#include <sys/stat.h>
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSystemInfo.h"
@@ -33,6 +34,9 @@
/// Was --debug passed as an argument to this process?
@property(readonly, nonatomic) BOOL debugFlag;
/// Holds the last processed hash of the static rules list.
@property(atomic) NSDictionary *cachedStaticRules;
@end
@implementation SNTConfigurator
@@ -44,6 +48,7 @@ NSString *const kSyncStateFilePath = @"/var/db/santa/sync-state.plist";
static NSString *const kMobileConfigDomain = @"com.google.santa";
/// The keys managed by a mobileconfig.
static NSString *const kStaticRules = @"StaticRules";
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
static NSString *const kSyncProxyConfigKey = @"SyncProxyConfiguration";
static NSString *const kSyncEnableCleanSyncEventUpload = @"SyncEnableCleanSyncEventUpload";
@@ -113,6 +118,7 @@ static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
static NSString *const kEnableAllEventUploadKey = @"EnableAllEventUpload";
static NSString *const kDisableUnknownEventUploadKey = @"DisableUnknownEventUpload";
// TODO(markowsky): move these to sync server only.
static NSString *const kMetricFormat = @"MetricFormat";
@@ -176,6 +182,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBBlockMessage : string,
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kStaticRules : array,
kSyncBaseURLKey : string,
kSyncProxyConfigKey : dictionary,
kClientAuthCertificateFileKey : string,
@@ -211,10 +218,12 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kMetricExportTimeout : number,
kMetricExtraLabels : dictionary,
kEnableAllEventUploadKey : number,
kDisableUnknownEventUploadKey : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
_configState = [self readForcedConfig];
[self cacheStaticRules];
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
[self startWatchingDefaults];
@@ -282,6 +291,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingStaticRules {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
return [self configStateSet];
}
@@ -402,6 +415,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingDisableUnknownEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
@@ -442,6 +459,26 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBlockUSBMount {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingBannedUSBBlockMessage {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingRemountUSBMode {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingRemountUSBBlockMessage {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingUsbBlockMessage {
return [self syncAndConfigStateSet];
}
#pragma mark Public Interface
- (SNTClientMode)clientMode {
@@ -549,6 +586,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return args;
}
- (NSDictionary<NSString *, SNTRule *> *)staticRules {
return self.cachedStaticRules;
}
- (NSURL *)syncBaseURL {
NSString *urlString = self.configState[kSyncBaseURLKey];
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
@@ -762,6 +803,17 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
[self updateSyncStateForKey:kEnableAllEventUploadKey value:@(enabled)];
}
- (BOOL)disableUnknownEventUpload {
NSNumber *n = self.syncState[kDisableUnknownEventUploadKey];
if (n) return [n boolValue];
return [self.configState[kDisableUnknownEventUploadKey] boolValue];
}
- (void)setDisableUnknownEventUpload:(BOOL)enabled {
[self updateSyncStateForKey:kDisableUnknownEventUploadKey value:@(enabled)];
}
- (BOOL)enableForkAndExitLogging {
NSNumber *number = self.configState[kEnableForkAndExitLogging];
return number ? [number boolValue] : NO;
@@ -964,6 +1016,24 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
///
- (void)handleChange {
self.configState = [self readForcedConfig];
[self cacheStaticRules];
}
///
/// Processes the StaticRules key to create SNTRule objects and caches them for quick use
///
- (void)cacheStaticRules {
NSArray *staticRules = self.configState[kStaticRules];
if (![staticRules isKindOfClass:[NSArray class]]) return;
NSMutableDictionary<NSString *, SNTRule *> *rules =
[NSMutableDictionary dictionaryWithCapacity:staticRules.count];
for (id rule in staticRules) {
if (![rule isKindOfClass:[NSDictionary class]]) return;
SNTRule *r = [[SNTRule alloc] initWithDictionary:rule];
rules[r.identifier] = r;
}
self.cachedStaticRules = [rules copy];
}
@end

View File

@@ -108,6 +108,11 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType);
*/
+ (instancetype)sharedInstance;
/**
* Resets all the metrics in this set. Intended only for testing.
*/
- (void)reset;
/**
* Add a root label to the MetricSet.
*/

View File

@@ -485,6 +485,10 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
return self;
}
- (void)reset {
_metrics = [[NSMutableDictionary alloc] init];
}
- (void)addRootLabel:(NSString *)label value:(NSString *)value {
@synchronized(self) {
_rootLabels[label] = value;

View File

@@ -64,6 +64,11 @@
type:(SNTRuleType)type
customMsg:(NSString *)customMsg;
///
/// Initialize with a dictionary received from a sync server.
///
- (instancetype)initWithDictionary:(NSDictionary *)dict;
///
/// Sets timestamp of rule to the current time.
///

View File

@@ -13,6 +13,7 @@
/// limitations under the License.
#import "Source/common/SNTRule.h"
#import "Source/common/SNTSyncConstants.h"
@interface SNTRule ()
@property(readwrite) NSUInteger timestamp;
@@ -48,6 +49,60 @@
return self;
}
// Converts rule information downloaded from the server into a SNTRule. Because any information
// not recorded by SNTRule is thrown away here, this method is also responsible for dealing with
// the extra bundle rule information (bundle_hash & rule_count).
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
self = [super init];
if (self) {
_identifier = dict[kRuleIdentifier];
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) {
_identifier = dict[kRuleSHA256];
}
if (![_identifier isKindOfClass:[NSString class]] || !_identifier.length) return nil;
NSString *policyString = dict[kRulePolicy];
if (![policyString isKindOfClass:[NSString class]]) return nil;
if ([policyString isEqual:kRulePolicyAllowlist] ||
[policyString isEqual:kRulePolicyAllowlistDeprecated]) {
_state = SNTRuleStateAllow;
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
_state = SNTRuleStateAllowCompiler;
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
_state = SNTRuleStateBlock;
} else if ([policyString isEqual:kRulePolicySilentBlocklist] ||
[policyString isEqual:kRulePolicySilentBlocklistDeprecated]) {
_state = SNTRuleStateSilentBlock;
} else if ([policyString isEqual:kRulePolicyRemove]) {
_state = SNTRuleStateRemove;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if (![ruleTypeString isKindOfClass:[NSString class]]) return nil;
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
_type = SNTRuleTypeBinary;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
_type = SNTRuleTypeCertificate;
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
_type = SNTRuleTypeTeamID;
} else {
return nil;
}
NSString *customMsg = dict[kRuleCustomMsg];
if ([customMsg isKindOfClass:[NSString class]] && customMsg.length) {
_customMsg = customMsg;
}
}
return self;
}
#pragma mark NSSecureCoding
#pragma clang diagnostic push

116
Source/common/SNTRuleTest.m Normal file
View File

@@ -0,0 +1,116 @@
/// Copyright 2022 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 <XCTest/XCTest.h>
#import "Source/common/SNTRule.h"
@interface SNTRuleTest : XCTestCase
@end
@implementation SNTRuleTest
- (void)testInitWithDictionaryValid {
SNTRule *sut;
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"BINARY",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
XCTAssertEqual(sut.state, SNTRuleStateAllow);
sut = [[SNTRule alloc] initWithDictionary:@{
@"sha256" : @"some-sort-of-identifier",
@"policy" : @"BLOCKLIST",
@"rule_type" : @"CERTIFICATE",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeCertificate);
XCTAssertEqual(sut.state, SNTRuleStateBlock);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"SILENT_BLOCKLIST",
@"rule_type" : @"TEAMID",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateSilentBlock);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST_COMPILER",
@"rule_type" : @"BINARY",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeBinary);
XCTAssertEqual(sut.state, SNTRuleStateAllowCompiler);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"REMOVE",
@"rule_type" : @"TEAMID",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateRemove);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"some-sort-of-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"TEAMID",
@"custom_msg" : @"A custom block message",
}];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.identifier, @"some-sort-of-identifier");
XCTAssertEqual(sut.type, SNTRuleTypeTeamID);
XCTAssertEqual(sut.state, SNTRuleStateAllow);
XCTAssertEqualObjects(sut.customMsg, @"A custom block message");
}
- (void)testInitWithDictionaryInvalid {
SNTRule *sut;
sut = [[SNTRule alloc] initWithDictionary:@{}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
@"policy" : @"OTHERPOLICY",
@"rule_type" : @"BINARY",
}];
XCTAssertNil(sut);
sut = [[SNTRule alloc] initWithDictionary:@{
@"identifier" : @"an-identifier",
@"policy" : @"ALLOWLIST",
@"rule_type" : @"OTHER_RULE_TYPE",
}];
XCTAssertNil(sut);
}
@end

View File

@@ -95,6 +95,12 @@
///
@property NSArray *signingChain;
///
/// If the executed file was signed, this is the Team ID if present in the signature information.
///
@property NSString *teamID;
///
/// The user who executed the binary.
///

View File

@@ -49,6 +49,7 @@
ENCODE(self.fileBundleVersionString, @"fileBundleVersionString");
ENCODE(self.signingChain, @"signingChain");
ENCODE(self.teamID, @"teamID");
ENCODE(self.executingUser, @"executingUser");
ENCODE(self.occurrenceDate, @"occurrenceDate");
@@ -93,6 +94,7 @@
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
_teamID = DECODE(NSString, @"teamID");
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");

View File

@@ -51,6 +51,7 @@ extern NSString *const kEnableTransitiveRules;
extern NSString *const kEnableTransitiveRulesDeprecated;
extern NSString *const kEnableTransitiveRulesSuperDeprecated;
extern NSString *const kEnableAllEventUpload;
extern NSString *const kDisableUnknownEventUpload;
extern NSString *const kEvents;
extern NSString *const kFileSHA256;
@@ -92,6 +93,7 @@ extern NSString *const kCertOrg;
extern NSString *const kCertOU;
extern NSString *const kCertValidFrom;
extern NSString *const kCertValidUntil;
extern NSString *const kTeamID;
extern NSString *const kQuarantineDataURL;
extern NSString *const kQuarantineRefererURL;
extern NSString *const kQuarantineTimestamp;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTSyncConstants.h"
#import "Source/common/SNTSyncConstants.h"
NSString *const kXSRFToken = @"X-XSRF-TOKEN";
@@ -52,6 +52,7 @@ NSString *const kEnableTransitiveRules = @"enable_transitive_rules";
NSString *const kEnableTransitiveRulesDeprecated = @"enabled_transitive_whitelisting";
NSString *const kEnableTransitiveRulesSuperDeprecated = @"transitive_whitelisting_enabled";
NSString *const kEnableAllEventUpload = @"enable_all_event_upload";
NSString *const kDisableUnknownEventUpload = @"disable_unknown_event_upload";
NSString *const kEvents = @"events";
NSString *const kFileSHA256 = @"file_sha256";
@@ -93,6 +94,7 @@ NSString *const kCertOrg = @"org";
NSString *const kCertOU = @"ou";
NSString *const kCertValidFrom = @"valid_from";
NSString *const kCertValidUntil = @"valid_until";
NSString *const kTeamID = @"team_id";
NSString *const kQuarantineDataURL = @"quarantine_data_url";
NSString *const kQuarantineRefererURL = @"quarantine_referer_url";
NSString *const kQuarantineTimestamp = @"quarantine_timestamp";

View File

@@ -20,7 +20,7 @@
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
///
/// Kernel ops
/// Cache ops
///
- (void)flushCache:(void (^)(BOOL))reply;
@@ -41,7 +41,6 @@
/// Config ops
///
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
@@ -52,6 +51,7 @@
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply;
///
/// Syncd Ops

View File

@@ -27,10 +27,16 @@ NSString *const kBundleID = @"com.google.santa.daemon";
@implementation SNTXPCControlInterface
+ (NSString *)serviceID {
#ifdef SANTAADHOC
// The mach service for an adhoc signed ES sysx uses the "endpoint-security" prefix instead of
// the teamid. In Santa's case it will be endpoint-security.com.google.santa.daemon.xpc.
return [NSString stringWithFormat:@"endpoint-security.%@.xpc", kBundleID];
#else
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
// "teamid.com.google.santa.daemon.xpc"
NSString *t = cs.signingInformation[@"teamid"];
return [NSString stringWithFormat:@"%@.%@.xpc", t, kBundleID];
#endif
}
+ (NSString *)systemExtensionID {

View File

@@ -28,12 +28,11 @@
@protocol SNTUnprivilegedDaemonControlXPC
///
/// Kernel ops
/// Cache Ops
///
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
///
/// Database ops
@@ -41,6 +40,7 @@
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate, int64_t compiler,
int64_t transitive, int64_t teamID))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)staticRuleCount:(void (^)(int64_t count))reply;
///
/// Decision ops
@@ -64,7 +64,6 @@
/// Config ops
///
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)xsrfToken:(void (^)(NSString *))reply;
- (void)clientMode:(void (^)(SNTClientMode))reply;
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;

View File

@@ -79,6 +79,7 @@ message Execution {
optional string original_path = 11;
repeated string args = 12;
optional string machine_id = 13;
optional string team_id = 14;
}
message DiskAppeared {

View File

@@ -74,11 +74,15 @@ macos_application(
"--force",
"--options library,kill,runtime",
],
entitlements = "Santa.app.entitlements",
entitlements = select({
"//:adhoc_build": "Santa.app-adhoc.entitlements",
# Non-adhoc builds get thier entitlements from the provisioning profile.
"//conditions:default": None,
}),
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",

View File

@@ -0,0 +1,8 @@
<?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>com.apple.developer.system-extension.install</key>
<true/>
</dict>
</plist>

View File

@@ -1,16 +0,0 @@
<?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>com.apple.application-identifier</key>
<string>EQHXZ8M8AV.com.google.santa</string>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>EQHXZ8M8AV</string>
<key>keychain-access-groups</key>
<array>
<string>EQHXZ8M8AV.com.google.santa</string>
</array>
</dict>
</plist>

View File

@@ -36,7 +36,7 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",

View File

@@ -69,7 +69,7 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
@@ -102,10 +102,10 @@ santa_unit_test(
santa_unit_test(
name = "SNTCommandMetricsTest",
srcs = [
"SNTCommand.h",
"SNTCommandController.h",
"Commands/SNTCommandMetrics.h",
"Commands/SNTCommandMetricsTest.m",
"SNTCommand.h",
"SNTCommandController.h",
],
structured_resources = glob(["Commands/testdata/*"]),
visibility = ["//:santa_package_group"],
@@ -116,8 +116,8 @@ santa_unit_test(
"//Source/common:SNTMetricSet",
"//Source/common:SNTXPCControlInterface",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@OCMock",
"@MOLXPCConnection",
"@OCMock",
],
)

View File

@@ -74,14 +74,15 @@ REGISTER_COMMAND_NAME(@"rule")
- (void)runWithArguments:(NSArray *)arguments {
SNTConfigurator *config = [SNTConfigurator configurator];
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
if ((config.syncBaseURL || config.staticRules.count) &&
![arguments containsObject:@"--check"]
#ifdef DEBUG
if ([config syncBaseURL] && ![arguments containsObject:@"--check"] &&
![arguments containsObject:@"--force"]) {
// DEBUG builds add a --force flag to allow manually adding/removing rules during testing.
&& ![arguments containsObject:@"--force"]) {
#else
if ([config syncBaseURL] && ![arguments containsObject:@"--check"]) {
) {
#endif
printf("SyncBaseURL is set, rules are managed centrally.\n");
printf("(SyncBaseURL/StaticRules is set, rules are managed centrally.)");
exit(1);
}

View File

@@ -48,16 +48,10 @@ REGISTER_COMMAND_NAME(@"status")
dispatch_group_t group = dispatch_group_create();
// Daemon status
__block BOOL driverConnected;
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
__block double cpuPeak, ramPeak;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] driverConnectionEstablished:^(BOOL connected) {
driverConnected = connected;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] clientMode:^(SNTClientMode cm) {
switch (cm) {
case SNTClientModeMonitor: clientMode = @"Monitor"; break;
@@ -113,6 +107,14 @@ REGISTER_COMMAND_NAME(@"status")
dispatch_group_leave(group);
}];
// Static rule count
__block int64_t staticRuleCount = -1;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] staticRuleCount:^(int64_t count) {
staticRuleCount = count;
dispatch_group_leave(group);
}];
// Sync status
__block NSDate *fullSyncLastSuccess;
dispatch_group_enter(group);
@@ -172,16 +174,16 @@ REGISTER_COMMAND_NAME(@"status")
NSString *ruleSyncLastSuccessStr =
[dateFormatter stringFromDate:ruleSyncLastSuccess] ?: fullSyncLastSuccessStr;
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSString *syncURLStr = configurator.syncBaseURL.absoluteString;
BOOL exportMetrics = [[SNTConfigurator configurator] exportMetrics];
NSURL *metricsURLStr = [[SNTConfigurator configurator] metricURL];
NSUInteger metricExportInterval = [[SNTConfigurator configurator] metricExportInterval];
BOOL exportMetrics = configurator.exportMetrics;
NSURL *metricsURLStr = configurator.metricURL;
NSUInteger metricExportInterval = configurator.metricExportInterval;
if ([arguments containsObject:@"--json"]) {
NSMutableDictionary *stats = [@{
@"daemon" : @{
@"driver_connected" : @(driverConnected),
@"driver_connected" : @(YES),
@"mode" : clientMode ?: @"null",
@"file_logging" : @(fileLogging),
@"watchdog_cpu_events" : @(cpuEvents),
@@ -200,6 +202,9 @@ REGISTER_COMMAND_NAME(@"status")
@"transitive_rules" : @(transitiveRuleCount),
@"events_pending_upload" : @(eventCount),
},
@"static_rules" : @{
@"rule_count" : @(staticRuleCount),
},
@"sync" : @{
@"server" : syncURLStr ?: @"null",
@"clean_required" : @(syncCleanReqd),
@@ -223,16 +228,15 @@ REGISTER_COMMAND_NAME(@"status")
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Driver Connected", driverConnected ? "Yes" : "No");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
printf(" %-25s | %s\n", "USB Blocking", (configurator.blockUSBMount ? "Yes" : "No"));
if (configurator.blockUSBMount && configurator.remountUSBMode.count > 0) {
printf(" %-25s | %s\n", "USB Remounting Mode:",
[[configurator.remountUSBMode componentsJoinedByString:@", "] UTF8String]);
}
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
if (cachingEnabled) {
printf(">>> Cache Info\n");
@@ -248,6 +252,11 @@ REGISTER_COMMAND_NAME(@"status")
printf(" %-25s | %lld\n", "Transitive Rules", transitiveRuleCount);
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
if ([SNTConfigurator configurator].staticRules.count) {
printf(">>> Static Rules\n");
printf(" %-25s | %lld\n", "Rules", staticRuleCount);
}
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);

View File

@@ -127,8 +127,8 @@ objc_library(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
],
)
@@ -150,13 +150,13 @@ objc_library(
objc_library(
name = "event_logs",
hdrs = ["Logs/SNTEventLog.h"],
deps = [
":file_event_logs",
":protobuf_event_logs",
":syslog_event_logs",
"//Source/common:SNTCommon",
],
hdrs = ["Logs/SNTEventLog.h"],
)
objc_library(
@@ -197,11 +197,10 @@ objc_library(
"IOKit",
],
deps = [
":SNTApplicationCoreMetrics",
":database_controller",
":endpoint_security_manager",
":event_logs",
":SNTApplicationCoreMetrics",
"//Source/common:SantaCache",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
@@ -222,6 +221,7 @@ objc_library(
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/common:SantaCache",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
@@ -287,11 +287,16 @@ macos_bundle(
"--force",
"--options library,kill,runtime",
],
entitlements = select({
"//:adhoc_build": "com.google.santa.daemon.systemextension-adhoc.entitlements",
# Non-adhoc builds get their entitlements from the provisioning profile.
"//conditions:default": None,
}),
infoplists = ["Info.plist"],
linkopts = ["-execute"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:daemon_dev",
}),
version = "//:version",
@@ -302,11 +307,11 @@ macos_bundle(
santa_unit_test(
name = "SNTExecutionControllerTest",
srcs = [
"SNTExecutionController.h",
"DataLayer/SNTDatabaseTable.h",
"DataLayer/SNTEventTable.h",
"DataLayer/SNTRuleTable.h",
"EventProviders/SNTEventProvider.h",
"SNTExecutionController.h",
"SNTExecutionControllerTest.m",
],
sdk_dylibs = [
@@ -364,6 +369,9 @@ santa_unit_test(
"DataLayer/SNTRuleTable.m",
"DataLayer/SNTRuleTableTest.m",
],
sdk_dylibs = [
"EndpointSecurity",
],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
@@ -375,9 +383,6 @@ santa_unit_test(
"@MOLCertificate",
"@MOLCodesignChecker",
],
sdk_dylibs = [
"EndpointSecurity",
],
)
santa_unit_test(
@@ -395,8 +400,8 @@ santa_unit_test(
],
deps = [
":EndpointSecurityTestLib",
"//Source/common:SNTConfigurator",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
@@ -443,6 +448,7 @@ santa_unit_test(
"EndpointSecurity",
"bsm",
],
tags = ["exclusive"],
deps = [
":EndpointSecurityTestLib",
":santad_lib",

View File

@@ -277,6 +277,19 @@ static void addPathsFromDefaultMuteSet(NSMutableSet *criticalPaths) API_AVAILABL
teamID:(NSString *)teamID {
__block SNTRule *rule;
// Look for a static rule that matches.
NSDictionary *staticRules = [[SNTConfigurator configurator] staticRules];
if (staticRules.count) {
rule = staticRules[binarySHA256];
if (rule.type == SNTRuleTypeBinary) return rule;
rule = staticRules[certificateSHA256];
if (rule.type == SNTRuleTypeCertificate) return rule;
rule = staticRules[teamID];
if (rule.type == SNTRuleTypeTeamID) return rule;
}
// Now query the database.
//
// NOTE: This code is written with the intention that the binary rule is searched for first
// as Santa is designed to go with the most-specific rule possible.
//

View File

@@ -69,6 +69,10 @@ API_UNAVAILABLE(ios, tvos, watchos)
es_new_client_result_t es_new_client(es_client_t *_Nullable *_Nonnull client,
es_handler_block_t _Nonnull handler);
API_AVAILABLE(macos(10.15)) API_UNAVAILABLE(ios, tvos, watchos)
es_return_t es_mute_process(es_client_t * _Nonnull client,
const audit_token_t * _Nonnull audit_token);
#if defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)

View File

@@ -301,6 +301,11 @@ es_new_client_result_t es_new_client(es_client_t *_Nullable *_Nonnull client,
return ES_NEW_CLIENT_RESULT_SUCCESS;
};
es_return_t es_mute_process(es_client_t * _Nonnull client,
const audit_token_t * _Nonnull audit_token) {
return ES_RETURN_SUCCESS;
}
#if defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)

View File

@@ -170,6 +170,7 @@ NS_ASSUME_NONNULL_BEGIN
es_event_type_t events[] = {
ES_EVENT_TYPE_AUTH_MOUNT,
ES_EVENT_TYPE_AUTH_REMOUNT,
};
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
@@ -199,44 +200,77 @@ NS_ASSUME_NONNULL_BEGIN
return;
}
long mountMode = m->event.mount.statfs->f_flags;
struct statfs *eventStatFS;
BOOL isRemount = NO;
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_MOUNT: eventStatFS = m->event.mount.statfs; break;
case ES_EVENT_TYPE_AUTH_REMOUNT:
eventStatFS = m->event.remount.statfs;
isRemount = YES;
break;
default:
LOGE(@"Unexpected Event Type passed to DeviceManager handleAuthMount: %d", m->event_type);
// Fail closed.
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
assert(0 && "SNTDeviceManager: unexpected event type");
return;
}
long mountMode = eventStatFS->f_flags;
pid_t pid = audit_token_to_pid(m->process->audit_token);
LOGI(@"SNTDeviceManager: mount syscall arriving from path: %s, pid: %d, fflags: %lu",
LOGD(@"SNTDeviceManager: mount syscall arriving from path: %s, pid: %d, fflags: %lu",
m->process->executable->path.data, pid, mountMode);
DADiskRef disk =
DADiskCreateFromBSDName(NULL, self.diskArbSession, m->event.mount.statfs->f_mntfromname);
DADiskRef disk = DADiskCreateFromBSDName(NULL, self.diskArbSession, eventStatFS->f_mntfromname);
CFAutorelease(disk);
// TODO(tnek): Log all of the other attributes available in diskInfo into a structured log format.
NSDictionary *diskInfo = CFBridgingRelease(DADiskCopyDescription(disk));
BOOL isInternal = [diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceInternalKey] boolValue];
BOOL isRemovable = [diskInfo[(__bridge NSString *)kDADiskDescriptionMediaRemovableKey] boolValue];
BOOL isUSB =
[diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey] isEqualTo:@"USB"];
BOOL isEjectable = [diskInfo[(__bridge NSString *)kDADiskDescriptionMediaEjectableKey] boolValue];
NSString *protocol = diskInfo[(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey];
BOOL isUSB = [protocol isEqualToString:@"USB"];
BOOL isVirtual = [protocol isEqualToString: @"Virtual Interface"];
if (!isRemovable || !isUSB) {
NSString *kind = diskInfo[(__bridge NSString *)kDADiskDescriptionMediaKindKey];
// TODO: check kind and protocol for banned things (e.g. MTP).
LOGD(@"SNTDeviceManager: DiskInfo Protocol: %@ Kind: %@ isInternal: %d isRemovable: %d "
@"isEjectable: %d",
protocol, kind, isInternal, isRemovable, isEjectable);
// If the device is internal or virtual we are okay with the operation. We
// also are okay with operations for devices that are non-removal as long as
// they are NOT a USB device.
if (isInternal || isVirtual || (!isRemovable && !isEjectable && !isUSB)) {
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
SNTDeviceEvent *event = [[SNTDeviceEvent alloc]
initWithOnName:[NSString stringWithUTF8String:m->event.mount.statfs->f_mntonname]
fromName:[NSString stringWithUTF8String:m->event.mount.statfs->f_mntfromname]];
initWithOnName:[NSString stringWithUTF8String:eventStatFS->f_mntonname]
fromName:[NSString stringWithUTF8String:eventStatFS->f_mntfromname]];
BOOL shouldRemount = self.remountArgs != nil && [self.remountArgs count] > 0;
if (shouldRemount) {
event.remountArgs = self.remountArgs;
long remountOpts = mountArgsToMask(self.remountArgs);
if (mountMode & remountOpts) {
LOGD(@"SNTDeviceManager: mountMode: %@", maskToMountArgs(mountMode));
LOGD(@"SNTDeviceManager: remountOpts: %@", maskToMountArgs(remountOpts));
if ((mountMode & remountOpts) == remountOpts && !isRemount) {
LOGD(@"SNTDeviceManager: Allowing as mount as flags match remountOpts");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
return;
}
long newMode = mountMode | remountOpts;
LOGI(@"SNTDeviceManager: remounting device '%s'->'%s', flags (%lu) -> (%lu)",
m->event.mount.statfs->f_mntfromname, m->event.mount.statfs->f_mntonname, mountMode,
newMode);
eventStatFS->f_mntfromname, eventStatFS->f_mntonname, mountMode, newMode);
[self remount:disk mountMode:newMode];
}
@@ -270,26 +304,34 @@ NS_ASSUME_NONNULL_BEGIN
// ES will kill our whole client if we don't meet the es_message auth deadline, so we try to
// gracefully handle it with a deny-by-default in the worst-case before it can do that.
// This isn't an issue for notify events, so we're in no rush for those.
std::shared_ptr<std::atomic<bool>> responded;
if (m->action_type == ES_ACTION_TYPE_AUTH) {
responded = std::make_shared<std::atomic<bool>>(false);
es_message_t *mc = es_copy_message(m);
dispatch_semaphore_t processingSema = dispatch_semaphore_create(0);
// Add 1 to the processing semaphore. We're not creating it with a starting
// value of 1 because that requires that the semaphore is not deallocated
// until its value matches the starting value, which we don't need.
dispatch_semaphore_signal(processingSema);
dispatch_semaphore_t deadlineExpiredSema = dispatch_semaphore_create(0);
if (mc->action_type == ES_ACTION_TYPE_AUTH) {
dispatch_after(timeout, self.esAuthQueue, ^(void) {
if (responded->load()) return;
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Handler already responded, nothing to do.
return;
}
LOGE(@"SNTDeviceManager: deadline reached: deny pid=%d ret=%d",
audit_token_to_pid(m->process->audit_token),
es_respond_auth_result(c, m, ES_AUTH_RESULT_DENY, false));
audit_token_to_pid(mc->process->audit_token),
es_respond_auth_result(c, mc, ES_AUTH_RESULT_DENY, false));
dispatch_semaphore_signal(deadlineExpiredSema);
});
}
// TODO(tnek): migrate to es_retain_message.
es_message_t *mc = es_copy_message(m);
dispatch_async(self.esAuthQueue, ^{
[self handleESMessage:m withClient:c];
if (m->action_type == ES_ACTION_TYPE_AUTH) {
responded->store(true);
[self handleESMessage:mc withClient:c];
if (dispatch_semaphore_wait(processingSema, DISPATCH_TIME_NOW) != 0) {
// Deadline expired, wait for deadline block to finish.
dispatch_semaphore_wait(deadlineExpiredSema, DISPATCH_TIME_FOREVER);
}
es_free_message(mc);
});
}
@@ -297,16 +339,17 @@ NS_ASSUME_NONNULL_BEGIN
- (void)handleESMessage:(const es_message_t *)m
withClient:(es_client_t *)c API_AVAILABLE(macos(10.15)) {
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_MOUNT: {
[self handleAuthMount:m withClient:c];
// Intentional fallthrough
case ES_EVENT_TYPE_AUTH_REMOUNT: {
[[fallthrough]];
}
// TODO(tnek): log any extra data here about mounts.
case ES_EVENT_TYPE_NOTIFY_MOUNT: {
case ES_EVENT_TYPE_AUTH_MOUNT: {
[self handleAuthMount:m withClient:c];
break;
}
default: LOGE(@"SNTDeviceManager: unexpected event type: %d", m->event_type);
default:
LOGE(@"SNTDeviceManager: unexpected event type: %d", m->event_type);
break;
}
}

View File

@@ -40,9 +40,11 @@
fclose(stdout);
}
- (ESResponse *)triggerTestMount:(SNTDeviceManager *)deviceManager
mockES:(MockEndpointSecurity *)mockES
mockDA:(MockDiskArbitration *)mockDA {
- (ESResponse *)triggerTestMountEvent:(SNTDeviceManager *)deviceManager
mockES:(MockEndpointSecurity *)mockES
mockDA:(MockDiskArbitration *)mockDA
eventType:(es_event_type_t)eventType
diskInfoOverrides:(NSDictionary *)diskInfo {
if (!deviceManager.subscribed) {
// [deviceManager listen] is synchronous, but we want to asynchronously dispatch it
// with an enforced timeout to ensure that we never run into issues where the client
@@ -84,26 +86,39 @@
@"DAMediaBSDName" : test_mntfromname,
};
if (diskInfo != nil) {
NSMutableDictionary *mergedDiskDescription = [disk.diskDescription mutableCopy];
for (NSString *key in diskInfo) {
mergedDiskDescription[key] = diskInfo[key];
}
disk.diskDescription = (NSDictionary *)mergedDiskDescription;
}
[mockDA insert:disk bsdName:test_mntfromname];
ESMessage *m = [[ESMessage alloc] initWithBlock:^(ESMessage *m) {
m.binaryPath = @"/System/Library/Filesystems/msdos.fs/Contents/Resources/mount_msdos";
m.message->action_type = ES_ACTION_TYPE_AUTH;
m.message->event_type = ES_EVENT_TYPE_AUTH_MOUNT;
m.message->event = (es_events_t){.mount = {.statfs = fs}};
m.message->event_type = eventType;
if (eventType == ES_EVENT_TYPE_AUTH_MOUNT) {
m.message->event = (es_events_t){.mount = {.statfs = fs}};
} else {
m.message->event = (es_events_t){.remount = {.statfs = fs}};
}
}];
XCTestExpectation *expectation = [self expectationWithDescription:@"Wait for response from ES"];
XCTestExpectation *mountExpectation =
[self expectationWithDescription:@"Wait for response from ES"];
__block ESResponse *got;
[mockES registerResponseCallback:ES_EVENT_TYPE_AUTH_MOUNT
[mockES registerResponseCallback:eventType
withCallback:^(ESResponse *r) {
got = r;
[expectation fulfill];
[mountExpectation fulfill];
}];
[mockES triggerHandler:m.message];
[self waitForExpectations:@[ expectation ] timeout:60.0];
[self waitForExpectations:@[ mountExpectation ] timeout:60.0];
free(fs);
return got;
@@ -118,7 +133,12 @@
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = NO;
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
}
@@ -145,7 +165,11 @@
[expectation fulfill];
};
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, YES);
@@ -179,7 +203,11 @@
[expectation fulfill];
};
ESResponse *got = [self triggerTestMount:deviceManager mockES:mockES mockDA:mockDA];
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
@@ -190,4 +218,74 @@
XCTAssertEqualObjects(gotmntfromname, @"/dev/disk2s1");
}
- (void)testEnsureRemountsCannotChangePerms {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
deviceManager.remountArgs = @[ @"noexec", @"rdonly" ];
XCTestExpectation *expectation =
[self expectationWithDescription:@"Wait for SNTDeviceManager's blockCallback to trigger"];
__block NSString *gotmntonname, *gotmntfromname;
__block NSArray<NSString *> *gotRemountedArgs;
deviceManager.deviceBlockCallback = ^(SNTDeviceEvent *event) {
gotRemountedArgs = event.remountArgs;
gotmntonname = event.mntonname;
gotmntfromname = event.mntfromname;
[expectation fulfill];
};
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_REMOUNT
diskInfoOverrides:nil];
XCTAssertEqual(got.result, ES_AUTH_RESULT_DENY);
XCTAssertEqual(mockDA.wasRemounted, YES);
[self waitForExpectations:@[ expectation ] timeout:10.0];
XCTAssertEqualObjects(gotRemountedArgs, deviceManager.remountArgs);
XCTAssertEqualObjects(gotmntonname, @"/Volumes/KATE'S 4G");
XCTAssertEqualObjects(gotmntfromname, @"/dev/disk2s1");
}
- (void)testEnsureDMGsDoNotPrompt {
MockEndpointSecurity *mockES = [MockEndpointSecurity mockEndpointSecurity];
[mockES reset];
MockDiskArbitration *mockDA = [MockDiskArbitration mockDiskArbitration];
[mockDA reset];
SNTDeviceManager *deviceManager = [[SNTDeviceManager alloc] init];
deviceManager.blockUSBMount = YES;
deviceManager.remountArgs = @[ @"noexec", @"rdonly" ];
deviceManager.deviceBlockCallback = ^(SNTDeviceEvent *event) {
XCTFail(@"Should not be called");
};
NSDictionary *diskInfo = @{
(__bridge NSString *)kDADiskDescriptionDeviceProtocolKey: @"Virtual Interface",
(__bridge NSString *)kDADiskDescriptionDeviceModelKey: @"Disk Image",
(__bridge NSString *)kDADiskDescriptionMediaNameKey: @"disk image",
};
ESResponse *got = [self triggerTestMountEvent:deviceManager
mockES:mockES
mockDA:mockDA
eventType:ES_EVENT_TYPE_AUTH_MOUNT
diskInfoOverrides:diskInfo];
XCTAssertEqual(got.result, ES_AUTH_RESULT_ALLOW);
XCTAssertEqual(mockDA.wasRemounted, NO);
}
@end

View File

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

View File

@@ -226,6 +226,7 @@
exec.argsArray = [(__bridge NSArray *)message.args_array mutableCopy];
exec.machineId =
[[SNTConfigurator configurator] enableMachineIDDecoration] ? self.machineID : nil;
exec.teamId = cd.teamID;
[self logWithSantaMessage:&message
wrapper:^(SNTPBSantaMessage *sm) {

View File

@@ -170,11 +170,15 @@
[outLog appendFormat:@"|sha256=%@", cd.sha256];
if (cd.certSHA256) {
if (cd.certSHA256.length) {
[outLog appendFormat:@"|cert_sha256=%@|cert_cn=%@", cd.certSHA256,
[self sanitizeString:cd.certCommonName]];
}
if (cd.teamID.length) {
[outLog appendFormat:@"|teamid=%@", cd.teamID];
}
if (cd.quarantineURL) {
[outLog appendFormat:@"|quarantine_url=%@", [self sanitizeString:cd.quarantineURL]];
}

View File

@@ -318,9 +318,14 @@ dispatch_source_t createDispatchTimer(uint64_t interval, uint64_t leeway, dispat
// This will handle retying connection establishment if there are issues with the service
// during initialization (missing binary, malformed plist, bad code signature, etc.).
// Once those issues are resolved the connection will establish.
// This will also handle reestablishment if the service crashes or is killed.
// This will also handle re-establishment if the service crashes or is killed.
WEAKIFY(self);
ss.invalidationHandler = ^(void) {
[self establishSyncServiceConnection];
STRONGIFY(self);
self.syncdQueue.syncConnection.invalidationHandler = nil;
[self performSelectorOnMainThread:@selector(establishSyncServiceConnection)
withObject:nil
waitUntilDone:YES];
};
[ss resume]; // If there are issues establishing the connection resume will block for 2 seconds.
self.syncdQueue.syncConnection = ss;

View File

@@ -43,7 +43,6 @@ double watchdogCPUPeak = 0;
double watchdogRAMPeak = 0;
@interface SNTDaemonControlController ()
@property NSString *_syncXsrfToken;
@property SNTPolicyProcessor *policyProcessor;
@property id<SNTEventProvider> eventProvider;
@property SNTNotificationQueue *notQueue;
@@ -85,10 +84,6 @@ double watchdogRAMPeak = 0;
reply([self.eventProvider checkCache:vnodeID]);
}
- (void)driverConnectionEstablished:(void (^)(BOOL))reply {
reply(self.eventProvider.connectionEstablished);
}
#pragma mark Database ops
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate, int64_t compiler,
@@ -144,6 +139,10 @@ double watchdogRAMPeak = 0;
teamID:teamID]);
}
- (void)staticRuleCount:(void (^)(int64_t count))reply {
reply([SNTConfigurator configurator].staticRules.count);
}
#pragma mark Decision Ops
- (void)decisionForFilePath:(NSString *)filePath
@@ -173,15 +172,6 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)xsrfToken:(void (^)(NSString *))reply {
reply(self._syncXsrfToken);
}
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply {
self._syncXsrfToken = token;
reply();
}
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply {
reply([[SNTConfigurator configurator] fullSyncLastSuccess]);
}
@@ -261,6 +251,15 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)disableUnknownEventUpload:(void (^)(BOOL))reply {
reply([SNTConfigurator configurator].disableUnknownEventUpload);
}
- (void)setDisableUnknownEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
[[SNTConfigurator configurator] setDisableUnknownEventUpload:enabled];
reply();
}
#pragma mark Metrics Ops
- (void)metrics:(void (^)(NSDictionary *))reply {

View File

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

View File

@@ -54,6 +54,8 @@
fclose(stdout);
[[SNTMetricSet sharedInstance] reset];
self.mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
@@ -111,7 +113,7 @@
break;
}
if (!foundField) {
if (!foundField && expectedValue.intValue != 0) {
XCTFail(@"failed to find %@ field value", expectedFieldValueName);
}
}
@@ -129,7 +131,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:@"AllowBinary" expected:@2];
[self checkMetricCounters:@"AllowBinary" expected:@1];
}
- (void)testBinaryBlockRule {
@@ -241,7 +243,7 @@
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:@"AllowBinary" expected:@2];
[self checkMetricCounters:kAllowTransitive expected:@1];
}
- (void)testBinaryAllowTransitiveRuleDisabled {
@@ -262,8 +264,8 @@
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kAllowBinary expected:@2];
[self checkMetricCounters:kAllowTransitive expected:@1];
[self checkMetricCounters:kAllowBinary expected:@0];
[self checkMetricCounters:kAllowTransitive expected:@0];
}
- (void)testDefaultDecision {
@@ -281,7 +283,7 @@
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kBlockUnknown expected:@2];
[self checkMetricCounters:kBlockUnknown expected:@1];
[self checkMetricCounters:kAllowUnknown expected:@1];
}
@@ -298,7 +300,7 @@
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowNoFileInfo expected:@2];
[self checkMetricCounters:kAllowNoFileInfo expected:@1];
}
- (void)testUnreadableFailClosedLockdown {
@@ -344,7 +346,7 @@
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowScope expected:@2];
[self checkMetricCounters:kAllowScope expected:@1];
}
- (void)testPageZero {
@@ -354,7 +356,39 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kBlockUnknown expected:@3];
[self checkMetricCounters:kBlockUnknown expected:@1];
}
- (void)testAllEventUpload {
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(YES);
OCMExpect([self.mockEventDatabase addStoredEvent:OCMOCK_ANY]);
SNTRule *rule = [[SNTRule alloc] init];
rule.state = SNTRuleStateAllow;
rule.type = SNTRuleTypeBinary;
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil teamID:nil])
.andReturn(rule);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
}
- (void)testDisableUnknownEventUpload {
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor);
OCMExpect([self.mockConfigurator enableAllEventUpload]).andReturn(NO);
OCMExpect([self.mockConfigurator disableUnknownEventUpload]).andReturn(YES);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockEventProvider postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
OCMVerify(never(), [self.mockEventDatabase addStoredEvent:OCMOCK_ANY]);
[self checkMetricCounters:kAllowUnknown expected:@1];
}
@end

View File

@@ -0,0 +1,8 @@
<?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>com.apple.developer.endpoint-security.client</key>
<true/>
</dict>
</plist>

View File

@@ -1,16 +0,0 @@
<?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>com.apple.application-identifier</key>
<string>EQHXZ8M8AV.com.google.santa.daemon</string>
<key>com.apple.developer.endpoint-security.client</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>EQHXZ8M8AV</string>
<key>keychain-access-groups</key>
<array>
<string>EQHXZ8M8AV.com.google.santa.daemon</string>
</array>
</dict>
</plist>

View File

@@ -42,8 +42,8 @@ santa_unit_test(
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@OCMock",
"@MOLAuthenticatingURLSession",
"@OCMock",
],
)
@@ -67,7 +67,7 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",

View File

@@ -62,8 +62,8 @@ santa_unit_test(
deps = [
":SNTMetricHTTPWriter",
"//Source/common:SNTConfigurator",
"@OCMock",
"@MOLAuthenticatingURLSession",
"@OCMock",
],
)

View File

@@ -26,8 +26,6 @@ objc_library(
"SNTPushNotifications.m",
"SNTPushNotificationsTracker.h",
"SNTPushNotificationsTracker.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncLogging.h",
@@ -57,6 +55,7 @@ objc_library(
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncServiceInterface",
@@ -78,8 +77,6 @@ santa_unit_test(
"SNTPushNotifications.m",
"SNTPushNotificationsTracker.h",
"SNTPushNotificationsTracker.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncLogging.h",
@@ -113,6 +110,7 @@ santa_unit_test(
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"@MOLAuthenticatingURLSession",
@@ -162,7 +160,7 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",

View File

@@ -17,8 +17,8 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncFCM.h"
#import "Source/santasyncservice/SNTSyncState.h"

View File

@@ -15,7 +15,7 @@
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
#import "Source/common/SNTLogging.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/common/SNTSyncConstants.h"
@interface SNTPushNotificationsTracker ()

View File

@@ -21,9 +21,9 @@
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/NSData+Zlib.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncLogging.h"
#import "Source/santasyncservice/SNTSyncState.h"
@@ -149,6 +149,7 @@
[signingChain addObject:certDict];
}
newEvent[kSigningChain] = signingChain;
ADDKEY(newEvent, kTeamID, event.teamID);
return newEvent;
#undef ADDKEY

View File

@@ -23,9 +23,9 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/SNTPushNotifications.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncEventUpload.h"
#import "Source/santasyncservice/SNTSyncLogging.h"
#import "Source/santasyncservice/SNTSyncPostflight.h"
@@ -53,6 +53,8 @@ static const uint8_t kMaxEnqueuedSyncs = 2;
@property NSUInteger eventBatchSize;
@property NSString *xsrfToken;
@end
// Called when the network state changes
@@ -120,6 +122,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
[self startReachability];
}
self.xsrfToken = syncState.xsrfToken;
}
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
@@ -152,6 +155,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
[self startReachability];
}
self.xsrfToken = syncState.xsrfToken;
}
- (void)isFCMListening:(void (^)(BOOL))reply {
@@ -222,6 +226,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
SNTSyncRuleDownload *p = [[SNTSyncRuleDownload alloc] initWithState:syncState];
BOOL ret = [p sync];
LOGD(@"Rule download %@", ret ? @"complete" : @"failed");
self.xsrfToken = syncState.xsrfToken;
});
}
@@ -246,6 +251,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
SNTSyncPreflight *p = [[SNTSyncPreflight alloc] initWithState:syncState];
if ([p sync]) {
SLOGD(@"Preflight complete");
self.xsrfToken = syncState.xsrfToken;
// Clean up reachability if it was started for a non-network error
[self stopReachability];
@@ -346,12 +352,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
SLOGW(@"Missing Machine Owner.");
}
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
syncState.xsrfToken = token;
dispatch_group_leave(group);
}];
syncState.xsrfToken = self.xsrfToken;
NSURLSessionConfiguration *sessConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessConfig.connectionProxyDictionary = [[SNTConfigurator configurator] syncProxyConfig];
@@ -394,7 +395,6 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
syncState.pushNotificationsToken = self.pushNotifications.token;
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
return syncState;
}

View File

@@ -16,8 +16,8 @@
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncState.h"
@implementation SNTSyncPostflight

View File

@@ -19,9 +19,9 @@
#import "Source/common/SNTCommon.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTSystemInfo.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncLogging.h"
#import "Source/santasyncservice/SNTSyncState.h"
@@ -117,6 +117,14 @@
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
NSNumber *disableUnknownEventUpload = resp[kDisableUnknownEventUpload];
[[self.daemonConn remoteObjectProxy]
setDisableUnknownEventUpload:[disableUnknownEventUpload boolValue]
reply:^{
dispatch_group_leave(group);
}];
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
// Don't let these go too low

View File

@@ -19,5 +19,4 @@
@class SNTRule;
@interface SNTSyncRuleDownload : SNTSyncStage
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict;
@end

View File

@@ -18,9 +18,9 @@
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTRule.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/SNTPushNotificationsTracker.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncLogging.h"
#import "Source/santasyncservice/SNTSyncState.h"
@@ -92,8 +92,9 @@
uint32_t count = 0;
for (NSDictionary *ruleDict in response[kRules]) {
SNTRule *rule = [self ruleFromDictionary:ruleDict];
SNTRule *rule = [[SNTRule alloc] initWithDictionary:ruleDict];
if (rule) {
[self processBundleNotificationsForRule:rule fromDictionary:ruleDict];
[newRules addObject:rule];
count++;
}
@@ -128,60 +129,14 @@
[tracker removeNotificationsForHashes:processed];
}
// Converts rule information downloaded from the server into a SNTRule. Because any information
// not recorded by SNTRule is thrown away here, this method is also responsible for dealing with
// the extra bundle rule information (bundle_hash & rule_count).
- (SNTRule *)ruleFromDictionary:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.identifier = dict[kRuleIdentifier];
if (newRule.identifier == nil) {
newRule.identifier = dict[kRuleSHA256];
}
NSString *policyString = dict[kRulePolicy];
if ([policyString isEqual:kRulePolicyAllowlist] ||
[policyString isEqual:kRulePolicyAllowlistDeprecated]) {
newRule.state = SNTRuleStateAllow;
} else if ([policyString isEqual:kRulePolicyAllowlistCompiler] ||
[policyString isEqual:kRulePolicyAllowlistCompilerDeprecated]) {
newRule.state = SNTRuleStateAllowCompiler;
} else if ([policyString isEqual:kRulePolicyBlocklist] ||
[policyString isEqual:kRulePolicyBlocklistDeprecated]) {
newRule.state = SNTRuleStateBlock;
} else if ([policyString isEqual:kRulePolicySilentBlocklist] ||
[policyString isEqual:kRulePolicySilentBlocklistDeprecated]) {
newRule.state = SNTRuleStateSilentBlock;
} else if ([policyString isEqual:kRulePolicyRemove]) {
newRule.state = SNTRuleStateRemove;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
newRule.type = SNTRuleTypeBinary;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
newRule.type = SNTRuleTypeCertificate;
} else if ([ruleTypeString isEqual:kRuleTypeTeamID]) {
newRule.type = SNTRuleTypeTeamID;
} else {
return nil;
}
NSString *customMsg = dict[kRuleCustomMsg];
if (customMsg.length) {
newRule.customMsg = customMsg;
}
- (void)processBundleNotificationsForRule:(SNTRule *)rule fromDictionary:(NSDictionary *)dict {
// Check rule for extra notification related info.
if (newRule.state == SNTRuleStateAllow || newRule.state == SNTRuleStateAllowCompiler) {
if (rule.state == SNTRuleStateAllow || rule.state == SNTRuleStateAllowCompiler) {
// primaryHash is the bundle hash if there was a bundle hash included in the rule, otherwise
// it is simply the binary hash.
NSString *primaryHash = dict[kFileBundleHash];
if (primaryHash.length != 64) {
primaryHash = newRule.identifier;
primaryHash = rule.identifier;
}
// As we read in rules, we update the "remaining count" information. This count represents the
@@ -190,8 +145,6 @@
decrementPendingRulesForHash:primaryHash
totalRuleCount:dict[kFileBundleBinaryCount]];
}
return newRule;
}
@end

View File

@@ -17,9 +17,9 @@
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/NSData+Zlib.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncLogging.h"
#import "Source/santasyncservice/SNTSyncState.h"
@@ -199,9 +199,6 @@
[self performRequest:request timeout:10 response:&response error:NULL];
if (response.statusCode == 200) {
NSDictionary *headers = [response allHeaderFields];
[[self.daemonConn remoteObjectProxy] setXsrfToken:headers[kXSRFToken]
reply:^{
}];
self.syncState.xsrfToken = headers[kXSRFToken];
SLOGD(@"Retrieved new XSRF token");
success = YES;

View File

@@ -20,8 +20,8 @@
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santasyncservice/SNTSyncConstants.h"
#import "Source/santasyncservice/SNTSyncEventUpload.h"
#import "Source/santasyncservice/SNTSyncPostflight.h"
#import "Source/santasyncservice/SNTSyncPreflight.h"
@@ -340,6 +340,8 @@
XCTAssertEqualObjects(cert[kCertValidFrom], @(1365806075));
XCTAssertEqualObjects(cert[kCertValidUntil], @(1618266875));
XCTAssertEqualObjects(event[kTeamID], @"012345678910");
event = events[1];
XCTAssertEqualObjects(event[kFileName], @"hub");
XCTAssertEqualObjects(event[kExecutingUser], @"foouser");

View File

@@ -91,6 +91,11 @@
<key>CF$UID</key>
<integer>6</integer>
</dict>
<key>teamID</key>
<dict>
<key>CF$UID</key>
<integer>39</integer>
</dict>
</dict>
<integer>14887</integer>
<string>ff98fa0c0a1095fedcbe4d388a9760e71399a5c3c017a847ffa545663b57929a</string>
@@ -393,6 +398,7 @@
</dict>
</array>
</dict>
<string>012345678910</string>
</array>
<key>$top</key>
<dict>

View File

@@ -1,4 +1,4 @@
#!/bin/sh
# TODO: Pull benchmarks from previous commit to check for regression
bazel test //:benchmarks --define=SANTA_BUILD_TYPE=ci
bazel test //:benchmarks --define=SANTA_BUILD_TYPE=adhoc

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,15 +5,14 @@ load(
"git_repository",
"new_git_repository",
)
git_repository(
name = "build_bazel_rules_apple",
commit = "7115f0188d141d57d64a6875735847c975956dae", # 0.34.0
remote = "https://github.com/bazelbuild/rules_apple.git",
)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "build_bazel_rules_apple",
sha256 = "36072d4f3614d309d6a703da0dfe48684ec4c65a89611aeb9590b45af7a3e592", # 1.0.1
urls = ["https://github.com/bazelbuild/rules_apple/releases/download/1.0.1/rules_apple.1.0.1.tar.gz"],
)
http_archive(
name = "rules_proto_grpc",
sha256 = "28724736b7ff49a48cb4b2b8cfa373f89edfcb9e8e492a8d5ab60aa3459314c8",

6
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
_site
.sass-cache
.jekyll-cache
.jekyll-metadata
vendor
Gemfile.lock

14
docs/Gemfile Normal file
View File

@@ -0,0 +1,14 @@
source "https://rubygems.org"
# Hello! This is where you manage which Jekyll version is used to run.
# When you want to use a different version, change it below, save the
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
#
# bundle exec jekyll serve
#
gem "github-pages", group: :jekyll_plugins
# If you have any plugins, put them here!
group :jekyll_plugins do
gem "jekyll-feed", "~> 0.12"
end
gem "webrick", "~> 1.7"

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

5
docs/binaries/index.md Normal file
View File

@@ -0,0 +1,5 @@
---
title: Binaries
has_children: true
nav_order: 5
---

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Binaries
---
# Santa GUI

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Binaries
---
# santabs

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Binaries
---
# santactl
@@ -367,7 +367,7 @@ Recursive lookups of an application or directory is a soon to be added feature
##### rule
The rule command is covered in the [Rules](rules.md) document.
The rule command is covered in the [Rules](../concepts/rules.md) document.
##### sync

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Binaries
---
# santad

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Events
@@ -71,6 +71,7 @@ JSON blob. Here is an example of Firefox being blocked and sent for upload:
"sha256": "b0b1730ecbc7ff4505142c49f1295e6eda6bcaed7e2c68c5be91b5a11001f024"
}
],
"team_id": "43AQ936H96",
"file_bundle_name": "Firefox",
"executing_user": "bur",
"ppid": 1,

View File

@@ -1,5 +1,5 @@
---
title: Details
title: Concepts
has_children: true
nav_order: 4
---
---

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Interprocess Communication (IPC)

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Logs

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Mode

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Rules

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -1,5 +1,5 @@
---
parent: Details
parent: Concepts
---
# Scopes

View File

@@ -1,6 +1,7 @@
---
title: Configuration
parent: Deployment
nav_order: 2
---
# Configuration
@@ -37,7 +38,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| BannedBlockMessage | String | This is the message shown to the user when a binary is blocked because of a rule if that rule doesn't provide a custom message. If this is not configured a reasonable default is provided. |
| ModeNotificationMonitor | String | The notification text to display when the client goes into Monitor mode. Defaults to "Switching into Monitor mode". |
| ModeNotificationLockdown | String | The notification text to display when the client goes into Lockdown mode. Defaults to "Switching into Lockdown mode". |
| SyncBaseURL | String | The base URL of the sync server. |
| <a name="sync-base-url"></a>SyncBaseURL | String | The base URL of the sync server. |
| SyncProxyConfiguration | Dictionary | The proxy configuration to use when syncing. See the [Apple Documentation](https://developer.apple.com/documentation/cfnetwork/global_proxy_settings_constants) for details on the keys that can be used in this dictionary. |
| SyncEnableCleanSyncEventUpload | Bool | If true, events will be uploaded to the sync server even if a clean sync is requested. Defaults to false. |
| ClientAuthCertificateFile | String | If set, this contains the location of a PKCS#12 certificate to be used for sync authentication. |
@@ -65,6 +66,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| MetricExportTimeout | Integer | Number of seconds to wait before a timeout occurs when exporting metrics. Defaults to 30. |
| MetricExtraLabels | Dictionary | A map of key value pairs to add to all metric root labels. (e.g. a=b,c=d) defaults to @{}). If a previously set key (e.g. host_name is set to "" then the key is remove from the metric root labels. Alternatively if a value is set for an existing key then the new value will override the old. |
| EnableAllEventUpload | Bool | If YES, the client will upload all execution events to the sync server, including those that were explicitly allowed. |
| DisableUnknownEventUpload | Bool | If YES, the client will *not* upload events for executions of unknown binaries allowed in monitor mode |
\*overridable by the sync server: run `santactl status` to check the current

View File

@@ -0,0 +1,26 @@
---
title: Getting Started
parent: Deployment
nav_order: 1
---
# Getting Started
This page shows you the process to get started with your deployment of Santa.
**Note:** You can combine each of the profiles listed in the following steps into a single profile containing the different payloads: configuration, TCC, system extension, and notifications.
1. (Optional) Set up a [sync server](../introduction/syncing-overview.md). For a list of open-source sync servers, see [Sync Servers](sync-servers.md). Without a sync server, [`santactl`](../binaries/santactl.md) can configure rules locally.
1. Create and install your Santa configuration profile to customize your deployment of Santa. See [Configuration](configuration.md) for a reference list of the available options and an [example profile](https://github.com/google/santa/blob/main/docs/deployment/com.google.santa.example.mobileconfig).
1. Install the TCC and system extension configuration profiles:
- The TCC profile provides Santa the access it requires to read files anywhere on disk. See an [example TCC profile](https://github.com/google/santa/blob/main/docs/deployment/tcc.configuration-profile-policy.santa.example.mobileconfig).
- The system extension profile allows Santa to run without approval from the user. See an [example system extension profile](https://github.com/google/santa/blob/main/docs/deployment/system-extension-policy.santa.example.mobileconfig).
1. (Optional) Customize and install the notification settings profile. This allows you to set up notifications to alert when Santa is switching [modes](../concepts/mode.md). See an [example notification settings profile](https://github.com/google/santa/blob/main/docs/deployment/notificationsettings.santa.example.mobileconfig).
The notifications modified through this profile are different to the main Santa GUI pop-ups. To configure the [Santa GUI](../binaries/santa-gui.md) notifications, use the [configuration profile](configuration.md) (in step 2).
1. Install the latest Santa package from [GitHub](https://github.com/google/santa/releases) (where you can also find release notes). The package is distributed as a `PKG` wrapped inside a `DMG`, both of which are properly signed and can be validated.

View File

@@ -0,0 +1,20 @@
---
title: Sync Servers
parent: Deployment
nav_order: 3
---
# Sync Servers
Santa's [SyncBaseURL](configuration.md#sync-base-url) configuration flag allows you to synchronize with a management server, which uploads events that have occurred on the machine and downloads new rules.
There are several open-source servers you can sync with:
* [Moroz](https://github.com/groob/moroz): A simple golang server that serves hard-coded rules from configuration files.
* [Rudolph](https://github.com/airbnb/rudolph): An AWS-based serverless sync service primarily built on API GW, DynamoDB, and Lambda components to reduce operational burden. Rudolph is designed to be fast, easy-to-use, and cost-efficient.
* [Zentral](https://github.com/zentralopensource/zentral/wiki): A centralized service that pulls data from multiple sources and deploys configurations to multiple services.
* [Zercurity](https://github.com/zercurity/zercurity): A dockerized service for managing and monitoring applications across a large fleet using Santa + Osquery.
Alternatively, `santactl` can configure rules locally without a sync server.
See the [Syncing Overview](../introduction/syncing-overview.md) page for an explanation of how syncing works in Santa.

View File

@@ -1,6 +1,7 @@
---
title: Troubleshooting
parent: Deployment
nav_order: 4
---
# Troubleshooting
@@ -10,7 +11,7 @@ this page will cover troublshooting the system extension and related topics.
## Confirming Status
While there's an entire page on [santactl](../details/santactl.md), it's one of the best ways to start
While there's an entire page on [santactl](../binaries/santactl.md), it's one of the best ways to start
determining the cause of an issue:
```sh
@@ -49,11 +50,11 @@ Santa as well as its details and live connection state
## Confirming Actions
Looking into [logs](../details/logs.md) would be instructive for the majority
of how Santa is operating, and the pages on [scopes](../details/scopes.md) and [rules](../details/rules.md) would assist in
Looking into [logs](../concepts/logs.md) would be instructive for the majority
of how Santa is operating, and the pages on [scopes](../concepts/scopes.md) and [rules](../concepts/rules.md) would assist in
determining precendence and why decisions are made. Most helpful is the output of
`/usr/local/bin/santactl`'s `fileinfo` verb when called with the path/binary in
question as described on the [santactl](../details/santactl.md) page.
question as described on the [santactl](../binaries/santactl.md) page.
Depending on the presence or implementation details of a sync server, there may
be queues and a process for allowing binaries or updated developer certificates.

View File

@@ -23,7 +23,7 @@ built from tagged commits, so if you wanted to build, run or test a specific
release you can checkout that tag:
```sh
git checkout 0.9.33
git checkout 2022.5
```
If you want to list all the tags in reverse order:
@@ -37,123 +37,102 @@ git tag --sort=-creatordate
Build a debug version of Santa:
```sh
bazel build //Source/santa_driver
bazel build //Source/santa:Santa
```
Build a release (optimized) version of Santa:
For developers who do not have access to Google's code signing certificate and
provisioning profiles, use the `--define=SANTA_BUILD_TYPE=adhoc` flag. This will
adhoc sign Santa and does not require provisioning profiles.
Note: In order to run an adhoc signed Santa SIP must be disabled. See the
running section below.
```sh
bazel build //Source/santa_driver -c opt
bazel build //Source/santa:Santa --define=SANTA_BUILD_TYPE=adhoc
```
The output for these commands will be a `santa-driver.zip` file under
`bazel-bin` which, when extracted, will contain all of Santa and should be
installed under `/Library/Extensions`. However, if you're working on Santa and
want a quick way to reload everything, see the next section.
#### Running
When working on Santa, it's useful to have a way to quickly reload all of the
Santa components. For this reason, there's a special rule in the Santa BUILD
file that will compile a new santa-driver, unload Santa if it's running, install
the new Santa in the right place and attempt to load it.
file that will build Santa, unload Santa if it's running, install the new
Santa in the right place and attempt to load it.
On macOS 10.11+ System Integrity Protection (SIP) prevents loading of kernel
extensions that are not signed by an Apple KEXT signing certificate. To be able
to load and test a non-release version of Santa, SIP will have to be configured
to allow non-Apple KEXT signing certificates.
Non-adhoc debug builds of Santa can only be run by Google developers. This is
because of bundle id and provisioning profile restrictions bound to Apple
developer accounts.
__This is only to be done a machine that is actively developing, unloading and
loading kernel extensions.__
```sh
bazel run :reload
```
1. Boot into Recovery Mode: Reboot and hold down `command+r`
Non-Google developers can use an adhoc build to run development builds of Santa.
System Integrity Protection (SIP) will need to be disabled in order to run an
adhoc build.
**This is only to be done a machine that is actively developing Santa.**
1. Boot into Recovery Mode:
* For Intel Macs reboot and hold down `command+r`.
* For Apple Silicon Macs press and hold the power button until “Loading
startup options” appears. Click Options, then click Continue. If asked,
select a volume to recover, then click Next.
2. From the utilities menu select `Terminal`
3. Disable the KEXT feature of SIP: `csrutil enable --without kext`
3. Disable the KEXT feature of SIP. The kext wording is legacy but the command
still works well for loading adhoc signed system extensions: `csrutil enable
--without kext`
4. Reboot
You should now be able to load and run a non-release version of Santa.
Build and run a debug version of Santa.
Build and run an adhoc debug version of Santa.
```sh
bazel run :reload
bazel run :reload --define=SANTA_BUILD_TYPE=adhoc
```
Build and run a release version of Santa.
Note: if you are currently running a release or non-adhoc dev build of Santa,
this new adhoc build will show up as a second instance of Santa. Remove the
non-adhoc instance like so:
```sh
bazel run :reload -c opt
systemextensionsctl uninstall EQHXZ8M8AV com.google.santa.daemon
```
#### Using Xcode
#### IDE Setup
While Bazel is a very convenient and powerful build system, it can still be
useful to use Xcode for actually working on the code. If you'd like to use Xcode
you can use [Tulsi](https://tulsi.bazel.build) to generate an `.xcodeproj` from
the BUILD file which will use Bazel for actually doing the builds.
We don't generally use Xcode when working on Santa but it's very useful to be
able to use an IDE when developing. We generally use clangd for this, using a
tool that will extract the appropriate compile commands automatically from our
Bazel build rules. To use this:
```sh
generate_xcodeproj.sh santa.tulsiproj
```
1) Run `bazel run @hedron_compile_commands//:refresh_all` to generate the
`compile_commands.json` file.
2) Follow the [instructions](https://github.com/hedronvision/bazel-compile-commands-extractor#editor-setup--for-autocomplete-based-on-compile_commandsjson)
for setting up your editor.
#### Debugging
Xcode and lldb can be used to debug Santa, similarly to any other project, with
some exceptions. Instead of clicking the play button to launch and attach to a
process, you can attach to an already running, or soon to by running, component
of Santa. To do this select the Debug menu and choose `Attach to Process by PID
or Name…`. Below are the four components of Santa and who to debug the process
as.
Note: santa-driver (the kernel extension) cannot be debugged by attaching with
Xcode.
Note: Xcode can attach to santad without interruption, however any breakpoints
in the decision making codepath can deadlock the machine. Using lldb directly to
attach to santad will deadlock the machine.
process | user
-------- | ----
santad | root
Santa* | me
santactl | me
santabs | root
Xcode will then wait for the process to start. Issue this command to restart all
the Santa processes in debug mode.
*The Santa (GUI) process is the only component of Santa that can be launched and
debugged from Xcode directly. All the other components are launched with
privileges and/or are scoped to an XPC service that launchd scopes to a hosting
bundle. Thus the need for the `Attach to Process by PID or Name…` technique. See
the [ipc](../details/ipc.md) document for for details.
```sh
bazel run :reload
```
Now the process is attached in Xcode and you can debug your day away.
lldb can be used to debug Santa, similarly to any other project, with some
exceptions. lldb can attach to com.google.santa.daemon, however any breakpoints
in the decision making codepath can deadlock the machine.
#### Tests
Run all the logic / unit tests
```sh
bazel test :unit_tests
```
Run all of santa-driver kernel extension tests
```sh
bazel run //Source/santa_driver:kernel_tests
bazel test :unit_tests --define=SANTA_BUILD_TYPE=adhoc --test_output=errors
```
#### Releases
Creates a release build of Santa with a version based of the newest tag. Also
saves the dsym files for each component of Santa. This makes debugging and
interpreting future crashes or kernel panics much easier.
interpreting future crashes much easier. Releases are handled by Google internal
infrastructure.
```sh
bazel build :release
bazel build --apple_generate_dsym -c opt :release
```

View File

@@ -1,5 +1,5 @@
---
title: Development
has_children: true
nav_order: 5
nav_order: 6
---

View File

@@ -3,51 +3,60 @@ title: Home
nav_order: 1
---
# Welcome
# Welcome to the Santa documentation
Santa is a binary authorization system for macOS. Here you will find the
documentation for understanding how Santa works, how to deploy it and how to
contribute.
Santa is a binary authorization system for macOS. It consists of a system extension that allows or denies attempted executions using a set of rules stored in a local database, a GUI agent that notifies the user in case of a block decision, a sync daemon responsible for syncing the database and a server, and a command-line utility for managing the system.
#### Introduction
It is named Santa because it keeps track of binaries that are naughty or nice.
The following documents give an overview of how Santa accomplishes binary
authorization at the enterprise scale.
## Features
* [Binary Authorization](introduction/binary-authorization-overview.md): How Santa makes allow or deny decisions for any `execve()` taking place.
* [**Multiple modes:**](concepts/mode.md) In the default `MONITOR` mode, all binaries except those marked as blocked will be allowed to run, whilst being logged and recorded in the events database. In `LOCKDOWN` mode, only listed binaries are allowed to run.
* [**Event logging:**](concepts/events.md) All binary launches are logged. When in either mode, all unknown or denied binaries are stored in the database to enable later aggregation.
* [**Certificate-based rules, with override levels:**](concepts/rules.md) Instead of relying on a binary's hash (or 'fingerprint'), executables can be allowed/blocked by their signing certificate. You can therefore allow/block all binaries by a given publisher that were signed with that cert across version updates. A binary can only be allowed by its certificate if its signature validates correctly but a rule for a binary's fingerprint will override a decision for a certificate; i.e. you can allowlist a certificate while blocking a binary signed with that certificate, or vice-versa.
* **Path-based rules (via NSRegularExpression/ICU):** Binaries can be allowed/blocked based on the path they are launched from by matching against a configurable regex.
* [**Failsafe cert rules:**](concepts/rules.md#built-in-rules) You cannot put in a deny rule that would block the certificate used to sign launchd, a.k.a. pid 1, and therefore all components used in macOS. The binaries in every OS update (and in some cases entire new versions) are therefore automatically allowed. This does not affect binaries from Apple's App Store, which use various certs that change regularly for common apps. Likewise, you cannot block Santa itself.
* [**Components validate each other:**](binaries/index.md) Each of the components (the daemons, the GUI agent, and the command-line utility) communicate with each other using XPC and check that their signing certificates are identical before any communication is accepted.
* **Caching:** Allowed binaries are cached so the processing required to make a request is only done if the binary isn't already cached.
## Documentation overview
### Introduction
The following pages give an overview of how Santa accomplishes authorization at enterprise scale.
* [Binary Authorization](introduction/binary-authorization-overview.md): How Santa makes allow or deny decisions for any execution taking place.
* [Syncing](introduction/syncing-overview.md): How configuration and rules are applied from a sync server.
#### Deployment
### Deployment
* [Getting Started](deployment/getting-started.md): A quick guide to setting up your deployment.
* [Configuration](deployment/configuration.md): The local and sync server configuration options, along with example needed mobileconfig files.
* [Troubleshooting](deployment/troubleshooting.md): While there are numerous pages with details on Santa, admins may appreciate a central place to branch off from with common practical issues.
* [Sync Servers](deployment/sync-servers.md): A list of open-source sync servers.
* [Troubleshooting](deployment/troubleshooting.md): How to troubleshoot issues with your Santa deployment.
#### Development
* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine.
* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa.
#### Details
For those who want even more details on how Santa works under the hood, this section is for you.
###### Binaries
There are five main components that make up Santa whose core functionality is described in snippets below. For additional detail on each component, visit their respective pages. These quick descriptions do not encompass all the jobs performed by each component, but do provide a quick look at the basic functionality utilized to achieve the goal of binary authorization.
* [santad](details/santad.md): A user-land root daemon that makes decisions.
* [santactl](details/santactl.md): A user-land anonymous daemon that communicates with a sync server for configurations and policies. santactl can also be used by a user to manually configure Santa when using the local configuration.
* [santa-gui](details/santa-gui.md): A user-land GUI daemon that displays notifications when an `execve()` is blocked.
* [santabs](details/santabs.md): A user-land root daemon that finds Mach-O binaries within a bundle and creates events for them.
###### Concepts
### Concepts
Additional documentation on the concepts that support the operation of the main components:
* [mode](details/mode.md): An operating mode, either Monitor or Lockdown.
* [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.
* [ipc](details/ipc.md): How all the components of Santa communicate.
* [mode](concepts/mode.md): An operating mode, either Monitor or Lockdown.
* [events](concepts/events.md): Represents an `execve()` that was blocked, or would have been blocked, depending on the mode.
* [rules](concepts/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](concepts/scopes.md): The level at which an `execve()` was allowed or denied from taking place.
* [ipc](concepts/ipc.md): How all the components of Santa communicate.
duction/syncing-overview.
* [logs](details/logs.md): What and where Santa logs.
* [logs](concepts/logs.md): What and where Santa logs.
### Binaries
The following pages describe the main components that make up Santa:
* [santad](binaries/santad.md): A root daemon that makes decisions.
* [santactl](binaries/santactl.md): A command-line utility for inspecting the state and managing local configuration of Santa.
* [santa-gui](binaries/santa-gui.md): A GUI daemon that displays notifications when an execution is blocked.
* [santabs](binaries/santabs.md): A root daemon that finds binaries within a bundle to allow for easier rule-creation of bundled applications.
### Development
* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine.
* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa.

View File

@@ -24,7 +24,7 @@ directly or indirectly, on the operations being performed.
This is a high level overview of the binary authorization decision
process. For a more detailed account of each part, see the respective
documentation. This flow does not cover the logging component of Santa, see the
[logs.md](../details/logs.md) documentation for more info.
[logs.md](../concepts/logs.md) documentation for more info.
###### Kernel Space
@@ -62,7 +62,7 @@ documentation. This flow does not cover the logging component of Santa, see the
a decision, extra care is taken to be as performant as possible.
2. santad uses the information it has gathered to make a decision to allow or
deny the `execve()`. There are more details on how these decisions are made
in the [rules.md](../details/rules.md) and [scopes.md](../details/scopes.md)
in the [rules.md](../concepts/rules.md) and [scopes.md](../concepts/scopes.md)
documents.
3. The decision is posted back to santa-driver.
4. If there was a deny decision, a message is sent to Santa GUI to display a

10
docs/known-limitations.md Normal file
View File

@@ -0,0 +1,10 @@
---
title: Known Limitations
nav_order: 7
---
## Known limitations
- Santa only blocks execution (execve and variants), it doesn't protect against dynamic libraries loaded with dlopen, libraries on disk that have been replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`.
- Scripts: Santa is currently written to ignore any execution that isn't a binary. After weighing the administration cost versus the benefit, we found it wasn't worthwhile to manage the execution of scripts. Additionally, a number of applications make use of temporary generated scripts and blocking these could cause problems. We're happy to revisit this (or at least make it an option) if it would be useful to others.

View File

@@ -5,11 +5,11 @@ package(
licenses(["notice"])
filegroup(
name="santa_dev",
srcs=["Santa_Dev.provisionprofile"]
name = "santa_dev",
srcs = ["Santa_Dev.provisionprofile"],
)
filegroup(
name="daemon_dev",
srcs=["Santa_Daemon_Dev.provisionprofile"],
name = "daemon_dev",
srcs = ["Santa_Daemon_Dev.provisionprofile"],
)

View File

@@ -1,123 +0,0 @@
{
"additionalFilePaths" : [
"Source/santa/BUILD",
"Source/santactl/BUILD",
"Source/santabundleservice/BUILD",
"Source/santa_driver/BUILD",
"Source/common/BUILD",
"Source/santad/BUILD",
"/BUILD",
"Source/santasyncservice/BUILD"
],
"buildTargets" : [
"//:benchmarks",
"//:unit_tests",
"//Source/santa:Santa",
"//Source/santa_driver:kernel_tests_bin",
"//Source/santabundleservice:santabundleservice",
"//Source/santactl:santactl",
"//Source/santad:com.google.santa.daemon",
"//Source/santasyncservice:santasyncservice"
],
"optionSet" : {
"BazelBuildOptionsDebug" : {
"p" : "$(inherited)"
},
"BazelBuildOptionsRelease" : {
"p" : "$(inherited)"
},
"BazelBuildStartupOptionsDebug" : {
"p" : "$(inherited)"
},
"BazelBuildStartupOptionsRelease" : {
"p" : "$(inherited)"
},
"BuildActionPostActionScript" : {
"p" : "$(inherited)"
},
"BuildActionPreActionScript" : {
"p" : "$(inherited)"
},
"CommandlineArguments" : {
"p" : "$(inherited)"
},
"EnvironmentVariables" : {
"p" : "$(inherited)"
},
"LaunchActionPostActionScript" : {
"p" : "$(inherited)"
},
"LaunchActionPreActionScript" : {
"p" : "$(inherited)"
},
"ProjectGenerationBazelStartupOptions" : {
"p" : "$(inherited)"
},
"TestActionPostActionScript" : {
"p" : "$(inherited)"
},
"TestActionPreActionScript" : {
"p" : "$(inherited)"
}
},
"projectName" : "santa",
"sourceFilters" : [
"Source/...",
"Source/common",
"Source/santa",
"Source/santa/Resources",
"Source/santabundleservice",
"Source/santactl",
"Source/santactl/Commands",
"Source/santactl/Commands/sync",
"Source/santad",
"Source/santad/DataLayer",
"Source/santad/EventProviders",
"Source/santad/Logs",
"external/...",
"external/FMDB",
"external/FMDB/src",
"external/FMDB/src/fmdb",
"external/MOLAuthenticatingURLSession",
"external/MOLAuthenticatingURLSession/Source",
"external/MOLAuthenticatingURLSession/Source/MOLAuthenticatingURLSession",
"external/MOLCertificate",
"external/MOLCertificate/Source",
"external/MOLCertificate/Source/MOLCertificate",
"external/MOLCodesignChecker",
"external/MOLCodesignChecker/Source",
"external/MOLCodesignChecker/Source/MOLCodesignChecker",
"external/MOLXPCConnection",
"external/MOLXPCConnection/Source",
"external/MOLXPCConnection/Source/MOLXPCConnection",
"external/bazel_skylib",
"external/bazel_skylib/lib",
"external/bazel_skylib/rules",
"external/bazel_tools",
"external/bazel_tools/tools",
"external/bazel_tools/tools/build_defs",
"external/bazel_tools/tools/build_defs/cc",
"external/build_bazel_apple_support",
"external/build_bazel_apple_support/lib",
"external/build_bazel_rules_apple",
"external/build_bazel_rules_apple/apple",
"external/build_bazel_rules_apple/apple/internal",
"external/build_bazel_rules_apple/apple/internal/aspects",
"external/build_bazel_rules_apple/apple/internal/partials",
"external/build_bazel_rules_apple/apple/internal/partials/support",
"external/build_bazel_rules_apple/apple/internal/resource_actions",
"external/build_bazel_rules_apple/apple/internal/resource_rules",
"external/build_bazel_rules_apple/apple/internal/testing",
"external/build_bazel_rules_apple/apple/internal/utils",
"external/build_bazel_rules_swift",
"external/build_bazel_rules_swift/swift",
"external/build_bazel_rules_swift/swift/internal",
"external/rules_cc",
"external/rules_cc/cc",
"external/rules_cc/cc/private",
"external/rules_cc/cc/private/rules_impl",
"external/rules_proto",
"external/rules_proto/proto",
"external/rules_proto/proto/private"
]
}

View File

@@ -1,24 +0,0 @@
{
"configDefaults" : {
"optionSet" : {
"ALWAYS_SEARCH_USER_PATHS" : {
"p" : "YES"
},
"GenerateRunfiles" : {
"p" : "YES"
}
}
},
"packages" : [
"",
"Source/common",
"Source/santa",
"Source/santa_driver",
"Source/santabundleservice",
"Source/santactl",
"Source/santad",
"Source/santasyncservice"
],
"projectName" : "santa",
"workspaceRoot" : ".."
}