Compare commits

...

63 Commits

Author SHA1 Message Date
Pete Markowsky
fd23a5c3b7 Fix up endTimestamp to be Monarch compliant (#879)
Fix up endTimestamp field to be Monarch compliant.
2022-08-16 22:32:29 -04:00
Russell Hancox
ec203e8796 Project: Rename Source/santa -> Source/gui (#877) 2022-08-12 14:19:01 -04:00
Russell Hancox
57ff69208d GUI: Missed a required dependency (#876) 2022-08-12 14:02:22 -04:00
Russell Hancox
f00b7d2ded GUI: Expose SNTNotificationManager.h for the test. (#875) 2022-08-12 13:46:25 -04:00
Russell Hancox
9791fdd53c Project: Add a GH action to prevent trailing whitespace (#873) 2022-08-12 12:46:11 -04:00
Russell Hancox
26e2203f1e GUI: Improve signing chain key reporting in distributed notifications. (#874)
Also add a group for GUI unit_tests and include in the overall project tests group.
2022-08-12 11:03:21 -04:00
Russell Hancox
4a47195d12 Santa: Post distributed notification when showing block UI (#870)
Fixes #869
2022-08-11 12:34:35 -04:00
Russell Hancox
4436e221df GUI: Add silent mode configuration option. (#871)
When enabled, this option disables *all* GUI notifications from Santa. This is intended for kiosk-style machines where it is not expected for users to _ever_ execute unknown binaries.

Fixes #862
2022-08-11 09:17:07 -04:00
Russell Hancox
deccc8a148 GUI: For App Store published apps, include team ID. (#872)
With this change, the publisher field for an App Store published app will be  instead of

Fixes #758
2022-08-11 08:15:42 -04:00
Henry S
06da796a4d Docs: add link to GitHub (#868) 2022-08-08 16:38:34 -04:00
Russell Hancox
7b99a76d0d Docs: Add StaticRules to example mobileconfig (#866) 2022-08-03 10:59:18 -04:00
Pete Markowsky
c2d3e99446 Sync Protocol Docs (#860)
Initial commit of sync protocol docs.
2022-07-28 17:27:43 -04:00
Russell Hancox
6db7fea8ae syncservice: Add tests for NSData+Zlib and Postflight (#864) 2022-07-26 13:05:35 -04:00
Kathryn Hancox
6fcb4cfe63 Docs: Add recommended rollout doc (#861) 2022-07-22 13:50:25 -04:00
bfreezy
8b55ee4da5 santad: only allow root read+write permissions on sync-state.plist (#858) 2022-07-18 13:32:08 -04:00
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
Russell Hancox
e639574973 Project: Fix layering for tests (#813) 2022-05-12 16:52:11 -04:00
Russell Hancox
636f9ea873 Project: Layering, missed a dependency (#812) 2022-05-12 14:49:18 -04:00
Russell Hancox
9099409915 Project: Enable layering check, fix all dependency violations (#811) 2022-05-12 14:26:08 -04:00
Russell Hancox
976f483a99 syncservice: Fix SNTSyncTest (#810)
Failing preflight early if the daemonConn doesn't return a response the tests. This fix is a bit awkward, I tried to add the defaults in setUp but then you can't overwrite the stubs in methods that need to do it
2022-05-12 09:54:00 -04:00
Tom Burgin
8a32b7a56b preflight sync: fix dispatch_group_wait return polarity (#809)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:55:42 -04:00
Tom Burgin
7eeb06b406 preflight sync: stop the sync if we cannot communicate with the daemon (#808)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 18:45:58 +00:00
Tom Burgin
4540a1c656 SNTConfigurator: remove mutability from sync state dict (#807)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-11 14:35:43 -04:00
Russell Hancox
acc7b32b24 GUI: Switch to UserNotification.framework notifications (#806) 2022-05-11 12:32:08 -04:00
Russell Hancox
b92d513f5d GUI: Fix message queuing (#805) 2022-05-11 09:59:38 -04:00
Tom Burgin
3458fccd4e santasyncservice: handle loading and unloading of the service in the pkg (#804)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-10 14:59:34 -04:00
Russell Hancox
fdfb00368c GUI: Update keys for EventDetailURL. (#802)
The previous change here (#797) was not backward compatible and would be difficult to roll out. This change restores the previously used key and adds 2 new ones for migration. The previous key is marked deprecated and will be removed in the future.
2022-05-09 13:46:13 -04:00
Tom Burgin
6bd369cfb2 santad: remove sema from sync service queue (#803)
Co-authored-by: Tom Burgin <bur@chromium.org>
2022-05-09 13:32:28 -04:00
Pete Markowsky
0df26c6214 Fix ES Mock Client Subscription issues (#801)
Fixes an issue with the ES mock where it was deleting all clients on an unsubscribe.
2022-05-06 14:34:42 -04:00
Russell Hancox
6e22da1d97 santad: Add 'null' event logger. Fixes #754 (#799) 2022-05-06 12:22:04 -04:00
Russell Hancox
1725809335 Add config to allow uploading all events (#800)
* Add config to allow uploading all events

This config can be enabled locally or by a sync server and causes the
client to upload all events, not just those for binaries that are or
would be blocked.

Fixes #689
2022-05-06 11:45:53 -04:00
Pete Markowsky
3eff49feda Added macos-12 to the build matrix. (#798) 2022-05-03 21:14:15 -04:00
Pete Markowsky
5caedebb06 Created a profiles package so provisioning profiles only need to be in one place. (#794) 2022-05-03 17:14:02 -04:00
Russell Hancox
d823028b72 Sync: Add option to enable event upload despite clean sync. (#796)
Related to #789
2022-05-03 15:15:42 -04:00
Russell Hancox
49b2d6e22a GUI: Add %bundle_or_file_sha% translation key (#797)
* GUI: Add %bundle_or_file_sha% translation key

This mimics the current behavior that %file_sha% previously had and
moves %file_sha% to the expected behavior or just showing the file's
SHA.

Related to #795
2022-05-03 14:59:01 -04:00
145 changed files with 2269 additions and 968 deletions

View File

@@ -1,13 +1,14 @@
name: Check Markdown links
name: Check Markdown
on:
on:
pull_request:
paths:
- "**.md"
jobs:
markdown-link-check:
markdown-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1
- run: "! git grep -EIn $'[ \t]+$'"

View File

@@ -1,84 +1,50 @@
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
run: ./Testing/lint.sh
build_userspace:
strategy:
fail-fast: false
matrix:
os: [macos-10.15, macos-11]
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:
fail-fast: false
matrix:
os: [macos-10.15, macos-11]
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 +58,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

14
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"],
)
@@ -73,14 +74,14 @@ launchctl load /Library/LaunchAgents/com.google.santa.plist
run_command(
name = "reload",
srcs = [
"//Source/santa:Santa",
"//Source/gui:Santa",
],
cmd = """
set -e
rm -rf /tmp/bazel_santa_reload
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa/Santa.zip >/dev/null
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/gui/Santa.zip >/dev/null
echo "You may be asked for your password for sudo"
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
@@ -95,7 +96,7 @@ echo "Time to stop being naughty"
genrule(
name = "release",
srcs = [
"//Source/santa:Santa",
"//Source/gui:Santa",
"Conf/install.sh",
"Conf/uninstall.sh",
"Conf/com.google.santa.bundleservice.plist",
@@ -190,6 +191,7 @@ test_suite(
name = "unit_tests",
tests = [
"//Source/common:unit_tests",
"//Source/gui:unit_tests",
"//Source/santactl:unit_tests",
"//Source/santad:unit_tests",
"//Source/santametricservice:unit_tests",

View File

@@ -26,6 +26,9 @@ mkdir -p /usr/local/bin
# Load com.google.santa.metricservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.metricservice.plist
# Load com.google.santa.syncservice
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.syncservice.plist
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "${GUI_USER}" ]] && exit 0

View File

@@ -9,6 +9,7 @@
/bin/launchctl remove com.google.santad || true
/bin/launchctl remove com.google.santa.bundleservice || true
/bin/launchctl remove com.google.santa.metricservice || true
/bin/launchctl remove com.google.santa.syncservice || true
/bin/sleep 1

View File

@@ -4,10 +4,10 @@
<img src="https://raw.githubusercontent.com/google/santa/main/Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</p>
Santa is a binary authorization system for macOS. It consists of a system
extension that monitors for executions, a daemon that makes execution decisions
Santa is a binary authorization system for macOS. It consists of a system
extension that monitors for executions, a daemon that makes execution decisions
based on the contents of a local database, a GUI agent that notifies the user in
case of a block decision and a command-line utility for managing the system and
case of a block decision and a command-line utility for managing the system and
synchronizing the database with a server.
It is named Santa because it keeps track of binaries that are naughty or nice.
@@ -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

@@ -1,7 +1,7 @@
# Reporting a Vulnerability
If you believe you have found a security vulnerability, we would appreciate private disclosure
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
disclosed publicly either when a new version with fixes is released or 90 days has passed,
whichever comes first.

View File

@@ -3,7 +3,6 @@ load("@rules_proto_grpc//objc:defs.bzl", "objc_proto_library")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -11,7 +10,6 @@ licenses(["notice"])
proto_library(
name = "santa_proto",
srcs = ["santa.proto"],
features = ["layering_check"],
deps = [
"@com_google_protobuf//:any_proto",
"@com_google_protobuf//:timestamp_proto",
@@ -28,7 +26,6 @@ objc_proto_library(
cc_library(
name = "SantaCache",
hdrs = ["SantaCache.h"],
features = ["layering_check"],
deps = ["//Source/common:SNTCommon"],
)
@@ -49,15 +46,7 @@ objc_library(
":SNTConfigurator",
":SNTLogging",
":SNTStoredEvent",
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
":SNTSystemInfo",
],
)
@@ -71,6 +60,7 @@ objc_library(
":SNTDeviceEvent",
":SNTLogging",
":SNTStoredEvent",
":SNTSystemInfo",
],
)
@@ -84,6 +74,15 @@ objc_library(
],
)
objc_library(
name = "SNTDeviceEvent",
srcs = ["SNTDeviceEvent.m"],
hdrs = ["SNTDeviceEvent.h"],
deps = [
":SNTCommonEnums",
],
)
objc_library(
name = "SNTAllowlistInfo",
srcs = ["SNTAllowlistInfo.m"],
@@ -101,6 +100,7 @@ objc_library(
hdrs = ["SNTConfigurator.h"],
deps = [
":SNTCommonEnums",
":SNTRule",
":SNTStrengthify",
":SNTSystemInfo",
],
@@ -129,7 +129,6 @@ cc_library(
"TARGET_OS_OSX",
"TARGET_OS_MAC",
],
features = ["layering_check"],
)
objc_library(
@@ -144,7 +143,6 @@ cc_library(
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
features = ["layering_check"],
deps = [":SNTLogging"],
)
@@ -152,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(
@@ -168,7 +175,12 @@ objc_library(
cc_library(
name = "SNTStrengthify",
hdrs = ["SNTStrengthify.h"],
features = ["layering_check"],
)
objc_library(
name = "SNTSyncConstants",
srcs = ["SNTSyncConstants.m"],
hdrs = ["SNTSyncConstants.h"],
)
objc_library(
@@ -201,10 +213,17 @@ objc_library(
name = "SNTXPCControlInterface",
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
defines = select({
"//:adhoc_build": ["SANTAADHOC"],
"//conditions:default": None,
}),
deps = [
":SNTCommonEnums",
":SNTConfigurator",
":SNTRule",
":SNTStoredEvent",
":SNTXPCUnprivilegedControlInterface",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
@@ -231,6 +250,7 @@ objc_library(
srcs = ["SNTXPCSyncServiceInterface.m"],
hdrs = ["SNTXPCSyncServiceInterface.h"],
deps = [
":SNTCommonEnums",
":SNTStoredEvent",
"@MOLXPCConnection",
],

View File

@@ -119,9 +119,16 @@
if (!formatStr.length) return nil;
if (event.fileSHA256) {
// This key is deprecated, use %file_identifier% or %bundle_or_file_identifier%
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
withString:event.fileBundleHash ?: event.fileSHA256];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_identifier%"
withString:event.fileSHA256];
formatStr =
[formatStr stringByReplacingOccurrencesOfString:@"%bundle_or_file_identifier%"
withString:event.fileBundleHash ?: event.fileSHA256];
}
if (event.executingUser) {
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"

View File

@@ -93,6 +93,7 @@ typedef NS_ENUM(NSInteger, SNTEventLogType) {
SNTEventLogTypeSyslog,
SNTEventLogTypeFilelog,
SNTEventLogTypeProtobuf,
SNTEventLogTypeNull,
};
// The return status of a sync.

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.
///
@@ -151,9 +179,10 @@
///
/// Defines how event logs are stored. Options are:
/// SNTEventLogTypeSyslog: Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog: Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeProtobuf: (BETA) Sent to a file on disk, using maildir format. Use
/// SNTEventLogTypeSyslog "syslog": Sent to ASL or ULS (if built with the 10.12 SDK or later).
/// SNTEventLogTypeFilelog "file": Sent to a file on disk. Use eventLogPath to specify a path.
/// SNTEventLogTypeNull "null": Logs nothing
/// SNTEventLogTypeProtobuf "protobuf": (BETA) Sent to a file on disk, using maildir format. Use
/// mailDirectory to specify a path. Use mailDirectoryFileSizeThresholdKB,
/// mailDirectorySizeThresholdMB and mailDirectoryEventMaxFlushTimeSec to configure
/// additional maildir format settings.
@@ -226,6 +255,16 @@
#pragma mark - GUI Settings
///
/// When silent mode is enabled, Santa will never show notifications for
/// blocked processes.
///
/// This can be a very confusing experience for users, use with caution.
///
/// Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableSilentMode;
///
/// The text to display when opening Santa.app.
/// If unset, the default text will be displayed.
@@ -411,6 +450,22 @@
///
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// If true, syncs will upload events when a clean sync is requested. Defaults to false.
///
@property(readonly, nonatomic) BOOL enableCleanSyncEventUpload;
///
/// If true, events will be uploaded for all executions, even those that are allowed.
/// Use with caution, this generates a lot of events. Defaults to false.
///
@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"
@@ -28,11 +29,14 @@
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
/// Holds the configurations from a sync server and mobileconfig.
@property NSMutableDictionary *syncState;
@property NSDictionary *syncState;
@property NSMutableDictionary *configState;
/// 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,8 +48,10 @@ 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";
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString *const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -60,7 +66,8 @@ static NSString *const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
static NSString *const kAboutText = @"AboutText";
static NSString *const kEnableSilentModeKey = @"EnableSilentMode";
static NSString *const kAboutTextKey = @"AboutText";
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
static NSString *const kEventDetailURLKey = @"EventDetailURL";
static NSString *const kEventDetailTextKey = @"EventDetailText";
@@ -111,6 +118,8 @@ static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
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";
@@ -146,7 +155,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kFullSyncLastSuccess : date,
kRuleSyncLastSuccess : date,
kSyncCleanRequired : number
kSyncCleanRequired : number,
kEnableAllEventUploadKey : number,
};
_forcedConfigKeyTypes = @{
kClientModeKey : number,
@@ -163,7 +173,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kAboutText : string,
kEnableSilentModeKey : string,
kAboutTextKey : string,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
@@ -173,6 +184,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBBlockMessage : string,
kModeNotificationMonitor : string,
kModeNotificationLockdown : string,
kStaticRules : array,
kSyncBaseURLKey : string,
kSyncProxyConfigKey : dictionary,
kClientAuthCertificateFileKey : string,
@@ -207,10 +219,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kMetricExportInterval : number,
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];
@@ -278,6 +293,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingStaticRules {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
return [self configStateSet];
}
@@ -286,6 +305,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSilentMode {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingAboutText {
return [self configStateSet];
}
@@ -394,6 +417,14 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableAllEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingDisableUnknownEventUpload {
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
return [self configStateSet];
}
@@ -434,6 +465,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 {
@@ -541,6 +592,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:@"/"];
@@ -562,8 +617,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : NO;
}
- (BOOL)enableSilentMode {
NSNumber *number = self.configState[kEnableSilentModeKey];
return number ? [number boolValue] : NO;
}
- (NSString *)aboutText {
return self.configState[kAboutText];
return self.configState[kAboutTextKey];
}
- (NSURL *)moreInfoURL {
@@ -693,6 +753,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return SNTEventLogTypeProtobuf;
} else if ([logType isEqualToString:@"syslog"]) {
return SNTEventLogTypeSyslog;
} else if ([logType isEqualToString:@"null"]) {
return SNTEventLogTypeNull;
} else if ([logType isEqualToString:@"file"]) {
return SNTEventLogTypeFilelog;
} else {
return SNTEventLogTypeFilelog;
}
@@ -734,6 +798,33 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : YES;
}
- (BOOL)enableCleanSyncEventUpload {
NSNumber *number = self.configState[kSyncEnableCleanSyncEventUpload];
return number ? [number boolValue] : NO;
}
- (BOOL)enableAllEventUpload {
NSNumber *n = self.syncState[kEnableAllEventUploadKey];
if (n) return [n boolValue];
return [self.configState[kEnableAllEventUploadKey] boolValue];
}
- (void)setEnableAllEventUpload:(BOOL)enabled {
[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;
@@ -879,7 +970,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
[syncState writeToFile:kSyncStateFilePath atomically:YES];
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0644}
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0600}
ofItemAtPath:kSyncStateFilePath
error:NULL];
}
@@ -936,6 +1027,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

@@ -50,6 +50,8 @@ extern NSString *const kEnableBundlesDeprecated;
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;
@@ -91,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";
@@ -51,6 +51,8 @@ NSString *const kEnableBundlesDeprecated = @"bundles_enabled";
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";
@@ -92,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;
@@ -51,6 +50,8 @@
- (void)setRemountUSBMode:(NSArray *)remountUSBMode reply:(void (^)(void))reply;
- (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

@@ -1,7 +1,12 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
load("//:helper.bzl", "santa_unit_test")
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
exports_files([
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
])
@@ -27,6 +32,9 @@ objc_library(
"SNTNotificationManager.m",
"main.m",
],
hdrs = [
"SNTNotificationManager.h",
],
data = [
"Resources/AboutWindow.xib",
"Resources/DeviceMessageWindow.xib",
@@ -36,13 +44,19 @@ objc_library(
"IOKit",
"SecurityInterface",
"SystemExtensions",
"UserNotifications",
],
deps = [
"//Source/common:SNTBlockMessage_SantaGUI",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCNotifierInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -65,14 +79,41 @@ 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,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
deps = [":SantaGUI_lib"],
)
santa_unit_test(
name = "SNTNotificationManagerTest",
srcs = [
"SNTNotificationManagerTest.m",
],
sdk_frameworks = [
"Cocoa",
],
deps = [
":SantaGUI_lib",
"//Source/common:SNTStoredEvent",
"@OCMock",
],
)
test_suite(
name = "unit_tests",
tests = [
":SNTNotificationManagerTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/gui/SNTAboutWindowController.h"
#import "Source/common/SNTConfigurator.h"

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAccessibleTextField.h"
#import "Source/gui/SNTAccessibleTextField.h"
@implementation SNTAccessibleTextField

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -20,8 +20,8 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTAboutWindowController.h"
#import "Source/gui/SNTNotificationManager.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@class SNTStoredEvent;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>
@@ -20,7 +20,7 @@
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@interface SNTBinaryMessageWindowController ()
/// The custom message to display for this event
@@ -139,7 +139,9 @@
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
return [NSString stringWithFormat:@"App Store (Team ID: %@)", self.event.teamID];
} else if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -12,12 +12,12 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@implementation SNTMessageWindow

View File

@@ -1,6 +1,6 @@
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@implementation SNTMessageWindowController
@@ -28,7 +28,8 @@
}
- (NSString *)messageHash {
return @"";
[self doesNotRecognizeSelector:_cmd];
return nil;
}
@end

View File

@@ -15,9 +15,9 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
///
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.

View File

@@ -12,9 +12,11 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTNotificationManager.h"
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <UserNotifications/UserNotifications.h>
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
@@ -22,8 +24,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/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@interface SNTNotificationManager ()
@@ -58,7 +61,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self.pendingNotifications removeObject:self.currentWindowController];
self.currentWindowController = nil;
if ([self.pendingNotifications count]) {
if (self.pendingNotifications.count) {
[self showQueuedWindow];
} else {
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
@@ -83,9 +86,7 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
- (BOOL)notificationAlreadyQueued:(SNTMessageWindowController *)pendingMsg {
for (SNTMessageWindowController *msg in self.pendingNotifications) {
if ([msg messageHash] == [pendingMsg messageHash]) {
return YES;
}
if ([[msg messageHash] isEqual:[pendingMsg messageHash]]) return YES;
}
return NO;
}
@@ -113,12 +114,59 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
[self postDistributedNotification:pendingMsg];
if (!self.currentWindowController) {
[self showQueuedWindow];
}
}
// For blocked execution notifications, post an NSDistributedNotificationCenter
// notification with the important details from the stored event. Distributed
// notifications are system-wide broadcasts that can be sent by apps and observed
// from separate processes. This allows users of Santa to write tools that
// perform actions when we block execution, such as trigger management tools or
// display an enterprise-specific UI (which is particularly useful when combined
// with the EnableSilentMode configuration option, to disable Santa's standard UI).
- (void)postDistributedNotification:(SNTMessageWindowController *)pendingMsg {
if (![pendingMsg isKindOfClass:[SNTBinaryMessageWindowController class]]) {
return;
}
SNTBinaryMessageWindowController *wc = (SNTBinaryMessageWindowController *)pendingMsg;
NSDistributedNotificationCenter *dc = [NSDistributedNotificationCenter defaultCenter];
NSMutableArray<NSDictionary *> *signingChain =
[NSMutableArray arrayWithCapacity:wc.event.signingChain.count];
for (MOLCertificate *cert in wc.event.signingChain) {
[signingChain addObject:@{
kCertSHA256 : cert.SHA256 ?: @"",
kCertCN : cert.commonName ?: @"",
kCertOrg : cert.orgName ?: @"",
kCertOU : cert.orgUnit ?: @"",
kCertValidFrom : @([cert.validFrom timeIntervalSince1970]) ?: @0,
kCertValidUntil : @([cert.validUntil timeIntervalSince1970]) ?: @0,
}];
}
NSDictionary *userInfo = @{
kFileSHA256 : wc.event.fileSHA256 ?: @"",
kFilePath : wc.event.filePath ?: @"",
kFileBundleName : wc.event.fileBundleName ?: @"",
kFileBundleID : wc.event.fileBundleID ?: @"",
kFileBundleVersion : wc.event.fileBundleVersion ?: @"",
kFileBundleShortVersionString : wc.event.fileBundleVersionString ?: @"",
kTeamID : wc.event.teamID ?: @"",
kExecutingUser : wc.event.executingUser ?: @"",
kExecutionTime : @([wc.event.occurrenceDate timeIntervalSince1970]) ?: @0,
kPID : wc.event.pid ?: @0,
kPPID : wc.event.ppid ?: @0,
kParentName : wc.event.parentName ?: @"",
kSigningChain : signingChain,
};
[dc postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:userInfo];
}
- (void)showQueuedWindow {
// Notifications arrive on a background thread but UI updates must happen on the main thread.
// This includes making windows.
@@ -209,31 +257,66 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
#pragma mark SNTNotifierXPC protocol methods
- (void)postClientModeNotification:(SNTClientMode)clientmode {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
NSString *customMsg;
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
switch (clientmode) {
case SNTClientModeMonitor:
un.informativeText = @"Switching into Monitor mode";
customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
case SNTClientModeMonitor: {
content.body = @"Switching into Monitor mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
case SNTClientModeLockdown:
un.informativeText = @"Switching into Lockdown mode";
customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
}
case SNTClientModeLockdown: {
content.body = @"Switching into Lockdown mode";
NSString *customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
if (!customMsg) break;
// If a custom message is added but as an empty string, disable notifications.
if (!customMsg.length) return;
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
content.body = [SNTBlockMessage stringFromHTML:customMsg];
break;
}
default: return;
}
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
UNNotificationRequest *req =
[UNNotificationRequest requestWithIdentifier:@"clientModeNotification"
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = @"Santa";
content.body = message ?: @"Requested application can now be run";
NSString *identifier = [NSString stringWithFormat:@"ruleSyncNotification_%@", content.body];
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:identifier
content:content
trigger:nil];
[un addNotificationRequest:req withCompletionHandler:nil];
}
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;
@@ -245,15 +328,9 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
[self queueMessage:pendingMsg];
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
NSUserNotification *un = [[NSUserNotification alloc] init];
un.title = @"Santa";
un.hasActionButton = NO;
un.informativeText = message ?: @"Requested application can now be run";
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
}
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;

View File

@@ -0,0 +1,76 @@
/// 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 <MOLCertificate/MOLCertificate.h>
// #import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "Source/gui/SNTNotificationManager.h"
#import "Source/common/SNTStoredEvent.h"
@class SNTBinaryMessageWindowController;
@interface SNTNotificationManager (Testing)
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
withController:(SNTBinaryMessageWindowController *)controller;
@end
@interface SNTNotificationManagerTest : XCTestCase
@end
@implementation SNTNotificationManagerTest
- (void)setUp {
[super setUp];
fclose(stdout);
}
- (void)testPostBlockNotificationSendsDistributedNotification {
SNTStoredEvent *ev = [[SNTStoredEvent alloc] init];
ev.fileSHA256 = @"the-sha256";
ev.filePath = @"/Applications/Safari.app/Contents/MacOS/Safari";
ev.fileBundleName = @"Safari";
ev.fileBundlePath = @"/Applications/Safari.app";
ev.fileBundleID = @"com.apple.Safari";
ev.fileBundleVersion = @"18614.1.14.1.15";
ev.fileBundleVersionString = @"16.0";
ev.executingUser = @"rah";
ev.occurrenceDate = [NSDate dateWithTimeIntervalSince1970:1660221048];
ev.decision = SNTEventStateBlockBinary;
ev.pid = @84156;
ev.ppid = @1;
ev.parentName = @"launchd";
SNTNotificationManager *sut = OCMPartialMock([[SNTNotificationManager alloc] init]);
OCMStub([sut hashBundleBinariesForEvent:OCMOCK_ANY withController:OCMOCK_ANY]).andDo(nil);
id dncMock = OCMClassMock([NSDistributedNotificationCenter class]);
OCMStub([dncMock defaultCenter]).andReturn(dncMock);
[sut postBlockNotification:ev withCustomMessage:@""];
OCMVerify([dncMock postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *userInfo) {
XCTAssertEqualObjects(userInfo[@"file_sha256"], @"the-sha256");
XCTAssertEqualObjects(userInfo[@"pid"], @84156);
XCTAssertEqualObjects(userInfo[@"ppid"], @1);
XCTAssertEqualObjects(userInfo[@"execution_time"], @1660221048);
return YES;
}]]);
}
@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.system-extension.install</key>
<true/>
</dict>
</plist>

View File

@@ -17,7 +17,7 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
@interface SNTSystemExtensionDelegate : NSObject <OSSystemExtensionRequestDelegate>
@end

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

@@ -2,6 +2,10 @@ load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_applicatio
licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
)
objc_library(
name = "santabs_lib",
srcs = [
@@ -12,6 +16,7 @@ objc_library(
deps = [
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"@FMDB",
@@ -31,8 +36,8 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -5,7 +5,6 @@ licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
objc_library(
@@ -53,6 +52,7 @@ objc_library(
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/santasyncservice:sync_lib",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
@@ -69,8 +69,8 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
deps = [":santactl_lib"],
@@ -90,6 +90,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTXPCControlInterface",
"@MOLCertificate",
"@MOLCodesignChecker",
@@ -100,12 +101,22 @@ santa_unit_test(
santa_unit_test(
name = "SNTCommandMetricsTest",
srcs = ["Commands/SNTCommandMetricsTest.m"],
srcs = [
"Commands/SNTCommandMetrics.h",
"Commands/SNTCommandMetricsTest.m",
"SNTCommand.h",
"SNTCommandController.h",
],
structured_resources = glob(["Commands/testdata/*"]),
visibility = ["//:santa_package_group"],
deps = [
":santactl_lib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/common:SNTXPCControlInterface",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@MOLXPCConnection",
"@OCMock",
],
)

View File

@@ -119,7 +119,7 @@ REGISTER_COMMAND_NAME(@"metrics")
printf(">>> Root Labels\n");
[self prettyPrintRootLabels:normalizedMetrics[@"root_labels"]];
printf("\n");
printf(">>> Metrics \n");
printf(">>> Metrics\n");
[self prettyPrintMetricValues:normalizedMetrics[@"metrics"]];
}

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

@@ -7,7 +7,7 @@
hostname | testHost
username | testUser
>>> Metrics
>>> Metrics
Metric Name | /santa/rules
Description | Number of rules
Type | SNTMetricTypeGaugeInt64

View File

@@ -3,7 +3,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -22,11 +21,14 @@ objc_library(
],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
],
)
@@ -52,6 +54,8 @@ objc_library(
deps = [
":SNTEventProvider",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
],
@@ -69,15 +73,20 @@ objc_library(
"Logs/SNTSyslogEventLog.h",
"SNTDatabaseController.h",
],
hdrs = [
"Logs/SNTEventLog.h",
],
deps = [
":database_controller",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"@FMDB",
],
)
@@ -92,7 +101,13 @@ objc_library(
],
deps = [
":event_logs_common",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTStoredEvent",
"//Source/common:santa_objc_proto",
],
)
@@ -108,6 +123,12 @@ objc_library(
deps = [
":endpoint_security_manager",
":event_logs_common",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
],
)
@@ -116,18 +137,25 @@ objc_library(
srcs = [
"Logs/SNTFileEventLog.h",
"Logs/SNTFileEventLog.m",
"Logs/SNTSyslogEventLog.h",
],
deps = [
":event_logs_common",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTStrengthify",
],
)
objc_library(
name = "event_logs",
hdrs = ["Logs/SNTEventLog.h"],
deps = [
":file_event_logs",
":protobuf_event_logs",
":syslog_event_logs",
"//Source/common:SNTCommon",
],
)
@@ -169,24 +197,34 @@ objc_library(
"IOKit",
],
deps = [
":SNTApplicationCoreMetrics",
":database_controller",
":endpoint_security_manager",
":event_logs",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTMetricSet",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCMetricServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"//Source/santad:SNTApplicationCoreMetrics",
"//Source/common:SNTXPCUnprivilegedControlInterface",
"//Source/common:SantaCache",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
],
)
@@ -210,6 +248,9 @@ objc_library(
"EventProviders/EndpointSecurityTestUtil.h",
"EventProviders/EndpointSecurityTestUtil.mm",
],
hdrs = [
"EventProviders/EndpointSecurityTestUtil.h",
],
sdk_dylibs = [
"EndpointSecurity",
"bsm",
@@ -246,12 +287,17 @@ 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,
"//conditions:default": "Santa_Daemon_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:daemon_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
@@ -261,6 +307,11 @@ macos_bundle(
santa_unit_test(
name = "SNTExecutionControllerTest",
srcs = [
"DataLayer/SNTDatabaseTable.h",
"DataLayer/SNTEventTable.h",
"DataLayer/SNTRuleTable.h",
"EventProviders/SNTEventProvider.h",
"SNTExecutionController.h",
"SNTExecutionControllerTest.m",
],
sdk_dylibs = [
@@ -273,6 +324,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
@@ -281,6 +333,8 @@ santa_unit_test(
"//Source/common:SNTRule",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SantaCache",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
"@OCMock",
@@ -315,8 +369,12 @@ santa_unit_test(
"DataLayer/SNTRuleTable.m",
"DataLayer/SNTRuleTableTest.m",
],
sdk_dylibs = [
"EndpointSecurity",
],
deps = [
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
@@ -325,9 +383,6 @@ santa_unit_test(
"@MOLCertificate",
"@MOLCodesignChecker",
],
sdk_dylibs = [
"EndpointSecurity",
],
)
santa_unit_test(
@@ -346,6 +401,8 @@ santa_unit_test(
deps = [
":EndpointSecurityTestLib",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
],
@@ -354,6 +411,8 @@ santa_unit_test(
santa_unit_test(
name = "SNTDeviceManagerTest",
srcs = [
"EventProviders/DiskArbitrationTestUtil.h",
"EventProviders/SNTDeviceManager.h",
"EventProviders/SNTDeviceManagerTest.mm",
],
minimum_os_version = "10.15",
@@ -366,6 +425,8 @@ santa_unit_test(
":EndpointSecurityTestLib",
":santad_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeviceEvent",
"//Source/common:SNTPrefixTree",
"//Source/common:SantaCache",
"@OCMock",
@@ -375,7 +436,9 @@ santa_unit_test(
santa_unit_test(
name = "SNTApplicationTest",
srcs = [
"SNTApplication.h",
"SNTApplicationTest.m",
"SNTDatabaseController.h",
],
data = [
"//Source/santad/testdata:binaryrules_testdata",
@@ -385,9 +448,13 @@ santa_unit_test(
"EndpointSecurity",
"bsm",
],
tags = ["exclusive"],
deps = [
":EndpointSecurityTestLib",
":santad_lib",
"//Source/common:SNTConfigurator",
"@FMDB",
"@MOLCertificate",
"@MOLCodesignChecker",
"@MOLXPCConnection",
"@OCMock",
@@ -425,6 +492,7 @@ santa_unit_test(
deps = [
":SNTApplicationCoreMetrics",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/common:SNTSystemInfo",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
@@ -435,13 +503,21 @@ santa_unit_test(
santa_unit_test(
name = "SNTProtobufEventLogTest",
srcs = [
"Logs/SNTLogOutput.h",
"Logs/SNTProtobufEventLog.h",
"Logs/SNTProtobufEventLogTest.m",
"Logs/SNTSimpleMaildir.h",
],
minimum_os_version = "10.15",
deps = [
":EndpointSecurityTestLib",
":event_logs",
"//Source/common:SNTAllowlistInfo",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTConfigurator",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:santa_objc_proto",
"@OCMock",
],

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

@@ -21,7 +21,8 @@ es_string_token_t MakeStringToken(const NSString *_Nonnull s);
es_file_t MakeESFile(const char *_Nonnull path);
es_process_t MakeESProcess(es_file_t *_Nonnull esFile);
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *_Nonnull instigator, struct timespec ts);
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *_Nonnull instigator,
struct timespec ts);
CF_EXTERN_C_END
@class ESMessage;
@@ -68,6 +69,21 @@ 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)
es_return_t es_muted_paths_events(es_client_t *_Nonnull client,
es_muted_paths_t *_Nonnull *_Nullable muted_paths);
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
void es_release_muted_paths(es_muted_paths_t *_Nonnull muted_paths);
#endif
API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos)
es_respond_result_t es_respond_auth_result(es_client_t *_Nonnull client,

View File

@@ -45,7 +45,8 @@ es_process_t MakeESProcess(es_file_t *esFile) {
return esProc;
}
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *instigator, struct timespec ts) {
es_message_t MakeESMessage(es_event_type_t eventType, es_process_t *instigator,
struct timespec ts) {
es_message_t esMsg = {};
esMsg.time = ts;
@@ -205,6 +206,18 @@ CF_EXTERN_C_END
[self.clients addObject:mockClient];
}
- (BOOL)removeClient:(es_client_t *_Nonnull)client {
MockESClient *clientToRemove = [self findClient:client];
if (!clientToRemove) {
NSLog(@"Attempted to remove unknown mock es client.");
return NO;
}
[self.clients removeObject:clientToRemove];
return YES;
}
- (void)triggerHandler:(es_message_t *_Nonnull)msg {
for (MockESClient *client in self.clients) {
if (client.subscriptions[msg->event_type]) {
@@ -233,17 +246,24 @@ CF_EXTERN_C_END
return ES_RESPOND_RESULT_SUCCESS;
};
- (MockESClient *)findClient:(es_client_t *)client {
for (MockESClient *c in self.clients) {
// Since we're mocking out a C interface and using this exact pointer as our
// client identifier, only check for pointer equality.
if (client == (__bridge es_client_t *)c) {
return c;
}
}
return nil;
}
- (void)setSubscriptions:(const es_event_type_t *_Nonnull)events
event_count:(uint32_t)event_count
value:(NSNumber *)value
client:(es_client_t *)client {
@synchronized(self) {
MockESClient *toUpdate = nil;
for (MockESClient *c in self.clients) {
if (client == (__bridge es_client_t *)c) {
toUpdate = c;
}
}
MockESClient *toUpdate = [self findClient:client];
if (toUpdate == nil) {
NSLog(@"setting subscription for unknown client");
return;
@@ -281,9 +301,36 @@ 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)
es_return_t es_muted_paths_events(es_client_t *_Nonnull client,
es_muted_paths_t *_Nonnull *_Nullable muted_paths) {
es_muted_paths_t *tmp = (es_muted_paths_t *)malloc(sizeof(es_muted_paths_t));
tmp->count = 0;
*muted_paths = (es_muted_paths_t *_Nullable)tmp;
return ES_RETURN_SUCCESS;
};
API_AVAILABLE(macos(12.0))
API_UNAVAILABLE(ios, tvos, watchos)
void es_release_muted_paths(es_muted_paths_t *_Nonnull muted_paths) {
free(muted_paths);
}
#endif
API_AVAILABLE(macos(10.15))
API_UNAVAILABLE(ios, tvos, watchos) es_return_t es_delete_client(es_client_t *_Nullable client) {
[[MockEndpointSecurity mockEndpointSecurity] reset];
if (![[MockEndpointSecurity mockEndpointSecurity] removeClient:client]) {
return ES_RETURN_ERROR;
}
return ES_RETURN_SUCCESS;
};

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

@@ -447,7 +447,11 @@
logger = [[SNTProtobufEventLog alloc] init];
break;
}
default: logger = nil;
case SNTEventLogTypeNull: {
// Messages sent to nil objects do nothing, which is perfect for a null logger.
logger = nil;
break;
}
}
});
return logger;

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]);
}
@@ -252,6 +242,24 @@ double watchdogRAMPeak = 0;
reply();
}
- (void)enableAllEventUpload:(void (^)(BOOL))reply {
reply([SNTConfigurator configurator].enableAllEventUpload);
}
- (void)setEnableAllEventUpload:(BOOL)enabled reply:(void (^)(void))reply {
[[SNTConfigurator configurator] setEnableAllEventUpload:enabled];
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,9 +195,9 @@ static NSString *const kPrinterProxyPostMonterey =
[self incrementEventCounters:cd.decision];
// Log to database if necessary.
if (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;
@@ -201,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);
@@ -300,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

@@ -25,7 +25,5 @@
- (void)addEvents:(NSArray<SNTStoredEvent *> *)events isFromBundle:(BOOL)isFromBundle;
- (void)addBundleEvent:(SNTStoredEvent *)event reply:(void (^)(SNTBundleEventAction))reply;
- (void)startSyncingEvents;
- (void)stopSyncingEvents;
@end

View File

@@ -23,7 +23,6 @@
@interface SNTSyncdQueue ()
@property NSCache<NSString *, NSDate *> *uploadBackoff;
@property dispatch_queue_t syncdQueue;
@property dispatch_semaphore_t sema;
@end
@implementation SNTSyncdQueue
@@ -34,7 +33,6 @@
_uploadBackoff = [[NSCache alloc] init];
_uploadBackoff.countLimit = 128;
_syncdQueue = dispatch_queue_create("com.google.syncd_queue", DISPATCH_QUEUE_SERIAL);
_sema = dispatch_semaphore_create(0);
}
return self;
}
@@ -66,25 +64,10 @@
}];
}
- (void)startSyncingEvents {
dispatch_semaphore_signal(self.sema);
}
- (void)stopSyncingEvents {
self.sema = dispatch_semaphore_create(0);
}
// Hold events for a few seconds to allow santad and santactl to establish connections.
// If the connections are not established in time drop the event from the queue.
// They will be uploaded during a full sync.
- (void)dispatchBlockOnSyncdQueue:(void (^)(void))block {
if (!block) return;
dispatch_async(self.syncdQueue, ^{
if (!dispatch_semaphore_wait(self.sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
if (block) block();
dispatch_semaphore_signal(self.sema);
} else {
LOGD(@"Dropping block %@ from com.google.syncd_queue", block);
}
block();
});
}

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

@@ -3,7 +3,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -15,6 +14,9 @@ objc_library(
"SNTMetricService.m",
"main.m",
],
hdrs = [
"SNTMetricService.h",
],
deps = [
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
@@ -36,7 +38,11 @@ santa_unit_test(
structured_resources = ["//Source/santametricservice/Formats:testdata"],
deps = [
":SNTMetricServiceLib",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTMetricSet",
"//Source/santametricservice/Formats:SNTMetricFormatTestHelper",
"@MOLAuthenticatingURLSession",
"@OCMock",
],
)
@@ -61,8 +67,8 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],

View File

@@ -2,7 +2,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -30,6 +29,9 @@ objc_library(
"SNTMetricRawJSONFormat.h",
"SNTMetricRawJSONFormat.m",
],
hdrs = [
"SNTMetricRawJSONFormat.h",
],
deps = [
":SNTMetricFormat",
"//Source/common:SNTLogging",
@@ -43,6 +45,9 @@ objc_library(
"SNTMetricMonarchJSONFormat.h",
"SNTMetricMonarchJSONFormat.m",
],
hdrs = [
"SNTMetricMonarchJSONFormat.h",
],
deps = [
":SNTMetricFormat",
"//Source/common:SNTLogging",
@@ -71,6 +76,7 @@ santa_unit_test(
deps = [
":SNTMetricFormatTestHelper",
":SNTMetricMonarchJSONFormat",
"@OCMock",
],
)

View File

@@ -100,7 +100,8 @@ const NSString *kKey = @"key";
}
}
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric {
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric
withEndTimestamp:(NSDate *)endTimestamp {
NSMutableArray<NSDictionary *> *monarchMetricData = [[NSMutableArray alloc] init];
for (NSString *fieldName in metric[@"fields"]) {
@@ -112,8 +113,8 @@ const NSString *kKey = @"key";
}
monarchDataEntry[kStartTimestamp] = [self->_dateFormatter stringFromDate:entry[@"created"]];
monarchDataEntry[kEndTimestamp] =
[self->_dateFormatter stringFromDate:entry[@"last_updated"]];
// Monarch wants all the end timestamp to be updated, even if the value does not change.
monarchDataEntry[kEndTimestamp] = [self->_dateFormatter stringFromDate:endTimestamp];
if (!metric[@"type"]) {
LOGE(@"metric type is nil");
@@ -157,10 +158,13 @@ const NSString *kKey = @"key";
}
/**
* formatMetric translates the SNTMetricSet metric entries into those consumable by Monarch.
* formatMetric translates the SNTMetricSet metric entries into those consumable
* by Monarch.
**/
- (NSDictionary *)formatMetric:(NSString *)name withMetric:(NSDictionary *)metric {
- (NSDictionary *)formatMetric:(NSString *)name
withValue:(NSDictionary *)metric
andEndtimestamp:(NSDate *)endTimestamp {
NSMutableDictionary *monarchMetric = [[NSMutableDictionary alloc] init];
monarchMetric[kMetricName] = name;
@@ -175,7 +179,7 @@ const NSString *kKey = @"key";
}
[self encodeValueAndStreamKindFor:name withMetric:metric into:monarchMetric];
monarchMetric[@"data"] = [self encodeDataForMetric:metric];
monarchMetric[@"data"] = [self encodeDataForMetric:metric withEndTimestamp:endTimestamp];
return monarchMetric;
}
@@ -185,10 +189,12 @@ const NSString *kKey = @"key";
**/
- (NSDictionary *)normalize:(NSDictionary *)metrics {
NSMutableArray<NSDictionary *> *monarchMetrics = [[NSMutableArray alloc] init];
NSDate *endTimestamp = [NSDate date];
for (NSString *metricName in metrics[@"metrics"]) {
[monarchMetrics addObject:[self formatMetric:metricName
withMetric:metrics[@"metrics"][metricName]]];
withValue:metrics[@"metrics"][metricName]
andEndtimestamp:endTimestamp]];
}
NSMutableArray<NSDictionary *> *rootLabels = [[NSMutableArray alloc] init];

View File

@@ -1,5 +1,7 @@
#import <XCTest/XCTest.h>
#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#import "Source/santametricservice/Formats/SNTMetricFormatTestHelper.h"
#import "Source/santametricservice/Formats/SNTMetricMonarchJSONFormat.h"
@@ -9,6 +11,10 @@
@implementation SNTMetricMonarchJSONFormatTest
- (void)testMetricsConversionToJSON {
id classMock = OCMClassMock([NSDate class]);
OCMStub([classMock date])
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
NSDictionary *validMetricsDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
SNTMetricMonarchJSONFormat *formatter = [[SNTMetricMonarchJSONFormat alloc] init];
NSError *err = nil;

View File

@@ -26,7 +26,7 @@
"data" : [
{
"int64Value" : 1,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -37,7 +37,7 @@
},
{
"int64Value" : 3,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -54,7 +54,7 @@
"data" : [
{
"int64Value" : 123456789,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
],
@@ -73,7 +73,7 @@
"data" : [
{
"int64Value" : 1,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -84,7 +84,7 @@
},
{
"int64Value" : 2,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -104,7 +104,7 @@
"data" : [
{
"boolValue" : true,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]
@@ -116,7 +116,7 @@
"data" : [
{
"int64Value" : 1250999830800,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]
@@ -127,7 +127,7 @@
"data" : [
{
"int64Value" : 987654321,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
],
@@ -141,7 +141,7 @@
"data" : [
{
"stringValue" : "20210809.0.1",
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]

View File

@@ -164,6 +164,10 @@ NSDictionary *validMetricsDict = nil;
}
- (void)testWritingMonarchJSONToAFile {
id classMock = OCMClassMock([NSDate class]);
OCMStub([classMock date])
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
OCMStub([self.mockConfigurator exportMetrics]).andReturn(YES);
OCMStub([self.mockConfigurator metricFormat]).andReturn(SNTMetricFormatTypeMonarchJSON);
OCMStub([self.mockConfigurator metricURL]).andReturn(self.jsonURL);

View File

@@ -2,7 +2,6 @@ load("//:helper.bzl", "santa_unit_test")
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
licenses(["notice"])
@@ -18,6 +17,9 @@ objc_library(
"SNTMetricFileWriter.h",
"SNTMetricFileWriter.m",
],
hdrs = [
"SNTMetricFileWriter.h",
],
deps = [
":SNTMetricWriter",
"//Source/common:SNTLogging",
@@ -31,6 +33,7 @@ santa_unit_test(
],
deps = [
":SNTMetricFileWriter",
"//Source/common:SNTConfigurator",
],
)
@@ -40,6 +43,9 @@ objc_library(
"SNTMetricHTTPWriter.h",
"SNTMetricHTTPWriter.m",
],
hdrs = [
"SNTMetricHTTPWriter.h",
],
deps = [
":SNTMetricWriter",
"//Source/common:SNTConfigurator",
@@ -55,6 +61,8 @@ santa_unit_test(
],
deps = [
":SNTMetricHTTPWriter",
"//Source/common:SNTConfigurator",
"@MOLAuthenticatingURLSession",
"@OCMock",
],
)

View File

@@ -5,7 +5,6 @@ licenses(["notice"])
package(
default_visibility = ["//:santa_package_group"],
features = ["-layering_check"],
)
objc_library(
@@ -27,8 +26,6 @@ objc_library(
"SNTPushNotifications.m",
"SNTPushNotificationsTracker.h",
"SNTPushNotificationsTracker.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncLogging.h",
@@ -50,11 +47,20 @@ objc_library(
deps = [
":FCM_lib",
":broadcaster_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCSyncServiceInterface",
"@MOLAuthenticatingURLSession",
"@MOLCertificate",
"@MOLXPCConnection",
],
)
@@ -71,8 +77,6 @@ santa_unit_test(
"SNTPushNotifications.m",
"SNTPushNotificationsTracker.h",
"SNTPushNotificationsTracker.m",
"SNTSyncConstants.h",
"SNTSyncConstants.m",
"SNTSyncEventUpload.h",
"SNTSyncEventUpload.m",
"SNTSyncLogging.h",
@@ -97,6 +101,7 @@ santa_unit_test(
deps = [
":FCM_lib",
":broadcaster_lib",
"//Source/common:SNTCommon",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDropRootPrivs",
@@ -104,13 +109,28 @@ santa_unit_test(
"//Source/common:SNTLogging",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTSystemInfo",
"//Source/common:SNTXPCControlInterface",
"@MOLAuthenticatingURLSession",
"@MOLCertificate",
"@MOLXPCConnection",
"@OCMock",
],
)
santa_unit_test(
name = "NSDataZlibTest",
srcs = [
"NSData+Zlib.h",
"NSData+Zlib.m",
"NSDataZlibTest.m",
],
resources = glob(["testdata/sync_preflight_basic.*"]),
sdk_dylibs = ["libz"],
)
objc_library(
name = "broadcaster_lib",
srcs = ["SNTSyncBroadcaster.m"],
@@ -129,6 +149,7 @@ objc_library(
"main.m",
],
deps = [
":broadcaster_lib",
":sync_lib",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTLogging",
@@ -150,8 +171,8 @@ macos_command_line_application(
infoplists = ["Info.plist"],
minimum_os_version = "10.15",
provisioning_profile = select({
"//:ci_build": None,
"//conditions:default": "Santa_Dev.provisionprofile",
"//:adhoc_build": None,
"//conditions:default": "//profiles:santa_dev",
}),
version = "//:version",
visibility = ["//:santa_package_group"],
@@ -161,6 +182,7 @@ macos_command_line_application(
test_suite(
name = "unit_tests",
tests = [
":NSDataZlibTest",
":SNTSyncTest",
],
visibility = ["//:santa_package_group"],

View File

@@ -0,0 +1,54 @@
/// 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/santasyncservice/NSData+Zlib.h"
@interface NSDataZlibTest : XCTestCase
@end
@implementation NSDataZlibTest
- (void)setUp {
[super setUp];
}
- (NSData *)dataFromFixture:(NSString *)file {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:file ofType:nil];
XCTAssertNotNil(path, @"failed to load testdata: %@", file);
return [NSData dataWithContentsOfFile:path];
}
- (void)testZlibCompressed {
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
NSData *want = [self dataFromFixture:@"sync_preflight_basic.z"];
XCTAssertEqualObjects([sut zlibCompressed], want);
}
- (void)testGzipCompressed {
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
NSData *want = [self dataFromFixture:@"sync_preflight_basic.gz"];
XCTAssertEqualObjects([sut gzipCompressed], want);
}
- (void)testCompressEmpty {
NSData *sut = [NSData data];
XCTAssertNil([sut zlibCompressed]);
XCTAssertNil([sut gzipCompressed]);
};
@end

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

@@ -17,12 +17,13 @@
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTConfigurator.h"
#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"
@@ -54,7 +55,7 @@
if (uploadEvents.count >= self.syncState.eventBatchSize) break;
}
if (!self.syncState.cleanSync) {
if (!self.syncState.cleanSync || [[SNTConfigurator configurator] enableCleanSyncEventUpload]) {
NSDictionary *r = [self performRequest:[self requestWithDictionary:@{kEvents : uploadEvents}]];
if (!r) return NO;
@@ -148,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];
@@ -266,8 +272,8 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
return [self eventUploadWithSyncState:syncState];
}
SLOGE(@"Preflight failed, will try again once %@ is reachable",
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
LOGE(@"Preflight failed, will try again once %@ is reachable",
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
[self startReachability];
return SNTSyncStatusTypePreflightFailed;
}
@@ -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"
@@ -75,7 +75,11 @@
dispatch_group_leave(group);
}];
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
// Stop the sync if we are unable to communicate with daemon.
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
SLOGE(@"Unable to communicate with daemon.");
return NO;
}
// If user requested it or we've never had a successful sync, try from a clean slate.
if (syncClean) {
@@ -106,6 +110,21 @@
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
NSNumber *enableAllEventUpload = resp[kEnableAllEventUpload];
[[self.daemonConn remoteObjectProxy] setEnableAllEventUpload:[enableAllEventUpload boolValue]
reply:^{
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;

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