mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d9af01353 | ||
|
|
9c6af7fc03 | ||
|
|
543b1a29fe | ||
|
|
625ec67789 | ||
|
|
c5696d71e7 | ||
|
|
5f3cef52de | ||
|
|
eeed0b5aa6 | ||
|
|
9ef171e663 | ||
|
|
ad1868a50f | ||
|
|
78643d3c49 | ||
|
|
8b22c85a64 | ||
|
|
58fe5d3d76 | ||
|
|
8b2227967e | ||
|
|
65693acea1 | ||
|
|
7cea383930 | ||
|
|
5ae2376158 | ||
|
|
e851337eac | ||
|
|
2e53834980 | ||
|
|
aef139e93c | ||
|
|
a9e5bf09a7 | ||
|
|
4ee3f281c3 | ||
|
|
462ce89d42 | ||
|
|
44117833c0 | ||
|
|
8b6e029da2 | ||
|
|
f183e246df | ||
|
|
c60a35f280 | ||
|
|
4f65965277 | ||
|
|
01e4e15b81 | ||
|
|
532cb37e0b | ||
|
|
9d379d3884 | ||
|
|
3e7a191bf7 | ||
|
|
c5a048f4d9 | ||
|
|
f4769bad90 | ||
|
|
254497ad15 | ||
|
|
0a83445838 | ||
|
|
eff287259e | ||
|
|
6f2c0e3457 | ||
|
|
38769f7cd1 | ||
|
|
fa785ad3c2 | ||
|
|
5dae0cabdd | ||
|
|
a8b4f4ea7e | ||
|
|
2221c93bbc | ||
|
|
d1c33baf35 | ||
|
|
d2bbdff373 | ||
|
|
db1d65f944 | ||
|
|
d17aeac2f4 | ||
|
|
7840270dd0 | ||
|
|
dcf44c9872 | ||
|
|
fc365c888f | ||
|
|
85f0782399 | ||
|
|
64bc34c302 | ||
|
|
e2fc4c735d | ||
|
|
ff9cb34490 | ||
|
|
60405f1e10 | ||
|
|
ac9d3b2adf | ||
|
|
7e8bd46da3 | ||
|
|
2f6ed455e5 | ||
|
|
8cb86b6d1d | ||
|
|
fc074f6014 | ||
|
|
a7856e60e8 | ||
|
|
41a40c9fbd | ||
|
|
8c18f6ebf5 | ||
|
|
949053fedd | ||
|
|
8d2c39b71d | ||
|
|
8f872fb4fc | ||
|
|
5512f8cf19 | ||
|
|
6742b38e31 | ||
|
|
d1635f7e11 | ||
|
|
e2b865c081 | ||
|
|
012b02de5d | ||
|
|
11ebead617 | ||
|
|
e3fbabfe37 | ||
|
|
8757da7822 | ||
|
|
428582f471 | ||
|
|
6e0effc0f4 | ||
|
|
683114fbec | ||
|
|
d9ebb4e3db | ||
|
|
e6aaf2f198 | ||
|
|
1c3757d4ab | ||
|
|
4346bb29c2 | ||
|
|
09655df8fc | ||
|
|
7504cd36e1 | ||
|
|
cafef66933 | ||
|
|
0c4e9d4b06 | ||
|
|
ac07f5d54b | ||
|
|
d116f7b01e | ||
|
|
63ca34bc54 | ||
|
|
c894029c33 | ||
|
|
de2bdd6653 | ||
|
|
2d066ad671 | ||
|
|
24854d4ad7 | ||
|
|
99ee0af178 | ||
|
|
bf6f78df09 | ||
|
|
c05806916b | ||
|
|
e48ce0cfe3 | ||
|
|
eabca469b9 | ||
|
|
f6dc36e812 | ||
|
|
ac7cbdfd16 |
25
.github/workflows/ci.yml
vendored
Normal file
25
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build Userspace
|
||||
run: bazel build --apple_generate_dsym -c opt :release --define=SANTA_BUILD_TYPE=ci
|
||||
- name: Build Driver
|
||||
run: bazel build --apple_generate_dsym -c opt :release_driver --define=SANTA_BUILD_TYPE=ci
|
||||
- name: Test
|
||||
run: bazel test :unit_tests --define=SANTA_BUILD_TYPE=ci
|
||||
14
.travis.yml
14
.travis.yml
@@ -1,14 +0,0 @@
|
||||
---
|
||||
os: osx
|
||||
osx_image: xcode11
|
||||
language: objective-c
|
||||
sudo: false
|
||||
|
||||
addons:
|
||||
homebrew:
|
||||
taps: bazelbuild/tap
|
||||
packages: bazelbuild/tap/bazel
|
||||
|
||||
script:
|
||||
- bazel build :release --show_progress_rate_limit=30.0 -c opt --apple_generate_dsym --color=no --verbose_failures --sandbox_debug
|
||||
- bazel test :unit_tests --show_progress_rate_limit=30.0 --test_output=errors --color=no --verbose_failures --sandbox_debug
|
||||
90
BUILD
90
BUILD
@@ -2,7 +2,7 @@ load("@build_bazel_rules_apple//apple:versioning.bzl", "apple_bundle_version")
|
||||
load("//:helper.bzl", "run_command")
|
||||
load("//:version.bzl", "SANTA_VERSION")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
package(default_visibility = ["//:santa_package_group"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
@@ -15,12 +15,32 @@ apple_bundle_version(
|
||||
short_version_string = SANTA_VERSION,
|
||||
)
|
||||
|
||||
# Used to detect release builds
|
||||
config_setting(
|
||||
name = "release_build",
|
||||
values = {"define": "SANTA_BUILD_TYPE=release"},
|
||||
visibility = [":santa_package_group"],
|
||||
)
|
||||
|
||||
# Used to detect CI builds
|
||||
config_setting(
|
||||
name = "ci_build",
|
||||
values = {"define": "SANTA_BUILD_TYPE=ci"},
|
||||
visibility = [":santa_package_group"],
|
||||
)
|
||||
|
||||
# Used to detect optimized builds
|
||||
config_setting(
|
||||
name = "opt_build",
|
||||
values = {"compilation_mode": "opt"},
|
||||
)
|
||||
|
||||
package_group(
|
||||
name = "santa_package_group",
|
||||
packages = ["//..."],
|
||||
)
|
||||
|
||||
|
||||
################################################################################
|
||||
# Loading/Unloading/Reloading
|
||||
################################################################################
|
||||
@@ -54,9 +74,9 @@ set -e
|
||||
|
||||
rm -rf /tmp/bazel_santa_reload
|
||||
unzip -d /tmp/bazel_santa_reload \
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa_driver/santa_driver.zip >/dev/null
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa_driver/santa_driver.zip >/dev/null
|
||||
unzip -d /tmp/bazel_santa_reload \
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa/Santa.zip >/dev/null
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa/Santa.zip >/dev/null
|
||||
echo "You may be asked for your password for sudo"
|
||||
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
|
||||
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
|
||||
@@ -72,7 +92,6 @@ genrule(
|
||||
name = "release",
|
||||
srcs = [
|
||||
"//Source/santa:Santa",
|
||||
"//Source/santa_driver",
|
||||
"Conf/install.sh",
|
||||
"Conf/uninstall.sh",
|
||||
"Conf/com.google.santa.bundleservice.plist",
|
||||
@@ -91,9 +110,9 @@ genrule(
|
||||
echo "Please add '-c opt' flag to bazel invocation"
|
||||
""",
|
||||
":opt_build": """
|
||||
# Extract santa_driver.zip and Santa.zip
|
||||
# Extract Santa.zip
|
||||
for SRC in $(SRCS); do
|
||||
if [ "$$(basename $${SRC})" == "santa_driver.zip" -o "$$(basename $${SRC})" == "Santa.zip" ]; then
|
||||
if [ "$$(basename $${SRC})" == "Santa.zip" ]; then
|
||||
mkdir -p $(@D)/binaries
|
||||
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
|
||||
fi
|
||||
@@ -110,10 +129,6 @@ genrule(
|
||||
# Gather together the dSYMs. Throw an error if no dSYMs were found
|
||||
for SRC in $(SRCS); do
|
||||
case $${SRC} in
|
||||
*santa-driver.kext.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
|
||||
;;
|
||||
*santad.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santad.dSYM
|
||||
@@ -130,6 +145,10 @@ genrule(
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/Santa.app.dSYM
|
||||
;;
|
||||
*com.google.santa.daemon.systemextension.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/com.google.santa.daemon.systemextension.dSYM
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@@ -152,12 +171,61 @@ genrule(
|
||||
heuristic_label_expansion = 0,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "release_driver",
|
||||
srcs = [
|
||||
"//Source/santa_driver",
|
||||
],
|
||||
outs = ["santa-driver-" + SANTA_VERSION + ".tar.gz"],
|
||||
cmd = select({
|
||||
"//conditions:default": """
|
||||
echo "ERROR: Trying to create a release tarball without optimization."
|
||||
echo "Please add '-c opt' flag to bazel invocation"
|
||||
""",
|
||||
":opt_build": """
|
||||
# Extract santa_driver.zip
|
||||
for SRC in $(SRCS); do
|
||||
if [ "$$(basename $${SRC})" == "santa_driver.zip" ]; then
|
||||
mkdir -p $(@D)/binaries
|
||||
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
# Gather together the dSYMs. Throw an error if no dSYMs were found
|
||||
for SRC in $(SRCS); do
|
||||
case $${SRC} in
|
||||
*santa-driver.kext.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Cause a build failure if the dSYMs are missing.
|
||||
if [[ ! -d "$(@D)/dsym" ]]; then
|
||||
echo "dsym dir missing: Did you forget to use --apple_generate_dsym?"
|
||||
echo "This flag is required for the 'release' target."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update all the timestamps to now. Bazel avoids timestamps to allow
|
||||
# builds to be hermetic and cacheable but for releases we want the
|
||||
# timestamps to be more-or-less correct.
|
||||
find $(@D)/{binaries,dsym} -exec touch {} \\;
|
||||
|
||||
# Create final output tar
|
||||
tar -C $(@D) -czpf $(@) binaries dsym
|
||||
""",
|
||||
}),
|
||||
heuristic_label_expansion = 0,
|
||||
)
|
||||
|
||||
test_suite(
|
||||
name = "unit_tests",
|
||||
tests = [
|
||||
"//Source/common:SantaCacheTest",
|
||||
"//Source/common:SNTFileInfoTest",
|
||||
"//Source/common:SNTPrefixTreeTest",
|
||||
"//Source/santa_driver:SantaCacheTest",
|
||||
"//Source/santactl:SNTCommandFileInfoTest",
|
||||
"//Source/santactl:SNTCommandSyncTest",
|
||||
"//Source/santad:SNTEventTableTest",
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
Want to contribute? Great! First, read this page (including the small print at the end).
|
||||
|
||||
### Before you contribute
|
||||
Before we can use your code, you must sign the
|
||||
[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual)
|
||||
(CLA), which you can do online. The CLA is necessary mainly because you own the
|
||||
copyright to your changes, even after your contribution becomes part of our
|
||||
codebase, so we need your permission to use and distribute your code. We also
|
||||
need to be sure of various other things—for instance that you'll tell us if you
|
||||
know that your code infringes on other people's patents. You don't have to sign
|
||||
the CLA until after you've submitted your code for review and a member has
|
||||
approved it, but you must do it before we can put your code into our codebase.
|
||||
|
||||
Before you start working on a larger contribution, you should get in touch with
|
||||
us first through the [issue tracker](https://github.com/google/santa/issues)
|
||||
with your idea so that we can help out and possibly guide you. Coordinating up
|
||||
front makes it much easier to avoid frustration later on.
|
||||
|
||||
### Code reviews
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. It's also a good idea to run the
|
||||
tests beforehand, which you can do with the following commands:
|
||||
|
||||
```sh
|
||||
rake tests:logic
|
||||
rake tests:kernel # only necessary if you're changing the kext code
|
||||
```
|
||||
### Code Style
|
||||
|
||||
All code submissions should try to match the surrounding code. Wherever possible,
|
||||
code should adhere to either the
|
||||
[Google Objective-C Style Guide](https://google.github.io/styleguide/objcguide.xml)
|
||||
or the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
|
||||
|
||||
### The small print
|
||||
Contributions made by corporations are covered by a different agreement than
|
||||
the one above, the [Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).
|
||||
1
CONTRIBUTING.md
Symbolic link
1
CONTRIBUTING.md
Symbolic link
@@ -0,0 +1 @@
|
||||
docs/development/contributing.md
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Load the kernel extension, santad, sync client
|
||||
# Load com.google.santa.daemon and com.google.santa.bundleservice
|
||||
# If a user is logged in, also load the GUI agent.
|
||||
# If the target volume is not /, do nothing
|
||||
|
||||
@@ -13,24 +13,19 @@
|
||||
mkdir -p /usr/local/bin
|
||||
/bin/ln -sf /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin/santactl
|
||||
|
||||
if [ $(uname -r | cut -d'.' -f1) -ge 19 ]; then
|
||||
# Running on 10.15+
|
||||
echo "Santa postinstall: running on 10.15+"
|
||||
/bin/rm -rf /Library/Extensions/santa-driver.kext
|
||||
/bin/rm -f /Library/LaunchDaemons/com.google.santad.plist
|
||||
else
|
||||
# Running on <10.15
|
||||
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
|
||||
fi
|
||||
# Remove the kext before com.google.santa.daemon loads if the SystemExtension is already present.
|
||||
# This prevents Santa from dueling itself if the "EnableSystemExtension" config is set to false.
|
||||
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 && rm -rf /Library/Extensions/santa-driver.kext
|
||||
|
||||
# Load the bundle service
|
||||
# Load com.google.santa.daemon, its main has logic to handle loading the kext
|
||||
# or relaunching itself as a SystemExtension.
|
||||
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
|
||||
|
||||
# Load com.google.santa.bundleservice
|
||||
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.bundleservice.plist
|
||||
|
||||
user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
if [[ -z "$user" ]]; then
|
||||
/Applications/Santa.app/Contents/MacOS/Santa --load-system-extension
|
||||
exit 0
|
||||
fi
|
||||
/bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.santa.plist
|
||||
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
[[ -z "${GUI_USER}" ]] && exit 0
|
||||
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl load /Library/LaunchAgents/com.google.santa.plist
|
||||
exit 0
|
||||
|
||||
@@ -19,11 +19,13 @@
|
||||
/bin/launchctl remove com.google.santasync
|
||||
/bin/rm -f /Library/LaunchDaemons/com.google.santasync.plist
|
||||
/bin/rm -rf /Applications/Santa.app
|
||||
/bin/rm -rf /Library/Extensions/santa-driver.kext
|
||||
|
||||
/bin/sleep 1
|
||||
|
||||
user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
|
||||
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santa
|
||||
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
[[ -z "${GUI_USER}" ]] && exit 0
|
||||
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santagui
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santa
|
||||
exit 0
|
||||
|
||||
22
Conf/com.google.santa.syncservice.plist
Normal file
22
Conf/com.google.santa.syncservice.plist
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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>Label</key>
|
||||
<string>com.google.santa.syncservice</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/Santa.app/Contents/MacOS/santasyncservice</string>
|
||||
<string>--syslog</string>
|
||||
</array>
|
||||
<key>MachServices</key>
|
||||
<dict>
|
||||
<key>com.google.santa.syncservice</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -31,10 +31,10 @@ fi
|
||||
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
|
||||
# Unload GUI agent if someone is logged in.
|
||||
[[ -n "${GUI_USER}" ]] && \
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santagui
|
||||
[[ -n "$GUI_USER" ]] && \
|
||||
/bin/launchctl asuser ${GUI_USER} /bin/launchctl remove com.google.santagui
|
||||
[[ -n "$GUI_USER" ]] && \
|
||||
/bin/launchctl asuser ${GUI_USER} /bin/launchctl remove com.google.santa
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl remove com.google.santa
|
||||
|
||||
# Cleanup cruft from old versions
|
||||
/bin/launchctl remove com.google.santasync >/dev/null 2>&1
|
||||
@@ -49,33 +49,30 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
|
||||
/bin/cp -r ${BINARIES}/Santa.app /Applications
|
||||
|
||||
# Only copy the kext if the SystemExtension is not present.
|
||||
# This prevents Santa from dueling itself if the "EnableSystemExtension" config is set to false.
|
||||
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 || /bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions && /usr/sbin/kextcache -update-volume / -bundle-id com.google.santa-driver
|
||||
|
||||
/bin/mkdir -p /usr/local/bin
|
||||
/bin/ln -s /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
|
||||
|
||||
/bin/cp ${CONF}/com.google.santa.plist /Library/LaunchAgents
|
||||
/bin/cp ${CONF}/com.google.santa.bundleservice.plist /Library/LaunchDaemons
|
||||
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/cp ${CONF}/com.google.santa.asl.conf /etc/asl/
|
||||
/bin/cp ${CONF}/com.google.santa.newsyslog.conf /etc/newsyslog.d/
|
||||
|
||||
# Reload syslogd to pick up ASL configuration change.
|
||||
/usr/bin/killall -HUP syslogd
|
||||
|
||||
# Only copy the kext and load santad if running pre-10.15
|
||||
if [ $(uname -r | cut -d'.' -f1) -lt 19 ]; then
|
||||
/bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions
|
||||
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
else
|
||||
/Applications/Santa.app/Contents/MacOS/Santa --load-system-extension
|
||||
fi
|
||||
# Load com.google.santa.daemon
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
|
||||
# Load the bundle service
|
||||
# Load com.google.santa.bundleservice
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
|
||||
|
||||
# Load GUI agent if someone is logged in.
|
||||
if [[ -n "$GUI_USER" ]]; then
|
||||
/bin/launchctl asuser ${GUI_USER} \
|
||||
/bin/launchctl load -w /Library/LaunchAgents/com.google.santa.plist
|
||||
fi
|
||||
[[ -z "${GUI_USER}" ]] && exit 0
|
||||
|
||||
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl load -w /Library/LaunchAgents/com.google.santa.plist
|
||||
exit 0
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[ "$EUID" != 0 ] && printf "%s\n" "This requires running as root/sudo." && exit 1
|
||||
|
||||
# For macOS 10.15+ this will block up to 60 seconds
|
||||
/Applications/Santa.app/Contents/MacOS/Santa --unload-system-extension
|
||||
/bin/launchctl list EQHXZ8M8AV.com.google.santa.daemon > /dev/null 2>&1 && /Applications/Santa.app/Contents/MacOS/Santa --unload-system-extension
|
||||
|
||||
/bin/launchctl remove com.google.santad
|
||||
sleep 1
|
||||
|
||||
@@ -11,7 +11,7 @@ PODS:
|
||||
- MOLAuthenticatingURLSession (~> 2.4)
|
||||
- MOLXPCConnection (1.2):
|
||||
- MOLCodesignChecker (~> 1.9)
|
||||
- OCMock (3.4.3)
|
||||
- OCMock (3.5)
|
||||
|
||||
DEPENDENCIES:
|
||||
- FMDB
|
||||
@@ -23,7 +23,7 @@ DEPENDENCIES:
|
||||
- OCMock
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
@@ -39,8 +39,8 @@ SPEC CHECKSUMS:
|
||||
MOLCodesignChecker: b0d5db9d2f9bd94e0fd093891a5d40e5ad77cbc0
|
||||
MOLFCMClient: 2bfbacd45cc11e1ca3c077e97b80401c4e4a54f1
|
||||
MOLXPCConnection: c27af5cb1c43b18319698b0e568a8ddc2fc1e306
|
||||
OCMock: 43565190abc78977ad44a61c0d20d7f0784d35ab
|
||||
OCMock: 4ab4577fc941af31f4a0398f6e7e230cf21fc72a
|
||||
|
||||
PODFILE CHECKSUM: d03767a9915896232523962c98d9ff7294aec2b7
|
||||
|
||||
COCOAPODS: 1.7.5
|
||||
COCOAPODS: 1.10.0
|
||||
|
||||
62
README.md
62
README.md
@@ -1,24 +1,24 @@
|
||||
# Santa [![Build Status][build-status-img]][build-status-link] [![Documentation Status][doc-status-img]][doc-status-link]
|
||||
|
||||
[build-status-img]: https://travis-ci.org/google/santa.png?branch=master
|
||||
[build-status-link]: https://travis-ci.org/google/santa
|
||||
[doc-status-img]: https://readthedocs.org/projects/santa/badge/?version=latest
|
||||
[doc-status-link]: https://santa.readthedocs.io/en/latest/?badge=latest
|
||||
# Santa 
|
||||
|
||||
<p align="center">
|
||||
<img src="./Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
</p>
|
||||
|
||||
Santa is a binary whitelisting/blacklisting system for macOS. It consists of a
|
||||
kernel extension that monitors for executions, a userland daemon that makes
|
||||
execution decisions based on the contents of a SQLite database, a GUI agent
|
||||
that notifies the user in case of a block decision and a command-line utility
|
||||
for managing the system and synchronizing the database with a server.
|
||||
Santa is a binary authorization system for macOS. It consists of a kernel
|
||||
extension (or a system extension on macOS 10.15+) that monitors for executions,
|
||||
a userland daemon that makes execution decisions based on the contents of a
|
||||
SQLite database, a GUI agent that notifies the user in 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.
|
||||
|
||||
Santa is a project of Google's Macintosh Operations Team.
|
||||
|
||||
# We're hiring!
|
||||
|
||||
Want to work on Santa at Google NYC? [Apply on our Careers page!](https://goo.gle/3tO060z)
|
||||
|
||||
# Docs
|
||||
|
||||
The Santa docs are stored in the
|
||||
@@ -38,26 +38,29 @@ If you believe you have a bug, feel free to report [an
|
||||
issue](https://github.com/google/santa/isues) and we'll respond as soon as we
|
||||
can.
|
||||
|
||||
If you believe you've found a vulnerability, please read the
|
||||
[security policy](https://github.com/google/santa/security/policy) for
|
||||
disclosure reporting.
|
||||
|
||||
# Admin-Related Features
|
||||
|
||||
* Multiple modes: In the default MONITOR mode, all binaries except those marked
|
||||
as blacklisted will be allowed to run, whilst being logged and recorded in
|
||||
the events database. In LOCKDOWN mode, only whitelisted binaries are allowed
|
||||
to run.
|
||||
as blocked will be allowed to run, whilst being logged and recorded in
|
||||
the events database. In LOCKDOWN mode, only listed binaries are allowed to
|
||||
run.
|
||||
|
||||
* Event logging: When the kext is loaded, all binary launches are logged. When
|
||||
in either mode, all unknown or denied binaries are stored in the database to
|
||||
enable later aggregation.
|
||||
|
||||
* Certificate-based rules, with override levels: Instead of relying on a
|
||||
binary's hash (or 'fingerprint'), executables can be whitelisted/blacklisted
|
||||
by their signing certificate. You can therefore trust/block all binaries by a
|
||||
binary's hash (or 'fingerprint'), executables can be allowed/blocked by their
|
||||
signing certificate. You can therefore allow/block all binaries by a
|
||||
given publisher that were signed with that cert across version updates. A
|
||||
binary can only be whitelisted by its certificate if its signature validates
|
||||
correctly, but a rule for a binary's fingerprint will override a decision for
|
||||
a certificate; i.e. you can whitelist a certificate while blacklisting a
|
||||
binary signed with that certificate, or vice-versa.
|
||||
binary can only be allowed by its certificate if its signature validates
|
||||
correctly but a rule for a binary's fingerprint will override a decision for
|
||||
a certificate; i.e. you can allowlist a certificate while blocking a binary
|
||||
signed with that certificate, or vice-versa.
|
||||
|
||||
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature
|
||||
to that found in Managed Client (the precursor to configuration profiles,
|
||||
@@ -70,10 +73,10 @@ can.
|
||||
* Failsafe cert rules: You cannot put in a deny rule that would block the
|
||||
certificate used to sign launchd, a.k.a. pid 1, and therefore all components
|
||||
used in macOS. The binaries in every OS update (and in some cases entire new
|
||||
versions) are therefore auto-whitelisted. This does not affect binaries from
|
||||
Apple's App Store, which use various certs that change regularly for common
|
||||
apps. Likewise, you cannot blacklist Santa itself, and Santa uses a distinct
|
||||
separate cert than other Google apps.
|
||||
versions) are therefore automatically allowed. This does not affect binaries
|
||||
from Apple's App Store, which use various certs that change regularly for
|
||||
common apps. Likewise, you cannot block Santa itself, and Santa uses a
|
||||
distinct separate cert than other Google apps.
|
||||
|
||||
# Intentions and Expectations
|
||||
|
||||
@@ -90,7 +93,7 @@ protect hosts in whatever other ways you see fit.
|
||||
|
||||
# Security and Performance-Related Features
|
||||
|
||||
* In-kernel caching: whitelisted binaries are cached in the kernel so the
|
||||
* In-kernel caching: allowed binaries are cached in the kernel so the
|
||||
processing required to make a request is only done if the binary isn't
|
||||
already cached.
|
||||
|
||||
@@ -125,7 +128,7 @@ protect hosts in whatever other ways you see fit.
|
||||
binary. This is because after weighing the administration cost vs the
|
||||
benefit, we found it wasn't worthwhile. Additionally, a number of
|
||||
applications make use of temporary generated scripts, which we can't possibly
|
||||
whitelist and not doing so would cause problems. We're happy to revisit this
|
||||
allowlist and not doing so would cause problems. We're happy to revisit this
|
||||
(or at least make it an option) if it would be useful to others.
|
||||
|
||||
# Sync Servers
|
||||
@@ -141,6 +144,9 @@ protect hosts in whatever other ways you see fit.
|
||||
* [Zentral](https://github.com/zentralopensource/zentral/wiki) - A
|
||||
centralized service that pulls data from multiple sources and deploy
|
||||
configurations to multiple services.
|
||||
* [Zercurity](https://github.com/zercurity/zercurity) - A dockerized service
|
||||
for managing and monitoring applications across a large fleet utilizing
|
||||
Santa + Osquery.
|
||||
|
||||
* Alternatively, `santactl` can configure rules locally (without a sync
|
||||
server).
|
||||
@@ -150,8 +156,8 @@ protect hosts in whatever other ways you see fit.
|
||||
A tool like Santa doesn't really lend itself to screenshots, so here's a video
|
||||
instead.
|
||||
|
||||
<p align="center"> <img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif"
|
||||
alt="Santa Block Video" /> </p>
|
||||
|
||||
<p align="center"> <img src="https://thumbs.gfycat.com/MadFatalAmphiuma-small.gif" alt="Santa Block Video" /> </p>
|
||||
|
||||
# Kext Signing
|
||||
Kernel extensions on macOS 10.9 and later must be signed using an Apple-provided
|
||||
|
||||
12
SECURITY.md
Normal file
12
SECURITY.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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
|
||||
disclosed publicly either when a new version with fixes is released or 90 days has passed,
|
||||
whichever comes first.
|
||||
|
||||
To report vulnerabilities to us privately, please e-mail `santa-team@google.com`.
|
||||
If you want to encrypt your e-mail, you can use our GPG key `0x92AFE41DAB49BBB6`
|
||||
available on pool.sks-keyservers.net:
|
||||
|
||||
`gpg --keyserver pool.sks-keyservers.net --recv-key 0x92AFE41DAB49BBB6`
|
||||
@@ -10,7 +10,7 @@
|
||||
0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SNTPrefixTree.cc */; };
|
||||
59502195B2982225D3706DCE /* libPods-santabundleservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */; };
|
||||
AD3736AF78C41A962C26D429 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C3E743944A9D77423AA1534 /* libPods-Santa.a */; };
|
||||
B5AE6BB811766CA492133559 /* libPods-santad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3700D40B536CA7F626B76156 /* libPods-santad.a */; };
|
||||
B617F8907035AD3DA2CB5C45 /* libPods-syncservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D10E53C5ACA15304D9A7CC95 /* libPods-syncservice.a */; };
|
||||
C71E472F22F0F97B00921CD9 /* com.google.santa.daemon in CopyFiles */ = {isa = PBXBuildFile; fileRef = C779C4E622F0F51400EE2541 /* com.google.santa.daemon */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
C71E473122F0FAA100921CD9 /* com.google.santa.daemon.systemextension in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7A8308022F0F81F00F856AC /* com.google.santa.daemon.systemextension */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C72ED2B62324962400255555 /* SNTEndpointSecurityManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = C72ED2B52324962400255555 /* SNTEndpointSecurityManager.mm */; };
|
||||
@@ -51,7 +51,6 @@
|
||||
C7658B352322C0A000F36578 /* SNTXPCBundleServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACC2322B84F00F36578 /* SNTXPCBundleServiceInterface.m */; };
|
||||
C7658B362322C0A300F36578 /* SNTXPCControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACA2322B84F00F36578 /* SNTXPCControlInterface.m */; };
|
||||
C7658B372322C0B000F36578 /* SNTXPCNotifierInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACF2322B84F00F36578 /* SNTXPCNotifierInterface.m */; };
|
||||
C7658B382322C0B400F36578 /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AC22322B84F00F36578 /* SNTXPCSyncdInterface.m */; };
|
||||
C7658B392322C0B800F36578 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AC52322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
C7658B3A2322C0C200F36578 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD12322B84F00F36578 /* SNTSystemInfo.m */; };
|
||||
C7658B3B2322C0E600F36578 /* SNTBlockMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AC72322B84F00F36578 /* SNTBlockMessage.m */; };
|
||||
@@ -93,15 +92,18 @@
|
||||
C7658B602322C2CB00F36578 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD12322B84F00F36578 /* SNTSystemInfo.m */; };
|
||||
C7658B612322C2DA00F36578 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD82322B84F00F36578 /* SNTLogging.m */; };
|
||||
C7658B622322C2E100F36578 /* SNTXPCControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACA2322B84F00F36578 /* SNTXPCControlInterface.m */; };
|
||||
C7658B632322C2E400F36578 /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AC22322B84F00F36578 /* SNTXPCSyncdInterface.m */; };
|
||||
C7658B642322C2F300F36578 /* SNTXPCUnprivilegedControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AC52322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.m */; };
|
||||
C7658B652322C30F00F36578 /* santactl in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7658B0C2322B95500F36578 /* santactl */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
C7CC459124B618780018C05C /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7CC459024B6184F0018C05C /* SNTXPCSyncdInterface.m */; };
|
||||
C7CC459224B6188B0018C05C /* SNTXPCSyncdInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7CC459024B6184F0018C05C /* SNTXPCSyncdInterface.m */; };
|
||||
C7CDA6FB233E73D20013622B /* SNTXPCBundleServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACC2322B84F00F36578 /* SNTXPCBundleServiceInterface.m */; };
|
||||
C7CDA6FC233E73D80013622B /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD02322B84F00F36578 /* SNTFileInfo.m */; };
|
||||
C7CDA6FD233E73E40013622B /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ADC2322B84F00F36578 /* SNTStoredEvent.m */; };
|
||||
C7CDA6FE233E73ED0013622B /* SNTXPCNotifierInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACF2322B84F00F36578 /* SNTXPCNotifierInterface.m */; };
|
||||
C7CDA6FF233E80160013622B /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD82322B84F00F36578 /* SNTLogging.m */; };
|
||||
C7CDA700233EB21A0013622B /* SNTXPCBundleServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACC2322B84F00F36578 /* SNTXPCBundleServiceInterface.m */; };
|
||||
C7CF38D3262DCD1800B0ABA7 /* SNTCommandSyncFCM.m in Sources */ = {isa = PBXBuildFile; fileRef = C7CF38D1262DCD1800B0ABA7 /* SNTCommandSyncFCM.m */; };
|
||||
C7CF38EB262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = C7CF38E9262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.mm */; };
|
||||
C7D35DE42322C99B000C5EB4 /* SantaDecisionManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B002322B84F00F36578 /* SantaDecisionManager.cc */; };
|
||||
C7D35DE52322C99E000C5EB4 /* SantaDriver.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658AFF2322B84F00F36578 /* SantaDriver.cc */; };
|
||||
C7D35DE62322C9A1000C5EB4 /* SantaDriverClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658AFA2322B84F00F36578 /* SantaDriverClient.cc */; };
|
||||
@@ -109,7 +111,16 @@
|
||||
C7F5C1AE233E72CC00A3F7FD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AF62322B84F00F36578 /* main.m */; };
|
||||
C7F5C1AF233E72CF00A3F7FD /* SNTBundleService.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AF22322B84F00F36578 /* SNTBundleService.m */; };
|
||||
C7F5C1B0233E735E00A3F7FD /* santabundleservice in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7F5C1A7233E72BC00A3F7FD /* santabundleservice */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
C7FA384524B6167800D192F9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FA383324B60F3300D192F9 /* main.m */; };
|
||||
C7FA384624B6167800D192F9 /* SNTSyncService.h in Sources */ = {isa = PBXBuildFile; fileRef = C7FA383424B60F7600D192F9 /* SNTSyncService.h */; };
|
||||
C7FA384724B6167800D192F9 /* SNTSyncService.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FA383524B60F7600D192F9 /* SNTSyncService.m */; };
|
||||
C7FA384824B6169400D192F9 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD82322B84F00F36578 /* SNTLogging.m */; };
|
||||
C7FA384924B6169400D192F9 /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ADC2322B84F00F36578 /* SNTStoredEvent.m */; };
|
||||
C7FA384A24B6169400D192F9 /* SNTXPCSyncServiceInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = C7FA383724B60FF800D192F9 /* SNTXPCSyncServiceInterface.m */; };
|
||||
C7FACBFA25646FE500CCB198 /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658ACD2322B84F00F36578 /* SNTConfigurator.m */; };
|
||||
C7FACC0225646FEB00CCB198 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AD12322B84F00F36578 /* SNTSystemInfo.m */; };
|
||||
D28CA4C618C62392319BB642 /* libPods-santactl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7714ABC7F247685608DACE7 /* libPods-santactl.a */; };
|
||||
D698C8C9E47554577ED4939F /* libPods-com.google.santa.daemon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C05F6AD95EB704B20828BDA1 /* libPods-com.google.santa.daemon.a */; };
|
||||
F5F5D1EF2AF051FEA97A3A59 /* libPods-sysx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 91FF0B4E62F1E90A88478993 /* libPods-sysx.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -181,6 +192,15 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
C7FA383A24B6136C00D192F9 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -189,12 +209,15 @@
|
||||
18183794C94BAEAD167B12EC /* Pods-santad.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.debug.xcconfig"; path = "Target Support Files/Pods-santad/Pods-santad.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
24CDFD218D8B35E34895AA6A /* libPods-santaxpcproxy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santaxpcproxy.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santabundleservice.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
3700D40B536CA7F626B76156 /* libPods-santad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santad.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4A98E2E9727FBEF27CDDA298 /* Pods-syncservice.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.debug.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
4C3E743944A9D77423AA1534 /* libPods-Santa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Santa.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4E28DBA012524ABF55F8300C /* Pods-com.google.santa.daemon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-com.google.santa.daemon.debug.xcconfig"; path = "Target Support Files/Pods-com.google.santa.daemon/Pods-com.google.santa.daemon.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
7AF15DF785BAA0EAB0BE340D /* Pods-santad.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.release.xcconfig"; path = "Target Support Files/Pods-santad/Pods-santad.release.xcconfig"; sourceTree = "<group>"; };
|
||||
7FEBB0D42E35B93E5B995B06 /* Pods-syncservice.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-syncservice.release.xcconfig"; path = "Target Support Files/Pods-syncservice/Pods-syncservice.release.xcconfig"; sourceTree = "<group>"; };
|
||||
91FF0B4E62F1E90A88478993 /* libPods-sysx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-sysx.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
B7714ABC7F247685608DACE7 /* libPods-santactl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santactl.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C05543B3701F50CA798B4B11 /* Pods-sysx.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-sysx.release.xcconfig"; path = "Target Support Files/Pods-sysx/Pods-sysx.release.xcconfig"; sourceTree = "<group>"; };
|
||||
C05F6AD95EB704B20828BDA1 /* libPods-com.google.santa.daemon.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-com.google.santa.daemon.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C72ED2B3232495CC00255555 /* SNTEventProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTEventProvider.h; sourceTree = "<group>"; };
|
||||
C72ED2B42324962400255555 /* SNTEndpointSecurityManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTEndpointSecurityManager.h; sourceTree = "<group>"; };
|
||||
C72ED2B52324962400255555 /* SNTEndpointSecurityManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SNTEndpointSecurityManager.mm; sourceTree = "<group>"; };
|
||||
@@ -290,7 +313,6 @@
|
||||
C7658ABF2322B84F00F36578 /* SNTCommonEnums.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTCommonEnums.h; sourceTree = "<group>"; };
|
||||
C7658AC02322B84F00F36578 /* SNTSystemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTSystemInfo.h; sourceTree = "<group>"; };
|
||||
C7658AC12322B84F00F36578 /* SNTFileInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTFileInfo.h; sourceTree = "<group>"; };
|
||||
C7658AC22322B84F00F36578 /* SNTXPCSyncdInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTXPCSyncdInterface.m; sourceTree = "<group>"; };
|
||||
C7658AC32322B84F00F36578 /* SNTStrengthify.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTStrengthify.h; sourceTree = "<group>"; };
|
||||
C7658AC42322B84F00F36578 /* SNTCachedDecision.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTCachedDecision.m; sourceTree = "<group>"; };
|
||||
C7658AC52322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTXPCUnprivilegedControlInterface.m; sourceTree = "<group>"; };
|
||||
@@ -311,7 +333,6 @@
|
||||
C7658AD42322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCUnprivilegedControlInterface.h; sourceTree = "<group>"; };
|
||||
C7658AD52322B84F00F36578 /* BUILD */ = {isa = PBXFileReference; lastKnownFileType = text; path = BUILD; sourceTree = "<group>"; };
|
||||
C7658AD62322B84F00F36578 /* SNTCachedDecision.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTCachedDecision.h; sourceTree = "<group>"; };
|
||||
C7658AD72322B84F00F36578 /* SNTXPCSyncdInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCSyncdInterface.h; sourceTree = "<group>"; };
|
||||
C7658AD82322B84F00F36578 /* SNTLogging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTLogging.m; sourceTree = "<group>"; };
|
||||
C7658AD92322B84F00F36578 /* SNTFileInfoTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTFileInfoTest.m; sourceTree = "<group>"; };
|
||||
C7658ADA2322B84F00F36578 /* SNTXPCControlInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCControlInterface.h; sourceTree = "<group>"; };
|
||||
@@ -357,18 +378,34 @@
|
||||
C779C2DE22F0E95000EE2541 /* Santa.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Santa.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C779C4E622F0F51400EE2541 /* com.google.santa.daemon */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.google.santa.daemon; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C7A8308022F0F81F00F856AC /* com.google.santa.daemon.systemextension */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = com.google.santa.daemon.systemextension; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C7CC458F24B6184F0018C05C /* SNTXPCSyncdInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCSyncdInterface.h; sourceTree = "<group>"; };
|
||||
C7CC459024B6184F0018C05C /* SNTXPCSyncdInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTXPCSyncdInterface.m; sourceTree = "<group>"; };
|
||||
C7CF38D1262DCD1800B0ABA7 /* SNTCommandSyncFCM.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncFCM.m; sourceTree = "<group>"; };
|
||||
C7CF38D2262DCD1800B0ABA7 /* SNTCommandSyncFCM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncFCM.h; sourceTree = "<group>"; };
|
||||
C7CF38E9262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SNTCachingEndpointSecurityManager.mm; sourceTree = "<group>"; };
|
||||
C7CF38EA262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCachingEndpointSecurityManager.h; sourceTree = "<group>"; };
|
||||
C7D35DDA2322C902000C5EB4 /* santa-driver.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "santa-driver.kext"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C7F5C1A7233E72BC00A3F7FD /* santabundleservice */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = santabundleservice; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C7FA383324B60F3300D192F9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
C7FA383424B60F7600D192F9 /* SNTSyncService.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTSyncService.h; sourceTree = "<group>"; };
|
||||
C7FA383524B60F7600D192F9 /* SNTSyncService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTSyncService.m; sourceTree = "<group>"; };
|
||||
C7FA383624B60FF800D192F9 /* SNTXPCSyncServiceInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTXPCSyncServiceInterface.h; sourceTree = "<group>"; };
|
||||
C7FA383724B60FF800D192F9 /* SNTXPCSyncServiceInterface.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTXPCSyncServiceInterface.m; sourceTree = "<group>"; };
|
||||
C7FA383C24B6136C00D192F9 /* syncservice */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = syncservice; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C7FA384324B6146600D192F9 /* BUILD */ = {isa = PBXFileReference; lastKnownFileType = text; path = BUILD; sourceTree = "<group>"; };
|
||||
C7FA384424B6146600D192F9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C7FBD1242396CACE005A0F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C7FBD1252396CACE005A0F6D /* Santa.app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Santa.app.entitlements; sourceTree = "<group>"; };
|
||||
C7FBD1262396CB17005A0F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C7FBD1272396CB62005A0F6D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C7FBD1282396CB62005A0F6D /* com.google.santa.daemon.systemextension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = com.google.santa.daemon.systemextension.entitlements; sourceTree = "<group>"; };
|
||||
CD71F19EA875F61754E81EBF /* Pods-Santa.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.release.xcconfig"; path = "Target Support Files/Pods-Santa/Pods-Santa.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D10E53C5ACA15304D9A7CC95 /* libPods-syncservice.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-syncservice.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D4808D8635FB5E8E5F4637BB /* Pods-santabundleservice.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santabundleservice.debug.xcconfig"; path = "Target Support Files/Pods-santabundleservice/Pods-santabundleservice.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
D7360306D7CFDD179D003266 /* Pods-sysx.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-sysx.debug.xcconfig"; path = "Target Support Files/Pods-sysx/Pods-sysx.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
D979E8ECE019FB93D1D381E7 /* Pods-santaxpcproxy.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santaxpcproxy.release.xcconfig"; path = "Target Support Files/Pods-santaxpcproxy/Pods-santaxpcproxy.release.xcconfig"; sourceTree = "<group>"; };
|
||||
E6D38874F31422095E853E99 /* Pods-Santa.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.debug.xcconfig"; path = "Target Support Files/Pods-Santa/Pods-Santa.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
E734E4FECEAA502AFF104E71 /* Pods-com.google.santa.daemon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-com.google.santa.daemon.release.xcconfig"; path = "Target Support Files/Pods-com.google.santa.daemon/Pods-com.google.santa.daemon.release.xcconfig"; sourceTree = "<group>"; };
|
||||
EDF64F091E796EC4013F5499 /* Pods-santaxpcproxy.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santaxpcproxy.debug.xcconfig"; path = "Target Support Files/Pods-santaxpcproxy/Pods-santaxpcproxy.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
FE1F7C320CAEA468FAAC05B0 /* Pods-santabundleservice.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santabundleservice.release.xcconfig"; path = "Target Support Files/Pods-santabundleservice/Pods-santabundleservice.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -396,7 +433,7 @@
|
||||
files = (
|
||||
C72ED2BC232584C100255555 /* libbsm.tbd in Frameworks */,
|
||||
C72ED2B82324A2FA00255555 /* libEndpointSecurity.tbd in Frameworks */,
|
||||
B5AE6BB811766CA492133559 /* libPods-santad.a in Frameworks */,
|
||||
D698C8C9E47554577ED4939F /* libPods-com.google.santa.daemon.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -423,12 +460,22 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C7FA383924B6136C00D192F9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B617F8907035AD3DA2CB5C45 /* libPods-syncservice.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0D3715F9233E680700BB624A /* EventProviders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C7CF38EA262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.h */,
|
||||
C7CF38E9262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.mm */,
|
||||
C7658A632322B84F00F36578 /* SNTDriverManager.h */,
|
||||
C7658A742322B84F00F36578 /* SNTDriverManager.m */,
|
||||
C72ED2B42324962400255555 /* SNTEndpointSecurityManager.h */,
|
||||
@@ -447,6 +494,7 @@
|
||||
C7658AF12322B84F00F36578 /* santabundleservice */,
|
||||
C7658A822322B84F00F36578 /* santactl */,
|
||||
C7658A5A2322B84F00F36578 /* santad */,
|
||||
C7FA383224B60EFB00D192F9 /* santasyncservice */,
|
||||
);
|
||||
path = Source;
|
||||
sourceTree = "<group>";
|
||||
@@ -552,6 +600,8 @@
|
||||
C7658AA22322B84F00F36578 /* SNTCommandSyncConstants.m */,
|
||||
C7658A982322B84F00F36578 /* SNTCommandSyncEventUpload.h */,
|
||||
C7658AA12322B84F00F36578 /* SNTCommandSyncEventUpload.m */,
|
||||
C7CF38D2262DCD1800B0ABA7 /* SNTCommandSyncFCM.h */,
|
||||
C7CF38D1262DCD1800B0ABA7 /* SNTCommandSyncFCM.m */,
|
||||
C7658A9E2322B84F00F36578 /* SNTCommandSyncManager.h */,
|
||||
C7658AA92322B84F00F36578 /* SNTCommandSyncManager.m */,
|
||||
C7658A9D2322B84F00F36578 /* SNTCommandSyncPostflight.h */,
|
||||
@@ -618,8 +668,10 @@
|
||||
C7658ACA2322B84F00F36578 /* SNTXPCControlInterface.m */,
|
||||
C7658AB12322B84F00F36578 /* SNTXPCNotifierInterface.h */,
|
||||
C7658ACF2322B84F00F36578 /* SNTXPCNotifierInterface.m */,
|
||||
C7658AD72322B84F00F36578 /* SNTXPCSyncdInterface.h */,
|
||||
C7658AC22322B84F00F36578 /* SNTXPCSyncdInterface.m */,
|
||||
C7CC458F24B6184F0018C05C /* SNTXPCSyncdInterface.h */,
|
||||
C7CC459024B6184F0018C05C /* SNTXPCSyncdInterface.m */,
|
||||
C7FA383624B60FF800D192F9 /* SNTXPCSyncServiceInterface.h */,
|
||||
C7FA383724B60FF800D192F9 /* SNTXPCSyncServiceInterface.m */,
|
||||
C7658AD42322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.h */,
|
||||
C7658AC52322B84F00F36578 /* SNTXPCUnprivilegedControlInterface.m */,
|
||||
C7658AB42322B84F00F36578 /* testdata */,
|
||||
@@ -756,10 +808,23 @@
|
||||
C7658B0C2322B95500F36578 /* santactl */,
|
||||
C7D35DDA2322C902000C5EB4 /* santa-driver.kext */,
|
||||
C7F5C1A7233E72BC00A3F7FD /* santabundleservice */,
|
||||
C7FA383C24B6136C00D192F9 /* syncservice */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C7FA383224B60EFB00D192F9 /* santasyncservice */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C7FA384324B6146600D192F9 /* BUILD */,
|
||||
C7FA384424B6146600D192F9 /* Info.plist */,
|
||||
C7FA383324B60F3300D192F9 /* main.m */,
|
||||
C7FA383424B60F7600D192F9 /* SNTSyncService.h */,
|
||||
C7FA383524B60F7600D192F9 /* SNTSyncService.m */,
|
||||
);
|
||||
path = santasyncservice;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D2DA9449115697B603A40259 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -767,11 +832,12 @@
|
||||
C72ED2B9232584AA00255555 /* libauditd.tbd */,
|
||||
C72ED2B72324A2FA00255555 /* libEndpointSecurity.tbd */,
|
||||
4C3E743944A9D77423AA1534 /* libPods-Santa.a */,
|
||||
3700D40B536CA7F626B76156 /* libPods-santad.a */,
|
||||
91FF0B4E62F1E90A88478993 /* libPods-sysx.a */,
|
||||
24CDFD218D8B35E34895AA6A /* libPods-santaxpcproxy.a */,
|
||||
2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */,
|
||||
B7714ABC7F247685608DACE7 /* libPods-santactl.a */,
|
||||
C05F6AD95EB704B20828BDA1 /* libPods-com.google.santa.daemon.a */,
|
||||
D10E53C5ACA15304D9A7CC95 /* libPods-syncservice.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -791,6 +857,10 @@
|
||||
FE1F7C320CAEA468FAAC05B0 /* Pods-santabundleservice.release.xcconfig */,
|
||||
052CCA75535669B953A31D6D /* Pods-santactl.debug.xcconfig */,
|
||||
0A60226B8B4F01BE817BAAA3 /* Pods-santactl.release.xcconfig */,
|
||||
4E28DBA012524ABF55F8300C /* Pods-com.google.santa.daemon.debug.xcconfig */,
|
||||
E734E4FECEAA502AFF104E71 /* Pods-com.google.santa.daemon.release.xcconfig */,
|
||||
4A98E2E9727FBEF27CDDA298 /* Pods-syncservice.debug.xcconfig */,
|
||||
7FEBB0D42E35B93E5B995B06 /* Pods-syncservice.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
@@ -920,6 +990,24 @@
|
||||
productReference = C7F5C1A7233E72BC00A3F7FD /* santabundleservice */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
C7FA383B24B6136C00D192F9 /* syncservice */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C7FA384024B6136C00D192F9 /* Build configuration list for PBXNativeTarget "syncservice" */;
|
||||
buildPhases = (
|
||||
20E29BAE9E821A33FE505A22 /* [CP] Check Pods Manifest.lock */,
|
||||
C7FA383824B6136C00D192F9 /* Sources */,
|
||||
C7FA383924B6136C00D192F9 /* Frameworks */,
|
||||
C7FA383A24B6136C00D192F9 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = syncservice;
|
||||
productName = syncservice;
|
||||
productReference = C7FA383C24B6136C00D192F9 /* syncservice */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -947,6 +1035,9 @@
|
||||
C7F5C1A6233E72BC00A3F7FD = {
|
||||
CreatedOnToolsVersion = 11.0;
|
||||
};
|
||||
C7FA383B24B6136C00D192F9 = {
|
||||
CreatedOnToolsVersion = 11.2.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = C779C2D922F0E95000EE2541 /* Build configuration list for PBXProject "Santa" */;
|
||||
@@ -968,6 +1059,7 @@
|
||||
C7658B0B2322B95500F36578 /* santactl */,
|
||||
C7D35DD92322C902000C5EB4 /* santa-driver */,
|
||||
C7F5C1A6233E72BC00A3F7FD /* santabundleservice */,
|
||||
C7FA383B24B6136C00D192F9 /* syncservice */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -999,6 +1091,28 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
20E29BAE9E821A33FE505A22 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-syncservice-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
26354ACD77806EB82836E5A3 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1036,7 +1150,7 @@
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-santad-checkManifestLockResult.txt",
|
||||
"$(DERIVED_FILE_DIR)/Pods-com.google.santa.daemon-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@@ -1119,6 +1233,7 @@
|
||||
C7658B522322C1A000F36578 /* SNTCommandSync.m in Sources */,
|
||||
C7658B452322C17700F36578 /* main.m in Sources */,
|
||||
C7658B5B2322C2B200F36578 /* SNTConfigurator.m in Sources */,
|
||||
C7CC459124B618780018C05C /* SNTXPCSyncdInterface.m in Sources */,
|
||||
C7658B5C2322C2B600F36578 /* SNTDropRootPrivs.m in Sources */,
|
||||
C7658B582322C1AE00F36578 /* SNTCommandSyncRuleDownload.m in Sources */,
|
||||
C7658B532322C1A200F36578 /* SNTCommandSyncConstants.m in Sources */,
|
||||
@@ -1126,6 +1241,7 @@
|
||||
C7658B492322C18500F36578 /* SNTCommandCacheHistogram.m in Sources */,
|
||||
C7658B472322C17D00F36578 /* SNTCommandController.m in Sources */,
|
||||
C7658B5D2322C2B800F36578 /* SNTFileInfo.m in Sources */,
|
||||
C7CF38D3262DCD1800B0ABA7 /* SNTCommandSyncFCM.m in Sources */,
|
||||
C7658B542322C1A400F36578 /* SNTCommandSyncEventUpload.m in Sources */,
|
||||
C7658B5F2322C2C700F36578 /* SNTStoredEvent.m in Sources */,
|
||||
C7658B4F2322C19700F36578 /* SNTCommandStatus.m in Sources */,
|
||||
@@ -1143,7 +1259,6 @@
|
||||
C7658B502322C19900F36578 /* SNTCommandVersion.m in Sources */,
|
||||
C7658B482322C18200F36578 /* SNTCommandBundleInfo.m in Sources */,
|
||||
C7658B612322C2DA00F36578 /* SNTLogging.m in Sources */,
|
||||
C7658B632322C2E400F36578 /* SNTXPCSyncdInterface.m in Sources */,
|
||||
C7658B592322C1B000F36578 /* SNTCommandSyncStage.m in Sources */,
|
||||
C7658B602322C2CB00F36578 /* SNTSystemInfo.m in Sources */,
|
||||
C7658B552322C1A700F36578 /* SNTCommandSyncManager.m in Sources */,
|
||||
@@ -1179,8 +1294,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C7CC459224B6188B0018C05C /* SNTXPCSyncdInterface.m in Sources */,
|
||||
C7658B332322C08B00F36578 /* SNTRule.m in Sources */,
|
||||
C7658B382322C0B400F36578 /* SNTXPCSyncdInterface.m in Sources */,
|
||||
C7CF38EB262DD07100B0ABA7 /* SNTCachingEndpointSecurityManager.mm in Sources */,
|
||||
C7658B1D2322BFFA00F36578 /* main.m in Sources */,
|
||||
0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */,
|
||||
C7658B282322C02300F36578 /* SNTDriverManager.m in Sources */,
|
||||
@@ -1239,12 +1355,27 @@
|
||||
C7CDA6FC233E73D80013622B /* SNTFileInfo.m in Sources */,
|
||||
C7CDA6FF233E80160013622B /* SNTLogging.m in Sources */,
|
||||
C7CDA6FD233E73E40013622B /* SNTStoredEvent.m in Sources */,
|
||||
C7FACBFA25646FE500CCB198 /* SNTConfigurator.m in Sources */,
|
||||
C7CDA6FE233E73ED0013622B /* SNTXPCNotifierInterface.m in Sources */,
|
||||
C7FACC0225646FEB00CCB198 /* SNTSystemInfo.m in Sources */,
|
||||
C7F5C1AE233E72CC00A3F7FD /* main.m in Sources */,
|
||||
C7F5C1AF233E72CF00A3F7FD /* SNTBundleService.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C7FA383824B6136C00D192F9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C7FA384824B6169400D192F9 /* SNTLogging.m in Sources */,
|
||||
C7FA384924B6169400D192F9 /* SNTStoredEvent.m in Sources */,
|
||||
C7FA384A24B6169400D192F9 /* SNTXPCSyncServiceInterface.m in Sources */,
|
||||
C7FA384524B6167800D192F9 /* main.m in Sources */,
|
||||
C7FA384624B6167800D192F9 /* SNTSyncService.h in Sources */,
|
||||
C7FA384724B6167800D192F9 /* SNTSyncService.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -1252,7 +1383,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 052CCA75535669B953A31D6D /* Pods-santactl.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1270,7 +1401,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 0A60226B8B4F01BE817BAAA3 /* Pods-santactl.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1338,7 +1469,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
@@ -1401,7 +1531,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santa/Santa.app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1425,7 +1555,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santa/Santa.app.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1445,10 +1575,10 @@
|
||||
};
|
||||
C779C4EB22F0F51400EE2541 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 18183794C94BAEAD167B12EC /* Pods-santad.debug.xcconfig */;
|
||||
baseConfigurationReference = 4E28DBA012524ABF55F8300C /* Pods-com.google.santa.daemon.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santad/com.google.santa.daemon.systemextension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1464,10 +1594,10 @@
|
||||
};
|
||||
C779C4EC22F0F51400EE2541 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AF15DF785BAA0EAB0BE340D /* Pods-santad.release.xcconfig */;
|
||||
baseConfigurationReference = E734E4FECEAA502AFF104E71 /* Pods-com.google.santa.daemon.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santad/com.google.santa.daemon.systemextension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1486,7 +1616,7 @@
|
||||
baseConfigurationReference = D7360306D7CFDD179D003266 /* Pods-sysx.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santad/com.google.santa.daemon.systemextension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1507,7 +1637,7 @@
|
||||
baseConfigurationReference = C05543B3701F50CA798B4B11 /* Pods-sysx.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = Source/santad/com.google.santa.daemon.systemextension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@@ -1526,7 +1656,7 @@
|
||||
C7D35DE02322C902000C5EB4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
@@ -1546,7 +1676,7 @@
|
||||
C7D35DE12322C902000C5EB4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
@@ -1567,7 +1697,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D4808D8635FB5E8E5F4637BB /* Pods-santabundleservice.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
@@ -1582,7 +1712,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = FE1F7C320CAEA468FAAC05B0 /* Pods-santabundleservice.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
@@ -1593,6 +1723,32 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C7FA384124B6136C00D192F9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 4A98E2E9727FBEF27CDDA298 /* Pods-syncservice.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C7FA384224B6136C00D192F9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7FEBB0D42E35B93E5B995B06 /* Pods-syncservice.release.xcconfig */;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = EQHXZ8M8AV;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -1659,6 +1815,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C7FA384024B6136C00D192F9 /* Build configuration list for PBXNativeTarget "syncservice" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C7FA384124B6136C00D192F9 /* Debug */,
|
||||
C7FA384224B6136C00D192F9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = C779C2D622F0E95000EE2541 /* Project object */;
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
|
||||
package(default_visibility = ["//:santa_package_group"])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
cc_library(
|
||||
name = "SantaCache",
|
||||
srcs = ["SantaCache.h"],
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SantaCacheTest",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaCacheTest.mm",
|
||||
],
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTBlockMessage",
|
||||
@@ -10,6 +25,7 @@ objc_library(
|
||||
hdrs = ["SNTBlockMessage.h"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTLogging",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
@@ -21,6 +37,7 @@ objc_library(
|
||||
defines = ["SANTAGUI"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTLogging",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
@@ -35,7 +52,7 @@ objc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
objc_library(
|
||||
name = "SNTCommonEnums",
|
||||
hdrs = ["SNTCommonEnums.h"],
|
||||
)
|
||||
@@ -46,7 +63,6 @@ objc_library(
|
||||
hdrs = ["SNTConfigurator.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTLogging",
|
||||
":SNTStrengthify",
|
||||
":SNTSystemInfo",
|
||||
],
|
||||
@@ -76,12 +92,18 @@ cc_library(
|
||||
cc_library(
|
||||
name = "SNTLoggingKernel",
|
||||
hdrs = ["SNTLogging.h"],
|
||||
copts = [
|
||||
"-mkernel",
|
||||
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
|
||||
],
|
||||
defines = ["KERNEL"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTLogging",
|
||||
srcs = ["SNTLogging.m"],
|
||||
hdrs = ["SNTLogging.h"],
|
||||
deps = [":SNTConfigurator"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
@@ -149,6 +171,7 @@ objc_library(
|
||||
srcs = ["SNTXPCControlInterface.m"],
|
||||
hdrs = ["SNTXPCControlInterface.h"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTStoredEvent",
|
||||
":SNTXPCUnprivilegedControlInterface",
|
||||
"@MOLXPCConnection",
|
||||
@@ -175,6 +198,16 @@ objc_library(
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCSyncServiceInterface",
|
||||
srcs = ["SNTXPCSyncServiceInterface.m"],
|
||||
hdrs = ["SNTXPCSyncServiceInterface.h"],
|
||||
deps = [
|
||||
":SNTStoredEvent",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCUnprivilegedControlInterface",
|
||||
srcs = ["SNTXPCUnprivilegedControlInterface.m"],
|
||||
@@ -196,6 +229,7 @@ santa_unit_test(
|
||||
resources = [
|
||||
"testdata/bad_pagezero",
|
||||
"testdata/missing_pagezero",
|
||||
"testdata/32bitplist",
|
||||
],
|
||||
structured_resources = glob([
|
||||
"testdata/BundleExample.app/**",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@implementation SNTBlockMessage
|
||||
|
||||
@@ -106,6 +107,9 @@
|
||||
+ (NSURL *)eventDetailURLForEvent:(SNTStoredEvent *)event {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
NSString *hostname = [SNTSystemInfo longHostname];
|
||||
NSString *uuid = [SNTSystemInfo hardwareUUID];
|
||||
NSString *serial = [SNTSystemInfo serialNumber];
|
||||
NSString *formatStr = config.eventDetailURL;
|
||||
if (!formatStr.length) return nil;
|
||||
|
||||
@@ -122,6 +126,15 @@
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%machine_id%"
|
||||
withString:config.machineID];
|
||||
}
|
||||
if (hostname.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%hostname%" withString:hostname];
|
||||
}
|
||||
if (uuid.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%uuid%" withString:uuid];
|
||||
}
|
||||
if (serial.length) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%serial%" withString:serial];
|
||||
}
|
||||
|
||||
return [NSURL URLWithString:formatStr];
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTKernelCommon.h"
|
||||
|
||||
@class MOLCertificate;
|
||||
|
||||
///
|
||||
/// Store information about executions from decision making for later logging.
|
||||
///
|
||||
@@ -26,8 +28,11 @@
|
||||
@property SNTEventState decision;
|
||||
@property NSString *decisionExtra;
|
||||
@property NSString *sha256;
|
||||
|
||||
@property NSString *certSHA256;
|
||||
@property NSString *certCommonName;
|
||||
@property NSArray<MOLCertificate *> *certChain;
|
||||
|
||||
@property NSString *quarantineURL;
|
||||
|
||||
@property NSString *customMsg;
|
||||
|
||||
@@ -29,13 +29,13 @@ typedef NS_ENUM(NSInteger, SNTRuleType) {
|
||||
typedef NS_ENUM(NSInteger, SNTRuleState) {
|
||||
SNTRuleStateUnknown,
|
||||
|
||||
SNTRuleStateWhitelist = 1,
|
||||
SNTRuleStateBlacklist = 2,
|
||||
SNTRuleStateSilentBlacklist = 3,
|
||||
SNTRuleStateAllow = 1,
|
||||
SNTRuleStateBlock = 2,
|
||||
SNTRuleStateSilentBlock = 3,
|
||||
SNTRuleStateRemove = 4,
|
||||
|
||||
SNTRuleStateWhitelistCompiler = 5,
|
||||
SNTRuleStateWhitelistTransitive = 6,
|
||||
SNTRuleStateAllowCompiler = 5,
|
||||
SNTRuleStateAllowTransitive = 6,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SNTClientMode) {
|
||||
|
||||
@@ -36,32 +36,32 @@
|
||||
- (void)setSyncServerClientMode:(SNTClientMode)newMode;
|
||||
|
||||
///
|
||||
/// The regex of whitelisted paths. Regexes are specified in ICU format.
|
||||
/// The regex of allowed paths. Regexes are specified in ICU format.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(readonly, nonatomic) NSRegularExpression *whitelistPathRegex;
|
||||
@property(readonly, nonatomic) NSRegularExpression *allowedPathRegex;
|
||||
|
||||
///
|
||||
/// Set the regex of whitelisted paths as received from a sync server.
|
||||
/// Set the regex of allowed paths as received from a sync server.
|
||||
///
|
||||
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re;
|
||||
- (void)setSyncServerAllowedPathRegex:(NSRegularExpression *)re;
|
||||
|
||||
///
|
||||
/// The regex of blacklisted paths. Regexes are specified in ICU format.
|
||||
/// The regex of blocked paths. Regexes are specified in ICU format.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(readonly, nonatomic) NSRegularExpression *blacklistPathRegex;
|
||||
@property(readonly, nonatomic) NSRegularExpression *blockedPathRegex;
|
||||
|
||||
///
|
||||
/// Set the regex of blacklisted paths as received from a sync server.
|
||||
/// Set the regex of blocked paths as received from a sync server.
|
||||
///
|
||||
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re;
|
||||
- (void)setSyncServerBlockedPathRegex:(NSRegularExpression *)re;
|
||||
|
||||
///
|
||||
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
|
||||
@@ -130,6 +130,14 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enablePageZeroProtection;
|
||||
|
||||
///
|
||||
/// Enable bad signature protection, defaults to NO.
|
||||
/// When enabled, a binary that is signed but has a bad signature (cert revoked, binary
|
||||
/// tampered with, etc.) will be blocked regardless of client-mode unless a binary allowlist
|
||||
/// rule exists.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableBadSignatureProtection;
|
||||
|
||||
///
|
||||
/// Defines how event logs are stored. Options are:
|
||||
/// SNTEventLogTypeSyslog: Sent to ASL or ULS (if built with the 10.12 SDK or later).
|
||||
@@ -156,6 +164,24 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableMachineIDDecoration;
|
||||
|
||||
///
|
||||
/// Use the bundled SystemExtension on macOS 10.15+, defaults to YES.
|
||||
/// Disable to continue using the bundled KEXT.
|
||||
/// This is a one way switch, if this is ever true on macOS 10.15+ the KEXT will be deleted.
|
||||
/// This gives admins control over the timing of switching to the SystemExtension. The intended use case is to have an MDM deliver
|
||||
/// the requisite SystemExtension and TCC profiles before attempting to load.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableSystemExtension;
|
||||
|
||||
///
|
||||
/// Use an internal cache for decisions instead of relying on the caching
|
||||
/// mechanism built-in to the EndpointSecurity framework. This may increase
|
||||
/// performance, particularly when Santa is run alongside other system
|
||||
/// extensions.
|
||||
/// Has no effect if the system extension is not being used. Defaults to NO.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableSysxCache;
|
||||
|
||||
#pragma mark - GUI Settings
|
||||
|
||||
///
|
||||
@@ -174,6 +200,9 @@
|
||||
/// %file_sha% -- SHA-256 of the file that was blocked.
|
||||
/// %machine_id% -- ID of the machine.
|
||||
/// %username% -- executing user.
|
||||
/// %serial% -- System's serial number.
|
||||
/// %uuid% -- System's UUID.
|
||||
/// %hostname% -- System's full hostname.
|
||||
///
|
||||
/// @note: This is not an NSURL because the format-string parsing is done elsewhere.
|
||||
///
|
||||
@@ -249,14 +278,14 @@
|
||||
///
|
||||
@property BOOL enableBundles;
|
||||
|
||||
#pragma mark Transitive Whitelisting Settings
|
||||
#pragma mark Transitive Allowlist Settings
|
||||
|
||||
///
|
||||
/// If YES, binaries marked with SNTRuleStateWhitelistCompiler rules are allowed to transitively
|
||||
/// whitelist any executables that they produce. If NO, SNTRuleStateWhitelistCompiler rules are
|
||||
/// interpreted as if they were simply SNTRuleStateWhitelist rules. Defaults to NO.
|
||||
/// If YES, binaries marked with SNTRuleStateAllowCompiler rules are allowed to transitively
|
||||
/// allow any executables that they produce. If NO, SNTRuleStateAllowCompiler rules are
|
||||
/// interpreted as if they were simply SNTRuleStateAllow rules. Defaults to NO.
|
||||
///
|
||||
@property BOOL enableTransitiveWhitelisting;
|
||||
@property BOOL enableTransitiveRules;
|
||||
|
||||
#pragma mark Server Auth Settings
|
||||
|
||||
@@ -295,6 +324,51 @@
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
|
||||
|
||||
///
|
||||
/// If true, forks and exits will be logged. Defaults to false.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableForkAndExitLogging;
|
||||
|
||||
///
|
||||
/// If true, ignore actions from other endpoint security clients. Defaults to false. This only
|
||||
/// applies when running as a sysx.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL ignoreOtherEndpointSecurityClients;
|
||||
|
||||
///
|
||||
/// If true, debug logging will be enabled for all Santa components. Defaults to false.
|
||||
/// Passing --debug as an executable argument will enable debug logging for that specific component.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableDebugLogging;
|
||||
|
||||
///
|
||||
/// If true, compressed requests from "santactl sync" will set "Content-Encoding" to "zlib"
|
||||
/// instead of the new default "deflate". If syncing with Upvote deployed at commit 0b4477d
|
||||
/// or below, set this option to true.
|
||||
/// Defaults to false.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableBackwardsCompatibleContentEncoding;
|
||||
|
||||
///
|
||||
/// Contains the FCM project name.
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *fcmProject;
|
||||
|
||||
///
|
||||
/// Contains the FCM project entity.
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *fcmEntity;
|
||||
|
||||
///
|
||||
/// Contains the FCM project API key.
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *fcmAPIKey;
|
||||
|
||||
///
|
||||
/// True if fcmProject, fcmEntity and fcmAPIKey are all set. Defaults to false.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL fcmEnabled;
|
||||
|
||||
///
|
||||
/// Retrieve an initialized singleton configurator object using the default file path.
|
||||
///
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@@ -24,13 +23,16 @@
|
||||
/// A NSUserDefaults object set to use the com.google.santa suite.
|
||||
@property(readonly, nonatomic) NSUserDefaults *defaults;
|
||||
|
||||
// Keys and expected value types.
|
||||
/// Keys and expected value types.
|
||||
@property(readonly, nonatomic) NSDictionary *syncServerKeyTypes;
|
||||
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
|
||||
|
||||
/// Holds the configurations from a sync server and mobileconfig.
|
||||
@property NSMutableDictionary *syncState;
|
||||
@property NSMutableDictionary *configState;
|
||||
|
||||
/// Was --debug passed as an argument to this process?
|
||||
@property(readonly, nonatomic) BOOL debugFlag;
|
||||
@end
|
||||
|
||||
@implementation SNTConfigurator
|
||||
@@ -66,6 +68,7 @@ static NSString *const kModeNotificationMonitor = @"ModeNotificationMonitor";
|
||||
static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
|
||||
|
||||
static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
|
||||
static NSString *const kEnableBadSignatureProtectionKey = @"EnableBadSignatureProtection";
|
||||
|
||||
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
|
||||
static NSString *const kFileChangesPrefixFiltersKey = @"FileChangesPrefixFilters";
|
||||
@@ -75,11 +78,27 @@ static NSString *const kEventLogPath = @"EventLogPath";
|
||||
|
||||
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
|
||||
|
||||
static NSString *const kEnableSystemExtension = @"EnableSystemExtension";
|
||||
static NSString *const kEnableSysxCache = @"EnableSysxCache";
|
||||
|
||||
static NSString *const kEnableForkAndExitLogging = @"EnableForkAndExitLogging";
|
||||
static NSString *const kIgnoreOtherEndpointSecurityClients = @"IgnoreOtherEndpointSecurityClients";
|
||||
static NSString *const kEnableDebugLogging = @"EnableDebugLogging";
|
||||
|
||||
static NSString *const kEnableBackwardsCompatibleContentEncoding = @"EnableBackwardsCompatibleContentEncoding";
|
||||
|
||||
static NSString *const kFCMProject = @"FCMProject";
|
||||
static NSString *const kFCMEntity = @"FCMEntity";
|
||||
static NSString *const kFCMAPIKey = @"FCMAPIKey";
|
||||
|
||||
// The keys managed by a sync server or mobileconfig.
|
||||
static NSString *const kClientModeKey = @"ClientMode";
|
||||
static NSString *const kEnableTransitiveWhitelistingKey = @"EnableTransitiveWhitelisting";
|
||||
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
|
||||
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
|
||||
static NSString *const kEnableTransitiveRulesKey = @"EnableTransitiveRules";
|
||||
static NSString *const kEnableTransitiveRulesKeyDeprecated = @"EnableTransitiveWhitelisting";
|
||||
static NSString *const kAllowedPathRegexKey = @"AllowedPathRegex";
|
||||
static NSString *const kAllowedPathRegexKeyDeprecated = @"WhitelistRegex";
|
||||
static NSString *const kBlockedPathRegexKey = @"BlockedPathRegex";
|
||||
static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
|
||||
|
||||
// The keys managed by a sync server.
|
||||
static NSString *const kFullSyncLastSuccess = @"FullSyncLastSuccess";
|
||||
@@ -97,21 +116,28 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
Class array = [NSArray class];
|
||||
_syncServerKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
kEnableTransitiveRulesKey : number,
|
||||
kEnableTransitiveRulesKeyDeprecated : number,
|
||||
kAllowedPathRegexKey : re,
|
||||
kAllowedPathRegexKeyDeprecated : re,
|
||||
kBlockedPathRegexKey : re,
|
||||
kBlockedPathRegexKeyDeprecated : re,
|
||||
kFullSyncLastSuccess : date,
|
||||
kRuleSyncLastSuccess : date,
|
||||
kSyncCleanRequired : number
|
||||
};
|
||||
_forcedConfigKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kEnableTransitiveRulesKey : number,
|
||||
kEnableTransitiveRulesKeyDeprecated : number,
|
||||
kFileChangesRegexKey : re,
|
||||
kFileChangesPrefixFiltersKey : array,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
kAllowedPathRegexKey : re,
|
||||
kAllowedPathRegexKeyDeprecated : re,
|
||||
kBlockedPathRegexKey : re,
|
||||
kBlockedPathRegexKeyDeprecated : re,
|
||||
kEnablePageZeroProtectionKey : number,
|
||||
kEnableBadSignatureProtectionKey : number,
|
||||
kMoreInfoURLKey : string,
|
||||
kEventDetailURLKey : string,
|
||||
kEventDetailTextKey : string,
|
||||
@@ -124,7 +150,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kClientAuthCertificatePasswordKey : string,
|
||||
kClientAuthCertificateCNKey : string,
|
||||
kClientAuthCertificateIssuerKey : string,
|
||||
kServerAuthRootsDataKey : data,
|
||||
kServerAuthRootsDataKey : data,
|
||||
kServerAuthRootsFileKey : string,
|
||||
kMachineOwnerKey : string,
|
||||
kMachineIDKey : string,
|
||||
@@ -135,11 +161,21 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kEventLogType : string,
|
||||
kEventLogPath : string,
|
||||
kEnableMachineIDDecoration : number,
|
||||
kEnableSystemExtension : number,
|
||||
kEnableSysxCache : number,
|
||||
kEnableForkAndExitLogging : number,
|
||||
kIgnoreOtherEndpointSecurityClients : number,
|
||||
kEnableDebugLogging : number,
|
||||
kEnableBackwardsCompatibleContentEncoding : number,
|
||||
kFCMProject : string,
|
||||
kFCMEntity : string,
|
||||
kFCMAPIKey : string,
|
||||
};
|
||||
_defaults = [NSUserDefaults standardUserDefaults];
|
||||
[_defaults addSuiteNamed:@"com.google.santa"];
|
||||
_configState = [self readForcedConfig];
|
||||
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
|
||||
_debugFlag = [[NSProcessInfo processInfo].arguments containsObject:@"--debug"];
|
||||
[self startWatchingDefaults];
|
||||
}
|
||||
return self;
|
||||
@@ -189,11 +225,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingWhitelistPathRegex {
|
||||
+ (NSSet *)keyPathsForValuesAffectingAllowlistPathRegex {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingBlacklistPathRegex {
|
||||
+ (NSSet *)keyPathsForValuesAffectingBlocklistPathRegex {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
@@ -297,10 +333,50 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableTransitiveWhitelisting {
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableTransitiveRules {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableSystemExtension {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableSysxCache {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableForkAndExitLogging {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingIgnoreOtherEndpointSecurityClients {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableDebugLogging {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFcmProject {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFcmEntity {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFcmAPIKey {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFcmEnabled {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
@@ -320,37 +396,58 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
- (void)setSyncServerClientMode:(SNTClientMode)newMode {
|
||||
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
|
||||
[self updateSyncStateForKey:kClientModeKey value:@(newMode)];
|
||||
} else {
|
||||
LOGW(@"Ignoring request to change client mode to %ld", newMode);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)enableTransitiveWhitelisting {
|
||||
NSNumber *n = self.syncState[kEnableTransitiveWhitelistingKey];
|
||||
if (n) {
|
||||
return [n boolValue];
|
||||
}
|
||||
return [self.configState[kEnableTransitiveWhitelistingKey] boolValue];
|
||||
- (BOOL)enableTransitiveRules {
|
||||
NSNumber *n = self.syncState[kEnableTransitiveRulesKey];
|
||||
if (n) return [n boolValue];
|
||||
|
||||
n = self.syncState[kEnableTransitiveRulesKeyDeprecated];
|
||||
if (n) return [n boolValue];
|
||||
|
||||
n = self.configState[kEnableTransitiveRulesKeyDeprecated];
|
||||
if (n) return [n boolValue];
|
||||
|
||||
return [self.configState[kEnableTransitiveRulesKey] boolValue];
|
||||
}
|
||||
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kEnableTransitiveWhitelistingKey value:@(enabled)];
|
||||
- (void)setEnableTransitiveRules:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kEnableTransitiveRulesKey value:@(enabled)];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)whitelistPathRegex {
|
||||
return self.syncState[kWhitelistRegexKey] ?: self.configState[kWhitelistRegexKey];
|
||||
- (NSRegularExpression *)allowedPathRegex {
|
||||
NSRegularExpression *r = self.syncState[kAllowedPathRegexKey];
|
||||
if (r) return r;
|
||||
|
||||
r = self.syncState[kAllowedPathRegexKeyDeprecated];
|
||||
if (r) return r;
|
||||
|
||||
r = self.configState[kAllowedPathRegexKey];
|
||||
if (r) return r;
|
||||
|
||||
return self.configState[kAllowedPathRegexKeyDeprecated];
|
||||
}
|
||||
|
||||
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kWhitelistRegexKey value:re];
|
||||
- (void)setSyncServerAllowedPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kAllowedPathRegexKey value:re];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)blacklistPathRegex {
|
||||
return self.syncState[kBlacklistRegexKey] ?: self.configState[kBlacklistRegexKey];
|
||||
- (NSRegularExpression *)blockedPathRegex {
|
||||
NSRegularExpression *r = self.syncState[kBlockedPathRegexKey];
|
||||
if (r) return r;
|
||||
|
||||
r = self.syncState[kBlockedPathRegexKeyDeprecated];
|
||||
if (r) return r;
|
||||
|
||||
r = self.configState[kBlockedPathRegexKey];
|
||||
if (r) return r;
|
||||
|
||||
return self.configState[kBlockedPathRegexKeyDeprecated];
|
||||
}
|
||||
|
||||
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kBlacklistRegexKey value:re];
|
||||
- (void)setSyncServerBlockedPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kBlockedPathRegexKey value:re];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)fileChangesRegex {
|
||||
@@ -361,7 +458,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
NSArray *filters = self.configState[kFileChangesPrefixFiltersKey];
|
||||
for (id filter in filters) {
|
||||
if (![filter isKindOfClass:[NSString class]]) {
|
||||
LOGE(@"Ignoring FileChangesPrefixFilters: array contains a non-string %@", filter);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -372,7 +468,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
NSString *urlString = self.configState[kSyncBaseURLKey];
|
||||
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
if (urlString && !url) LOGW(@"SyncBaseURL is not a valid URL!");
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -381,6 +476,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return number ? [number boolValue] : YES;
|
||||
}
|
||||
|
||||
- (BOOL)enableBadSignatureProtection {
|
||||
NSNumber *number = self.configState[kEnableBadSignatureProtectionKey];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (NSURL *)moreInfoURL {
|
||||
return [NSURL URLWithString:self.configState[kMoreInfoURLKey]];
|
||||
}
|
||||
@@ -501,6 +601,58 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableSystemExtension {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm fileExistsAtPath:@"/Library/Extensions/santa-driver.kext"]) return YES;
|
||||
NSNumber *number = self.configState[kEnableSystemExtension];
|
||||
return number ? [number boolValue] : YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)enableSysxCache {
|
||||
NSNumber *number = self.configState[kEnableSysxCache];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableForkAndExitLogging {
|
||||
NSNumber *number = self.configState[kEnableForkAndExitLogging];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)ignoreOtherEndpointSecurityClients {
|
||||
NSNumber *number = self.configState[kIgnoreOtherEndpointSecurityClients];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableDebugLogging {
|
||||
NSNumber *number = self.configState[kEnableDebugLogging];
|
||||
return [number boolValue] || self.debugFlag;
|
||||
}
|
||||
|
||||
- (BOOL)enableBackwardsCompatibleContentEncoding {
|
||||
NSNumber *number = self.configState[kEnableBackwardsCompatibleContentEncoding];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (NSString *)fcmProject {
|
||||
return self.configState[kFCMProject];
|
||||
}
|
||||
|
||||
- (NSString *)fcmEntity {
|
||||
return self.configState[kFCMEntity];
|
||||
}
|
||||
|
||||
- (NSString *)fcmAPIKey {
|
||||
return self.configState[kFCMAPIKey];
|
||||
}
|
||||
|
||||
- (BOOL)fcmEnabled {
|
||||
return (self.fcmProject.length && self.fcmEntity.length && self.fcmAPIKey.length);
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
///
|
||||
@@ -547,8 +699,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
if (geteuid() != 0) return;
|
||||
// Either remove
|
||||
NSMutableDictionary *syncState = self.syncState.mutableCopy;
|
||||
syncState[kWhitelistRegexKey] = [syncState[kWhitelistRegexKey] pattern];
|
||||
syncState[kBlacklistRegexKey] = [syncState[kBlacklistRegexKey] pattern];
|
||||
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
|
||||
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
|
||||
[syncState writeToFile:kSyncStateFilePath atomically:YES];
|
||||
[[NSFileManager defaultManager] setAttributes:@{ NSFilePosixPermissions : @0644 }
|
||||
ofItemAtPath:kSyncStateFilePath error:NULL];
|
||||
@@ -586,8 +738,9 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
}
|
||||
|
||||
- (void)startWatchingDefaults {
|
||||
// Only santad should listen.
|
||||
if (geteuid() != 0) return;
|
||||
// Only com.google.santa.daemon should listen.
|
||||
NSString *processName = [[NSProcessInfo processInfo] processName];
|
||||
if (![processName isEqualToString:@"com.google.santa.daemon"]) return;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(defaultsChanged:)
|
||||
name:NSUserDefaultsDidChangeNotification
|
||||
|
||||
@@ -143,67 +143,71 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256 {
|
||||
const int MAX_CHUNK_SIZE = 256 * 1024; // 256 KB
|
||||
const size_t chunkSize = _fileSize > MAX_CHUNK_SIZE ? MAX_CHUNK_SIZE : _fileSize;
|
||||
char chunk[chunkSize];
|
||||
char *chunk = malloc(chunkSize);
|
||||
|
||||
CC_SHA1_CTX c1;
|
||||
CC_SHA256_CTX c256;
|
||||
@try {
|
||||
CC_SHA1_CTX c1;
|
||||
CC_SHA256_CTX c256;
|
||||
|
||||
if (sha1) CC_SHA1_Init(&c1);
|
||||
if (sha256) CC_SHA256_Init(&c256);
|
||||
if (sha1) CC_SHA1_Init(&c1);
|
||||
if (sha256) CC_SHA256_Init(&c256);
|
||||
|
||||
int fd = self.fileHandle.fileDescriptor;
|
||||
int fd = self.fileHandle.fileDescriptor;
|
||||
|
||||
fcntl(fd, F_RDAHEAD, 1);
|
||||
struct radvisory radv;
|
||||
radv.ra_offset = 0;
|
||||
const int MAX_ADVISORY_READ = 10 * 1024 * 1024;
|
||||
radv.ra_count = (int)_fileSize < MAX_ADVISORY_READ ? (int)_fileSize : MAX_ADVISORY_READ;
|
||||
fcntl(fd, F_RDADVISE, &radv);
|
||||
ssize_t bytesRead;
|
||||
fcntl(fd, F_RDAHEAD, 1);
|
||||
struct radvisory radv;
|
||||
radv.ra_offset = 0;
|
||||
const int MAX_ADVISORY_READ = 10 * 1024 * 1024;
|
||||
radv.ra_count = (int)_fileSize < MAX_ADVISORY_READ ? (int)_fileSize : MAX_ADVISORY_READ;
|
||||
fcntl(fd, F_RDADVISE, &radv);
|
||||
ssize_t bytesRead;
|
||||
|
||||
for (uint64_t offset = 0; offset < _fileSize;) {
|
||||
bytesRead = pread(fd, chunk, chunkSize, offset);
|
||||
if (bytesRead > 0) {
|
||||
if (sha1) CC_SHA1_Update(&c1, chunk, (CC_LONG)bytesRead);
|
||||
if (sha256) CC_SHA256_Update(&c256, chunk, (CC_LONG)bytesRead);
|
||||
offset += bytesRead;
|
||||
} else if (bytesRead == -1 && errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
for (uint64_t offset = 0; offset < _fileSize;) {
|
||||
bytesRead = pread(fd, chunk, chunkSize, offset);
|
||||
if (bytesRead > 0) {
|
||||
if (sha1) CC_SHA1_Update(&c1, chunk, (CC_LONG)bytesRead);
|
||||
if (sha256) CC_SHA256_Update(&c256, chunk, (CC_LONG)bytesRead);
|
||||
offset += bytesRead;
|
||||
} else if (bytesRead == -1 && errno == EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We turn off Read Ahead that we turned on
|
||||
fcntl(fd, F_RDAHEAD, 0);
|
||||
if (sha1) {
|
||||
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
|
||||
CC_SHA1_Final(digest, &c1);
|
||||
NSString *const SHA1FormatString =
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
|
||||
*sha1 = [[NSString alloc]
|
||||
initWithFormat:SHA1FormatString, digest[0], digest[1], digest[2],
|
||||
digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12],
|
||||
digest[13], digest[14], digest[15], digest[16],
|
||||
digest[17], digest[18], digest[19]];
|
||||
}
|
||||
if (sha256) {
|
||||
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256_Final(digest, &c256);
|
||||
NSString *const SHA256FormatString =
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
|
||||
// We turn off Read Ahead that we turned on
|
||||
fcntl(fd, F_RDAHEAD, 0);
|
||||
if (sha1) {
|
||||
unsigned char digest[CC_SHA1_DIGEST_LENGTH];
|
||||
CC_SHA1_Final(digest, &c1);
|
||||
NSString *const SHA1FormatString =
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
|
||||
*sha1 = [[NSString alloc]
|
||||
initWithFormat:SHA1FormatString, digest[0], digest[1], digest[2],
|
||||
digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12],
|
||||
digest[13], digest[14], digest[15], digest[16],
|
||||
digest[17], digest[18], digest[19]];
|
||||
}
|
||||
if (sha256) {
|
||||
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256_Final(digest, &c256);
|
||||
NSString *const SHA256FormatString =
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
|
||||
|
||||
*sha256 = [[NSString alloc]
|
||||
initWithFormat:SHA256FormatString, digest[0], digest[1], digest[2],
|
||||
digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12],
|
||||
digest[13], digest[14], digest[15], digest[16],
|
||||
digest[17], digest[18], digest[19], digest[20],
|
||||
digest[21], digest[22], digest[23], digest[24],
|
||||
digest[25], digest[26], digest[27], digest[28],
|
||||
digest[29], digest[30], digest[31]];
|
||||
*sha256 = [[NSString alloc]
|
||||
initWithFormat:SHA256FormatString, digest[0], digest[1], digest[2],
|
||||
digest[3], digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11], digest[12],
|
||||
digest[13], digest[14], digest[15], digest[16],
|
||||
digest[17], digest[18], digest[19], digest[20],
|
||||
digest[21], digest[22], digest[23], digest[24],
|
||||
digest[25], digest[26], digest[27], digest[28],
|
||||
digest[29], digest[30], digest[31]];
|
||||
}
|
||||
} @finally {
|
||||
free(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +362,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
while (pathComponents.count > 1) {
|
||||
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
|
||||
if ([bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
|
||||
if (!ancestor ||
|
||||
if ((!ancestor && bndl.bundlePath.pathExtension.length) ||
|
||||
[[self allowedAncestorExtensions] containsObject:bndl.bundlePath.pathExtension]) {
|
||||
bundle = bndl;
|
||||
}
|
||||
@@ -547,24 +551,51 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
for (uint32_t i = 0; i < ncmds; ++i) {
|
||||
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
|
||||
if (!cmdData) return nil;
|
||||
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
|
||||
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
|
||||
if (memcmp(lc->segname, "__TEXT", 6) == 0) {
|
||||
|
||||
if (is64) {
|
||||
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
|
||||
if (lc->cmd == LC_SEGMENT_64 && memcmp(lc->segname, "__TEXT", 6) == 0) {
|
||||
nsects = lc->nsects;
|
||||
offset += sz_segment;
|
||||
break;
|
||||
}
|
||||
offset += lc->cmdsize;
|
||||
} else {
|
||||
struct segment_command *lc = (struct segment_command *)[cmdData bytes];
|
||||
if (lc->cmd == LC_SEGMENT && memcmp(lc->segname, "__TEXT", 6) == 0) {
|
||||
nsects = lc->nsects;
|
||||
offset += sz_segment;
|
||||
break;
|
||||
}
|
||||
offset += lc->cmdsize;
|
||||
}
|
||||
offset += lc->cmdsize;
|
||||
}
|
||||
|
||||
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
|
||||
for (uint32_t i = 0; i < nsects; ++i) {
|
||||
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
|
||||
if (!sectData) return nil;
|
||||
struct section_64 *sect = (struct section_64 *)[sectData bytes];
|
||||
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
|
||||
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
|
||||
uint64_t sectoffset, sectsize = 0;
|
||||
BOOL found = NO;
|
||||
if (is64) {
|
||||
struct section_64 *sect = (struct section_64 *)[sectData bytes];
|
||||
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
|
||||
sectoffset = sect->offset;
|
||||
sectsize = sect->size;
|
||||
found = YES;
|
||||
}
|
||||
} else {
|
||||
struct section *sect = (struct section *)[sectData bytes];
|
||||
if (sect && memcmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
|
||||
sectoffset = sect->offset;
|
||||
sectsize = sect->size;
|
||||
found = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(mhwo.offset + sectoffset,
|
||||
sectsize)];
|
||||
if (!plistData) return nil;
|
||||
NSDictionary *plist;
|
||||
plist = [NSPropertyListSerialization propertyListWithData:plistData
|
||||
|
||||
@@ -39,9 +39,8 @@
|
||||
sut = [[SNTFileInfo alloc] initWithPath:@"../../../../../../../../../../../../../../../bin/ls"];
|
||||
XCTAssertEqualObjects(sut.path, @"/bin/ls");
|
||||
|
||||
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/AppleFileServer"];
|
||||
XCTAssertEqualObjects(sut.path, @"/System/Library/CoreServices/AppleFileServer.app/"
|
||||
@"Contents/MacOS/AppleFileServer");
|
||||
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/DirectoryService"];
|
||||
XCTAssertEqualObjects(sut.path, @"/usr/libexec/dspluginhelperd");
|
||||
}
|
||||
|
||||
- (void)testSHA1 {
|
||||
@@ -72,7 +71,6 @@
|
||||
XCTAssertTrue(sut.isExecutable);
|
||||
|
||||
XCTAssertFalse(sut.isDylib);
|
||||
XCTAssertFalse(sut.isFat);
|
||||
XCTAssertFalse(sut.isKext);
|
||||
XCTAssertFalse(sut.isScript);
|
||||
}
|
||||
@@ -106,7 +104,7 @@
|
||||
}
|
||||
|
||||
- (void)testDylibs {
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/libsqlite3.dylib"];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/lib/system/libsystem_platform.dylib"];
|
||||
|
||||
XCTAssertTrue(sut.isMachO);
|
||||
XCTAssertTrue(sut.isDylib);
|
||||
@@ -231,10 +229,16 @@
|
||||
}
|
||||
|
||||
- (void)testEmbeddedInfoPlist {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"32bitplist"
|
||||
ofType:@""];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
XCTAssertNotNil([sut infoPlist]);
|
||||
XCTAssertEqualObjects([sut infoPlist][@"CFBundleShortVersionString"], @"1.0");
|
||||
XCTAssertEqualObjects([sut infoPlist][@"CFBundleIdentifier"], @"com.google.i386plist");
|
||||
|
||||
// csreq is installed on all machines with Xcode installed. If you're running these tests,
|
||||
// it should be available..
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
|
||||
|
||||
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/csreq"];
|
||||
XCTAssertNotNil([sut infoPlist]);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
/// Common defines between kernel <-> userspace
|
||||
///
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifndef SANTA__COMMON__KERNELCOMMON_H
|
||||
#define SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
// Defines the name of the userclient class and the driver bundle ID.
|
||||
#define USERCLIENT_CLASS "com_google_SantaDriver"
|
||||
#define USERCLIENT_ID "com.google.santa-driver"
|
||||
@@ -81,6 +82,8 @@ typedef enum {
|
||||
ACTION_NOTIFY_EXCHANGE = 34,
|
||||
ACTION_NOTIFY_DELETE = 35,
|
||||
ACTION_NOTIFY_WHITELIST = 36,
|
||||
ACTION_NOTIFY_FORK = 37,
|
||||
ACTION_NOTIFY_EXIT = 38,
|
||||
|
||||
// ERROR
|
||||
ACTION_ERROR = 99,
|
||||
@@ -116,6 +119,7 @@ typedef struct {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
int pidversion;
|
||||
pid_t ppid;
|
||||
char path[MAXPATHLEN];
|
||||
char newpath[MAXPATHLEN];
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
|
||||
#import <asl.h>
|
||||
#import <pthread.h>
|
||||
|
||||
@@ -39,20 +41,13 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
dispatch_once(&pred, ^{
|
||||
binaryName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([binaryName isEqualToString:@"com.google.santa.daemon"]) {
|
||||
useSyslog = YES;
|
||||
pthread_key_create(&syslogKey, syslogClientDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
// If debug logging is enabled, the process must be restarted.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
|
||||
if ([SNTConfigurator configurator].enableDebugLogging) {
|
||||
logLevel = LOG_LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
// If requested, redirect output to syslog.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"]) {
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
|
||||
[binaryName isEqualToString:@"com.google.santa.daemon"]) {
|
||||
useSyslog = YES;
|
||||
pthread_key_create(&syslogKey, syslogClientDestructor);
|
||||
}
|
||||
@@ -93,7 +88,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
break;
|
||||
case LOG_LEVEL_DEBUG:
|
||||
levelName = "D";
|
||||
syslogLevel = ASL_LEVEL_DEBUG;
|
||||
// Log debug messages at the same ASL level as INFO.
|
||||
// While it would make sense to use DEBUG, watching debug-level logs
|
||||
// in Console means enabling all debug logs, which is absurdly noisy.
|
||||
syslogLevel = ASL_LEVEL_NOTICE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,9 +92,6 @@ class SNTPrefixTree {
|
||||
lck_rw_t *spt_lock_;
|
||||
lck_mtx_t *spt_add_lock_;
|
||||
#else // KERNEL
|
||||
void *spt_lock_grp_;
|
||||
void *spt_lock_grp_attr_;
|
||||
void *spt_lock_attr_;
|
||||
pthread_rwlock_t spt_lock_;
|
||||
std::mutex *spt_add_lock_;
|
||||
#endif // KERNEL
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
customMsg:customMsg
|
||||
timestamp:0];
|
||||
// Initialize timestamp to current time if rule is transitive.
|
||||
if (self && state == SNTRuleStateWhitelistTransitive) {
|
||||
if (self && state == SNTRuleStateAllowTransitive) {
|
||||
[self resetTimestamp];
|
||||
}
|
||||
return self;
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setAllowedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBlockedPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#import <MOLCodesignChecker/MOLCodesignChecker.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@@ -25,7 +27,7 @@ NSString *const kBundleID = @"com.google.santa.daemon";
|
||||
@implementation SNTXPCControlInterface
|
||||
|
||||
+ (NSString *)serviceID {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([[SNTConfigurator configurator] enableSystemExtension]) {
|
||||
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
|
||||
// "teamid.com.google.santa.daemon.xpc"
|
||||
NSString *t = cs.signingInformation[@"teamid"];
|
||||
|
||||
56
Source/common/SNTXPCSyncServiceInterface.h
Normal file
56
Source/common/SNTXPCSyncServiceInterface.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/// Copyright 2020 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
/// A block that reports the number of rules processed.
|
||||
/// TODO(bur): Add more details about the sync.
|
||||
typedef void (^SNTFullSyncReplyBlock)(NSNumber *rulesProcessed);
|
||||
|
||||
/// Protocol implemented by syncservice and utilized by daemon and ctl for communication with a sync server.
|
||||
@protocol SNTSyncServiceXPC
|
||||
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events fromBundle:(BOOL)fromBundle;
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
|
||||
reply:(void (^)(SNTBundleEventAction))reply;
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply;
|
||||
- (void)performFullSyncWithReply:(SNTFullSyncReplyBlock)reply;
|
||||
@end
|
||||
|
||||
@interface SNTXPCSyncServiceInterface : NSObject
|
||||
|
||||
///
|
||||
/// Returns an initialized NSXPCInterface for the SNTSyncServiceXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning.
|
||||
///
|
||||
+ (NSXPCInterface *)syncServiceInterface;
|
||||
|
||||
///
|
||||
/// Returns the MachService ID for this service.
|
||||
///
|
||||
+ (NSString *)serviceID;
|
||||
|
||||
///
|
||||
/// Retrieve a pre-configured MOLXPCConnection for communicating with syncservice.
|
||||
/// Connections just needs any handlers set and then can be resumed and used.
|
||||
///
|
||||
+ (MOLXPCConnection *)configuredConnection;
|
||||
|
||||
@end
|
||||
|
||||
43
Source/common/SNTXPCSyncServiceInterface.m
Normal file
43
Source/common/SNTXPCSyncServiceInterface.m
Normal file
@@ -0,0 +1,43 @@
|
||||
/// Copyright 2020 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/common/SNTXPCSyncServiceInterface.h"
|
||||
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCSyncServiceInterface
|
||||
|
||||
+ (NSXPCInterface *)syncServiceInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncServiceXPC)];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(postEventsToSyncServer:fromBundle:)
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
+ (NSString *)serviceID {
|
||||
return @"com.google.santa.syncservice";
|
||||
}
|
||||
|
||||
+ (MOLXPCConnection *)configuredConnection {
|
||||
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
|
||||
privileged:YES];
|
||||
c.remoteInterface = [self syncServiceInterface];
|
||||
return c;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -71,7 +71,7 @@
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)enableBundles:(void (^)(BOOL))reply;
|
||||
- (void)enableTransitiveWhitelisting:(void (^)(BOOL))reply;
|
||||
- (void)enableTransitiveRules:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
// TODO(rah): Consider templatizing these.
|
||||
#define panic(args...) printf(args); printf("\n"); abort()
|
||||
#define IOMallocAligned(sz, alignment) malloc(sz);
|
||||
#define IOFreeAligned(addr, sz) free(addr)
|
||||
@@ -37,6 +36,8 @@
|
||||
#define OSTestAndClear(bit, addr) OSAtomicTestAndClear(bit, addr) == 0
|
||||
#define OSIncrementAtomic(addr) OSAtomicIncrement64((volatile int64_t *)addr)
|
||||
#define OSDecrementAtomic(addr) OSAtomicDecrement64((volatile int64_t *)addr)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif // KERNEL
|
||||
|
||||
/**
|
||||
@@ -78,6 +79,7 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
if (unlikely(per_bucket > 64)) per_bucket = 64;
|
||||
max_size_ = maximum_size;
|
||||
bucket_count_ = (1 << (32 - __builtin_clz((((uint32_t)max_size_ / per_bucket) - 1) ?: 1)));
|
||||
if (unlikely(bucket_count_ > UINT32_MAX)) bucket_count_ = UINT32_MAX;
|
||||
buckets_ = (struct bucket *)IOMallocAligned(bucket_count_ * sizeof(struct bucket), 2);
|
||||
bzero(buckets_, bucket_count_ * sizeof(struct bucket));
|
||||
}
|
||||
@@ -197,6 +199,11 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
if (per_bucket_counts == nullptr || array_size == nullptr || start_bucket == nullptr) return;
|
||||
|
||||
uint64_t start = *start_bucket;
|
||||
if (start >= bucket_count_) {
|
||||
*start_bucket = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t size = *array_size;
|
||||
if (start + size > bucket_count_) size = bucket_count_ - start;
|
||||
|
||||
@@ -357,4 +364,8 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef KERNEL
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTACACHE_H
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Source/santa_driver/SantaCache.h"
|
||||
#include "Source/common/SantaCache.h"
|
||||
|
||||
@interface SantaCacheTest : XCTestCase
|
||||
@end
|
||||
@@ -251,7 +251,7 @@ template<> uint64_t SantaCacheHasher<S>(S const& s) {
|
||||
}
|
||||
|
||||
- (void)testStructKeys {
|
||||
auto sut = SantaCache<S, uint64_t>(10, 2);
|
||||
auto sut = SantaCache<S, uint64_t>(10, 2);
|
||||
|
||||
S s1 = {1024, 2048};
|
||||
S s2 = {4096, 8192};
|
||||
@@ -265,4 +265,22 @@ template<> uint64_t SantaCacheHasher<S>(S const& s) {
|
||||
XCTAssertEqual(sut.get(s3), 30);
|
||||
}
|
||||
|
||||
- (void)testBucketCounts {
|
||||
auto sut = new SantaCache<uint64_t, uint64_t>(UINT16_MAX, 1);
|
||||
|
||||
// These tests verify that the bucket_counts() function can't be abused
|
||||
// with integer {over,under}flow issues in the input or going out-of-bounds
|
||||
// on the buckets array.
|
||||
uint16_t size = 2048;
|
||||
uint64_t start = (UINT64_MAX - 2047);
|
||||
uint16_t per_bucket_counts[2048];
|
||||
sut->bucket_counts(per_bucket_counts, &size, &start);
|
||||
XCTAssertEqual(start, 0, @"Check a high start can't overflow");
|
||||
|
||||
size = UINT16_MAX;
|
||||
start = UINT16_MAX - 1;
|
||||
sut->bucket_counts(per_bucket_counts, &size, &start);
|
||||
XCTAssertEqual(start, 0, @"Check a large size can't overflow");
|
||||
}
|
||||
|
||||
@end
|
||||
BIN
Source/common/testdata/32bitplist
vendored
Executable file
BIN
Source/common/testdata/32bitplist
vendored
Executable file
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
exports_files([
|
||||
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
|
||||
])
|
||||
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
|
||||
objc_library(
|
||||
name = "SantaGUI_lib",
|
||||
srcs = [
|
||||
@@ -35,6 +35,7 @@ objc_library(
|
||||
deps = [
|
||||
"//Source/common:SNTBlockMessage_SantaGUI",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"@MOLCodesignChecker",
|
||||
@@ -54,7 +55,11 @@ macos_application(
|
||||
bundle_name = "Santa",
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//conditions:default": "Santa_Dev.provisionprofile",
|
||||
}),
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":SantaGUI_lib"],
|
||||
)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="title">Santa is an application whitelisting system for macOS.
|
||||
<string key="title">Santa is an application control system for macOS.
|
||||
|
||||
There are no user-configurable settings.</string>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import <SystemExtensions/SystemExtensions.h>
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
@@ -25,7 +23,7 @@
|
||||
#import "Source/santa/SNTAboutWindowController.h"
|
||||
#import "Source/santa/SNTNotificationManager.h"
|
||||
|
||||
@interface SNTAppDelegate ()<OSSystemExtensionRequestDelegate>
|
||||
@interface SNTAppDelegate ()
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@property SNTNotificationManager *notificationManager;
|
||||
@property MOLXPCConnection *daemonListener;
|
||||
@@ -36,16 +34,6 @@
|
||||
#pragma mark App Delegate methods
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
LOGI(@"Requesting SystemExtension activation");
|
||||
NSString *e = [SNTXPCControlInterface systemExtensionID];
|
||||
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
||||
OSSystemExtensionRequest *req = [OSSystemExtensionRequest activationRequestForExtension:e
|
||||
queue:q];
|
||||
req.delegate = self;
|
||||
[[OSSystemExtensionManager sharedManager] submitRequest:req];
|
||||
}
|
||||
|
||||
[self setupMenu];
|
||||
self.notificationManager = [[SNTNotificationManager alloc] init];
|
||||
|
||||
@@ -135,33 +123,4 @@
|
||||
[NSApp setMainMenu:mainMenu];
|
||||
}
|
||||
|
||||
#pragma mark OSSystemExtensionRequestDelegate
|
||||
|
||||
- (OSSystemExtensionReplacementAction)request:(OSSystemExtensionRequest *)request
|
||||
actionForReplacingExtension:(OSSystemExtensionProperties *)old
|
||||
withExtension:(OSSystemExtensionProperties *)new
|
||||
API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request for replacement", request.identifier);
|
||||
#ifdef DEBUG
|
||||
return OSSystemExtensionReplacementActionReplace;
|
||||
#else
|
||||
return [old.bundleVersion isEqualToString:new.bundleVersion]
|
||||
? OSSystemExtensionReplacementActionCancel : OSSystemExtensionReplacementActionReplace;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)requestNeedsUserApproval:(OSSystemExtensionRequest *)request API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request needs user approval", request.identifier);
|
||||
}
|
||||
|
||||
- (void)request:(OSSystemExtensionRequest *)request
|
||||
didFailWithError:(NSError *)error API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request did fail: %@", request.identifier, error);
|
||||
}
|
||||
|
||||
- (void)request:(OSSystemExtensionRequest *)request
|
||||
didFinishWithResult:(OSSystemExtensionRequestResult)result API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request did finish: %ld", request.identifier, (long)result);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <SystemExtensions/SystemExtensions.h>
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santa/SNTAppDelegate.h"
|
||||
|
||||
@@ -66,6 +67,10 @@ int main(int argc, const char *argv[]) {
|
||||
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
||||
OSSystemExtensionRequest *req;
|
||||
if (sysxOperation.intValue == 1) {
|
||||
if (![[SNTConfigurator configurator] enableSystemExtension]) {
|
||||
NSLog(@"EnableSystemExtension is disabled");
|
||||
exit(1);
|
||||
}
|
||||
NSLog(@"Requesting SystemExtension activation");
|
||||
req = [OSSystemExtensionRequest activationRequestForExtension:e queue:q];
|
||||
} else if (sysxOperation.intValue == 2) {
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load(
|
||||
"@build_bazel_rules_apple//apple:macos.bzl",
|
||||
"macos_command_line_application",
|
||||
"macos_kernel_extension",
|
||||
)
|
||||
load("//:helper.bzl", "run_command", "santa_unit_test")
|
||||
load("//:helper.bzl", "run_command")
|
||||
load("//:version.bzl", "SANTA_VERSION")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "santa_driver_lib",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaDecisionManager.cc",
|
||||
"SantaDecisionManager.h",
|
||||
"SantaDriver.cc",
|
||||
@@ -23,6 +22,7 @@ cc_library(
|
||||
copts = [
|
||||
"-mkernel",
|
||||
"-fapple-kext",
|
||||
"-Wno-ossharedptr-misuse",
|
||||
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
|
||||
],
|
||||
defines = [
|
||||
@@ -32,6 +32,7 @@ cc_library(
|
||||
"SANTA_VERSION=" + SANTA_VERSION,
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SNTKernelCommon",
|
||||
"//Source/common:SNTLoggingKernel",
|
||||
"//Source/common:SNTPrefixTreeKernel",
|
||||
@@ -39,15 +40,6 @@ cc_library(
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SantaCacheTest",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaCacheTest.mm",
|
||||
],
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
macos_kernel_extension(
|
||||
name = "santa_driver",
|
||||
bundle_id = "com.google.santa-driver",
|
||||
@@ -55,7 +47,7 @@ macos_kernel_extension(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":santa_driver_lib"],
|
||||
)
|
||||
|
||||
|
||||
@@ -185,23 +185,35 @@ void SantaDecisionManager::SetLogPort(mach_port_t port) {
|
||||
}
|
||||
|
||||
IOMemoryDescriptor *SantaDecisionManager::GetDecisionMemoryDescriptor() const {
|
||||
return decision_dataqueue_->getMemoryDescriptor();
|
||||
lck_mtx_lock(decision_dataqueue_lock_);
|
||||
IOMemoryDescriptor *r = decision_dataqueue_->getMemoryDescriptor();
|
||||
lck_mtx_unlock(decision_dataqueue_lock_);
|
||||
return r;
|
||||
}
|
||||
|
||||
IOMemoryDescriptor *SantaDecisionManager::GetLogMemoryDescriptor() const {
|
||||
return log_dataqueue_->getMemoryDescriptor();
|
||||
lck_mtx_lock(log_dataqueue_lock_);
|
||||
IOMemoryDescriptor *r = log_dataqueue_->getMemoryDescriptor();
|
||||
lck_mtx_unlock(log_dataqueue_lock_);
|
||||
return r;
|
||||
}
|
||||
|
||||
#pragma mark Listener Control
|
||||
|
||||
kern_return_t SantaDecisionManager::StartListener() {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
vnode_listener_ = kauth_listen_scope(
|
||||
KAUTH_SCOPE_VNODE, vnode_scope_callback, reinterpret_cast<void *>(this));
|
||||
#pragma clang diagnostic pop
|
||||
if (!vnode_listener_) return kIOReturnInternalError;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
fileop_listener_ = kauth_listen_scope(
|
||||
KAUTH_SCOPE_FILEOP, fileop_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
#pragma clang diagnostic pop
|
||||
if (!fileop_listener_) return kIOReturnInternalError;
|
||||
|
||||
LOGD("Listeners started.");
|
||||
@@ -210,11 +222,14 @@ kern_return_t SantaDecisionManager::StartListener() {
|
||||
}
|
||||
|
||||
kern_return_t SantaDecisionManager::StopListener() {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
kauth_unlisten_scope(vnode_listener_);
|
||||
vnode_listener_ = nullptr;
|
||||
|
||||
kauth_unlisten_scope(fileop_listener_);
|
||||
fileop_listener_ = nullptr;
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// Wait for any active invocations to finish before returning
|
||||
do {
|
||||
@@ -601,7 +616,7 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
pid_t pid = proc_pid(proc);
|
||||
pid_t ppid = proc_ppid(proc);
|
||||
// pid_t is 32-bit; pid is in upper 32 bits, ppid in lower.
|
||||
uint64_t val = ((uint64_t)pid << 32) | (ppid & 0xFFFFFFFF);
|
||||
uint64_t val = ((uint64_t)pid << 32) | ((uint64_t)ppid & 0xFFFFFFFF);
|
||||
vnode_pid_map_->set(vnode_id, val);
|
||||
if (returnedAction == ACTION_RESPOND_ALLOW_COMPILER && ppid != 0) {
|
||||
// Do some additional bookkeeping for compilers:
|
||||
@@ -659,8 +674,8 @@ void SantaDecisionManager::FileOpCallback(
|
||||
uint64_t val = vnode_pid_map_->get(vnode_id);
|
||||
if (val) {
|
||||
// pid_t is 32-bit, so pid is in upper 32 bits, ppid in lower.
|
||||
message->pid = (val >> 32);
|
||||
message->ppid = (val & ~0xFFFFFFFF00000000);
|
||||
message->pid = (pid_t)(val >> 32);
|
||||
message->ppid = (pid_t)(val & ~0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
PostToLogQueue(message);
|
||||
@@ -801,7 +816,7 @@ extern "C" int vnode_scope_callback(
|
||||
// We only care about regular files.
|
||||
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
|
||||
|
||||
if ((action & KAUTH_VNODE_EXECUTE) && !(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
if ((action & (int)KAUTH_VNODE_EXECUTE) && !(action & (int)KAUTH_VNODE_ACCESS)) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
int result = sdm->VnodeCallback(credential,
|
||||
reinterpret_cast<vfs_context_t>(arg0),
|
||||
@@ -809,9 +824,9 @@ extern "C" int vnode_scope_callback(
|
||||
reinterpret_cast<int *>(arg3));
|
||||
sdm->DecrementListenerInvocations();
|
||||
return result;
|
||||
} else if (action & KAUTH_VNODE_WRITE_DATA || action & KAUTH_VNODE_APPEND_DATA) {
|
||||
} else if (action & (int)KAUTH_VNODE_WRITE_DATA || action & (int)KAUTH_VNODE_APPEND_DATA) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
if (!(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
if (!(action & (int)KAUTH_VNODE_ACCESS)) {
|
||||
auto vnode_id = sdm->GetVnodeIDForVnode(reinterpret_cast<vfs_context_t>(arg0), vp);
|
||||
sdm->RemoveFromCache(vnode_id);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include "Source/common/SantaCache.h"
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#include "Source/common/SNTPrefixTree.h"
|
||||
#include "Source/santa_driver/SantaCache.h"
|
||||
|
||||
///
|
||||
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
|
||||
|
||||
@@ -293,7 +293,7 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
{ &SantaDriverClient::filemod_prefix_filter_reset, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
if (selector > static_cast<UInt32>(kSantaUserClientNMethods)) {
|
||||
if (selector >= static_cast<UInt32>(kSantaUserClientNMethods)) {
|
||||
return kIOReturnBadArgument;
|
||||
}
|
||||
|
||||
|
||||
@@ -360,7 +360,7 @@
|
||||
}
|
||||
|
||||
// Now replace the contents of the test file (which is cached) with the contents of /bin/ed,
|
||||
// which is 'blacklisted' by SHA-256 during the tests.
|
||||
// which is blocked by SHA-256 during the tests.
|
||||
FILE *infile = fopen("/bin/ed", "r");
|
||||
FILE *outfile = fopen(target.UTF8String, "w");
|
||||
int ch;
|
||||
|
||||
@@ -26,6 +26,6 @@ macos_command_line_application(
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":santabs_lib"],
|
||||
)
|
||||
|
||||
@@ -46,6 +46,7 @@ objc_library(
|
||||
sdk_dylibs = ["libz"],
|
||||
sdk_frameworks = ["IOKit"],
|
||||
deps = [
|
||||
":FCM_lib",
|
||||
"//Source/common:SNTCachedDecision",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
"//Source/common:SNTConfigurator",
|
||||
@@ -64,18 +65,27 @@ objc_library(
|
||||
"@FMDB",
|
||||
"@MOLAuthenticatingURLSession",
|
||||
"@MOLCodesignChecker",
|
||||
"@MOLFCMClient",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "FCM_lib",
|
||||
srcs = ["Commands/sync/SNTCommandSyncFCM.m"],
|
||||
hdrs = ["Commands/sync/SNTCommandSyncFCM.h"],
|
||||
sdk_frameworks = ["SystemConfiguration"],
|
||||
deps = [
|
||||
"@MOLAuthenticatingURLSession",
|
||||
],
|
||||
)
|
||||
|
||||
macos_command_line_application(
|
||||
name = "santactl",
|
||||
bundle_id = "com.google.santa.ctl",
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":santactl_lib"],
|
||||
)
|
||||
|
||||
@@ -130,6 +140,7 @@ santa_unit_test(
|
||||
],
|
||||
sdk_dylibs = ["libz"],
|
||||
deps = [
|
||||
":FCM_lib",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTDropRootPrivs",
|
||||
@@ -140,7 +151,6 @@ santa_unit_test(
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCSyncdInterface",
|
||||
"@MOLAuthenticatingURLSession",
|
||||
"@MOLFCMClient",
|
||||
"@MOLXPCConnection",
|
||||
"@OCMock",
|
||||
],
|
||||
|
||||
@@ -54,16 +54,16 @@ REGISTER_COMMAND_NAME(@"checkcache")
|
||||
[[self.daemonConn remoteObjectProxy] checkCacheForVnodeID:vnodeID
|
||||
withReply:^(santa_action_t action) {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
LOGI(@"File exists in [whitelist] kernel cache");
|
||||
LOGI(@"File exists in [allowlist] kernel cache");
|
||||
exit(0);
|
||||
} else if (action == ACTION_RESPOND_DENY) {
|
||||
LOGI(@"File exists in [blacklist] kernel cache");
|
||||
LOGI(@"File exists in [blocklist] kernel cache");
|
||||
exit(0);
|
||||
} else if (action == ACTION_RESPOND_ALLOW_COMPILER) {
|
||||
LOGI(@"File exists in [whitelist compiler] kernel cache");
|
||||
LOGI(@"File exists in [allowlist compiler] kernel cache");
|
||||
exit(0);
|
||||
} else if (action == ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE) {
|
||||
LOGI(@"File exists in [whitelist pending_transitive] kernel cache");
|
||||
LOGI(@"File exists in [allowlist pending_transitive] kernel cache");
|
||||
exit(0);
|
||||
} else if (action == ACTION_UNSET) {
|
||||
LOGE(@"File does not exist in cache");
|
||||
|
||||
@@ -366,7 +366,7 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
return kCommunicationErrorMsg;
|
||||
} else {
|
||||
NSMutableString *output =
|
||||
(SNTEventStateAllow & state) ? @"Whitelisted".mutableCopy : @"Blacklisted".mutableCopy;
|
||||
(SNTEventStateAllow & state) ? @"Allowed".mutableCopy : @"Blocked".mutableCopy;
|
||||
switch (state) {
|
||||
case SNTEventStateAllowUnknown:
|
||||
case SNTEventStateBlockUnknown:
|
||||
|
||||
@@ -49,10 +49,10 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
+ (NSString *)longHelpText {
|
||||
return (@"Usage: santactl rule [options]\n"
|
||||
@" One of:\n"
|
||||
@" --whitelist: add to whitelist\n"
|
||||
@" --blacklist: add to blacklist\n"
|
||||
@" --silent-blacklist: add to silent blacklist\n"
|
||||
@" --compiler: whitelist and mark as a compiler\n"
|
||||
@" --allow: add to allow\n"
|
||||
@" --block: add to block\n"
|
||||
@" --silent-block: add to silent block\n"
|
||||
@" --compiler: allow and mark as a compiler\n"
|
||||
@" --remove: remove existing rule\n"
|
||||
@" --check: check for an existing rule\n"
|
||||
@"\n"
|
||||
@@ -96,14 +96,17 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
for (NSUInteger i = 0; i < arguments.count; ++i) {
|
||||
NSString *arg = arguments[i];
|
||||
|
||||
if ([arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateWhitelist;
|
||||
} else if ([arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateBlacklist;
|
||||
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateSilentBlacklist;
|
||||
if ([arg caseInsensitiveCompare:@"--allow"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateAllow;
|
||||
} else if ([arg caseInsensitiveCompare:@"--block"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateBlock;
|
||||
} else if ([arg caseInsensitiveCompare:@"--silent-block"] == NSOrderedSame ||
|
||||
[arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateSilentBlock;
|
||||
} else if ([arg caseInsensitiveCompare:@"--compiler"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateWhitelistCompiler;
|
||||
newRule.state = SNTRuleStateAllowCompiler;
|
||||
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateRemove;
|
||||
} else if ([arg caseInsensitiveCompare:@"--check"] == NSOrderedSame) {
|
||||
@@ -190,7 +193,7 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
fileSHA256:fileSHA256
|
||||
certificateSHA256:certificateSHA256
|
||||
reply:^(SNTEventState s) {
|
||||
output = (SNTEventStateAllow & s) ? @"Whitelisted".mutableCopy : @"Blacklisted".mutableCopy;
|
||||
output = (SNTEventStateAllow & s) ? @"Allowed".mutableCopy : @"Blocked".mutableCopy;
|
||||
switch (s) {
|
||||
case SNTEventStateAllowUnknown:
|
||||
case SNTEventStateBlockUnknown:
|
||||
@@ -241,7 +244,7 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
[[daemonConn remoteObjectProxy] databaseRuleForBinarySHA256:fileSHA256
|
||||
certificateSHA256:certificateSHA256
|
||||
reply:^(SNTRule *r) {
|
||||
if (r.state == SNTRuleStateWhitelistTransitive) {
|
||||
if (r.state == SNTRuleStateAllowTransitive) {
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:r.timestamp];
|
||||
[output appendString:[NSString stringWithFormat:@"\nlast access date: %@", [date description]]];
|
||||
}
|
||||
|
||||
@@ -84,10 +84,13 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
|
||||
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
|
||||
|
||||
SNTConfigurator *configurator = [SNTConfigurator configurator];
|
||||
|
||||
BOOL cachingEnabled = (![configurator enableSystemExtension] || [configurator enableSysxCache]);
|
||||
|
||||
// Kext status
|
||||
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
|
||||
if (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
if (cachingEnabled) {
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
|
||||
rootCacheCount = rootCache;
|
||||
@@ -156,10 +159,10 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
}];
|
||||
}
|
||||
|
||||
__block BOOL transitiveWhitelistingEnabled = NO;
|
||||
__block BOOL enableTransitiveRules = NO;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] enableTransitiveWhitelisting:^(BOOL response) {
|
||||
transitiveWhitelistingEnabled = response;
|
||||
[[self.daemonConn remoteObjectProxy] enableTransitiveRules:^(BOOL response) {
|
||||
enableTransitiveRules = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
@@ -202,12 +205,11 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"last_successful_rule" : ruleSyncLastSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"Connected" : @"Disconnected",
|
||||
@"bundle_scanning" : @(enableBundles),
|
||||
@"transitive_whitelisting" : @(transitiveWhitelistingEnabled),
|
||||
@"transitive_rules" : @(enableTransitiveRules),
|
||||
},
|
||||
} mutableCopy];
|
||||
if (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
stats[@"kernel"] = @{
|
||||
if (cachingEnabled) {
|
||||
stats[@"cache"] = @{
|
||||
@"root_cache_count" : @(rootCacheCount),
|
||||
@"non_root_cache_count": @(nonRootCacheCount),
|
||||
};
|
||||
@@ -224,9 +226,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
|
||||
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
if (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
printf(">>> Kernel Info\n");
|
||||
if (cachingEnabled) {
|
||||
printf(">>> Cache Info\n");
|
||||
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
|
||||
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
|
||||
}
|
||||
@@ -246,8 +247,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(" %-25s | %s\n", "Push Notifications",
|
||||
(pushNotifications ? "Connected" : "Disconnected"));
|
||||
printf(" %-25s | %s\n", "Bundle Scanning", (enableBundles ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Transitive Whitelisting",
|
||||
(transitiveWhitelistingEnabled ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Transitive Rules",
|
||||
(enableTransitiveRules? "Yes" : "No"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTFileInfo.h"
|
||||
#import "Source/common/SNTKernelCommon.h"
|
||||
#import "Source/santactl/SNTCommand.h"
|
||||
@@ -70,7 +71,7 @@ REGISTER_COMMAND_NAME(@"version")
|
||||
}
|
||||
|
||||
- (NSString *)santaKextVersion {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([[SNTConfigurator configurator] enableSystemExtension]) {
|
||||
return @"un-needed (SystemExtension being used)";
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,9 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Connect to santad while we are root, so that we pass the XPC authentication.
|
||||
+ (BOOL)requiresDaemonConn {
|
||||
return NO;
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
@@ -55,9 +56,6 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
}
|
||||
|
||||
- (void)runWithArguments:(NSArray *)arguments {
|
||||
// Connect to santad while we are root, so that we pass the XPC authentication
|
||||
[self.daemonConn resume];
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
LOGE(@"Failed to drop root privileges. Exiting.");
|
||||
|
||||
@@ -29,19 +29,23 @@ extern NSString *const kClientMode;
|
||||
extern NSString *const kClientModeMonitor;
|
||||
extern NSString *const kClientModeLockdown;
|
||||
extern NSString *const kCleanSync;
|
||||
extern NSString *const kWhitelistRegex;
|
||||
extern NSString *const kBlacklistRegex;
|
||||
extern NSString *const kAllowedPathRegex;
|
||||
extern NSString *const kAllowedPathRegexDeprecated;
|
||||
extern NSString *const kBlockedPathRegex;
|
||||
extern NSString *const kBlockedPathRegexDeprecated;
|
||||
extern NSString *const kBinaryRuleCount;
|
||||
extern NSString *const kCertificateRuleCount;
|
||||
extern NSString *const kCompilerRuleCount;
|
||||
extern NSString *const kTransitiveRuleCount;
|
||||
extern NSString *const kFullSyncInterval;
|
||||
extern NSString *const kFCMToken;
|
||||
extern NSString *const kFCMFullSyncInterval;
|
||||
extern NSString *const kFCMGlobalRuleSyncDeadline;
|
||||
extern NSString *const kEnableBundles;
|
||||
extern NSString *const kEnableBundles_OLD;
|
||||
extern NSString *const kEnableTransitiveWhitelisting;
|
||||
extern NSString *const kEnableTransitiveWhitelisting_OLD;
|
||||
extern NSString *const kEnableBundlesDeprecated;
|
||||
extern NSString *const kEnableTransitiveRules;
|
||||
extern NSString *const kEnableTransitiveRulesDeprecated;
|
||||
extern NSString *const kEnableTransitiveRulesSuperDeprecated;
|
||||
|
||||
extern NSString *const kEvents;
|
||||
extern NSString *const kFileSHA256;
|
||||
@@ -90,10 +94,14 @@ extern NSString *const kEventUploadBundleBinaries;
|
||||
extern NSString *const kRules;
|
||||
extern NSString *const kRuleSHA256;
|
||||
extern NSString *const kRulePolicy;
|
||||
extern NSString *const kRulePolicyWhitelist;
|
||||
extern NSString *const kRulePolicyWhitelistCompiler;
|
||||
extern NSString *const kRulePolicyBlacklist;
|
||||
extern NSString *const kRulePolicySilentBlacklist;
|
||||
extern NSString *const kRulePolicyAllowlist;
|
||||
extern NSString *const kRulePolicyAllowlistDeprecated;
|
||||
extern NSString *const kRulePolicyAllowlistCompiler;
|
||||
extern NSString *const kRulePolicyAllowlistCompilerDeprecated;
|
||||
extern NSString *const kRulePolicyBlocklist;
|
||||
extern NSString *const kRulePolicyBlocklistDeprecated;
|
||||
extern NSString *const kRulePolicySilentBlocklist;
|
||||
extern NSString *const kRulePolicySilentBlocklistDeprecated;
|
||||
extern NSString *const kRulePolicyRemove;
|
||||
extern NSString *const kRuleType;
|
||||
extern NSString *const kRuleTypeBinary;
|
||||
|
||||
@@ -29,21 +29,24 @@ NSString *const kClientMode = @"client_mode";
|
||||
NSString *const kClientModeMonitor = @"MONITOR";
|
||||
NSString *const kClientModeLockdown = @"LOCKDOWN";
|
||||
NSString *const kCleanSync = @"clean_sync";
|
||||
NSString *const kWhitelistRegex = @"whitelist_regex";
|
||||
NSString *const kBlacklistRegex = @"blacklist_regex";
|
||||
NSString *const kAllowedPathRegex = @"allowed_path_regex";
|
||||
NSString *const kAllowedPathRegexDeprecated = @"whitelist_regex";
|
||||
NSString *const kBlockedPathRegex = @"blocked_path_regex";
|
||||
NSString *const kBlockedPathRegexDeprecated = @"blacklist_regex";
|
||||
NSString *const kBinaryRuleCount = @"binary_rule_count";
|
||||
NSString *const kCertificateRuleCount = @"certificate_rule_count";
|
||||
NSString *const kCompilerRuleCount = @"compiler_rule_count";
|
||||
NSString *const kTransitiveRuleCount = @"transitive_rule_count";
|
||||
NSString *const kFullSyncInterval = @"full_sync_interval";
|
||||
NSString *const kFCMToken = @"fcm_token";
|
||||
NSString *const kFCMFullSyncInterval = @"fcm_full_sync_interval";
|
||||
NSString *const kFCMGlobalRuleSyncDeadline = @"fcm_global_rule_sync_deadline";
|
||||
|
||||
// NOTE: Both of the _OLD values will be removed at some indeterminate point in the future.
|
||||
NSString *const kEnableBundles = @"enable_bundles";
|
||||
NSString *const kEnableBundles_OLD = @"bundles_enabled";
|
||||
NSString *const kEnableTransitiveWhitelisting = @"enabled_transitive_whitelisting";
|
||||
NSString *const kEnableTransitiveWhitelisting_OLD = @"transitive_whitelisting_enabled";
|
||||
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 kEvents = @"events";
|
||||
NSString *const kFileSHA256 = @"file_sha256";
|
||||
@@ -92,10 +95,14 @@ NSString *const kEventUploadBundleBinaries = @"event_upload_bundle_binaries";
|
||||
NSString *const kRules = @"rules";
|
||||
NSString *const kRuleSHA256 = @"sha256";
|
||||
NSString *const kRulePolicy = @"policy";
|
||||
NSString *const kRulePolicyWhitelist = @"WHITELIST";
|
||||
NSString *const kRulePolicyWhitelistCompiler = @"WHITELIST_COMPILER";
|
||||
NSString *const kRulePolicyBlacklist = @"BLACKLIST";
|
||||
NSString *const kRulePolicySilentBlacklist = @"SILENT_BLACKLIST";
|
||||
NSString *const kRulePolicyAllowlist = @"ALLOWLIST";
|
||||
NSString *const kRulePolicyAllowlistDeprecated = @"WHITELIST";
|
||||
NSString *const kRulePolicyAllowlistCompiler = @"ALLOWLIST_COMPILER";
|
||||
NSString *const kRulePolicyAllowlistCompilerDeprecated = @"WHITELIST_COMPILER";
|
||||
NSString *const kRulePolicyBlocklist = @"BLOCKLIST";
|
||||
NSString *const kRulePolicyBlocklistDeprecated = @"BLACKLIST";
|
||||
NSString *const kRulePolicySilentBlocklist = @"SILENT_BLOCKLIST";
|
||||
NSString *const kRulePolicySilentBlocklistDeprecated = @"SILENT_BLACKLIST";
|
||||
NSString *const kRulePolicyRemove = @"REMOVE";
|
||||
NSString *const kRuleType = @"rule_type";
|
||||
NSString *const kRuleTypeBinary = @"BINARY";
|
||||
|
||||
@@ -52,14 +52,16 @@
|
||||
if (event.idx) [eventIds addObject:event.idx];
|
||||
if (uploadEvents.count >= self.syncState.eventBatchSize) break;
|
||||
}
|
||||
|
||||
if (!self.syncState.cleanSync) {
|
||||
NSDictionary *r = [self performRequest:[self requestWithDictionary:@{ kEvents: uploadEvents }]];
|
||||
if (!r) return NO;
|
||||
|
||||
NSDictionary *r = [self performRequest:[self requestWithDictionary:@{ kEvents: uploadEvents }]];
|
||||
if (!r) return NO;
|
||||
// A list of bundle hashes that require their related binary events to be uploaded.
|
||||
self.syncState.bundleBinaryRequests = r[kEventUploadBundleBinaries];
|
||||
|
||||
// A list of bundle hashes that require their related binary events to be uploaded.
|
||||
self.syncState.bundleBinaryRequests = r[kEventUploadBundleBinaries];
|
||||
|
||||
LOGI(@"Uploaded %lu events", uploadEvents.count);
|
||||
LOGI(@"Uploaded %lu events", uploadEvents.count);
|
||||
}
|
||||
|
||||
// Remove event IDs. For Bundle Events the ID is 0 so nothing happens.
|
||||
[[self.daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:[eventIds allObjects]];
|
||||
|
||||
115
Source/santactl/Commands/sync/SNTCommandSyncFCM.h
Normal file
115
Source/santactl/Commands/sync/SNTCommandSyncFCM.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/// Copyright 2021 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/** A block that takes a NSString object as an argument. */
|
||||
typedef void (^SNTCommandSyncFCMTokenHandler)(NSString *);
|
||||
|
||||
/** A block that takes a NSDictionary object as an argument. */
|
||||
typedef void (^SNTCommandSyncFCMMessageHandler)(NSDictionary *);
|
||||
|
||||
/** A block that takes a NSHTTPURLResponse and NSError object as an argument. */
|
||||
typedef void (^SNTCommandSyncFCMConnectionErrorHandler)(NSHTTPURLResponse *, NSError *);
|
||||
|
||||
/** A block that takes a NSDictionary and NSError object as arguments. */
|
||||
typedef void (^SNTCommandSyncFCMAcknowledgeErrorHandler)(NSDictionary *, NSError *);
|
||||
|
||||
@interface SNTCommandSyncFCM : NSObject
|
||||
|
||||
/** Returns YES if connected to FCM. */
|
||||
@property(readonly, nonatomic) BOOL isConnected;
|
||||
|
||||
/** A block to be executed when the FCM token changes */
|
||||
@property(copy) SNTCommandSyncFCMTokenHandler tokenHandler;
|
||||
|
||||
/** A block to be executed when there is an issue with acknowledging a message. */
|
||||
@property(copy) SNTCommandSyncFCMAcknowledgeErrorHandler acknowledgeErrorHandler;
|
||||
|
||||
/** A block to be executed when there is a non-recoverable issue with the FCM Connection. */
|
||||
@property(copy) SNTCommandSyncFCMConnectionErrorHandler connectionErrorHandler;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* The designated initializer.
|
||||
*
|
||||
* @param project FCM project
|
||||
* @param entity FCM entity
|
||||
* @param apiKey FCM apiKey
|
||||
* @param connectDelayMax Optional, max delay (seconds) when calling connect
|
||||
* @param backoffMax Optional, max backoff (seconds) when the connection is interrupted
|
||||
* @param fatalCodes Optional, do not attempt to reconnect if a fatal code is returned
|
||||
* @param sessionConfiguration Optional, the desired NSURLSessionConfiguration
|
||||
* @param messageHandler The block to be called for every message received
|
||||
*
|
||||
* @note If the host argument is nil, https://fcm-stream.googleapis.com will be used.
|
||||
* @note If the connectDelayMax argument is 0, a default value of 10 will be used.
|
||||
* @note If the backoffMax argument is 0, a default value of 900 will be used.
|
||||
* @note If the fatalCodes argument is nil, @[@302, @400, @403] will be used.
|
||||
* @note If the sessionConfiguration argument is nil, defaultSessionConfiguration will be used.
|
||||
*
|
||||
* @return An initialized SNTCommandSyncFCM object
|
||||
*/
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
connectDelayMax:(uint32_t)connectDelayMax
|
||||
backoffMax:(uint32_t)backoffMax
|
||||
fatalCodes:(NSArray<NSNumber *> *)fatalCodes
|
||||
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler
|
||||
NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/** A convenience initializer. Optional args will use their zero values. */
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler;
|
||||
|
||||
/** A convenience initializer. Optional args will use their zero values. */
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler;
|
||||
|
||||
/**
|
||||
* Opens a connection to FCM and starts listening for messages.
|
||||
*
|
||||
* @note A random delay will occur before the connection is made.
|
||||
* @note If there is a failure in the connection, reconnection will occur once FCM is reachable.
|
||||
* Failed reconnections will backoff exponentially up to the defined max.
|
||||
*/
|
||||
- (void)connect;
|
||||
|
||||
/**
|
||||
* Acknowledges a FCM message. Each message received must be acknowledged.
|
||||
*
|
||||
* @param message A FCM message
|
||||
*
|
||||
* @note Calls the acknowledgeErrorHandler block property when an acknowledge error occurs.
|
||||
*/
|
||||
- (void)acknowledgeMessage:(NSDictionary *)message;
|
||||
|
||||
/**
|
||||
* Closes all FCM connections. Stops Reachability. Outstanding tasks will be canceled.
|
||||
*
|
||||
* @note After disconnect is called the receiver is considered dead. A new MOLFCMClient object
|
||||
* will need to be created to begin listening for messages.
|
||||
* @note After disconnect the receiver can hold a reference to itself for up to 15 minutes.
|
||||
*/
|
||||
- (void)disconnect;
|
||||
|
||||
@end
|
||||
480
Source/santactl/Commands/sync/SNTCommandSyncFCM.m
Normal file
480
Source/santactl/Commands/sync/SNTCommandSyncFCM.m
Normal file
@@ -0,0 +1,480 @@
|
||||
/// Copyright 2021 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncFCM.h"
|
||||
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOGD(format, ...) NSLog(format, ##__VA_ARGS__);
|
||||
#else // DEBUG
|
||||
#define LOGD(format, ...)
|
||||
#endif // DEBUG
|
||||
|
||||
/** FCM checkin and register components */
|
||||
static NSString *const kFCMCheckinHost = @"https://android.clients.google.com";
|
||||
static NSString *const kFCMCheckin = @"/checkin";
|
||||
static NSString *const kFCMCheckinBody = @"{'checkin':{}, 'version':3}";
|
||||
static NSString *const kFCMRegister = @"/c2dm/register3";
|
||||
|
||||
/** FCM connect and ack components */
|
||||
static NSString *const kFCMConnectHost = @"https://fcmconnection.googleapis.com";
|
||||
static NSString *const kFCMConnect = @"/v1alpha1:connectDownstream";
|
||||
static NSString *const kFCMAck = @"/v1alpha1:ack";
|
||||
|
||||
/** FCM client keys */
|
||||
static NSString *const kAndroidIDKey = @"android_id";
|
||||
static NSString *const kVersionInfoKey = @"version_info";
|
||||
static NSString *const kSecurityTokenKey = @"security_token";
|
||||
|
||||
/** HTTP Header Constants */
|
||||
static NSString *const kFCMApplicationForm = @"application/x-www-form-urlencoded";
|
||||
static NSString *const kFCMApplicationJSON = @"application/json";
|
||||
static NSString *const kFCMContentType = @"Content-Type";
|
||||
|
||||
/** Default 15 minute backoff maximum */
|
||||
static const uint32_t kDefaultBackoffMaxSeconds = 900;
|
||||
|
||||
/** Default 10 sec connect delay maximum */
|
||||
static const uint32_t kDefaultConnectDelayMaxSeconds = 10;
|
||||
|
||||
#pragma mark MOLFCMClient Extension
|
||||
|
||||
@interface SNTCommandSyncFCM () {
|
||||
/** URL components for client registration, receiving and acknowledging messages. */
|
||||
NSURLComponents *_checkinComponents;
|
||||
NSURLComponents *_registerComponents;
|
||||
NSURLComponents *_connectComponents;
|
||||
NSURLComponents *_ackComponents;
|
||||
|
||||
/** Holds the NSURLSession object generated by the MOLAuthenticatingURLSession object. */
|
||||
NSURLSession *_session;
|
||||
|
||||
/** Holds the current backoff seconds. */
|
||||
uint32_t _backoffSeconds;
|
||||
|
||||
/** Holds the max connect and backoff seconds. */
|
||||
uint32_t _connectDelayMaxSeconds;
|
||||
uint32_t _backoffMaxSeconds;
|
||||
|
||||
NSArray<NSNumber *> *_fatalHTTPStatusCodes;
|
||||
}
|
||||
|
||||
/** NSURLSession wrapper used for https communication with the FCM service. */
|
||||
@property(nonatomic) MOLAuthenticatingURLSession *authSession;
|
||||
|
||||
/** The block to be called for every message. */
|
||||
@property(copy, nonatomic) SNTCommandSyncFCMMessageHandler messageHandler;
|
||||
|
||||
/** Is used throughout the class to reconnect to FCM after a connection loss. */
|
||||
@property SCNetworkReachabilityRef reachability;
|
||||
|
||||
/** FCM client identities. */
|
||||
@property(nonatomic, readonly) NSString *project;
|
||||
@property(nonatomic, readonly) NSString *entity;
|
||||
@property(nonatomic, readonly) NSString *apiKey;
|
||||
|
||||
/** FCM client checkin data */
|
||||
@property NSString *androidID;
|
||||
@property NSString *versionInfo;
|
||||
@property NSString *securityToken;
|
||||
|
||||
/** Called by the reachability handler when the host becomes reachable. */
|
||||
- (void)reachabilityRestored;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark SCNetworkReachabilityCallBack
|
||||
|
||||
/** Called when the network state changes. */
|
||||
static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags,
|
||||
void *info) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (flags & kSCNetworkReachabilityFlagsReachable) {
|
||||
SNTCommandSyncFCM *FCMClient = (__bridge SNTCommandSyncFCM *)info;
|
||||
SEL s = @selector(reachabilityRestored);
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:FCMClient selector:s object:nil];
|
||||
[FCMClient performSelector:s withObject:nil afterDelay:1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@implementation SNTCommandSyncFCM
|
||||
|
||||
#pragma mark init/dealloc methods
|
||||
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
connectDelayMax:(uint32_t)connectDelayMax
|
||||
backoffMax:(uint32_t)backoffMax
|
||||
fatalCodes:(NSArray<NSNumber *> *)fatalCodes
|
||||
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_project = project;
|
||||
_entity = entity;
|
||||
_apiKey = apiKey;
|
||||
_checkinComponents = [NSURLComponents componentsWithString:kFCMCheckinHost];
|
||||
_checkinComponents.path = kFCMCheckin;
|
||||
_registerComponents = [NSURLComponents componentsWithString:kFCMCheckinHost];
|
||||
_registerComponents.path = kFCMRegister;
|
||||
_connectComponents = [NSURLComponents componentsWithString:kFCMConnectHost];
|
||||
_connectComponents.path = kFCMConnect;
|
||||
_ackComponents = [NSURLComponents componentsWithString:kFCMConnectHost];
|
||||
_ackComponents.path = kFCMAck;
|
||||
|
||||
_messageHandler = messageHandler;
|
||||
|
||||
_authSession = [[MOLAuthenticatingURLSession alloc]
|
||||
initWithSessionConfiguration:sessionConfiguration
|
||||
?: [NSURLSessionConfiguration
|
||||
defaultSessionConfiguration]];
|
||||
_authSession.dataTaskDidReceiveDataBlock = [self dataTaskDidReceiveDataBlock];
|
||||
_authSession.taskDidCompleteWithErrorBlock = [self taskDidCompleteWithErrorBlock];
|
||||
|
||||
_session = _authSession.session;
|
||||
|
||||
_connectDelayMaxSeconds = connectDelayMax ?: kDefaultConnectDelayMaxSeconds;
|
||||
_backoffMaxSeconds = backoffMax ?: kDefaultBackoffMaxSeconds;
|
||||
_fatalHTTPStatusCodes = fatalCodes ?: @[ @302, @400, @401, @403, @404 ];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
sessionConfiguration:(NSURLSessionConfiguration *)sessionConfiguration
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
|
||||
return [self initWithProject:project
|
||||
entity:entity
|
||||
apiKey:apiKey
|
||||
connectDelayMax:0
|
||||
backoffMax:0
|
||||
fatalCodes:nil
|
||||
sessionConfiguration:sessionConfiguration
|
||||
messageHandler:messageHandler];
|
||||
}
|
||||
|
||||
- (instancetype)initWithProject:(NSString *)project
|
||||
entity:(NSString *)entity
|
||||
apiKey:(NSString *)apiKey
|
||||
messageHandler:(SNTCommandSyncFCMMessageHandler)messageHandler {
|
||||
return [self initWithProject:project
|
||||
entity:entity
|
||||
apiKey:apiKey
|
||||
connectDelayMax:0
|
||||
backoffMax:0
|
||||
fatalCodes:nil
|
||||
sessionConfiguration:nil
|
||||
messageHandler:messageHandler];
|
||||
}
|
||||
|
||||
/** Before this object is released ensure reachability release. */
|
||||
- (void)dealloc {
|
||||
[self stopReachability];
|
||||
}
|
||||
|
||||
#pragma mark property methods
|
||||
|
||||
- (BOOL)isConnected {
|
||||
if (!self.androidID || !self.androidID || !self.securityToken) return NO;
|
||||
for (NSURLSessionDataTask *dataTask in [self dataTasks]) {
|
||||
if (dataTask.state == NSURLSessionTaskStateRunning) return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark reachability methods
|
||||
|
||||
- (void)reachabilityRestored {
|
||||
LOGD(@"Reachability restored. Reconnect after a backoff of %i seconds", _backoffSeconds);
|
||||
[self stopReachability];
|
||||
dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, _backoffSeconds * NSEC_PER_SEC);
|
||||
dispatch_after(t, dispatch_get_main_queue(), ^{
|
||||
[self connectHelper];
|
||||
});
|
||||
}
|
||||
|
||||
/** Start listening for network state changes on a background thread. */
|
||||
- (void)startReachability {
|
||||
if (self.reachability) return;
|
||||
LOGD(@"Reachability started.");
|
||||
self.reachability =
|
||||
SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, kFCMConnectHost.UTF8String);
|
||||
SCNetworkReachabilityContext context = {.info = (__bridge void *)self};
|
||||
if (SCNetworkReachabilitySetCallback(self.reachability, reachabilityHandler, &context)) {
|
||||
SCNetworkReachabilitySetDispatchQueue(
|
||||
self.reachability, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop listening for network state changes. */
|
||||
- (void)stopReachability {
|
||||
if (self.reachability) {
|
||||
SCNetworkReachabilitySetDispatchQueue(self.reachability, NULL);
|
||||
if (self.reachability) CFRelease(self.reachability);
|
||||
self.reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark message methods
|
||||
|
||||
- (void)connect {
|
||||
uint32_t ms = arc4random_uniform(_connectDelayMaxSeconds * 1000);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ms * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
|
||||
[self connectHelper];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)connectHelper {
|
||||
LOGD(@"Connecting...");
|
||||
[self cancelConnections];
|
||||
|
||||
// Reuse checkin credentials / FCM token if allready registered.
|
||||
if (!self.androidID || !self.androidID || !self.securityToken) return [self checkin];
|
||||
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:_connectComponents.URL];
|
||||
[URLRequest addValue:kFCMApplicationJSON forHTTPHeaderField:kFCMContentType];
|
||||
URLRequest.HTTPMethod = @"GET";
|
||||
[self setCheckinAuthorization:URLRequest];
|
||||
[[_session dataTaskWithRequest:URLRequest] resume];
|
||||
}
|
||||
|
||||
- (void)checkin {
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:_checkinComponents.URL];
|
||||
[URLRequest addValue:kFCMApplicationJSON forHTTPHeaderField:kFCMContentType];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
URLRequest.HTTPBody = [kFCMCheckinBody dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[[_session dataTaskWithRequest:URLRequest] resume];
|
||||
}
|
||||
|
||||
- (void)checkinDataHandler:(NSData *)data {
|
||||
id jo = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
|
||||
LOGD(@"checkin: %@", jo);
|
||||
NSDictionary *checkin = [self extractCheckinFrom:jo];
|
||||
if (!checkin) return;
|
||||
self.androidID = [(NSNumber *)checkin[kAndroidIDKey] stringValue];
|
||||
self.versionInfo = checkin[kVersionInfoKey];
|
||||
self.securityToken = [(NSNumber *)checkin[kSecurityTokenKey] stringValue];
|
||||
}
|
||||
|
||||
- (NSDictionary *)extractCheckinFrom:(id)jo {
|
||||
if (!jo) return nil;
|
||||
if (![jo isKindOfClass:[NSDictionary class]]) return nil;
|
||||
if (!jo[kAndroidIDKey]) return nil;
|
||||
if (![jo[kAndroidIDKey] isKindOfClass:[NSNumber class]]) return nil;
|
||||
if (!jo[kVersionInfoKey]) return nil;
|
||||
if (![jo[kVersionInfoKey] isKindOfClass:[NSString class]]) return nil;
|
||||
if (!jo[kSecurityTokenKey]) return nil;
|
||||
if (![jo[kSecurityTokenKey] isKindOfClass:[NSNumber class]]) return nil;
|
||||
return jo;
|
||||
}
|
||||
|
||||
- (void)register {
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:_registerComponents.URL];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
URLRequest.HTTPBody = [kFCMCheckinBody dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSURLComponents *params = [[NSURLComponents alloc] init];
|
||||
params.queryItems = @[
|
||||
[NSURLQueryItem queryItemWithName:@"app" value:self.project],
|
||||
[NSURLQueryItem queryItemWithName:@"info" value:self.versionInfo],
|
||||
[NSURLQueryItem queryItemWithName:@"sender" value:self.entity],
|
||||
[NSURLQueryItem queryItemWithName:@"device" value:self.androidID],
|
||||
[NSURLQueryItem queryItemWithName:@"X-scope" value:@"*"],
|
||||
];
|
||||
URLRequest.HTTPBody = [params.query dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[URLRequest addValue:kFCMApplicationForm forHTTPHeaderField:kFCMContentType];
|
||||
NSString *aid = [NSString stringWithFormat:@"AidLogin %@:%@", self.androidID, self.securityToken];
|
||||
[URLRequest addValue:aid forHTTPHeaderField:@"Authorization"];
|
||||
[[_session dataTaskWithRequest:URLRequest] resume];
|
||||
}
|
||||
|
||||
- (void)registerDataHandler:(NSData *)data {
|
||||
NSString *t = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
NSArray *c = [t componentsSeparatedByString:@"="];
|
||||
if (c.count == 2) {
|
||||
NSString *tok = c[1];
|
||||
if ([tok isEqualToString:@"PHONE_REGISTRATION_ERROR"]) {
|
||||
LOGD(@"register: PHONE_REGISTRATION_ERROR - retrying");
|
||||
sleep(1);
|
||||
return [self register];
|
||||
}
|
||||
if (self.tokenHandler) self.tokenHandler(tok);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)acknowledgeMessage:(NSDictionary *)message {
|
||||
if (!message[@"messageId"]) return;
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:_ackComponents.URL];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
[URLRequest addValue:kFCMApplicationJSON forHTTPHeaderField:kFCMContentType];
|
||||
[self setCheckinAuthorization:URLRequest];
|
||||
NSDictionary *b = @{@"ack" : @{@"messageId" : message[@"messageId"]}};
|
||||
NSData *body = [NSJSONSerialization dataWithJSONObject:b options:0 error:NULL];
|
||||
URLRequest.HTTPBody = body;
|
||||
[[_session dataTaskWithRequest:URLRequest
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (((NSHTTPURLResponse *)response).statusCode != 200) {
|
||||
if (self.acknowledgeErrorHandler) {
|
||||
self.acknowledgeErrorHandler(message, error);
|
||||
}
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
[self stopReachability];
|
||||
[_session invalidateAndCancel];
|
||||
_session = nil;
|
||||
}
|
||||
|
||||
- (void)cancelConnections {
|
||||
for (NSURLSessionDataTask *dataTask in [self dataTasks]) {
|
||||
[dataTask cancel];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSURLSessionDataTask *> *)dataTasks {
|
||||
__block NSArray *dataTasks;
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
[_session getTasksWithCompletionHandler:^(NSArray *data, NSArray *upload, NSArray *download) {
|
||||
dataTasks = data;
|
||||
dispatch_semaphore_signal(sema);
|
||||
}];
|
||||
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
return dataTasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse FCM data; extract and call self.messageHandler for each message.
|
||||
*
|
||||
* Expected format:
|
||||
* [{
|
||||
* "noOp": {}
|
||||
* }
|
||||
* , <-- start of new chunk
|
||||
* {
|
||||
* "noOp": {}
|
||||
* }
|
||||
*
|
||||
*/
|
||||
- (void)processMessagesFromData:(NSData *)data {
|
||||
if (!data) return;
|
||||
NSMutableString *raw = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (raw.length < 2) return;
|
||||
// Add an opening bracket if this is a message in the middle of the stream.
|
||||
[raw replaceOccurrencesOfString:@",\r" withString:@"[" options:0 range:NSMakeRange(0, 2)];
|
||||
// Always add a closing bracket.
|
||||
[raw appendString:@"]"];
|
||||
NSError *err;
|
||||
id jo = [NSJSONSerialization JSONObjectWithData:[raw dataUsingEncoding:NSUTF8StringEncoding]
|
||||
options:0
|
||||
error:&err];
|
||||
if (!jo) {
|
||||
if (err) LOGD(@"processMessagesFromData err: %@", err);
|
||||
return;
|
||||
}
|
||||
LOGD(@"processMessagesFromData: %@", jo);
|
||||
|
||||
if (![jo isKindOfClass:[NSArray class]]) return;
|
||||
for (id md in jo) {
|
||||
if (![md isKindOfClass:[NSDictionary class]]) continue;
|
||||
NSDictionary *m = md[@"message"];
|
||||
if ([m isKindOfClass:[NSDictionary class]]) self.messageHandler(m);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)handleHTTPReponse:(NSHTTPURLResponse *)HTTPResponse error:(NSError *)error {
|
||||
if (HTTPResponse.statusCode == 200) {
|
||||
_backoffSeconds = 0;
|
||||
if ([HTTPResponse.URL.path isEqualToString:kFCMCheckin]) {
|
||||
// If checkin was successful, start listening for messages and continue to register.
|
||||
[self connectHelper];
|
||||
return [self register];
|
||||
} else if ([HTTPResponse.URL.path isEqualToString:kFCMConnect]) {
|
||||
// connect will re-connect.
|
||||
return [self connectHelper];
|
||||
} // register may be called more than once, don't do anything in response.
|
||||
} else if ([_fatalHTTPStatusCodes containsObject:@(HTTPResponse.statusCode)]) {
|
||||
if (self.connectionErrorHandler) self.connectionErrorHandler(HTTPResponse, error);
|
||||
} else {
|
||||
// If no backoff is set, start out with 5 - 15 seconds.
|
||||
// If a backoff is already set, double it, with a max of kBackoffMaxSeconds.
|
||||
_backoffSeconds = _backoffSeconds * 2 ?: arc4random_uniform(11) + 5;
|
||||
if (_backoffSeconds > _backoffMaxSeconds) _backoffSeconds = _backoffMaxSeconds;
|
||||
if (error) LOGD(@"handleHTTPReponse err: %@", error);
|
||||
[self startReachability];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCheckinAuthorization:(NSMutableURLRequest *)URLRequest {
|
||||
NSString *a = [NSString
|
||||
stringWithFormat:@"checkin %@:%@ %@", self.androidID, self.securityToken, self.versionInfo];
|
||||
[URLRequest addValue:a forHTTPHeaderField:@"Authorization"];
|
||||
[URLRequest addValue:self.apiKey forHTTPHeaderField:@"X-Goog-Api-Key"];
|
||||
}
|
||||
|
||||
#pragma mark NSURLSession block property and methods
|
||||
|
||||
/**
|
||||
* MOLAuthenticatingURLSession is the NSURLSessionDelegate. It will call this block every time
|
||||
* the URLSession:task:didCompleteWithError: is called. This allows MOLFCMClient to be notified
|
||||
* when a task ends while using delegate methods.
|
||||
*/
|
||||
- (void (^)(NSURLSession *, NSURLSessionDataTask *, NSData *))dataTaskDidReceiveDataBlock {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
return ^(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data) {
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
NSString *path = dataTask.originalRequest.URL.path;
|
||||
if ([path isEqualToString:kFCMCheckin]) {
|
||||
return [strongSelf checkinDataHandler:data];
|
||||
} else if ([path isEqualToString:kFCMRegister]) {
|
||||
return [strongSelf registerDataHandler:data];
|
||||
} else if ([dataTask.originalRequest.URL.path isEqualToString:kFCMConnect]) {
|
||||
[strongSelf processMessagesFromData:data];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* MOLAuthenticatingURLSession is the NSURLSessionDataDelegate. It will call this block every time
|
||||
* the URLSession:dataTask:didReceiveData: is called. This allows for message data chunks to be
|
||||
* processed as they appear in the FCM buffer. For Content-Type: text/html there is a 512 byte
|
||||
* buffer that must be filled before data is returned. Content-Type: application/json does not use
|
||||
* a buffer and data is returned as soon as it is available.
|
||||
*
|
||||
* TODO:(bur) Follow up with FCM on Content-Type: application/json. Currently FCM returns data with
|
||||
* Content-Type: text/html. Messages under 512 bytes will not be processed until the connection
|
||||
* drains.
|
||||
*/
|
||||
- (void (^)(NSURLSession *, NSURLSessionTask *, NSError *))taskDidCompleteWithErrorBlock {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
return ^(NSURLSession *session, NSURLSessionTask *task, NSError *error) {
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
// task.response can be nil when an NSURLError* occurs
|
||||
if (task.response && ![task.response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
if (strongSelf.connectionErrorHandler) strongSelf.connectionErrorHandler(nil, error);
|
||||
return;
|
||||
}
|
||||
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)task.response;
|
||||
[strongSelf handleHTTPReponse:HTTPResponse error:error];
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -15,7 +15,6 @@
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncManager.h"
|
||||
|
||||
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>
|
||||
#import <MOLFCMClient/MOLFCMClient.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
@@ -28,6 +27,7 @@
|
||||
#import "Source/common/SNTXPCSyncdInterface.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncConstants.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncEventUpload.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncFCM.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncPostflight.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncPreflight.h"
|
||||
#import "Source/santactl/Commands/sync/SNTCommandSyncRuleDownload.h"
|
||||
@@ -47,19 +47,22 @@ static NSString *const kFCMTargetHostIDKey = @"target_host_id";
|
||||
|
||||
@property(nonatomic) NSCache *dispatchLock;
|
||||
|
||||
// whitelistNotifications dictionary stores info from FCM messages. The binary/bundle hash is used
|
||||
// allowlistNotifications dictionary stores info from FCM messages. The binary/bundle hash is used
|
||||
// as a key mapping to values that are themselves dictionaries. These dictionary values contain the
|
||||
// name of the binary/bundle and a count of associated binary rules.
|
||||
@property(nonatomic) NSMutableDictionary *whitelistNotifications;
|
||||
@property(nonatomic) NSMutableDictionary *allowlistNotifications;
|
||||
|
||||
// whitelistNotificationQueue is used to serialize access to the whitelistNotifications dictionary.
|
||||
@property(nonatomic) NSOperationQueue *whitelistNotificationQueue;
|
||||
// allowlistNotificationQueue is used to serialize access to the allowlistNotifications dictionary.
|
||||
@property(nonatomic) NSOperationQueue *allowlistNotificationQueue;
|
||||
|
||||
@property NSUInteger fullSyncInterval;
|
||||
|
||||
@property NSUInteger FCMFullSyncInterval;
|
||||
@property NSUInteger FCMGlobalRuleSyncDeadline;
|
||||
@property NSUInteger eventBatchSize;
|
||||
|
||||
@property MOLFCMClient *FCMClient;
|
||||
@property SNTCommandSyncFCM *FCMClient;
|
||||
@property NSString *FCMToken;
|
||||
|
||||
@property(nonatomic) MOLXPCConnection *daemonConn;
|
||||
|
||||
@@ -106,8 +109,8 @@ static void reachabilityHandler(
|
||||
[self lockAction:kRuleSync];
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
syncState.targetedRuleSync = self.targetedRuleSync;
|
||||
syncState.whitelistNotifications = self.whitelistNotifications;
|
||||
syncState.whitelistNotificationQueue = self.whitelistNotificationQueue;
|
||||
syncState.allowlistNotifications = self.allowlistNotifications;
|
||||
syncState.allowlistNotificationQueue = self.allowlistNotificationQueue;
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
@@ -118,10 +121,11 @@ static void reachabilityHandler(
|
||||
[self unlockAction:kRuleSync];
|
||||
}];
|
||||
_dispatchLock = [[NSCache alloc] init];
|
||||
_whitelistNotifications = [NSMutableDictionary dictionary];
|
||||
_whitelistNotificationQueue = [[NSOperationQueue alloc] init];
|
||||
_whitelistNotificationQueue.maxConcurrentOperationCount = 1; // make this a serial queue
|
||||
_allowlistNotifications = [NSMutableDictionary dictionary];
|
||||
_allowlistNotificationQueue = [[NSOperationQueue alloc] init];
|
||||
_allowlistNotificationQueue.maxConcurrentOperationCount = 1; // make this a serial queue
|
||||
|
||||
_fullSyncInterval = kDefaultFullSyncInterval;
|
||||
_eventBatchSize = kDefaultEventBatchSize;
|
||||
_FCMFullSyncInterval = kDefaultFCMFullSyncInterval;
|
||||
_FCMGlobalRuleSyncDeadline = kDefaultFCMGlobalRuleSyncDeadline;
|
||||
@@ -182,40 +186,46 @@ static void reachabilityHandler(
|
||||
#pragma mark push notification methods
|
||||
|
||||
- (void)listenForPushNotificationsWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
if ([self.FCMClient.FCMToken isEqualToString:syncState.FCMToken]) {
|
||||
LOGD(@"Continue with the current FCMToken");
|
||||
if ([self.FCMToken isEqualToString:syncState.FCMToken]) {
|
||||
LOGD(@"Already listening for push notifications");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGD(@"Start listening for push notifications");
|
||||
|
||||
WEAKIFY(self);
|
||||
|
||||
[self.FCMClient disconnect];
|
||||
NSString *machineID = syncState.machineID;
|
||||
self.FCMClient = [[MOLFCMClient alloc] initWithFCMToken:syncState.FCMToken
|
||||
sessionConfiguration:syncState.session.configuration.copy
|
||||
messageHandler:^(NSDictionary *message) {
|
||||
if (!message || [message isEqual:@{}]) return;
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
self.FCMClient = [[SNTCommandSyncFCM alloc] initWithProject:config.fcmProject
|
||||
entity:config.fcmEntity
|
||||
apiKey:config.fcmAPIKey
|
||||
sessionConfiguration:syncState.session.configuration.copy
|
||||
messageHandler:^(NSDictionary *message) {
|
||||
if (!message || message[@"noOp"]) return;
|
||||
STRONGIFY(self);
|
||||
LOGD(@"%@", message);
|
||||
[self.FCMClient acknowledgeMessage:message];
|
||||
[self processFCMMessage:message withMachineID:machineID];
|
||||
}];
|
||||
|
||||
self.FCMClient.tokenHandler = ^(NSString *t) {
|
||||
STRONGIFY(self);
|
||||
LOGD(@"tokenHandler: %@", t);
|
||||
self.FCMToken = t;
|
||||
[self preflightOnly:YES];
|
||||
};
|
||||
|
||||
self.FCMClient.connectionErrorHandler = ^(NSHTTPURLResponse *response, NSError *error) {
|
||||
STRONGIFY(self);
|
||||
if (response) LOGE(@"FCM fatal response: %@", response);
|
||||
if (error) LOGE(@"FCM fatal error: %@", error);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
self.FCMToken = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kDefaultFullSyncInterval];
|
||||
};
|
||||
|
||||
self.FCMClient.loggingBlock = ^(NSString *log) {
|
||||
LOGD(@"FCMClient: %@", log);
|
||||
};
|
||||
|
||||
[self.FCMClient connect];
|
||||
}
|
||||
|
||||
@@ -243,8 +253,8 @@ static void reachabilityHandler(
|
||||
NSString *fileHash = message[kFCMFileHashKey];
|
||||
NSString *fileName = message[kFCMFileNameKey];
|
||||
if (fileName && fileHash) {
|
||||
[self.whitelistNotificationQueue addOperationWithBlock:^{
|
||||
self.whitelistNotifications[fileHash] = @{ kFileName : fileName }.mutableCopy;
|
||||
[self.allowlistNotificationQueue addOperationWithBlock:^{
|
||||
self.allowlistNotifications[fileHash] = @{ kFileName : fileName }.mutableCopy;
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -334,6 +344,11 @@ static void reachabilityHandler(
|
||||
#pragma mark syncing chain
|
||||
|
||||
- (void)preflight {
|
||||
[self preflightOnly:NO];
|
||||
}
|
||||
|
||||
- (void)preflightOnly:(BOOL)preflightOnly {
|
||||
LOGD(@"Preflight starting");
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
SNTCommandSyncPreflight *p = [[SNTCommandSyncPreflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
@@ -345,17 +360,19 @@ static void reachabilityHandler(
|
||||
self.eventBatchSize = syncState.eventBatchSize;
|
||||
|
||||
// Start listening for push notifications with a full sync every FCMFullSyncInterval
|
||||
if (syncState.daemon && syncState.FCMToken) {
|
||||
if (syncState.daemon && [SNTConfigurator configurator].fcmEnabled) {
|
||||
self.FCMFullSyncInterval = syncState.FCMFullSyncInterval;
|
||||
self.FCMGlobalRuleSyncDeadline = syncState.FCMGlobalRuleSyncDeadline;
|
||||
[self listenForPushNotificationsWithSyncState:syncState];
|
||||
} else if (syncState.daemon) {
|
||||
LOGD(@"FCMToken not provided. Sync every %lu min.", kDefaultFullSyncInterval / 60);
|
||||
LOGD(@"FCM not enabled. Sync every %lu min.", syncState.fullSyncInterval / 60);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kDefaultFullSyncInterval];
|
||||
self.fullSyncInterval = syncState.fullSyncInterval;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:self.fullSyncInterval];
|
||||
}
|
||||
|
||||
if (preflightOnly) return;
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
} else {
|
||||
if (!syncState.daemon) {
|
||||
@@ -369,6 +386,7 @@ static void reachabilityHandler(
|
||||
}
|
||||
|
||||
- (void)eventUploadWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
LOGD(@"Event upload starting");
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Event upload complete");
|
||||
@@ -380,6 +398,7 @@ static void reachabilityHandler(
|
||||
}
|
||||
|
||||
- (void)ruleDownloadWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
LOGD(@"Rule download starting");
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
@@ -391,6 +410,7 @@ static void reachabilityHandler(
|
||||
}
|
||||
|
||||
- (void)postflightWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
LOGD(@"Postflight starting");
|
||||
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Postflight complete");
|
||||
@@ -478,6 +498,11 @@ static void reachabilityHandler(
|
||||
syncState.daemonConn = self.daemonConn;
|
||||
syncState.daemon = self.daemon;
|
||||
|
||||
syncState.compressedContentEncoding =
|
||||
config.enableBackwardsCompatibleContentEncoding ? @"zlib" : @"deflate";
|
||||
|
||||
syncState.FCMToken = self.FCMToken;
|
||||
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
return syncState;
|
||||
}
|
||||
|
||||
@@ -49,16 +49,16 @@
|
||||
[[self.daemonConn remoteObjectProxy] setSyncCleanRequired:NO reply:replyBlock];
|
||||
}
|
||||
|
||||
// Update whitelist/blacklist regexes
|
||||
if (self.syncState.whitelistRegex) {
|
||||
// Update allowlist/blocklist regexes
|
||||
if (self.syncState.allowlistRegex) {
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setWhitelistPathRegex:self.syncState.whitelistRegex
|
||||
reply:replyBlock];
|
||||
[[self.daemonConn remoteObjectProxy] setAllowedPathRegex:self.syncState.allowlistRegex
|
||||
reply:replyBlock];
|
||||
}
|
||||
if (self.syncState.blacklistRegex) {
|
||||
if (self.syncState.blocklistRegex) {
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setBlacklistPathRegex:self.syncState.blacklistRegex
|
||||
reply:replyBlock];
|
||||
[[self.daemonConn remoteObjectProxy] setBlockedPathRegex:self.syncState.blocklistRegex
|
||||
reply:replyBlock];
|
||||
}
|
||||
|
||||
// Update last sync success
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
requestDict[kOSBuild] = [SNTSystemInfo osBuild];
|
||||
requestDict[kSantaVer] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
requestDict[kPrimaryUser] = self.syncState.machineOwner;
|
||||
if (self.syncState.FCMToken) requestDict[kFCMToken] = self.syncState.FCMToken;
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter(group);
|
||||
@@ -87,33 +88,34 @@
|
||||
|
||||
dispatch_group_enter(group);
|
||||
NSNumber *enableBundles = resp[kEnableBundles];
|
||||
if (!enableBundles) {
|
||||
enableBundles = resp[kEnableBundles_OLD];
|
||||
}
|
||||
if (!enableBundles) enableBundles = resp[kEnableBundlesDeprecated];
|
||||
[[self.daemonConn remoteObjectProxy] setEnableBundles:[enableBundles boolValue] reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_enter(group);
|
||||
NSNumber *enableTransitiveWhitelisting = resp[kEnableTransitiveWhitelisting];
|
||||
if (!enableTransitiveWhitelisting) {
|
||||
enableTransitiveWhitelisting = resp[kEnableTransitiveWhitelisting_OLD];
|
||||
}
|
||||
BOOL enabled = [enableTransitiveWhitelisting boolValue];
|
||||
[[self.daemonConn remoteObjectProxy] setEnableTransitiveWhitelisting:enabled reply:^{
|
||||
NSNumber *enableTransitiveRules = resp[kEnableTransitiveRules];
|
||||
if (!enableTransitiveRules) enableTransitiveRules = resp[kEnableTransitiveRulesDeprecated];
|
||||
if (!enableTransitiveRules) enableTransitiveRules = resp[kEnableTransitiveRulesSuperDeprecated];
|
||||
BOOL enabled = [enableTransitiveRules boolValue];
|
||||
[[self.daemonConn remoteObjectProxy] setEnableTransitiveRules:enabled reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
|
||||
self.syncState.FCMToken = resp[kFCMToken];
|
||||
|
||||
// Don't let these go too low
|
||||
NSUInteger value = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.FCMFullSyncInterval =
|
||||
(value < kDefaultFullSyncInterval) ? kDefaultFCMFullSyncInterval : value;
|
||||
value = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
|
||||
NSUInteger FCMIntervalValue = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.FCMFullSyncInterval = (FCMIntervalValue < kDefaultFullSyncInterval)
|
||||
? kDefaultFCMFullSyncInterval
|
||||
: FCMIntervalValue;
|
||||
FCMIntervalValue = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
|
||||
self.syncState.FCMGlobalRuleSyncDeadline =
|
||||
(value < 60) ? kDefaultFCMGlobalRuleSyncDeadline : value;
|
||||
(FCMIntervalValue < 60) ? kDefaultFCMGlobalRuleSyncDeadline : FCMIntervalValue;
|
||||
|
||||
// Check if our sync interval has changed
|
||||
NSUInteger intervalValue = [resp[kFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.fullSyncInterval = (intervalValue < 60) ? kDefaultFullSyncInterval : intervalValue;
|
||||
|
||||
if ([resp[kClientMode] isEqual:kClientModeMonitor]) {
|
||||
self.syncState.clientMode = SNTClientModeMonitor;
|
||||
@@ -121,12 +123,16 @@
|
||||
self.syncState.clientMode = SNTClientModeLockdown;
|
||||
}
|
||||
|
||||
if ([resp[kWhitelistRegex] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.whitelistRegex = resp[kWhitelistRegex];
|
||||
if ([resp[kAllowedPathRegex] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.allowlistRegex = resp[kAllowedPathRegex];
|
||||
} else if ([resp[kAllowedPathRegexDeprecated] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.allowlistRegex = resp[kAllowedPathRegexDeprecated];
|
||||
}
|
||||
|
||||
if ([resp[kBlacklistRegex] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.blacklistRegex = resp[kBlacklistRegex];
|
||||
if ([resp[kBlockedPathRegex] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.blocklistRegex = resp[kBlockedPathRegex];
|
||||
} else if ([resp[kBlockedPathRegexDeprecated] isKindOfClass:[NSString class]]) {
|
||||
self.syncState.blocklistRegex = resp[kBlockedPathRegexDeprecated];
|
||||
}
|
||||
|
||||
if ([resp[kCleanSync] boolValue]) {
|
||||
|
||||
@@ -60,11 +60,11 @@
|
||||
}];
|
||||
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
|
||||
LOGI(@"Added %lu rules", newRules.count);
|
||||
LOGI(@"Processed %lu rules", newRules.count);
|
||||
|
||||
// Send out push notifications about any newly whitelisted binaries
|
||||
// Send out push notifications about any newly allowed binaries
|
||||
// that had been previously blocked by santad.
|
||||
[self.syncState.whitelistNotificationQueue addOperationWithBlock:^{
|
||||
[self.syncState.allowlistNotificationQueue addOperationWithBlock:^{
|
||||
[self announceUnblockingRules:newRules];
|
||||
}];
|
||||
|
||||
@@ -82,32 +82,38 @@
|
||||
NSDictionary *requestDict = cursor ? @{kCursor : cursor} : @{};
|
||||
NSDictionary *response = [self performRequest:[self requestWithDictionary:requestDict]];
|
||||
|
||||
if (![response isKindOfClass:[NSDictionary class]]) {
|
||||
if (![response isKindOfClass:[NSDictionary class]] ||
|
||||
![response[kRules] isKindOfClass:[NSArray class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
for (NSDictionary *ruleDict in response[kRules]) {
|
||||
SNTRule *rule = [self ruleFromDictionary:ruleDict];
|
||||
if (rule) [newRules addObject:rule];
|
||||
if (rule) {
|
||||
[newRules addObject:rule];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
LOGI(@"Received %u rules", count);
|
||||
cursor = response[kCursor];
|
||||
} while (cursor);
|
||||
return newRules;
|
||||
}
|
||||
|
||||
// Send out push notifications for whitelisted bundles/binaries whose rule download was preceded by
|
||||
// Send out push notifications for allowed bundles/binaries whose rule download was preceded by
|
||||
// an associated announcing FCM message.
|
||||
- (void)announceUnblockingRules:(NSArray<SNTRule *> *)newRules {
|
||||
if (!self.syncState.targetedRuleSync) return;
|
||||
|
||||
NSMutableArray *processed = [NSMutableArray array];
|
||||
|
||||
for (NSString *key in self.syncState.whitelistNotifications) {
|
||||
for (NSString *key in self.syncState.allowlistNotifications) {
|
||||
// Each notifier object is a dictionary with name and count keys. If the count has been
|
||||
// decremented to zero, then this means that we have downloaded all of the rules associated with
|
||||
// this SHA256 hash (which might be a bundle hash or a binary hash), in which case we are OK to
|
||||
// show a notification that the named bundle/binary can be run.
|
||||
NSDictionary *notifier = self.syncState.whitelistNotifications[key];
|
||||
NSDictionary *notifier = self.syncState.allowlistNotifications[key];
|
||||
NSNumber *remaining = notifier[kFileBundleBinaryCount];
|
||||
if (remaining && [remaining intValue] == 0) {
|
||||
[processed addObject:key];
|
||||
@@ -117,8 +123,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all entries from whitelistNotifications dictionary that had zero count.
|
||||
[self.syncState.whitelistNotifications removeObjectsForKeys:processed];
|
||||
// Remove all entries from allowlistNotifications dictionary that had zero count.
|
||||
[self.syncState.allowlistNotifications removeObjectsForKeys:processed];
|
||||
}
|
||||
|
||||
|
||||
@@ -133,14 +139,18 @@
|
||||
if (newRule.shasum.length != 64) return nil;
|
||||
|
||||
NSString *policyString = dict[kRulePolicy];
|
||||
if ([policyString isEqual:kRulePolicyWhitelist]) {
|
||||
newRule.state = SNTRuleStateWhitelist;
|
||||
} else if ([policyString isEqual:kRulePolicyWhitelistCompiler]) {
|
||||
newRule.state = SNTRuleStateWhitelistCompiler;
|
||||
} else if ([policyString isEqual:kRulePolicyBlacklist]) {
|
||||
newRule.state = SNTRuleStateBlacklist;
|
||||
} else if ([policyString isEqual:kRulePolicySilentBlacklist]) {
|
||||
newRule.state = SNTRuleStateSilentBlacklist;
|
||||
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 {
|
||||
@@ -162,7 +172,7 @@
|
||||
}
|
||||
|
||||
// Check rule for extra notification related info.
|
||||
if (newRule.state == SNTRuleStateWhitelist || newRule.state == SNTRuleStateWhitelistCompiler) {
|
||||
if (newRule.state == SNTRuleStateAllow || newRule.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];
|
||||
@@ -171,10 +181,10 @@
|
||||
}
|
||||
|
||||
// As we read in rules, we update the "remaining count" information stored in
|
||||
// whitelistNotifications. This count represents the number of rules associated with the primary
|
||||
// allowlistNotifications. This count represents the number of rules associated with the primary
|
||||
// hash that still need to be downloaded and added.
|
||||
[self.syncState.whitelistNotificationQueue addOperationWithBlock:^{
|
||||
NSMutableDictionary *notifier = self.syncState.whitelistNotifications[primaryHash];
|
||||
[self.syncState.allowlistNotificationQueue addOperationWithBlock:^{
|
||||
NSMutableDictionary *notifier = self.syncState.allowlistNotifications[primaryHash];
|
||||
if (notifier) {
|
||||
NSNumber *ruleCount = dict[kFileBundleBinaryCount];
|
||||
NSNumber *remaining = notifier[kFileBundleBinaryCount];
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
NSData *compressed = [requestBody zlibCompressed];
|
||||
if (compressed) {
|
||||
requestBody = compressed;
|
||||
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
|
||||
[req setValue:self.syncState.compressedContentEncoding forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
|
||||
[req setHTTPBody:requestBody];
|
||||
@@ -83,20 +83,33 @@
|
||||
- (NSDictionary *)performRequest:(NSURLRequest *)request timeout:(NSTimeInterval)timeout {
|
||||
NSHTTPURLResponse *response;
|
||||
NSError *error;
|
||||
NSData *data = [self performRequest:request timeout:timeout response:&response error:&error];
|
||||
NSData *data;
|
||||
|
||||
// If the original request failed, attempt to get a new XSRF token and try again.
|
||||
// Unfortunately some servers cause NSURLSession to return 'client cert required' or
|
||||
// 'could not parse response' when a 403 occurs and SSL cert auth is enabled.
|
||||
if ((response.statusCode == 403 ||
|
||||
error.code == NSURLErrorClientCertificateRequired ||
|
||||
error.code == NSURLErrorCannotParseResponse) &&
|
||||
[self fetchXSRFToken]) {
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
[mutableRequest setValue:self.syncState.xsrfToken forHTTPHeaderField:kXSRFToken];
|
||||
return [self performRequest:mutableRequest timeout:timeout];
|
||||
for (int attempt = 1; attempt < 6; ++attempt) {
|
||||
if (attempt > 1) {
|
||||
struct timespec ts = {.tv_sec = (attempt * 2)};
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
LOGD(@"Performing request, attempt %d", attempt);
|
||||
data = [self performRequest:request timeout:timeout response:&response error:&error];
|
||||
if (response.statusCode == 200) break;
|
||||
|
||||
// If the original request failed because of an auth error, attempt to get a new XSRF token and
|
||||
// try again. Unfortunately some servers cause NSURLSession to return 'client cert required' or
|
||||
// 'could not parse response' when a 403 occurs and SSL cert auth is enabled.
|
||||
if ((response.statusCode == 403 ||
|
||||
error.code == NSURLErrorClientCertificateRequired ||
|
||||
error.code == NSURLErrorCannotParseResponse) &&
|
||||
[self fetchXSRFToken]) {
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
[mutableRequest setValue:self.syncState.xsrfToken forHTTPHeaderField:kXSRFToken];
|
||||
request = mutableRequest;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the final attempt resulted in an error, log the error and return nil.
|
||||
if (response.statusCode != 200) {
|
||||
long code;
|
||||
NSString *errStr;
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
/// An XSRF token to send in the headers with each request.
|
||||
@property NSString *xsrfToken;
|
||||
|
||||
/// Full sync interval in seconds without FCM to update kDefaultFullSyncInterval => when FCM
|
||||
/// is not used, defaults to 10m.
|
||||
@property NSUInteger fullSyncInterval;
|
||||
|
||||
/// An FCM token to subscribe to push notifications.
|
||||
@property(copy) NSString *FCMToken;
|
||||
|
||||
@@ -50,8 +54,8 @@
|
||||
|
||||
/// Settings sent from server during preflight that are set during postflight.
|
||||
@property SNTClientMode clientMode;
|
||||
@property NSString *whitelistRegex;
|
||||
@property NSString *blacklistRegex;
|
||||
@property NSString *allowlistRegex;
|
||||
@property NSString *blocklistRegex;
|
||||
|
||||
/// Clean sync flag, if True, all existing rules should be deleted before inserting any new rules.
|
||||
@property BOOL cleanSync;
|
||||
@@ -69,9 +73,13 @@
|
||||
@property BOOL targetedRuleSync;
|
||||
|
||||
/// Reference to the sync manager's ruleSyncCache. Used to lookup binary names for notifications.
|
||||
@property(weak) NSMutableDictionary *whitelistNotifications;
|
||||
@property(weak) NSMutableDictionary *allowlistNotifications;
|
||||
|
||||
/// Reference to the serial operation queue used for accessing whitelistNotifications.
|
||||
@property(weak) NSOperationQueue *whitelistNotificationQueue;
|
||||
/// Reference to the serial operation queue used for accessing allowlistNotifications.
|
||||
@property(weak) NSOperationQueue *allowlistNotificationQueue;
|
||||
|
||||
/// The header value for ContentEncoding when sending compressed content.
|
||||
/// Either "deflate" (default) or "zlib".
|
||||
@property(copy) NSString *compressedContentEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@@ -184,8 +184,8 @@
|
||||
XCTAssertTrue([sut sync]);
|
||||
XCTAssertEqual(self.syncState.clientMode, SNTClientModeMonitor);
|
||||
XCTAssertEqual(self.syncState.eventBatchSize, 100);
|
||||
XCTAssertNil(self.syncState.whitelistRegex);
|
||||
XCTAssertNil(self.syncState.blacklistRegex);
|
||||
XCTAssertNil(self.syncState.allowlistRegex);
|
||||
XCTAssertNil(self.syncState.blocklistRegex);
|
||||
}
|
||||
|
||||
- (void)testPreflightDatabaseCounts {
|
||||
@@ -375,15 +375,15 @@
|
||||
|
||||
NSArray *rules = @[
|
||||
[[SNTRule alloc] initWithShasum:@"ee382e199f7eda58863a93a7854b930ade35798bc6856ee8e6ab6ce9277f0eab"
|
||||
state:SNTRuleStateBlacklist
|
||||
state:SNTRuleStateBlock
|
||||
type:SNTRuleTypeBinary
|
||||
customMsg:@""],
|
||||
[[SNTRule alloc] initWithShasum:@"46f8c706d0533a54554af5fc163eea704f10c08b30f8a5db12bfdc04fb382fc3"
|
||||
state:SNTRuleStateWhitelist
|
||||
state:SNTRuleStateAllow
|
||||
type:SNTRuleTypeCertificate
|
||||
customMsg:@""],
|
||||
[[SNTRule alloc] initWithShasum:@"7846698e47ef41be80b83fb9e2b98fa6dc46c9188b068bff323c302955a00142"
|
||||
state:SNTRuleStateBlacklist
|
||||
state:SNTRuleStateBlock
|
||||
type:SNTRuleTypeCertificate
|
||||
customMsg:@"Hi There"],
|
||||
];
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"whitelist_regex": null, "client_mode": "MONITOR", "blacklist_regex": null, "batch_size": 100}
|
||||
{"allowed_path_regex": null, "client_mode": "MONITOR", "blocked_path_regex": null, "batch_size": 100}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"whitelist_regex": null, "client_mode": "LOCKDOWN", "blacklist_regex": null, "batch_size": 100}
|
||||
{"whitelist_regex": null, "client_mode": "LOCKDOWN", "blacklist_regex": null, "batch_size": 100}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"rules": [{"rule_type": "BINARY", "policy": "BLACKLIST", "sha256": "ee382e199f7eda58863a93a7854b930ade35798bc6856ee8e6ab6ce9277f0eab", "custom_msg": ""},{"rule_type": "CERTIFICATE", "policy": "WHITELIST", "sha256": "46f8c706d0533a54554af5fc163eea704f10c08b30f8a5db12bfdc04fb382fc3", "custom_msg": ""}],"cursor": "this-is-a-cursor="}
|
||||
{"rules": [{"rule_type": "BINARY", "policy": "BLOCKLIST", "sha256": "ee382e199f7eda58863a93a7854b930ade35798bc6856ee8e6ab6ce9277f0eab", "custom_msg": ""},{"rule_type": "CERTIFICATE", "policy": "ALLOWLIST", "sha256": "46f8c706d0533a54554af5fc163eea704f10c08b30f8a5db12bfdc04fb382fc3", "custom_msg": ""}],"cursor": "this-is-a-cursor="}
|
||||
|
||||
@@ -16,6 +16,8 @@ objc_library(
|
||||
"EventProviders/SNTDriverManager.m",
|
||||
"EventProviders/SNTEndpointSecurityManager.h",
|
||||
"EventProviders/SNTEndpointSecurityManager.mm",
|
||||
"EventProviders/SNTCachingEndpointSecurityManager.h",
|
||||
"EventProviders/SNTCachingEndpointSecurityManager.mm",
|
||||
"EventProviders/SNTEventProvider.h",
|
||||
"Logs/SNTEventLog.h",
|
||||
"Logs/SNTEventLog.m",
|
||||
@@ -50,6 +52,7 @@ objc_library(
|
||||
"IOKit",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SNTBlockMessage",
|
||||
"//Source/common:SNTCachedDecision",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
@@ -77,8 +80,12 @@ macos_bundle(
|
||||
infoplists = ["Info.plist"],
|
||||
linkopts = ["-execute"],
|
||||
minimum_os_version = "10.9",
|
||||
provisioning_profile = select({
|
||||
"//:ci_build": None,
|
||||
"//conditions:default": "Santa_Daemon_Dev.provisionprofile",
|
||||
}),
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":santad_lib"],
|
||||
)
|
||||
|
||||
@@ -115,6 +122,7 @@ santa_unit_test(
|
||||
"bsm",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SNTBlockMessage",
|
||||
"//Source/common:SNTCachedDecision",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import "Source/santad/DataLayer/SNTDatabaseTable.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
@interface SNTDatabaseTable ()
|
||||
@@ -27,16 +29,25 @@
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
__block BOOL bail = NO;
|
||||
|
||||
[db inDatabase:^(FMDatabase *db) {
|
||||
if (![db goodConnection]) {
|
||||
if ([db lastErrorCode] == SQLITE_LOCKED) {
|
||||
LOGW(@"The database '%@' is locked by another process. Aborting.", [db databasePath]);
|
||||
[db close];
|
||||
bail = YES;
|
||||
return;
|
||||
}
|
||||
[db close];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[db databasePath] error:NULL];
|
||||
[db open];
|
||||
}
|
||||
}];
|
||||
|
||||
_dbQ = db;
|
||||
if (bail) return nil;
|
||||
|
||||
_dbQ = db;
|
||||
[self updateTableSchema];
|
||||
}
|
||||
return self;
|
||||
|
||||
@@ -56,7 +56,10 @@
|
||||
NSData *eventData;
|
||||
NSNumber *idx = [rs objectForColumn:@"idx"];
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
eventData = [NSKeyedArchiver archivedDataWithRootObject:se];
|
||||
#pragma clang diagnostic pop
|
||||
[db executeUpdate:@"UPDATE events SET eventdata=? WHERE idx=?", eventData, idx];
|
||||
} @catch (NSException *exception) {
|
||||
[db executeUpdate:@"DELETE FROM events WHERE idx=?", idx];
|
||||
@@ -98,7 +101,10 @@
|
||||
|
||||
NSData *eventData;
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
|
||||
#pragma clang diagnostic pop
|
||||
} @catch (NSException *exception) {
|
||||
continue;
|
||||
}
|
||||
@@ -158,7 +164,10 @@
|
||||
SNTStoredEvent *event;
|
||||
|
||||
@try {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
|
||||
#pragma clang diagnostic pop
|
||||
event.idx = event.idx ?: @((uint32_t)[rs intForColumn:@"idx"]);
|
||||
} @catch (NSException *exception) {
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
__block NSUInteger count = 0;
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
count = [db longForQuery:@"SELECT COUNT(*) FROM rules WHERE state=?",
|
||||
@(SNTRuleStateWhitelistCompiler)];
|
||||
@(SNTRuleStateAllowCompiler)];
|
||||
}];
|
||||
return count;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
__block NSUInteger count = 0;
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
count = [db longForQuery:@"SELECT COUNT(*) FROM rules WHERE state=?",
|
||||
@(SNTRuleStateWhitelistTransitive)];
|
||||
@(SNTRuleStateAllowTransitive)];
|
||||
}];
|
||||
return count;
|
||||
}
|
||||
@@ -216,7 +216,7 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
// if no existing rule has matched.
|
||||
if (!rule && [certificateSHA256 isEqual:self.launchdCSInfo.leafCertificate.SHA256]) {
|
||||
rule = [[SNTRule alloc] initWithShasum:certificateSHA256
|
||||
state:SNTRuleStateWhitelist
|
||||
state:SNTRuleStateAllow
|
||||
type:SNTRuleTypeCertificate
|
||||
customMsg:nil
|
||||
timestamp:0];
|
||||
@@ -278,23 +278,23 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
}
|
||||
|
||||
- (BOOL)addedRulesShouldFlushDecisionCache:(NSArray *)rules {
|
||||
// Check for non-plain-whitelist rules first before querying the database.
|
||||
// Check for non-plain-allowlist rules first before querying the database.
|
||||
for (SNTRule *rule in rules) {
|
||||
if (rule.state != SNTRuleStateWhitelist) return YES;
|
||||
if (rule.state != SNTRuleStateAllow) return YES;
|
||||
}
|
||||
|
||||
// If still here, then all rules in the array are whitelist rules. So now we look for whitelist
|
||||
// rules where there is a previously existing whitelist compiler rule for the same shasum.
|
||||
// If still here, then all rules in the array are allowlist rules. So now we look for allowlist
|
||||
// rules where there is a previously existing allowlist compiler rule for the same shasum.
|
||||
// If so we find such a rule, then cache should be flushed.
|
||||
__block BOOL flushDecisionCache = NO;
|
||||
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
||||
for (SNTRule *rule in rules) {
|
||||
// Whitelist certificate rules are ignored
|
||||
// Allowlist certificate rules are ignored
|
||||
if (rule.type == SNTRuleTypeCertificate) continue;
|
||||
|
||||
if ([db longForQuery:
|
||||
@"SELECT COUNT(*) FROM rules WHERE shasum=? AND type=? AND state=? LIMIT 1",
|
||||
rule.shasum, @(SNTRuleTypeBinary), @(SNTRuleStateWhitelistCompiler)] > 0) {
|
||||
rule.shasum, @(SNTRuleTypeBinary), @(SNTRuleStateAllowCompiler)] > 0) {
|
||||
flushDecisionCache = YES;
|
||||
break;
|
||||
}
|
||||
@@ -330,7 +330,7 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
if (![db executeUpdate:@"DELETE FROM rules WHERE state=? AND timestamp < ?",
|
||||
@(SNTRuleStateWhitelistTransitive), @(outdatedTimestamp)]) {
|
||||
@(SNTRuleStateAllowTransitive), @(outdatedTimestamp)]) {
|
||||
LOGE(@"Could not remove outdated transitive rules");
|
||||
}
|
||||
}];
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
- (SNTRule *)_exampleBinaryRule {
|
||||
SNTRule *r = [[SNTRule alloc] init];
|
||||
r.shasum = @"a";
|
||||
r.state = SNTRuleStateBlacklist;
|
||||
r.state = SNTRuleStateBlock;
|
||||
r.type = SNTRuleTypeBinary;
|
||||
r.customMsg = @"A rule";
|
||||
return r;
|
||||
@@ -46,7 +46,7 @@
|
||||
- (SNTRule *)_exampleCertRule {
|
||||
SNTRule *r = [[SNTRule alloc] init];
|
||||
r.shasum = @"b";
|
||||
r.state = SNTRuleStateWhitelist;
|
||||
r.state = SNTRuleStateAllow;
|
||||
r.type = SNTRuleTypeCertificate;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// Copyright 2021 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
|
||||
|
||||
@interface SNTCachingEndpointSecurityManager : SNTEndpointSecurityManager
|
||||
@end
|
||||
@@ -0,0 +1,200 @@
|
||||
/// Copyright 2021 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santad/EventProviders/SNTCachingEndpointSecurityManager.h"
|
||||
|
||||
#import "Source/common/SantaCache.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
#include <bsm/libbsm.h>
|
||||
#include <EndpointSecurity/EndpointSecurity.h>
|
||||
|
||||
uint64_t GetCurrentUptime() {
|
||||
return clock_gettime_nsec_np(CLOCK_MONOTONIC);
|
||||
}
|
||||
template<> uint64_t SantaCacheHasher<santa_vnode_id_t>(santa_vnode_id_t const& t) {
|
||||
return (SantaCacheHasher<uint64_t>(t.fsid) << 1) ^ SantaCacheHasher<uint64_t>(t.fileid);
|
||||
}
|
||||
|
||||
@implementation SNTCachingEndpointSecurityManager {
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *_decisionCache;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// TODO(rah): Consider splitting into root/non-root cache
|
||||
_decisionCache = new SantaCache<santa_vnode_id_t, uint64_t>();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_decisionCache) delete _decisionCache;
|
||||
}
|
||||
|
||||
- (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
|
||||
auto vnode_id = [self vnodeIDForFile:m->event.exec.target->executable];
|
||||
while (true) {
|
||||
// Check to see if item is in cache
|
||||
auto return_action = [self checkCache:vnode_id];
|
||||
|
||||
// If item was in cache with a valid response, return it.
|
||||
// If item is in cache but hasn't received a response yet, sleep for a bit.
|
||||
// If item is not in cache, break out of loop and forward request to callback.
|
||||
if (RESPONSE_VALID(return_action)) {
|
||||
switch (return_action) {
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW_COMPILER: {
|
||||
pid_t pid = audit_token_to_pid(m->process->audit_token);
|
||||
[self setIsCompilerPID:pid];
|
||||
// Don't let ES cache compilers
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
|
||||
break;
|
||||
}
|
||||
return YES;
|
||||
} else if (return_action == ACTION_REQUEST_BINARY || return_action == ACTION_RESPOND_ACK) {
|
||||
// TODO(rah): Look at a replacement for msleep(), maybe NSCondition
|
||||
usleep(5000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[self addToCache:vnode_id decision:ACTION_REQUEST_BINARY currentTicks:0];
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
|
||||
API_AVAILABLE(macos(10.15)) {
|
||||
es_respond_result_t ret;
|
||||
switch (action) {
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
[self setIsCompilerPID:sm.pid];
|
||||
|
||||
// Allow the exec and cache in our internal cache but don't let ES cache, because then
|
||||
// we won't see future execs of the compiler in order to record the PID.
|
||||
[self addToCache:sm.vnode_id
|
||||
decision:ACTION_RESPOND_ALLOW_COMPILER
|
||||
currentTicks:GetCurrentUptime()];
|
||||
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
|
||||
ES_AUTH_RESULT_ALLOW, false);
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE:
|
||||
[self addToCache:sm.vnode_id decision:ACTION_RESPOND_ALLOW currentTicks:GetCurrentUptime()];
|
||||
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
|
||||
ES_AUTH_RESULT_ALLOW, true);
|
||||
break;
|
||||
case ACTION_RESPOND_DENY:
|
||||
[self addToCache:sm.vnode_id decision:ACTION_RESPOND_DENY currentTicks:GetCurrentUptime()];
|
||||
OS_FALLTHROUGH;
|
||||
case ACTION_RESPOND_TOOLONG:
|
||||
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
|
||||
ES_AUTH_RESULT_DENY, false);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
return ES_RESPOND_RESULT_SUCCESS;
|
||||
default:
|
||||
ret = ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)addToCache:(santa_vnode_id_t)identifier
|
||||
decision:(santa_action_t)decision
|
||||
currentTicks:(uint64_t)microsecs {
|
||||
switch (decision) {
|
||||
case ACTION_REQUEST_BINARY:
|
||||
_decisionCache->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
_decisionCache->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
case ACTION_RESPOND_DENY: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
if (!_decisionCache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
_decisionCache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
_decisionCache->set(identifier, val, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// TODO(rah): Look at a replacement for wakeup(), maybe NSCondition
|
||||
}
|
||||
|
||||
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly API_AVAILABLE(macos(10.15)) {
|
||||
_decisionCache->clear();
|
||||
if (!self.connectionEstablished) return YES; // if not connected, there's nothing to flush.
|
||||
return es_clear_cache(self.client) == ES_CLEAR_CACHE_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)cacheCounts {
|
||||
return @[@(_decisionCache->count()), @(0)];
|
||||
}
|
||||
|
||||
- (NSArray<NSNumber *> *)cacheBucketCount {
|
||||
// TODO: add this, maybe.
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
|
||||
auto result = ACTION_UNSET;
|
||||
uint64_t decision_time = 0;
|
||||
|
||||
uint64_t cache_val = _decisionCache->get(vnodeID);
|
||||
if (cache_val == 0) return result;
|
||||
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
result = (santa_action_t)(cache_val >> 56);
|
||||
decision_time = (cache_val & ~(0xFF00000000000000));
|
||||
|
||||
if (RESPONSE_VALID(result)) {
|
||||
if (result == ACTION_RESPOND_DENY) {
|
||||
auto expiry_time = decision_time + (500 * 100000); // kMaxCacheDenyTimeMilliseconds
|
||||
if (expiry_time < GetCurrentUptime()) {
|
||||
_decisionCache->remove(vnodeID);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeID {
|
||||
_decisionCache->remove(vnodeID);
|
||||
// TODO(rah): Look at a replacement for wakeup(), maybe NSCondition
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -310,6 +310,8 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
|
||||
}
|
||||
|
||||
- (void)fileModificationPrefixFilterAdd:(NSArray *)filters {
|
||||
while (!self.connectionEstablished) usleep(100000); // 100ms
|
||||
|
||||
uint64_t n = 0;
|
||||
uint32_t n_len = 1;
|
||||
|
||||
@@ -334,6 +336,8 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
|
||||
LOGE(@"Prefix filter tree is full!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOGI(@"Added prefix filter: %s", buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,5 +17,18 @@
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
#include "Source/santad/EventProviders/SNTEventProvider.h"
|
||||
|
||||
#include <EndpointSecurity/EndpointSecurity.h>
|
||||
|
||||
// Gleaned from https://opensource.apple.com/source/xnu/xnu-4903.241.1/bsd/sys/proc_internal.h
|
||||
const pid_t PID_MAX = 99999;
|
||||
|
||||
@interface SNTEndpointSecurityManager : NSObject<SNTEventProvider>
|
||||
- (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file;
|
||||
|
||||
- (BOOL)isCompilerPID:(pid_t)pid;
|
||||
- (void)setIsCompilerPID:(pid_t)pid;
|
||||
- (void)setNotCompilerPID:(pid_t)pid;
|
||||
|
||||
@property(readonly, nonatomic) es_client_t *client;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,20 +16,19 @@
|
||||
|
||||
#include "Source/common/SNTPrefixTree.h"
|
||||
|
||||
#import "Source/common/SantaCache.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
#include <EndpointSecurity/EndpointSecurity.h>
|
||||
#include <atomic>
|
||||
#include <bsm/libbsm.h>
|
||||
#include <libproc.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
// Gleaned from https://opensource.apple.com/source/xnu/xnu-4903.241.1/bsd/sys/proc_internal.h
|
||||
#define PID_MAX 99999
|
||||
@interface SNTEndpointSecurityManager () {
|
||||
std::atomic<bool> _compilerPIDs[PID_MAX];
|
||||
}
|
||||
|
||||
@interface SNTEndpointSecurityManager ()
|
||||
|
||||
@property(nonatomic) es_client_t *client;
|
||||
@property(nonatomic) SNTPrefixTree *prefixTree;
|
||||
@property(nonatomic, copy) void (^decisionCallback)(santa_message_t);
|
||||
@property(nonatomic, copy) void (^logCallback)(santa_message_t);
|
||||
@@ -39,9 +38,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation SNTEndpointSecurityManager {
|
||||
std::atomic<bool> _compilerPIDs[PID_MAX];
|
||||
}
|
||||
@implementation SNTEndpointSecurityManager
|
||||
|
||||
- (instancetype)init API_AVAILABLE(macos(10.15)) {
|
||||
self = [super init];
|
||||
@@ -72,12 +69,27 @@
|
||||
|
||||
- (void)establishClient API_AVAILABLE(macos(10.15)) {
|
||||
while (!self.client) {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
es_client_t *client = NULL;
|
||||
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
|
||||
pid_t pid = audit_token_to_pid(m->process->audit_token);
|
||||
int pidversion = audit_token_to_pidversion(m->process->audit_token);
|
||||
|
||||
// If enabled, skip any action generated from another endpoint security client.
|
||||
if (m->process->is_es_client && config.ignoreOtherEndpointSecurityClients) {
|
||||
if (m->action_type == ES_ACTION_TYPE_AUTH) {
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, false);
|
||||
}
|
||||
if (self.selfPID != pid) {
|
||||
LOGD(@"Skipping event type: 0x%x from es_client pid: %d", m->event_type, pid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the following checks on this serial queue.
|
||||
// Some checks are simple filters that avoid copying m.
|
||||
// However, the bulk of the work done here is to support transitive whitelisting.
|
||||
pid_t pid = audit_token_to_pid(m->process->audit_token);
|
||||
switch (m->event_type) {
|
||||
case ES_EVENT_TYPE_NOTIFY_EXEC: {
|
||||
// Deny results are currently logged when ES_EVENT_TYPE_AUTH_EXEC posts a deny.
|
||||
@@ -91,8 +103,11 @@
|
||||
// Ignore unmodified files
|
||||
if (!m->event.close.modified) return;
|
||||
|
||||
// Remove from decision cache in case this is invalidating a cached binary.
|
||||
[self removeCacheEntryForVnodeID:[self vnodeIDForFile:m->event.close.target]];
|
||||
|
||||
// Create a transitive rule if the file was modified by a running compiler
|
||||
if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) {
|
||||
if ([self isCompilerPID:pid]) {
|
||||
santa_message_t sm = {};
|
||||
BOOL truncated = [self populateBufferFromESFile:m->event.close.target
|
||||
buffer:sm.path
|
||||
@@ -102,8 +117,12 @@
|
||||
sm.path, pid);
|
||||
break;
|
||||
}
|
||||
if ([@(sm.path) hasPrefix:@"/dev/"]) {
|
||||
break;
|
||||
}
|
||||
sm.action = ACTION_NOTIFY_WHITELIST;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
LOGI(@"CLOSE: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
|
||||
self.decisionCallback(sm);
|
||||
}
|
||||
@@ -113,7 +132,7 @@
|
||||
}
|
||||
case ES_EVENT_TYPE_NOTIFY_RENAME: {
|
||||
// Create a transitive rule if the file was renamed by a running compiler
|
||||
if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) {
|
||||
if ([self isCompilerPID:pid]) {
|
||||
santa_message_t sm = {};
|
||||
BOOL truncated = [self populateRenamedNewPathFromESMessage:m->event.rename
|
||||
buffer:sm.path
|
||||
@@ -123,8 +142,12 @@
|
||||
sm.path, pid);
|
||||
break;
|
||||
}
|
||||
if ([@(sm.path) hasPrefix:@"/dev/"]) {
|
||||
break;
|
||||
}
|
||||
sm.action = ACTION_NOTIFY_WHITELIST;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
LOGI(@"RENAME: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
|
||||
self.decisionCallback(sm);
|
||||
}
|
||||
@@ -134,9 +157,37 @@
|
||||
}
|
||||
case ES_EVENT_TYPE_NOTIFY_EXIT: {
|
||||
// Update the set of running compiler PIDs
|
||||
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
|
||||
[self setNotCompilerPID:pid];
|
||||
|
||||
// Do not log exits
|
||||
// Skip the standard pipeline and just log.
|
||||
if (![config enableForkAndExitLogging]) return;
|
||||
santa_message_t sm = {};
|
||||
sm.action = ACTION_NOTIFY_EXIT;
|
||||
sm.pid = pid;
|
||||
sm.pidversion = pidversion;
|
||||
sm.ppid = m->process->original_ppid;
|
||||
audit_token_t at = m->process->audit_token;
|
||||
sm.uid = audit_token_to_ruid(at);
|
||||
sm.gid = audit_token_to_rgid(at);
|
||||
dispatch_async(self.esNotifyQueue, ^{
|
||||
self.logCallback(sm);
|
||||
});
|
||||
return;
|
||||
}
|
||||
case ES_EVENT_TYPE_NOTIFY_FORK: {
|
||||
// Skip the standard pipeline and just log.
|
||||
if (![config enableForkAndExitLogging]) return;
|
||||
santa_message_t sm = {};
|
||||
sm.action = ACTION_NOTIFY_FORK;
|
||||
sm.ppid = m->event.fork.child->original_ppid;
|
||||
audit_token_t at = m->event.fork.child->audit_token;
|
||||
sm.pid = audit_token_to_pid(at);
|
||||
sm.pidversion = audit_token_to_pidversion(at);
|
||||
sm.uid = audit_token_to_ruid(at);
|
||||
sm.gid = audit_token_to_rgid(at);
|
||||
dispatch_async(self.esNotifyQueue, ^{
|
||||
self.logCallback(sm);
|
||||
});
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
@@ -146,15 +197,12 @@
|
||||
|
||||
switch (m->action_type) {
|
||||
case ES_ACTION_TYPE_AUTH: {
|
||||
// Create a timer to deny the execution 2 seconds before the deadline,
|
||||
// Create a timer to deny the execution 5 seconds before the deadline,
|
||||
// if a response hasn't already been sent. This block will still be enqueued if
|
||||
// the the deadline - 2 secs is < DISPATCH_TIME_NOW.
|
||||
// As of 10.15.2, a typical deadline is 60 seconds.
|
||||
// TODO(bur/rah): Possibly cache decisions made after the deadline. Currently a
|
||||
// large enough binary will never be allowed to execute. This should be a rare edge case;
|
||||
// it's probably not worth adding a caching layer just for this.
|
||||
// the the deadline - 5 secs is < DISPATCH_TIME_NOW.
|
||||
// As of 10.15.5, a typical deadline is 60 seconds.
|
||||
auto responded = std::make_shared<std::atomic<bool>>(false);
|
||||
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -2), self.esAuthQueue, ^(void) {
|
||||
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -5), self.esAuthQueue, ^(void) {
|
||||
if (responded->load()) return;
|
||||
LOGE(@"Deadline reached: deny pid=%d ret=%d",
|
||||
pid, es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false));
|
||||
@@ -190,7 +238,7 @@
|
||||
switch (ret) {
|
||||
case ES_NEW_CLIENT_RESULT_SUCCESS:
|
||||
LOGI(@"Connected to EndpointSecurity");
|
||||
self.client = client;
|
||||
_client = client;
|
||||
return;
|
||||
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
|
||||
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
|
||||
@@ -205,6 +253,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)respondFromCache:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)messageHandler:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
|
||||
santa_message_t sm = {};
|
||||
sm.es_message = (void *)m;
|
||||
@@ -215,6 +267,10 @@
|
||||
|
||||
switch (m->event_type) {
|
||||
case ES_EVENT_TYPE_AUTH_EXEC: {
|
||||
if ([self respondFromCache:m]) {
|
||||
return;
|
||||
}
|
||||
|
||||
sm.action = ACTION_REQUEST_BINARY;
|
||||
targetFile = m->event.exec.target->executable;
|
||||
targetProcess = m->event.exec.target;
|
||||
@@ -231,14 +287,59 @@
|
||||
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
|
||||
for (int i = 0; i < argCount; ++i) {
|
||||
es_string_token_t arg = es_exec_arg(&(m->event.exec), i);
|
||||
[args addObject:[[NSString alloc] initWithBytes:arg.data
|
||||
length:arg.length
|
||||
encoding:NSUTF8StringEncoding]];
|
||||
NSString *argStr = [[NSString alloc] initWithBytes:arg.data
|
||||
length:arg.length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if (argStr.length) [args addObject:argStr];
|
||||
}
|
||||
sm.args_array = (void *)CFBridgingRetain(args);
|
||||
callback = self.logCallback;
|
||||
break;
|
||||
}
|
||||
case ES_EVENT_TYPE_AUTH_UNLINK: {
|
||||
es_string_token_t pathToken = m->event.unlink.target->path;
|
||||
NSString *path = [[NSString alloc] initWithBytes:pathToken.data
|
||||
length:pathToken.length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if (([path isEqualToString:@"/private/var/db/santa/rules.db"] ||
|
||||
[path isEqualToString:@"/private/var/db/santa/events.db"]) &&
|
||||
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
|
||||
LOGW(@"Preventing attempt to delete Santa databases!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
}
|
||||
case ES_EVENT_TYPE_AUTH_RENAME: {
|
||||
es_string_token_t pathToken = m->event.rename.source->path;
|
||||
NSString *path = [[NSString alloc] initWithBytes:pathToken.data
|
||||
length:pathToken.length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
if (([path isEqualToString:@"/private/var/db/santa/rules.db"] ||
|
||||
[path isEqualToString:@"/private/var/db/santa/events.db"]) &&
|
||||
audit_token_to_pid(m->process->audit_token) != self.selfPID) {
|
||||
LOGW(@"Preventing attempt to rename Santa databases!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
}
|
||||
case ES_EVENT_TYPE_AUTH_KEXTLOAD: {
|
||||
es_string_token_t identifier = m->event.kextload.identifier;
|
||||
NSString *ident = [[NSString alloc] initWithBytes:identifier.data
|
||||
length:identifier.length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if ([ident isEqualToString:@"com.google.santa-driver"]) {
|
||||
LOGW(@"Preventing attempt to load Santa kext!");
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, true);
|
||||
return;
|
||||
}
|
||||
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
|
||||
return;
|
||||
}
|
||||
case ES_EVENT_TYPE_NOTIFY_CLOSE: {
|
||||
sm.action = ACTION_NOTIFY_WRITE;
|
||||
targetFile = m->event.close.target;
|
||||
@@ -301,6 +402,7 @@
|
||||
sm.uid = audit_token_to_ruid(targetProcess->audit_token);
|
||||
sm.gid = audit_token_to_rgid(targetProcess->audit_token);
|
||||
sm.pid = audit_token_to_pid(targetProcess->audit_token);
|
||||
sm.pidversion = audit_token_to_pidversion(targetProcess->audit_token);
|
||||
sm.ppid = targetProcess->original_ppid;
|
||||
proc_name((m->event_type == ES_EVENT_TYPE_AUTH_EXEC) ? sm.ppid : sm.pid, sm.pname, 1024);
|
||||
callback(sm);
|
||||
@@ -310,9 +412,18 @@
|
||||
while (!self.connectionEstablished) usleep(100000); // 100ms
|
||||
|
||||
self.decisionCallback = callback;
|
||||
es_event_type_t events[] = { ES_EVENT_TYPE_AUTH_EXEC, ES_EVENT_TYPE_NOTIFY_EXIT };
|
||||
es_return_t sret = es_subscribe(self.client, events, 2);
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_AUTH_EXEC: %d", sret);
|
||||
es_event_type_t events[] = {
|
||||
ES_EVENT_TYPE_AUTH_EXEC,
|
||||
ES_EVENT_TYPE_AUTH_UNLINK,
|
||||
ES_EVENT_TYPE_AUTH_RENAME,
|
||||
ES_EVENT_TYPE_AUTH_KEXTLOAD,
|
||||
|
||||
// This is in the decision callback because it's used for detecting
|
||||
// the exit of a 'compiler' used by transitive whitelisting.
|
||||
ES_EVENT_TYPE_NOTIFY_EXIT,
|
||||
};
|
||||
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe to auth events: %d", sret);
|
||||
|
||||
// There's a gap between creating a client and subscribing to events. Creating the client
|
||||
// triggers a cache flush automatically but any events that happen in this gap could be allowed
|
||||
@@ -330,9 +441,10 @@
|
||||
ES_EVENT_TYPE_NOTIFY_LINK,
|
||||
ES_EVENT_TYPE_NOTIFY_RENAME,
|
||||
ES_EVENT_TYPE_NOTIFY_UNLINK,
|
||||
ES_EVENT_TYPE_NOTIFY_FORK,
|
||||
};
|
||||
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_NOTIFY_EXEC: %d", sret);
|
||||
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe to notify events: %d", sret);
|
||||
}
|
||||
|
||||
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
|
||||
@@ -340,12 +452,8 @@
|
||||
es_respond_result_t ret;
|
||||
switch (action) {
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
if (sm.pid >= PID_MAX) {
|
||||
LOGE(@"Unable to watch compiler pid=%d >= pid_max=%d", sm.pid, PID_MAX);
|
||||
} else {
|
||||
LOGD(@"Watching compiler pid=%d path=%s", sm.pid, sm.path);
|
||||
self->_compilerPIDs[sm.pid].store(true);
|
||||
}
|
||||
[self setIsCompilerPID:sm.pid];
|
||||
|
||||
// Allow the exec, but don't cache the decision so subsequent execs of the compiler get
|
||||
// marked appropriately.
|
||||
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
|
||||
@@ -451,4 +559,30 @@
|
||||
return truncated;
|
||||
}
|
||||
|
||||
- (santa_vnode_id_t)vnodeIDForFile:(es_file_t *)file {
|
||||
return {
|
||||
.fsid = (uint64_t)file->stat.st_dev,
|
||||
.fileid = file->stat.st_ino,
|
||||
};
|
||||
}
|
||||
|
||||
- (BOOL)isCompilerPID:(pid_t)pid {
|
||||
return (pid && pid < PID_MAX && self->_compilerPIDs[pid].load());
|
||||
}
|
||||
|
||||
- (void)setIsCompilerPID:(pid_t)pid {
|
||||
if (pid < 1) {
|
||||
LOGE(@"Unable to watch compiler pid=%d", pid);
|
||||
} else if (pid >= PID_MAX) {
|
||||
LOGE(@"Unable to watch compiler pid=%d >= PID_MAX(%d)", pid, PID_MAX);
|
||||
} else {
|
||||
self->_compilerPIDs[pid].store(true);
|
||||
LOGD(@"Watching compiler pid=%d", pid);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setNotCompilerPID:(pid_t)pid {
|
||||
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
- (void)logDeniedExecution:(SNTCachedDecision *)cd withMessage:(santa_message_t)message;
|
||||
- (void)logAllowedExecution:(santa_message_t)message;
|
||||
- (void)logBundleHashingEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
- (void)logFork:(santa_message_t)message;
|
||||
- (void)logExit:(santa_message_t)message;
|
||||
- (void)writeLog:(NSString *)log;
|
||||
|
||||
// Methods for storing, retrieving, and removing cached decisions.
|
||||
|
||||
@@ -82,6 +82,13 @@
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
}
|
||||
|
||||
- (void)logFork:(santa_message_t)message {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
}
|
||||
- (void)logExit:(santa_message_t)message {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
}
|
||||
|
||||
- (void)writeLog:(NSString *)log {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
}
|
||||
@@ -106,7 +113,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Whenever a cached decision resulting from a transitive whitelist rule is used to allow the
|
||||
// Whenever a cached decision resulting from a transitive allowlist rule is used to allow the
|
||||
// execution of a binary, we update the timestamp on the transitive rule in the rules database.
|
||||
// To prevent writing to the database too often, we space out consecutive writes by 3600 seconds.
|
||||
- (void)resetTimestampForCachedDecision:(SNTCachedDecision *)cd {
|
||||
@@ -114,7 +121,7 @@
|
||||
NSDate *lastUpdate = [self.timestampResetMap objectForKey:cd.sha256];
|
||||
if (!lastUpdate || -[lastUpdate timeIntervalSinceNow] > 3600) {
|
||||
SNTRule *rule = [[SNTRule alloc] initWithShasum:cd.sha256
|
||||
state:SNTRuleStateWhitelistTransitive
|
||||
state:SNTRuleStateAllowTransitive
|
||||
type:SNTRuleTypeBinary
|
||||
customMsg:nil];
|
||||
[[SNTDatabaseController ruleTable] resetTimestampForRule:rule];
|
||||
|
||||
@@ -65,8 +65,8 @@
|
||||
char ppath[PATH_MAX] = "(null)";
|
||||
proc_pidpath(message.pid, ppath, PATH_MAX);
|
||||
|
||||
[outStr appendFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|user=%@|gid=%d|group=%@",
|
||||
message.pid, message.ppid, message.pname, ppath,
|
||||
[outStr appendFormat:@"|pid=%d|pidversion=%d|ppid=%d|process=%s|processpath=%s|uid=%d|user=%@|gid=%d|group=%@",
|
||||
message.pid, message.pidversion, message.ppid, message.pname, ppath,
|
||||
message.uid, [self nameForUID:message.uid],
|
||||
message.gid, [self nameForGID:message.gid]];
|
||||
|
||||
@@ -169,8 +169,8 @@
|
||||
mode = @"U"; break;
|
||||
}
|
||||
|
||||
[outLog appendFormat:@"|pid=%d|ppid=%d|uid=%d|user=%@|gid=%d|group=%@|mode=%@|path=%@",
|
||||
message.pid, message.ppid,
|
||||
[outLog appendFormat:@"|pid=%d|pidversion=%d|ppid=%d|uid=%d|user=%@|gid=%d|group=%@|mode=%@|path=%@",
|
||||
message.pid, message.pidversion, message.ppid,
|
||||
message.uid, [self nameForUID:message.uid],
|
||||
message.gid, [self nameForGID:message.gid],
|
||||
mode, [self sanitizeString:@(message.path)]];
|
||||
@@ -268,6 +268,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)logFork:(santa_message_t)message {
|
||||
NSString *s = [NSString stringWithFormat:@"action=FORK|pid=%d|pidversion=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.pidversion, message.ppid, message.uid, message.gid];
|
||||
[self writeLog:s];
|
||||
|
||||
}
|
||||
|
||||
- (void)logExit:(santa_message_t)message {
|
||||
NSString *s = [NSString stringWithFormat:@"action=EXIT|pid=%d|pidversion=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.pidversion, message.ppid, message.uid, message.gid];
|
||||
[self writeLog:s];
|
||||
}
|
||||
|
||||
- (void)writeLog:(NSString *)log {
|
||||
LOGI(@"%@", log);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#import "Source/santad/DataLayer/SNTRuleTable.h"
|
||||
#import "Source/santad/DataLayer/SNTEventTable.h"
|
||||
#import "Source/santad/EventProviders/SNTDriverManager.h"
|
||||
#import "Source/santad/EventProviders/SNTCachingEndpointSecurityManager.h"
|
||||
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
|
||||
#import "Source/santad/EventProviders/SNTEventProvider.h"
|
||||
#import "Source/santad/Logs/SNTFileEventLog.h"
|
||||
@@ -54,10 +55,18 @@
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
SNTConfigurator *configurator = [SNTConfigurator configurator];
|
||||
|
||||
// Choose an event logger.
|
||||
// Locate and connect to driver / SystemExtension
|
||||
if (@available(macOS 10.15, *)) {
|
||||
LOGI(@"Using EndpointSecurity as event provider.");
|
||||
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
|
||||
if ([configurator enableSystemExtension]) {
|
||||
if ([configurator enableSysxCache]) {
|
||||
LOGI(@"Using CachingEndpointSecurity as event provider.");
|
||||
_eventProvider = [[SNTCachingEndpointSecurityManager alloc] init];
|
||||
} else {
|
||||
LOGI(@"Using EndpointSecurity as event provider.");
|
||||
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
|
||||
}
|
||||
} else {
|
||||
LOGI(@"Using Kauth as event provider.");
|
||||
_eventProvider = [[SNTDriverManager alloc] init];
|
||||
@@ -80,8 +89,7 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Choose an event logger.
|
||||
SNTConfigurator *configurator = [SNTConfigurator configurator];
|
||||
|
||||
switch ([configurator eventLogType]) {
|
||||
case SNTEventLogTypeSyslog:
|
||||
_eventLog = [[SNTSyslogEventLog alloc] init];
|
||||
@@ -91,12 +99,14 @@
|
||||
break;
|
||||
}
|
||||
|
||||
// The filter is reset when santad disconnects from the driver.
|
||||
// Add the default filters.
|
||||
[_eventProvider fileModificationPrefixFilterAdd:@[ @"/.", @"/dev/" ]];
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), ^{
|
||||
// The filter is reset when santad disconnects from the driver.
|
||||
// Add the default filters.
|
||||
[self.eventProvider fileModificationPrefixFilterAdd:@[ @"/.", @"/dev/" ]];
|
||||
|
||||
// TODO(bur): Add KVO handling for fileChangesPrefixFilters.
|
||||
[_eventProvider fileModificationPrefixFilterAdd:[configurator fileChangesPrefixFilters]];
|
||||
// TODO(bur): Add KVO handling for fileChangesPrefixFilters.
|
||||
[self.eventProvider fileModificationPrefixFilterAdd:[configurator fileChangesPrefixFilters]];
|
||||
});
|
||||
|
||||
self.notQueue = [[SNTNotificationQueue alloc] init];
|
||||
SNTSyncdQueue *syncdQueue = [[SNTSyncdQueue alloc] init];
|
||||
@@ -117,13 +127,19 @@
|
||||
options:bits
|
||||
context:NULL];
|
||||
[configurator addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(whitelistPathRegex))
|
||||
forKeyPath:NSStringFromSelector(@selector(allowedPathRegex))
|
||||
options:bits
|
||||
context:NULL];
|
||||
[configurator addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(blacklistPathRegex))
|
||||
forKeyPath:NSStringFromSelector(@selector(blockedPathRegex))
|
||||
options:bits
|
||||
context:NULL];
|
||||
if (![configurator enableSystemExtension]) {
|
||||
[configurator addObserver:self
|
||||
forKeyPath:NSStringFromSelector(@selector(enableSystemExtension))
|
||||
options:bits
|
||||
context:NULL];
|
||||
}
|
||||
|
||||
// Establish XPC listener for Santa and santactl connections
|
||||
SNTDaemonControlController *dc =
|
||||
@@ -212,6 +228,12 @@
|
||||
[self->_eventLog logAllowedExecution:message];
|
||||
break;
|
||||
}
|
||||
case ACTION_NOTIFY_FORK:
|
||||
[self->_eventLog logFork:message];
|
||||
break;
|
||||
case ACTION_NOTIFY_EXIT:
|
||||
[self->_eventLog logExit:message];
|
||||
break;
|
||||
default:
|
||||
LOGE(@"Received log request without a valid action: %d", message.action);
|
||||
break;
|
||||
@@ -299,17 +321,25 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
NSURL *old = [change[oldKey] isKindOfClass:[NSURL class]] ? change[oldKey] : nil;
|
||||
if (!new && !old) return;
|
||||
if (![new.absoluteString isEqualToString:old.absoluteString]) [self syncBaseURLDidChange:new];
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(whitelistPathRegex))] ||
|
||||
[keyPath isEqualToString:NSStringFromSelector(@selector(blacklistPathRegex))]) {
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(allowedPathRegex))] ||
|
||||
[keyPath isEqualToString:NSStringFromSelector(@selector(blockedPathRegex))]) {
|
||||
NSRegularExpression *new =
|
||||
[change[newKey] isKindOfClass:[NSRegularExpression class]] ? change[newKey] : nil;
|
||||
NSRegularExpression *old =
|
||||
[change[oldKey] isKindOfClass:[NSRegularExpression class]] ? change[oldKey] : nil;
|
||||
if (!new && !old) return;
|
||||
if (![new.pattern isEqualToString:old.pattern]) {
|
||||
LOGI(@"Changed [white|black]list regex, flushing cache");
|
||||
LOGI(@"Changed [allow|deny]list regex, flushing cache");
|
||||
[self.eventProvider flushCacheNonRootOnly:NO];
|
||||
}
|
||||
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(enableSystemExtension))]) {
|
||||
BOOL new = [change[newKey] isKindOfClass:[NSNumber class]] ? [change[newKey] boolValue] : NO;
|
||||
BOOL old = [change[oldKey] isKindOfClass:[NSNumber class]] ? [change[oldKey] boolValue] : NO;
|
||||
if (old == NO && new == YES) {
|
||||
LOGI(@"EnableSystemExtension changed NO -> YES");
|
||||
LOGI(@"The penultimate exit.");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
}
|
||||
|
||||
// Assume that this method is called only when we already know that the writing process is a
|
||||
// compiler. It checks if the closed file is executable, and if so, transitively whitelists it.
|
||||
// compiler. It checks if the closed file is executable, and if so, transitively allowlists it.
|
||||
// The passed in message contains the pid of the writing process and path of closed file.
|
||||
- (void)createTransitiveRule:(santa_message_t)message {
|
||||
[self saveFakeDecision:message];
|
||||
@@ -69,13 +69,13 @@
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:@(target)];
|
||||
if (fi.isExecutable) {
|
||||
// Check if there is an existing (non-transitive) rule for this file. We leave existing rules
|
||||
// alone, so that a whitelist or blacklist rule can't be overwritten by a transitive one.
|
||||
// alone, so that a allowlist or blocklist rule can't be overwritten by a transitive one.
|
||||
SNTRuleTable *ruleTable = [SNTDatabaseController ruleTable];
|
||||
SNTRule *prevRule = [ruleTable ruleForBinarySHA256:fi.SHA256 certificateSHA256:nil];
|
||||
if (!prevRule || prevRule.state == SNTRuleStateWhitelistTransitive) {
|
||||
// Construct a new transitive whitelist rule for the executable.
|
||||
if (!prevRule || prevRule.state == SNTRuleStateAllowTransitive) {
|
||||
// Construct a new transitive allowlist rule for the executable.
|
||||
SNTRule *rule = [[SNTRule alloc] initWithShasum:fi.SHA256
|
||||
state:SNTRuleStateWhitelistTransitive
|
||||
state:SNTRuleStateAllowTransitive
|
||||
type:SNTRuleTypeBinary
|
||||
customMsg:@""];
|
||||
|
||||
@@ -85,8 +85,8 @@
|
||||
LOGE(@"unable to add new transitive rule to database: %@", err.localizedDescription);
|
||||
} else {
|
||||
[self.eventLog
|
||||
writeLog:[NSString stringWithFormat:@"action=WHITELIST|pid=%d|path=%s|sha256=%@",
|
||||
message.pid, target, fi.SHA256]];
|
||||
writeLog:[NSString stringWithFormat:@"action=ALLOWLIST|pid=%d|pidversion=%d|path=%s|sha256=%@",
|
||||
message.pid, message.pidversion, target, fi.SHA256]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,9 +107,9 @@ double watchdogRAMPeak = 0;
|
||||
reply:(void (^)(NSError *error))reply {
|
||||
SNTRuleTable *ruleTable = [SNTDatabaseController ruleTable];
|
||||
|
||||
// If any rules are added that are not plain whitelist rules, then flush decision cache.
|
||||
// In particular, the addition of whitelist compiler rules should cause a cache flush.
|
||||
// We also flush cache if a whitelist compiler rule is replaced with a whitelist rule.
|
||||
// If any rules are added that are not plain allowlist rules, then flush decision cache.
|
||||
// In particular, the addition of allowlist compiler rules should cause a cache flush.
|
||||
// We also flush cache if a allowlist compiler rule is replaced with a allowlist rule.
|
||||
BOOL flushCache = (cleanSlate || [ruleTable addedRulesShouldFlushDecisionCache:rules]);
|
||||
|
||||
NSError *error;
|
||||
@@ -208,19 +208,19 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
||||
- (void)setAllowedPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setSyncServerWhitelistPathRegex:re];
|
||||
[[SNTConfigurator configurator] setSyncServerAllowedPathRegex:re];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
||||
- (void)setBlockedPathRegex:(NSString *)pattern reply:(void (^)(void))reply {
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setSyncServerBlacklistPathRegex:re];
|
||||
[[SNTConfigurator configurator] setSyncServerBlockedPathRegex:re];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -233,12 +233,12 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)enableTransitiveWhitelisting:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].enableTransitiveWhitelisting);
|
||||
- (void)enableTransitiveRules:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].enableTransitiveRules);
|
||||
}
|
||||
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setEnableTransitiveWhitelisting:enabled];
|
||||
- (void)setEnableTransitiveRules:(BOOL)enabled reply:(void (^)(void))reply {
|
||||
[[SNTConfigurator configurator] setEnableTransitiveRules:enabled];
|
||||
reply();
|
||||
}
|
||||
|
||||
|
||||
@@ -115,50 +115,29 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
[self.eventProvider postAction:ACTION_RESPOND_ACK forMessage:message];
|
||||
}
|
||||
|
||||
// If the binary is a critical system binary, don't check its signature.
|
||||
// The binary was validated by santad at startup.
|
||||
SNTCachedDecision *cd = self.ruleTable.criticalSystemBinaries[binInfo.SHA256];
|
||||
MOLCodesignChecker *csInfo; // Needed further down in this scope.
|
||||
if (!cd) {
|
||||
// Get codesigning info about the file but only if it's a Mach-O.
|
||||
if (binInfo.isMachO) {
|
||||
NSError *csError;
|
||||
csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path
|
||||
fileDescriptor:binInfo.fileHandle.fileDescriptor
|
||||
error:&csError];
|
||||
// Ignore codesigning if there are any errors with the signature.
|
||||
if (csError) csInfo = nil;
|
||||
}
|
||||
|
||||
// Actually make the decision (and refresh rule access timestamp).
|
||||
cd = [self.policyProcessor decisionForFileInfo:binInfo
|
||||
fileSHA256:nil
|
||||
certificateSHA256:csInfo.leafCertificate.SHA256];
|
||||
cd.certCommonName = csInfo.leafCertificate.commonName;
|
||||
}
|
||||
|
||||
SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo];
|
||||
cd.vnodeId = message.vnode_id;
|
||||
|
||||
// Formulate an initial action from the decision.
|
||||
santa_action_t action =
|
||||
(SNTEventStateAllow & cd.decision) ? ACTION_RESPOND_ALLOW : ACTION_RESPOND_DENY;
|
||||
|
||||
// Upgrade the action to ACTION_RESPOND_ALLOW_COMPILER when appropriate, because we want the
|
||||
// kernel to track this information in its decision cache.
|
||||
if (cd.decision == SNTEventStateAllowCompiler) {
|
||||
action = ACTION_RESPOND_ALLOW_COMPILER;
|
||||
}
|
||||
|
||||
// Save decision details for logging the execution later. For transitive rules, we also use
|
||||
// the shasum stored in the decision details to update the rule's timestamp whenever an
|
||||
// ACTION_NOTIFY_EXEC message related to the transitive rule is received.
|
||||
NSString *ttyPath;
|
||||
if (action == ACTION_RESPOND_ALLOW || action == ACTION_RESPOND_ALLOW_COMPILER) {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
[_eventLog cacheDecision:cd];
|
||||
} else {
|
||||
ttyPath = [self ttyPathForPID:message.ppid];
|
||||
}
|
||||
|
||||
// Upgrade the action to ACTION_RESPOND_ALLOW_COMPILER when appropriate, because we want the
|
||||
// kernel to track this information in its decision cache.
|
||||
if (cd.decision == SNTEventStateAllowCompiler) {
|
||||
action = ACTION_RESPOND_ALLOW_COMPILER;
|
||||
}
|
||||
|
||||
// Send the decision to the kernel.
|
||||
[self.eventProvider postAction:action forMessage:message];
|
||||
|
||||
@@ -174,7 +153,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
se.filePath = binInfo.path;
|
||||
se.decision = cd.decision;
|
||||
|
||||
se.signingChain = csInfo.certificates;
|
||||
se.signingChain = cd.certChain;
|
||||
se.pid = @(message.pid);
|
||||
se.ppid = @(message.ppid);
|
||||
se.parentName = @(message.pname);
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
|
||||
|
||||
OCMStub([self.mockCodesignChecker initWithBinaryPath:OCMOCK_ANY
|
||||
fileDescriptor:0
|
||||
error:[OCMArg setTo:NULL]])
|
||||
.andReturn(self.mockCodesignChecker);
|
||||
|
||||
@@ -61,6 +60,8 @@
|
||||
OCMStub([self.mockFileInfo alloc]).andReturn(self.mockFileInfo);
|
||||
OCMStub([self.mockFileInfo initWithPath:OCMOCK_ANY
|
||||
error:[OCMArg setTo:nil]]).andReturn(self.mockFileInfo);
|
||||
OCMStub([self.mockFileInfo codesignCheckerWithError:[OCMArg setTo:nil]])
|
||||
.andReturn(self.mockCodesignChecker);
|
||||
|
||||
self.mockRuleDatabase = OCMClassMock([SNTRuleTable class]);
|
||||
self.mockEventDatabase = OCMClassMock([SNTEventTable class]);
|
||||
@@ -97,12 +98,12 @@
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistRule {
|
||||
- (void)testBinaryAllowRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelist;
|
||||
rule.state = SNTRuleStateAllow;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
@@ -111,12 +112,12 @@
|
||||
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryBlacklistRule {
|
||||
- (void)testBinaryBlockRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateBlacklist;
|
||||
rule.state = SNTRuleStateBlock;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
@@ -126,7 +127,7 @@
|
||||
|
||||
}
|
||||
|
||||
- (void)testCertificateWhitelistRule {
|
||||
- (void)testCertificateAllowRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
|
||||
id cert = OCMClassMock([MOLCertificate class]);
|
||||
@@ -134,7 +135,7 @@
|
||||
OCMStub([cert SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelist;
|
||||
rule.state = SNTRuleStateAllow;
|
||||
rule.type = SNTRuleTypeCertificate;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:nil certificateSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
@@ -143,7 +144,7 @@
|
||||
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testCertificateBlacklistRule {
|
||||
- (void)testCertificateBlockRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
|
||||
id cert = OCMClassMock([MOLCertificate class]);
|
||||
@@ -151,7 +152,7 @@
|
||||
OCMStub([cert SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateBlacklist;
|
||||
rule.state = SNTRuleStateBlock;
|
||||
rule.type = SNTRuleTypeCertificate;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:nil certificateSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
@@ -160,13 +161,13 @@
|
||||
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistCompilerRule {
|
||||
- (void)testBinaryAllowCompilerRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(YES);
|
||||
OCMStub([self.mockConfigurator enableTransitiveRules]).andReturn(YES);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistCompiler;
|
||||
rule.state = SNTRuleStateAllowCompiler;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
@@ -176,13 +177,13 @@
|
||||
forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistCompilerRuleDisabled {
|
||||
- (void)testBinaryAllowCompilerRuleDisabled {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(NO);
|
||||
OCMStub([self.mockConfigurator enableTransitiveRules]).andReturn(NO);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistCompiler;
|
||||
rule.state = SNTRuleStateAllowCompiler;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
@@ -191,13 +192,13 @@
|
||||
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistTransitiveRule {
|
||||
- (void)testBinaryAllowTransitiveRule {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(YES);
|
||||
OCMStub([self.mockConfigurator enableTransitiveRules]).andReturn(YES);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistTransitive;
|
||||
rule.state = SNTRuleStateAllowTransitive;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
@@ -206,14 +207,14 @@
|
||||
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistTransitiveRuleDisabled {
|
||||
- (void)testBinaryAllowTransitiveRuleDisabled {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
|
||||
OCMStub([self.mockConfigurator enableTransitiveWhitelisting]).andReturn(NO);
|
||||
OCMStub([self.mockConfigurator enableTransitiveRules]).andReturn(NO);
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = SNTRuleStateWhitelistTransitive;
|
||||
rule.state = SNTRuleStateAllowTransitive;
|
||||
rule.type = SNTRuleTypeBinary;
|
||||
OCMStub([self.mockRuleDatabase ruleForBinarySHA256:@"a" certificateSHA256:nil]).andReturn(rule);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTKernelCommon.h"
|
||||
|
||||
@class MOLCodesignChecker;
|
||||
@class SNTCachedDecision;
|
||||
@class SNTFileInfo;
|
||||
@class SNTRuleTable;
|
||||
@@ -38,23 +39,23 @@
|
||||
/// @param fileInfo A SNTFileInfo object.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileSHA256 and certificateSHA256 are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
/// @note This method can also be used to generate a SNTCachedDecision object without any
|
||||
/// artifacts on disk. Simply pass nil to fileInfo and pass in the desired SHA256s.
|
||||
/// @param certificateSHA256 The pre-calculated SHA256 hash of the leaf certificate. If nil, the
|
||||
/// signature will be validated on the binary represented by fileInfo.
|
||||
///
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nullable SNTFileInfo *)fileInfo
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256;
|
||||
|
||||
/// Convenience initializer with nil hashes for both the file and certificate.
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo;
|
||||
|
||||
///
|
||||
/// A wrapper for decisionForFileInfo:fileSHA256:certificateSHA256:. This method is slower as it
|
||||
/// has to create the SNTFileInfo object. This is mainly used by the santactl binary because
|
||||
/// SNTFileInfo is not SecureCoding compliant. If the SHA256 hash of the file has already been
|
||||
/// calculated, use the fileSHA256 parameter to save a second calculation of the hash.
|
||||
///
|
||||
- (nonnull SNTCachedDecision *)decisionForFilePath:(nullable NSString *)filePath
|
||||
- (nonnull SNTCachedDecision *)decisionForFilePath:(nonnull NSString *)filePath
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import "Source/santad/SNTPolicyProcessor.h"
|
||||
|
||||
#import <MOLCodesignChecker/MOLCodesignChecker.h>
|
||||
|
||||
#include "Source/common/SNTLogging.h"
|
||||
|
||||
#import "Source/common/SNTCachedDecision.h"
|
||||
@@ -36,13 +38,34 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (SNTCachedDecision *)decisionForFileInfo:(SNTFileInfo *)fileInfo
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256 {
|
||||
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256 {
|
||||
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
|
||||
cd.sha256 = fileSHA256 ?: fileInfo.SHA256;
|
||||
cd.certSHA256 = certificateSHA256;
|
||||
|
||||
// If the binary is a critical system binary, don't check its signature.
|
||||
// The binary was validated at startup when the rule table was initialized.
|
||||
SNTCachedDecision *systemCd = self.ruleTable.criticalSystemBinaries[cd.sha256];
|
||||
if (systemCd) return systemCd;
|
||||
|
||||
NSError *csInfoError;
|
||||
if (certificateSHA256.length) {
|
||||
cd.certSHA256 = certificateSHA256;
|
||||
} else {
|
||||
// Grab the code signature, if there's an error don't try to capture
|
||||
// any of the signature details.
|
||||
MOLCodesignChecker *csInfo = [fileInfo codesignCheckerWithError:&csInfoError];
|
||||
if (csInfoError) {
|
||||
csInfo = nil;
|
||||
cd.decisionExtra =
|
||||
[NSString stringWithFormat:@"Signature ignored due to error: %ld", (long)csInfoError.code];
|
||||
} else {
|
||||
cd.certSHA256 = csInfo.leafCertificate.SHA256;
|
||||
cd.certCommonName = csInfo.leafCertificate.commonName;
|
||||
cd.certChain = csInfo.certificates;
|
||||
}
|
||||
}
|
||||
cd.quarantineURL = fileInfo.quarantineDataURL;
|
||||
|
||||
SNTRule *rule = [self.ruleTable ruleForBinarySHA256:cd.sha256
|
||||
@@ -51,30 +74,30 @@
|
||||
switch (rule.type) {
|
||||
case SNTRuleTypeBinary:
|
||||
switch (rule.state) {
|
||||
case SNTRuleStateWhitelist:
|
||||
case SNTRuleStateAllow:
|
||||
cd.decision = SNTEventStateAllowBinary;
|
||||
return cd;
|
||||
case SNTRuleStateSilentBlacklist:
|
||||
case SNTRuleStateSilentBlock:
|
||||
cd.silentBlock = YES;
|
||||
case SNTRuleStateBlacklist:
|
||||
case SNTRuleStateBlock:
|
||||
cd.customMsg = rule.customMsg;
|
||||
cd.decision = SNTEventStateBlockBinary;
|
||||
return cd;
|
||||
case SNTRuleStateWhitelistCompiler:
|
||||
// If transitive whitelisting is enabled, then SNTRuleStateWhiteListCompiler rules
|
||||
case SNTRuleStateAllowCompiler:
|
||||
// If transitive rules are enabled, then SNTRuleStateAllowListCompiler rules
|
||||
// become SNTEventStateAllowCompiler decisions. Otherwise we treat the rule as if
|
||||
// it were SNTRuleStateWhitelist.
|
||||
if ([[SNTConfigurator configurator] enableTransitiveWhitelisting]) {
|
||||
// it were SNTRuleStateAllow.
|
||||
if ([[SNTConfigurator configurator] enableTransitiveRules]) {
|
||||
cd.decision = SNTEventStateAllowCompiler;
|
||||
} else {
|
||||
cd.decision = SNTEventStateAllow;
|
||||
cd.decision = SNTEventStateAllowBinary;
|
||||
}
|
||||
return cd;
|
||||
case SNTRuleStateWhitelistTransitive:
|
||||
// If transitive whitelisting is enabled, then SNTRuleStateWhitelistTransitive
|
||||
case SNTRuleStateAllowTransitive:
|
||||
// If transitive rules are enabled, then SNTRuleStateAllowTransitive
|
||||
// rules become SNTEventStateAllowTransitive decisions. Otherwise, we treat the
|
||||
// rule as if it were SNTRuleStateUnknown.
|
||||
if ([[SNTConfigurator configurator] enableTransitiveWhitelisting]) {
|
||||
if ([[SNTConfigurator configurator] enableTransitiveRules]) {
|
||||
cd.decision = SNTEventStateAllowTransitive;
|
||||
return cd;
|
||||
} else {
|
||||
@@ -85,12 +108,13 @@
|
||||
break;
|
||||
case SNTRuleTypeCertificate:
|
||||
switch (rule.state) {
|
||||
case SNTRuleStateWhitelist:
|
||||
case SNTRuleStateAllow:
|
||||
cd.decision = SNTEventStateAllowCertificate;
|
||||
return cd;
|
||||
case SNTRuleStateSilentBlacklist:
|
||||
case SNTRuleStateSilentBlock:
|
||||
cd.silentBlock = YES;
|
||||
case SNTRuleStateBlacklist:
|
||||
// intentional fallthrough
|
||||
case SNTRuleStateBlock:
|
||||
cd.customMsg = rule.customMsg;
|
||||
cd.decision = SNTEventStateBlockCertificate;
|
||||
return cd;
|
||||
@@ -102,14 +126,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
NSString *msg = [self fileIsScopeBlacklisted:fileInfo];
|
||||
if ([[SNTConfigurator configurator] enableBadSignatureProtection] &&
|
||||
csInfoError && csInfoError.code != errSecCSUnsigned) {
|
||||
cd.decisionExtra =
|
||||
[NSString stringWithFormat:@"Blocked due to signature error: %ld", (long)csInfoError.code];
|
||||
cd.decision = SNTEventStateBlockCertificate;
|
||||
return cd;
|
||||
}
|
||||
|
||||
NSString *msg = [self fileIsScopeBlocked:fileInfo];
|
||||
if (msg) {
|
||||
cd.decisionExtra = msg;
|
||||
cd.decision = SNTEventStateBlockScope;
|
||||
return cd;
|
||||
}
|
||||
|
||||
msg = [self fileIsScopeWhitelisted:fileInfo];
|
||||
msg = [self fileIsScopeAllowed:fileInfo];
|
||||
if (msg) {
|
||||
cd.decisionExtra = msg;
|
||||
cd.decision = SNTEventStateAllowScope;
|
||||
@@ -129,15 +161,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (SNTCachedDecision *)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256 {
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo {
|
||||
return [self decisionForFileInfo:fileInfo fileSHA256:nil certificateSHA256:nil];
|
||||
}
|
||||
|
||||
- (nonnull SNTCachedDecision *)decisionForFilePath:(nonnull NSString *)filePath
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256 {
|
||||
SNTFileInfo *fileInfo;
|
||||
if (filePath) {
|
||||
NSError *error;
|
||||
fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
|
||||
if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);
|
||||
}
|
||||
NSError *error;
|
||||
fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
|
||||
if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);
|
||||
return [self decisionForFileInfo:fileInfo
|
||||
fileSHA256:fileSHA256
|
||||
certificateSHA256:certificateSHA256];
|
||||
@@ -148,17 +182,17 @@
|
||||
///
|
||||
/// Files that are out of scope:
|
||||
/// + Non Mach-O files that are not part of an installer package.
|
||||
/// + Files in whitelisted path.
|
||||
/// + Files in allowed path.
|
||||
///
|
||||
/// @return @c YES if file is in scope, @c NO otherwise.
|
||||
///
|
||||
- (NSString *)fileIsScopeWhitelisted:(SNTFileInfo *)fi {
|
||||
- (NSString *)fileIsScopeAllowed:(SNTFileInfo *)fi {
|
||||
if (!fi) return nil;
|
||||
|
||||
// Determine if file is within a whitelisted path
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] whitelistPathRegex];
|
||||
// Determine if file is within an allowed path
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] allowedPathRegex];
|
||||
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
|
||||
return @"Whitelist Regex";
|
||||
return @"Allowed Path Regex";
|
||||
}
|
||||
|
||||
// If file is not a Mach-O file, we're not interested.
|
||||
@@ -169,12 +203,12 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)fileIsScopeBlacklisted:(SNTFileInfo *)fi {
|
||||
- (NSString *)fileIsScopeBlocked:(SNTFileInfo *)fi {
|
||||
if (!fi) return nil;
|
||||
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] blacklistPathRegex];
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] blockedPathRegex];
|
||||
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
|
||||
return @"Blacklist Regex";
|
||||
return @"Blocked Path Regex";
|
||||
}
|
||||
|
||||
if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/santad/EventProviders/SNTDriverManager.h"
|
||||
#import "Source/santad/SNTApplication.h"
|
||||
@@ -95,11 +96,21 @@ void cleanup() {
|
||||
[fm removeItemAtPath:@"/Library/LaunchDaemons/com.google.santad.plist" error:NULL];
|
||||
[SNTDriverManager unloadDriver];
|
||||
[fm removeItemAtPath:@"/Library/Extensions/santa-driver.kext" error:NULL];
|
||||
|
||||
LOGI(@"loading com.google.santa.daemon as a SystemExtension");
|
||||
NSTask *t = [[NSTask alloc] init];
|
||||
t.launchPath = [@(kSantaAppPath) stringByAppendingString:@"/Contents/MacOS/Santa"];
|
||||
t.arguments = @[ @"--load-system-extension" ];
|
||||
[t launch];
|
||||
[t waitUntilExit];
|
||||
|
||||
t = [[NSTask alloc] init];
|
||||
t.launchPath = @"/bin/launchctl";
|
||||
t.arguments = @[ @"remove", @"com.google.santad" ];
|
||||
[t launch];
|
||||
[t waitUntilExit];
|
||||
|
||||
// This exit will likely never be called because the above launchctl command kill us.
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -119,7 +130,7 @@ int main(int argc, const char *argv[]) {
|
||||
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
|
||||
|
||||
// Handle the case of macOS < 10.15 updating to >= 10.15.
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([[SNTConfigurator configurator] enableSystemExtension]) {
|
||||
if ([pi.arguments.firstObject isEqualToString:@(kSantaDPath)]) cleanup();
|
||||
}
|
||||
|
||||
|
||||
28
Source/santasyncservice/BUILD
Normal file
28
Source/santasyncservice/BUILD
Normal file
@@ -0,0 +1,28 @@
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
objc_library(
|
||||
name = "santass_lib",
|
||||
srcs = [
|
||||
"SNTSyncService.h",
|
||||
"SNTSyncService.m",
|
||||
"main.m",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTXPCSyncServiceInterface",
|
||||
"@MOLCodesignChecker",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
macos_command_line_application(
|
||||
name = "santasyncservice",
|
||||
bundle_id = "com.google.santa.syncservice",
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//:santa_package_group"],
|
||||
deps = [":santass_lib"],
|
||||
)
|
||||
26
Source/santasyncservice/Info.plist
Normal file
26
Source/santasyncservice/Info.plist
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>santasyncservice</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>santasyncservice</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.santa.syncservice</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>santasyncservice</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google LLC.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
20
Source/santasyncservice/SNTSyncService.h
Normal file
20
Source/santasyncservice/SNTSyncService.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/// Copyright 2020 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "Source/common/SNTXPCSyncServiceInterface.h"
|
||||
|
||||
@interface SNTSyncService : NSObject<SNTSyncServiceXPC>
|
||||
@end
|
||||
29
Source/santasyncservice/SNTSyncService.m
Normal file
29
Source/santasyncservice/SNTSyncService.m
Normal file
@@ -0,0 +1,29 @@
|
||||
/// Copyright 2020 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 "SNTSyncService.h"
|
||||
|
||||
// TODO(bur): Add "santactl sync --daemon" behavior here.
|
||||
@implementation SNTSyncService
|
||||
|
||||
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events fromBundle:(BOOL)fromBundle {}
|
||||
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
|
||||
reply:(void (^)(SNTBundleEventAction))reply {}
|
||||
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply {}
|
||||
|
||||
- (void)performFullSyncWithReply:(SNTFullSyncReplyBlock)reply {}
|
||||
|
||||
@end
|
||||
35
Source/santasyncservice/main.m
Normal file
35
Source/santasyncservice/main.m
Normal file
@@ -0,0 +1,35 @@
|
||||
/// Copyright 2020 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTXPCSyncServiceInterface.h"
|
||||
#import "Source/santasyncservice/SNTSyncService.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@autoreleasepool {
|
||||
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
||||
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
|
||||
MOLXPCConnection *c =
|
||||
[[MOLXPCConnection alloc] initServerWithName:[SNTXPCSyncServiceInterface serviceID]];
|
||||
c.privilegedInterface = c.unprivilegedInterface =
|
||||
[SNTXPCSyncServiceInterface syncServiceInterface];
|
||||
c.exportedObject = [[SNTSyncService alloc] init];
|
||||
[c resume];
|
||||
[[NSRunLoop mainRunLoop] run];
|
||||
}
|
||||
}
|
||||
19
WORKSPACE
19
WORKSPACE
@@ -8,18 +8,21 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl",
|
||||
git_repository(
|
||||
name = "build_bazel_rules_apple",
|
||||
remote = "https://github.com/bazelbuild/rules_apple.git",
|
||||
commit = "97951dce38d",
|
||||
tag = "0.21.2",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")
|
||||
apple_rules_dependencies()
|
||||
|
||||
load("@build_bazel_apple_support//lib:repositories.bzl", "apple_support_dependencies")
|
||||
apple_support_dependencies()
|
||||
|
||||
# Macops MOL* dependencies
|
||||
|
||||
git_repository(
|
||||
name = "MOLAuthenticatingURLSession",
|
||||
remote = "https://github.com/google/macops-molauthenticatingurlsession.git",
|
||||
tag = "v2.8",
|
||||
tag = "v3.0",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
@@ -34,16 +37,10 @@ git_repository(
|
||||
tag = "v2.2",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "MOLFCMClient",
|
||||
remote = "https://github.com/google/macops-molfcmclient.git",
|
||||
tag = "v2.0",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "MOLXPCConnection",
|
||||
remote = "https://github.com/google/macops-molxpcconnection.git",
|
||||
tag = "v2.0",
|
||||
tag = "v2.1",
|
||||
)
|
||||
|
||||
# FMDB
|
||||
@@ -51,7 +48,7 @@ git_repository(
|
||||
new_git_repository(
|
||||
name = "FMDB",
|
||||
remote = "https://github.com/ccgus/fmdb.git",
|
||||
tag = "v2.7",
|
||||
tag = "2.7.7",
|
||||
build_file_content = """
|
||||
objc_library(
|
||||
name = "FMDB",
|
||||
@@ -69,7 +66,7 @@ objc_library(
|
||||
new_git_repository(
|
||||
name = "OCMock",
|
||||
remote = "https://github.com/erikdoe/ocmock",
|
||||
tag = "v3.4.3",
|
||||
tag = "v3.8.1",
|
||||
build_file_content = """
|
||||
objc_library(
|
||||
name = "OCMock",
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
theme: jekyll-theme-cayman
|
||||
theme: jekyll-theme-cayman
|
||||
|
||||
plugins:
|
||||
- jekyll-redirect-from
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ Two configuration methods can be used to control Santa: a local configuration pr
|
||||
| ----------------------------- | ---------- | ---------------------------------------- |
|
||||
| ClientMode* | Integer | 1 = MONITOR, 2 = LOCKDOWN, defaults to MONITOR |
|
||||
| FileChangesRegex* | String | The regex of paths to log file changes. Regexes are specified in ICU format. |
|
||||
| WhitelistRegex* | String | A regex to whitelist if the binary or certificate scopes did not allow execution. Regexes are specified in ICU format. |
|
||||
| BlacklistRegex* | String | A regex to blacklist if the binary or certificate scopes did not block an execution. Regexes are specified in ICU format. |
|
||||
| EnablePageZeroProtection | Bool | Enable `__PAGEZERO` protection, defaults to YES. If this flag is set to YES, 32-bit binaries that are missing the `__PAGEZERO` segment will be blocked even in MONITOR mode, **unless** the binary is whitelisted by an explicit rule. |
|
||||
| AllowedPathRegex* | String | A regex to allow if the binary or certificate scopes did not allow/block execution. Regexes are specified in ICU format. |
|
||||
| BlockedPathRegex* | String | A regex to block if the binary or certificate scopes did not allow/block an execution. Regexes are specified in ICU format. |
|
||||
| EnablePageZeroProtection | Bool | Enable `__PAGEZERO` protection, defaults to YES. If this flag is set to YES, 32-bit binaries that are missing the `__PAGEZERO` segment will be blocked even in MONITOR mode, **unless** the binary is allowed by an explicit rule. |
|
||||
| MoreInfoURL | String | The URL to open when the user clicks "More Info..." when opening Santa.app. If unset, the button will not be displayed. |
|
||||
| EventDetailURL | String | See the [EventDetailURL](#eventdetailurl) section below. |
|
||||
| EventDetailText | String | Related to the above property, this string represents the text to show on the button. |
|
||||
@@ -52,8 +52,9 @@ This property contains a kind of format string to be turned into the URL to send
|
||||
| %file_sha% | SHA-256 of the file that was blocked |
|
||||
| %machine_id% | ID of the machine |
|
||||
| %username% | The executing user |
|
||||
| %bundle_id% | Bundle ID of the binary, if applicable |
|
||||
| %bundle_ver% | Bundle version of the binary, if applicable |
|
||||
| %serial% | System's serial number |
|
||||
| %uuid% | System's UUID |
|
||||
| %hostname% | System's full hostname |
|
||||
|
||||
For example: `https://sync-server-hostname/%machine_id%/%file_sha%`
|
||||
|
||||
@@ -167,13 +168,14 @@ Configuration profiles have a `.mobileconfig` file extension. There are many way
|
||||
| clean_sync** | Bool | If set to `True` Santa will clear all local rules and download a fresh copy from the sync-server. Defaults to `False`. |
|
||||
| batch_size | Integer | The number of rules to download or events to upload per request. Multiple requests will be made if there is more work than can fit in single request. Defaults to 50. |
|
||||
| upload_logs_url** | String | If set, the endpoint to send Santa's current logs. No default. |
|
||||
| whitelist_regex | String | Same as the "Local Configuration" WhitelistRegex. No default. |
|
||||
| blacklist_regex | String | Same as the "Local Configuration" BlacklistRegex. No default. |
|
||||
| allowed_path_regex | String | Same as the "Local Configuration" AllowedPathRegex. No default. |
|
||||
| blocked_path_regex | String | Same as the "Local Configuration" BlockedPathRegex. No default. |
|
||||
| full_sync_interval* | Integer | The max time to wait before performing a full sync with the server. Defaults to 600 secs (10 minutes) if not set. |
|
||||
| fcm_token* | String | The FCM token used by Santa to listen for FCM messages. Unique for every machine. No default. |
|
||||
| fcm_full_sync_interval* | Integer | The full sync interval if a fcm_token is set. Defaults to 14400 secs (4 hours). |
|
||||
| fcm_global_rule_sync_deadline* | Integer | The max time to wait before performing a rule sync when a global rule sync FCM message is received. This allows syncing to be staggered for global events to avoid spikes in server load. Defaults to 600 secs (10 min). |
|
||||
| enable_bundles* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. |
|
||||
| enabled_transitive_whitelisting | Bool | If set to `True` the transitive whitelisting feature is enabled. Defaults to `False`. |
|
||||
| enable_transitive_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
|
||||
|
||||
*Held only in memory. Not persistent upon process restart.
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NotificationSettings</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>AlertType</key>
|
||||
<integer>1</integer>
|
||||
<key>BadgesEnabled</key>
|
||||
<true/>
|
||||
<key>BundleIdentifier</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>CriticalAlertEnabled</key>
|
||||
<true/>
|
||||
<key>NotificationsEnabled</key>
|
||||
<true/>
|
||||
<key>ShowInLockScreen</key>
|
||||
<true/>
|
||||
<key>ShowInNotificationCenter</key>
|
||||
<true/>
|
||||
<key>SoundsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>Notifications Payload</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.notificationsettings.F1817DA0-0044-43DD-9540-36EBC60FDA8F</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.notificationsettings</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>510236AE-D7F8-4131-A4CA-5CC930C51866</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Configures your Mac to automatically enable Notifications settings for Santa</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>Santa Notifications settings</string>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.notificationsettings.069CA123-6129-46A5-8FD1-49322E5A5755</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<true/>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>069CA123-6129-46A5-8FD1-49322E5A5755</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
<?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">
|
||||
<dict>
|
||||
<!-- See https://developer.apple.com/documentation/devicemanagement/systemextensions for payload descriptions -->
|
||||
<key>PayloadUUID</key>
|
||||
<string>40C19D5B-76D7-4C1C-BC9D-2F7EB29CFF4D</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.system-extension-policy.40C19D5B-76D7-4C1C-BC9D-2F7EB29CFF4D</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>System Extensions</string>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Configures your Mac to automatically enable Santa's EndpointSecurityExtension</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<true/>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>PayloadUUID</key>
|
||||
<string>98D01A7B-ADC1-43C8-AB8E-8BDC25FCA3C9</string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.system-extension-policy</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.system-extension-policy.98D01A7B-ADC1-43C8-AB8E-8BDC25FCA3C9</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>System Extensions</string>
|
||||
<key>PayloadDescription</key>
|
||||
<string/>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
<key>AllowUserOverrides</key>
|
||||
<true/>
|
||||
<key>AllowedSystemExtensions</key>
|
||||
<dict>
|
||||
<key>EQHXZ8M8AV</key>
|
||||
<array>
|
||||
<string>com.google.santa.daemon</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>AllowedSystemExtensionTypes</key>
|
||||
<dict>
|
||||
<key>EQHXZ8M8AV</key>
|
||||
<array>
|
||||
<string>EndpointSecurityExtension</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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>
|
||||
<!-- See https://developer.apple.com/documentation/devicemanagement/privacypreferencespolicycontrol for payload descriptions -->
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Configures Privacy Preferences Policy Control settings</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>Privacy Preferences Policy Control</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.TCC.configuration-profile-policy.2416BA4B-CBFC-4719-B02F-20251B881D6F</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.TCC.configuration-profile-policy</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>2416BA4B-CBFC-4719-B02F-20251B881D6F</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>Services</key>
|
||||
<dict>
|
||||
<key>SystemPolicyAllFiles</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Allowed</key>
|
||||
<true/>
|
||||
<key>CodeRequirement</key>
|
||||
<string>identifier "com.google.santa.daemon" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = EQHXZ8M8AV</string>
|
||||
<key>Comment</key>
|
||||
<string></string>
|
||||
<key>Identifier</key>
|
||||
<string>com.google.santa.daemon</string>
|
||||
<key>IdentifierType</key>
|
||||
<string>bundleID</string>
|
||||
<key>StaticCode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Allowed</key>
|
||||
<true/>
|
||||
<key>CodeRequirement</key>
|
||||
<string>identifier "com.google.santa.bundleservice" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = EQHXZ8M8AV</string>
|
||||
<key>Comment</key>
|
||||
<string></string>
|
||||
<key>Identifier</key>
|
||||
<string>com.google.santa.bundleservice</string>
|
||||
<key>IdentifierType</key>
|
||||
<string>bundleID</string>
|
||||
<key>StaticCode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Allowed</key>
|
||||
<true/>
|
||||
<key>CodeRequirement</key>
|
||||
<string>identifier "com.google.santa" and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = EQHXZ8M8AV</string>
|
||||
<key>Comment</key>
|
||||
<string></string>
|
||||
<key>Identifier</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>IdentifierType</key>
|
||||
<string>bundleID</string>
|
||||
<key>StaticCode</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDescription</key>
|
||||
<string>tcc.configuration-profile-policy.santa.example</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>tcc.configuration-profile-policy.santa.example</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa.TCC.configuration-profile-policy.089CBCFB-F2AA-407C-9F2A-A12967FE20BC</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>089CBCFB-F2AA-407C-9F2A-A12967FE20BC</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,16 +1,34 @@
|
||||
# Events
|
||||
|
||||
Events are a defined set of data, core to how Santa interacts with a sync server. Events are generated when there is a blocked `execve()` while in Lockdown or Monitor mode. Events are also generated in Monitor mode for an `execve()` that was allowed to run, but would have been blocked in Lockdown mode. This allows an admin to roll out Santa to their macOS fleet in Monitor mode but still collect meaningful data. The events collected while in Monitor mode can be used to build a reasonably comprehensive whitelist of signing certificates and binaries before switching the fleet to Lockdown mode.
|
||||
Events are a defined set of data, core to how Santa interacts with a sync
|
||||
server. Events are generated when there is a blocked `execve()` while in
|
||||
Lockdown or Monitor mode. Events are also generated in Monitor mode for an
|
||||
`execve()` that was allowed to run, but would have been blocked in Lockdown
|
||||
mode. This allows an admin to roll out Santa to their macOS fleet in Monitor
|
||||
mode but still collect meaningful data. The events collected while in Monitor
|
||||
mode can be used to build a reasonably comprehensive allowlist of signing
|
||||
certificates and binaries before switching the fleet to Lockdown mode.
|
||||
|
||||
##### Event Data
|
||||
|
||||
Events begin their life as an [SNTStoredEvent](https://github.com/google/santa/blob/master/Source/common/SNTStoredEvent.h) object. The SNTStoredEvent class is just a simple storage class that has properties for all the relevant bits of information. More importantly the class implements the [NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding?language=objc) protocol. This allows the objects to be encoded and decoded for storage in the events sqlite3 database on disk and sent over XPC to another process.
|
||||
Events begin their life as an
|
||||
[SNTStoredEvent](https://github.com/google/santa/blob/master/Source/common/SNTStoredEvent.h)
|
||||
object. The SNTStoredEvent class is just a simple storage class that has
|
||||
properties for all the relevant bits of information. More importantly the class
|
||||
implements the
|
||||
[NSSecureCoding](https://developer.apple.com/documentation/foundation/nssecurecoding?language=objc)
|
||||
protocol. This allows the objects to be encoded and decoded for storage in the
|
||||
events sqlite3 database on disk and sent over XPC to another process.
|
||||
|
||||
Events are temporarily stored in a database until they are uploaded. The format is subject the change; accessing the events database directly will most likely break in future releases. If direct access to the events database is required, raise a [issue on the Santa GitHub](https://github.com/google/santa/issues).
|
||||
Events are temporarily stored in a database until they are uploaded. The format
|
||||
is subject the change; accessing the events database directly will most likely
|
||||
break in future releases. If direct access to the events database is required,
|
||||
raise a [issue on the Santa GitHub](https://github.com/google/santa/issues).
|
||||
|
||||
###### JSON
|
||||
|
||||
Before an event is uploaded to a sync server, the event data is copied into a JSON blob. Here is an example of Firefox being blocked and sent for upload:
|
||||
Before an event is uploaded to a sync server, the event data is copied into a
|
||||
JSON blob. Here is an example of Firefox being blocked and sent for upload:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -73,18 +91,30 @@ Before an event is uploaded to a sync server, the event data is copied into a JS
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### Event Lifecycle
|
||||
|
||||
1. santad generates a new event
|
||||
2. santad compares, or adds if not present, the event's SHA-256 file hash to an in-memory cache with a timeout of 10 min. If an event with an matching hash is present in cache, the event is dropped.
|
||||
3. santad saves the event to `/var/db/santa/events.db` with a unique ID assigned as the key.
|
||||
4. santad sends an XPC message to the santactl daemon. The message contains the event with instructions to upload the event immediately. This is non-blocking and is performed on a background thread.
|
||||
1. santad generates a new event
|
||||
2. santad compares, or adds if not present, the event's SHA-256 file hash to an
|
||||
in-memory cache with a timeout of 10 min. If an event with an matching hash
|
||||
is present in cache, the event is dropped.
|
||||
3. santad saves the event to `/var/db/santa/events.db` with a unique ID
|
||||
assigned as the key.
|
||||
4. santad sends an XPC message to the santactl daemon. The message contains the
|
||||
event with instructions to upload the event immediately. This is
|
||||
non-blocking and is performed on a background thread.
|
||||
|
||||
##### Bundle Events
|
||||
|
||||
Bundle events are a special type of event that are generated when a sync server supports receiving the associated bundle events, instead of just the original event. For example: `/Applications/Keynote.app/Contents/MacOS/Keynote` is blocked and an event representing the binary is uploaded. A whitelist rule is created for that one binary. Great, you can now run `/Applications/Keynote.app/Contents/MacOS/Keynote`, but what about all the other supporting binaries contained in the bundle? You would have to wait until they are executed until an event would be generated. It is very common for a bundle to contain multiple binaries, as shown here with Keynote.app. Waiting to get a block is not a very good user experience.
|
||||
Bundle events are a special type of event that are generated when a sync server
|
||||
supports receiving the associated bundle events, instead of just the original
|
||||
event. For example: `/Applications/Keynote.app/Contents/MacOS/Keynote` is
|
||||
blocked and an event representing the binary is uploaded. An allow rule is
|
||||
created for that one binary. Great, you can now run
|
||||
`/Applications/Keynote.app/Contents/MacOS/Keynote`, but what about all the other
|
||||
supporting binaries contained in the bundle? You would have to wait until they
|
||||
are executed until an event would be generated. It is very common for a bundle
|
||||
to contain multiple binaries, as shown here with Keynote.app. Waiting to get a
|
||||
block is not a very good user experience.
|
||||
|
||||
```sh
|
||||
⇒ santactl bundleinfo /Applications/Keynote.app
|
||||
@@ -92,50 +122,59 @@ Hashing time: 1047 ms
|
||||
9 events found
|
||||
BundleHash: b475667ab1ab6eddea48bfc2bed76fcef89b8f85ed456c8068351292f7cb4806
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: be3aa404ee79c2af863132b93b0eedfdbc34c6e35d4fda2ade6dd637692ead84
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.MovieCompatibilityConverter.xpc/Contents/MacOS/com.apple.iWork.MovieCompatibilityConverter
|
||||
SHA-256: be3aa404ee79c2af863132b93b0eedfdbc34c6e35d4fda2ade6dd637692ead84
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.MovieCompatibilityConverter.xpc/Contents/MacOS/com.apple.iWork.MovieCompatibilityConverter
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: 3b2582fd5e7652b653276b3980c248dc973e8082e9d0678c96a08d7d1a8366ba
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.PICTConverter.xpc/Contents/MacOS/com.apple.iWork.PICTConverter
|
||||
SHA-256: 3b2582fd5e7652b653276b3980c248dc973e8082e9d0678c96a08d7d1a8366ba
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.PICTConverter.xpc/Contents/MacOS/com.apple.iWork.PICTConverter
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: f1bf3be05d511d7c7f651cf7b130d4977f8d28d0bfcd7c5de4144b95eaab7ad7
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor
|
||||
SHA-256: f1bf3be05d511d7c7f651cf7b130d4977f8d28d0bfcd7c5de4144b95eaab7ad7
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: b59bc8548c91088a40d9023abb5d22fa8731b4aa17693fcb5b98c795607d219a
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.BitmapTracer.xpc/Contents/MacOS/com.apple.iWork.BitmapTracer
|
||||
SHA-256: b59bc8548c91088a40d9023abb5d22fa8731b4aa17693fcb5b98c795607d219a
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.BitmapTracer.xpc/Contents/MacOS/com.apple.iWork.BitmapTracer
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: 08cb407f541d867f1a63dc3ae44eeedd5181ca06c61df6ef62b5dc7192951a4b
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCUtilities32
|
||||
SHA-256: 08cb407f541d867f1a63dc3ae44eeedd5181ca06c61df6ef62b5dc7192951a4b
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCUtilities32
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: b965ae7be992d1ce818262752d0cf44297a88324a593c67278d78ca4d16fcc39
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor.TCUtilities32
|
||||
SHA-256: b965ae7be992d1ce818262752d0cf44297a88324a593c67278d78ca4d16fcc39
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.xpc/Contents/XPCServices/com.apple.iWork.TCMovieExtractor.TCUtilities32.xpc/Contents/MacOS/com.apple.iWork.TCMovieExtractor.TCUtilities32
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: 59668dc27314f0f6f5daa5f02b564c176f64836c88e2dfe166e90548f47336f1
|
||||
Path: /Applications/Keynote.app/Contents/MacOS/Keynote
|
||||
SHA-256: 59668dc27314f0f6f5daa5f02b564c176f64836c88e2dfe166e90548f47336f1
|
||||
Path: /Applications/Keynote.app/Contents/MacOS/Keynote
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: 7ce324f919b14e14d327004b09f83ca81345fd4438c87ead4b699f89e9485595
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.ExternalResourceValidator.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceValidator
|
||||
SHA-256: 7ce324f919b14e14d327004b09f83ca81345fd4438c87ead4b699f89e9485595
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/XPCServices/com.apple.iWork.ExternalResourceValidator.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceValidator
|
||||
BundleID: com.apple.iWork.Keynote
|
||||
SHA-256: 6b47f551565d886388eeec5e876b6de9cdd71ef36d43b0762e6ebf02bdd8515d
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceAccessor
|
||||
SHA-256: 6b47f551565d886388eeec5e876b6de9cdd71ef36d43b0762e6ebf02bdd8515d
|
||||
Path: /Applications/Keynote.app/Contents/XPCServices/com.apple.iWork.ExternalResourceAccessor.xpc/Contents/MacOS/com.apple.iWork.ExternalResourceAccessor
|
||||
```
|
||||
|
||||
Bundle events provide a mechanism to generate and upload events for all the executable Mach-O binaries within a bundle. To enable bundle event generation a configuration must be set in the preflight sync stage on the sync server. Once set the sync server can use bundle events to drive a better user experience.
|
||||
Bundle events provide a mechanism to generate and upload events for all the
|
||||
executable Mach-O binaries within a bundle. To enable bundle event generation a
|
||||
configuration must be set in the preflight sync stage on the sync server. Once
|
||||
set the sync server can use bundle events to drive a better user experience.
|
||||
|
||||
Bundle events can be differentiated by the existence of these fields:
|
||||
|
||||
| Field | Value |
|
||||
| ------------------------ | ---------------------------------------- |
|
||||
| decision | BUNDLE_BINARY |
|
||||
| file_bundle_hash | Super Hash of all binary hashes |
|
||||
| file_bundle_hash_millis | The time in milliseconds it took to find all of the binaries, hash and produce a super hash |
|
||||
| file_bundle_binary_count | Number of binaries within the bundle |
|
||||
| Field | Value |
|
||||
| ------------------------ | ----------------------------------------------- |
|
||||
| decision | BUNDLE_BINARY |
|
||||
| file_bundle_hash | Super Hash of all binary hashes |
|
||||
| file_bundle_hash_millis | The time in milliseconds it took to find all of |
|
||||
| | the binaries, hash and produce a super hash |
|
||||
| file_bundle_binary_count | Number of binaries within the bundle |
|
||||
|
||||
To avoid redundant uploads of a bundle event Santa will wait for the sync server to ask for them. The server will respond to event uploads with a request like this:
|
||||
To avoid redundant uploads of a bundle event Santa will wait for the sync server
|
||||
to ask for them. The server will respond to event uploads with a request like
|
||||
this:
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------------- | ---------------------------------------- |
|
||||
| event_upload_bundle_binaries | An array of bundle hashes that the sync server needs to be uploaded |
|
||||
|
||||
When santactl receives this type of request, it sends an XPC reply to santad to save all the bundle events to the events.db. It then attempts to upload all the bundle events, purging the successes from the events.db. Any failures will be uploaded during the next full sync.
|
||||
| Field | Value |
|
||||
| ---------------------------- | --------------------------------------- |
|
||||
| event_upload_bundle_binaries | An array of bundle hashes that the sync |
|
||||
| | server needs to be uploaded |
|
||||
|
||||
When santactl receives this type of request, it sends an XPC reply to santad to
|
||||
save all the bundle events to the events.db. It then attempts to upload all the
|
||||
bundle events, purging the successes from the events.db. Any failures will be
|
||||
uploaded during the next full sync.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user