Compare commits

..

57 Commits
0.9.32 ... 1.8

Author SHA1 Message Date
Tom Burgin
7504cd36e1 Simplify install scripts (#447)
* installer to respect EnableSystemExtension

* conform
2020-02-26 12:58:12 -05:00
Tom Burgin
cafef66933 version bump (#446) 2020-02-25 15:14:42 -05:00
Tom Burgin
0c4e9d4b06 slurp up com.google.santa.daemon dsyms (#445) 2020-02-21 18:28:15 -05:00
Tom Burgin
ac07f5d54b santad: add prefixes on a background thread (#444)
* add prefixes on a background thread

* version bump
2020-02-21 16:54:42 -05:00
Tom Burgin
d116f7b01e santad: wait for driver connection before adding prefix filters (#443)
* wait for driver connection before adding prefix filters

* version bump

* fix travis build
2020-02-21 14:58:12 -05:00
Tom Burgin
63ca34bc54 santad: fix launch path and args for loading the system extension (#442)
* missing /

* version bump

* that was close
2020-02-20 20:01:42 -05:00
Tom Burgin
c894029c33 version bump to 1.3 (#441) 2020-02-19 17:08:30 -05:00
Tom Burgin
de2bdd6653 update EnableSystemExtension when the config changes 🤦 (#440) 2020-02-19 17:03:58 -05:00
Tom Burgin
2d066ad671 version bump to 1.2 (#439) 2020-02-19 14:06:20 -05:00
Tom Burgin
24854d4ad7 Config: EnableSystemExtension option (#438)
* Config: add EnableSystemExtension option

* format

* i don't trust kvo

* review updates
2020-02-18 17:48:06 -05:00
Russell Hancox
99ee0af178 Project: bump version to 1.1 (#436) 2020-02-12 11:17:44 -05:00
Russell Hancox
bf6f78df09 common: Eliminate VLA usage in SNTFileInfo (#435)
VLAs complicate static analysis and bloat stack size. Replace VLA allocation with calls to malloc and free
2020-02-11 10:55:57 -05:00
Russell Hancox
c05806916b santad: Add config flag to block all binaries with bad signatures. (#434)
* santad: Add option to block all binaries with bad signatures.
2020-02-10 13:45:22 -05:00
Russell Hancox
e48ce0cfe3 santad: Move signature fetching into SNTPolicyProcessor (#433)
This also removes an unnecessary hash, checks code signatures on non-MachO files (which is rare but possible) and fixes a rare crash in EndpointSecurityManager
2020-02-07 14:32:00 -05:00
Tom Burgin
eabca469b9 update readme with a note about system extension (#431) 2020-02-06 12:50:33 -05:00
Russell Hancox
f6dc36e812 santactl/sync: Skip event upload for clean sync
This lets a clean sync clear out the existing events without attempting to upload them.
2020-01-13 14:56:01 -05:00
Russell Hancox
ac7cbdfd16 Project: update apple rules to 0.19.0 2020-01-13 14:25:38 -05:00
Tom Burgin
d1d008af0a don't log TRUNCATE and don't log fileops from com.google.santa.daemon (#428)
* don't log TRUNCATE and don't log fileops from com.google.santa.daemon

* review updates
2019-12-20 14:00:16 -05:00
Tom Burgin
5db56e01f5 cleanup 10.14 -> 10.15 upgrade artifacts (#427)
* cleanup 10.14 -> 10.15 upgrade artifacts

* exit exit

* exit exit
2019-12-19 15:56:59 -05:00
Tom Burgin
726c49bec5 com.google.santa.daemon: handle es deadline (#426)
* com.google.santa.daemon: deny execs that are about to exceed the es deadline

* update comment

* actually handle the deadline
2019-12-16 13:03:20 -05:00
Tom Burgin
ae5db5dde7 com.google.santa.daemon: lookup the tty for deny decisions before posting the decision (#425) 2019-12-13 15:24:21 -05:00
Tom Burgin
2671807f0e com.google.santa.daemon: don't reload if versions have not changed (#424) 2019-12-12 14:02:23 -05:00
Tom Burgin
70c8626016 fix com.google.santa.daemon path for critical system binary checking (#423) 2019-12-12 11:41:16 -05:00
Tom Burgin
436c472a49 es event provider: support transitive whitelisting (#422)
* es event provider: support transitive whitelisting

* remove vector

* truncate check

* consistent log style

* review updates
2019-12-12 11:30:05 -05:00
Tom Burgin
ed5be6b062 com.google.santa.daemon: async es message handling (#421)
* fix Santa.xcodeproj

* com.google.santa.daemon: some es tweaks

* review updates
2019-12-09 11:21:12 -05:00
Russell Hancox
a38f24728a santactl/status: Remove kext section of status on 10.15+ 2019-11-25 19:32:54 -05:00
Russell Hancox
4af026356f santactl/version: print useful status for santa-driver on 10.15 2019-11-25 19:32:30 -05:00
Russell Hancox
c6e1bb5618 santad: Fix Apple-cert trust 2019-11-25 19:31:51 -05:00
Tom Burgin
e64d2e7ad4 Update README.md (#416) 2019-11-10 12:18:33 -05:00
Russell Hancox
3d393e9aa4 santa-driver: Workaround 10.15 SDK Dispatch() issue 2019-11-09 08:18:51 -05:00
Russell Hancox
b8f3122ee9 santad: Don't need macos_command_line_application anymore 2019-11-08 22:22:09 -05:00
Russell Hancox
8acfa6591e santa-driver: Fix compilation of SNTPrefixTree 2019-11-08 22:22:09 -05:00
Russell Hancox
25b75b0e1b santad: Re-work targets to avoid unnecessary postprocessing 2019-11-08 22:22:09 -05:00
Russell Hancox
cb01b77f84 Project: no longer need to move the embedded provisionprofile 2019-11-08 22:22:09 -05:00
Russell Hancox
61582a0324 Project: standardize Info.plist and entitlement paths 2019-11-08 22:22:09 -05:00
Russell Hancox
a17b5d51a4 Project: more BUILD file cleanups, remove commented provisioning_profile attrs 2019-11-08 22:22:09 -05:00
Russell Hancox
447ea8674b Project: run buildifier on all bazel files, fix typo (#405) 2019-11-08 22:22:09 -05:00
Russell Hancox
c5eec850e1 Project: update santad path (#404) 2019-11-08 22:22:09 -05:00
Russell Hancox
1870631150 Project: Update bazel rules for endpointsec (#403) 2019-11-08 22:22:09 -05:00
Russell Hancox
20ed1659c1 santad: Don't store rules for santad/launchd, keep in-mem (#402) 2019-11-08 22:22:09 -05:00
Tom Burgin
258de3efba handle all ACTION_RESPOND_* (#401) 2019-11-08 22:22:09 -05:00
Tom Burgin
394fd5fab9 add required santad entitlements (#400) 2019-11-08 22:22:09 -05:00
Russell Hancox
53b7ef86ed santad: Log file changes, use prefix trees (#398) 2019-11-08 22:22:09 -05:00
Russell Hancox
423479771e santad: Use args from endpointsecurity rather than using the sysctl (#396)
This should be much more reliable and, in theory, faster.
2019-11-08 22:22:09 -05:00
Tom Burgin
933271826b simplify santabundleservice xpc connection protocol (#397)
* simplify santabundleservice xpc connection protocol

* fix BUILD deps

* fix BUILD deps

* know
2019-11-08 22:22:09 -05:00
Tom Burgin
880170ea7d make santabundleservice a command line app (#395)
* make santabundleservice a command line app

* bazel - don't build santabs.xpc
2019-11-08 22:22:09 -05:00
Russell Hancox
e58ec37881 santad: Fix BUILD after moving EventProviders (#394) 2019-11-08 22:22:09 -05:00
Russell Hancox
dece50dd10 Logging: under 10.15, force santad into syslog mode (#393) 2019-11-08 22:22:09 -05:00
Russell Hancox
9db9fc6009 santad: Move event providers into a new group, make ES connection logic smarter (#392) 2019-11-08 22:22:09 -05:00
Russell Hancox
f38c030805 Add file IDs to messages (#391) 2019-11-08 22:22:09 -05:00
Tom Burgin
d8060d3af9 update component paths (#390) 2019-11-08 22:22:09 -05:00
Russell Hancox
34b4090b42 Project: fix some new Xcode11 warnings (#389) 2019-11-08 22:22:09 -05:00
Tom Burgin
c6ca3d64b3 add SNTEventProvider interface (#388)
* Add SNTEventProvider interface

* execution controller test should use the event provider interface

* * Xcode project: Use manual signing
* SNTEndpointSecurityManager: Don't cache deny decisions
* Review updates

* review updates
2019-11-08 22:22:09 -05:00
Tom Burgin
4913426631 * Added Xcode project + pods (#387)
* * Added Xcode project + pods
* Cleaned up unused SNTXPCUnprivilegedControlInterface MachServices id.
* Change santad's MachServices id to be compatible with the default SystemExtension namespace template.

* pods

* bazel

* switch MachService name for 10.15+

* build with SystemExensions framework

* build with Xcode 11

* launchd.plist fix

* use @available

* * Request SystemExtension activation on a background thread.
* Create a constant for the "com.google.santa.daemon" SystemExtension id.
2019-11-08 22:22:09 -05:00
Russell Hancox
455a1c76c3 Docs: update building docs. Fixes #411.
Will need updating again when we merge endpointsec.
2019-11-08 22:11:35 -05:00
Russell Hancox
e5a5f6f9fb Bump MOLAuthenticatingURLSession version (#384) 2019-08-02 16:16:51 -04:00
ancdesign
7ef88d06a5 fix typo (inital -> intial) (#378) 2019-07-29 08:29:04 -04:00
109 changed files with 3621 additions and 962 deletions

6
.gitignore vendored
View File

@@ -1,3 +1,9 @@
.DS_Store
default.profraw
*.provisionprofile
bazel-*
Pods
Santa.xcodeproj/xcuserdata
Santa.xcodeproj/project.xcworkspace
Santa.xcworkspace/xcuserdata
Santa.xcworkspace/xcshareddata

View File

@@ -1,6 +1,6 @@
---
os: osx
osx_image: xcode10.2
osx_image: xcode11
language: objective-c
sudo: false
@@ -8,6 +8,7 @@ addons:
homebrew:
taps: bazelbuild/tap
packages: bazelbuild/tap/bazel
update: true
script:
- bazel build :release --show_progress_rate_limit=30.0 -c opt --apple_generate_dsym --color=no --verbose_failures --sandbox_debug

45
BUILD
View File

@@ -1,13 +1,13 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
exports_files(["LICENSE"])
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"])
licenses(["notice"])
exports_files(["LICENSE"])
# The version label for mac_* rules.
apple_bundle_version(
name = "version",
@@ -28,8 +28,9 @@ run_command(
name = "unload",
cmd = """
sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist 2>/dev/null
sudo launchctl unload /Library/LaunchDaemons/com.google.santa.bundleservice.plist 2>/dev/null
sudo kextunload -b com.google.santa-driver 2>/dev/null
launchctl unload /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null
launchctl unload /Library/LaunchAgents/com.google.santa.plist 2>/dev/null
""",
)
@@ -37,19 +38,25 @@ run_command(
name = "load",
cmd = """
sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist
launchctl load /Library/LaunchAgents/com.google.santagui.plist
sudo launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
launchctl load /Library/LaunchAgents/com.google.santa.plist
""",
)
run_command(
name = "reload",
srcs = ["//Source/santa_driver"],
srcs = [
"//Source/santa:Santa",
"//Source/santa_driver",
],
cmd = """
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
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-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
@@ -64,11 +71,13 @@ echo "Time to stop being naughty"
genrule(
name = "release",
srcs = [
"//Source/santa:Santa",
"//Source/santa_driver",
"Conf/install.sh",
"Conf/uninstall.sh",
"Conf/com.google.santa.bundleservice.plist",
"Conf/com.google.santad.plist",
"Conf/com.google.santagui.plist",
"Conf/com.google.santa.plist",
"Conf/com.google.santa.asl.conf",
"Conf/com.google.santa.newsyslog.conf",
"Conf/Package/Makefile",
@@ -82,9 +91,9 @@ genrule(
echo "Please add '-c opt' flag to bazel invocation"
""",
":opt_build": """
# Extract santa_driver.zip
# Extract santa_driver.zip and Santa.zip
for SRC in $(SRCS); do
if [[ $$(basename $${SRC}) == "santa_driver.zip" ]]; then
if [ "$$(basename $${SRC})" == "santa_driver.zip" -o "$$(basename $${SRC})" == "Santa.zip" ]; then
mkdir -p $(@D)/binaries
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
fi
@@ -113,14 +122,18 @@ genrule(
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santactl.dSYM
;;
*santabs.xpc.dSYM*Info.plist)
*santabundleservice.dSYM*Info.plist)
mkdir -p $(@D)/dsym
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santabs.xpc.dSYM
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santabundleservice.dSYM
;;
*Santa.app.dSYM*Info.plist)
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
@@ -134,7 +147,7 @@ genrule(
# 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,conf,dsym} -exec touch {} \;
find $(@D)/{binaries,conf,dsym} -exec touch {} \\;
# Create final output tar
tar -C $(@D) -czpf $(@) binaries dsym conf
@@ -147,8 +160,8 @@ test_suite(
name = "unit_tests",
tests = [
"//Source/common:SNTFileInfoTest",
"//Source/common:SNTPrefixTreeTest",
"//Source/santa_driver:SantaCacheTest",
"//Source/santa_driver:SantaPrefixTreeTest",
"//Source/santactl:SNTCommandFileInfoTest",
"//Source/santactl:SNTCommandSyncTest",
"//Source/santad:SNTEventTableTest",

View File

@@ -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
@@ -9,20 +9,19 @@
# Restart syslogd to pick up ASL configuration change
/usr/bin/killall -HUP syslogd
/sbin/kextload /Library/Extensions/santa-driver.kext
sleep 1
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
sleep 1
# Create hopefully useful symlink for santactl
mkdir -p /usr/local/bin
/bin/ln -sf /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin/santactl
/bin/ln -sf /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin/santactl
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "$user" ]] && exit 0
/bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
# 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
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

View File

@@ -6,22 +6,26 @@
[[ $3 != "/" ]] && exit 0
/bin/launchctl remove com.google.santad
/bin/launchctl remove com.google.santad || true
/bin/launchctl remove com.google.santa.bundleservice || true
sleep 1
/bin/sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1 || true
# Remove cruft from old Santa versions
/bin/rm /usr/libexec/santad
/bin/rm /usr/sbin/santactl
/bin/rm -f /usr/libexec/santad
/bin/rm -f /usr/sbin/santactl
/bin/launchctl remove com.google.santasync
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santasync.plist
/bin/rm -rf /Applications/Santa.app
/bin/rm -rf /Library/Extensions/santa-driver.kext
sleep 1
/bin/sleep 1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
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

View 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>Label</key>
<string>com.google.santa.bundleservice</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/santabundleservice</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.bundleservice</key>
<true/>
</dict>
<key>RunAtLoad</key>
<false/>
<key>KeepAlive</key>
<false/>
<key>ProcessType</key>
<string>Interactive</string>
<key>ThrottleInterval</key>
<integer>0</integer>
</dict>
</plist>

View File

@@ -3,10 +3,10 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.google.santagui</string>
<string>com.google.santa</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Extensions/santa-driver.kext/Contents/Resources/Santa.app/Contents/MacOS/Santa</string>
<string>/Applications/Santa.app/Contents/MacOS/Santa</string>
<string>--syslog</string>
</array>
<key>RunAtLoad</key>

View File

@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!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.santad</string>
<key>ProgramArguments</key>
<array>
<string>/Library/Extensions/santa-driver.kext/Contents/MacOS/santad</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCControl</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true />
<key>ProcessType</key>
<string>Interactive</string>
<key>Label</key>
<string>com.google.santad</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>com.google.santa.daemon</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ProcessType</key>
<string>Interactive</string>
</dict>
</plist>

View File

@@ -21,6 +21,9 @@ fi
# Unload santad and scheduled sync job.
/bin/launchctl remove com.google.santad >/dev/null 2>&1
# Unload bundle service
/bin/launchctl remove com.google.santa.bundleservice >/dev/null 2>&1
# Unload kext.
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
@@ -28,8 +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
/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
@@ -40,31 +45,31 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
/bin/rm -rf /Library/Extensions/santa-driver.kext 2>&1
# Copy new files.
/bin/mkdir -p /var/db/santa
/bin/cp -r ${BINARIES}/Santa.app /Applications
/bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions
/bin/mkdir -p /usr/local/bin
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
if [ ! -d /var/db/santa ] ; then
/bin/mkdir /var/db/santa
fi
/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.santagui.plist /Library/LaunchAgents
/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
# Load kext.
/sbin/kextload /Library/Extensions/santa-driver.kext
# Load santad and scheduled sync jobs.
# Load com.google.santa.daemon
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
# Load GUI agent if someone is logged in.
[[ -n "$GUI_USER" ]] && \
/bin/launchctl asuser ${GUI_USER} \
/bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
# Load com.google.santa.bundleservice
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
# Load GUI agent if someone is logged in.
[[ -z "${GUI_USER}" ]] && exit 0
/bin/launchctl asuser "${GUI_USER}" /bin/launchctl load -w /Library/LaunchAgents/com.google.santa.plist
exit 0

View File

@@ -6,18 +6,24 @@
[ "$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 remove com.google.santad
sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&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
# and to clean out the log config, although it won't write after wiping the binary
/usr/bin/killall -HUP syslogd
# delete artifacts on-disk
/bin/rm -rf /Applications/Santa.app
/bin/rm -rf /Library/Extensions/santa-driver.kext
/bin/rm -f /Library/LaunchAgents/com.google.santagui.plist
/bin/rm -f /Library/LaunchAgents/com.google.santa.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santad.plist
/bin/rm -f /Library/LaunchDaemons/com.google.santa.bundleservice.plist
/bin/rm -f /private/etc/asl/com.google.santa.asl.conf
/bin/rm -f /private/etc/newsyslog.d/com.google.santa.newsyslog.conf
/bin/rm -f /usr/local/bin/santactl # just a symlink

22
Podfile Normal file
View File

@@ -0,0 +1,22 @@
def common_pods
pod 'MOLXPCConnection'
pod 'MOLCodesignChecker'
pod 'FMDB'
pod 'MOLCertificate'
pod 'OCMock'
pod 'MOLAuthenticatingURLSession'
pod 'MOLFCMClient'
end
project './Santa.xcodeproj'
project = Xcodeproj::Project.open "./Santa.xcodeproj"
project.targets.each do |t|
if t.name == "santa-driver"
next
end
target t.name do
common_pods
end
end

46
Podfile.lock Normal file
View File

@@ -0,0 +1,46 @@
PODS:
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- MOLAuthenticatingURLSession (2.4):
- MOLCertificate (~> 1.8)
- MOLCertificate (1.9)
- MOLCodesignChecker (1.10):
- MOLCertificate (~> 1.8)
- MOLFCMClient (1.8):
- MOLAuthenticatingURLSession (~> 2.4)
- MOLXPCConnection (1.2):
- MOLCodesignChecker (~> 1.9)
- OCMock (3.5)
DEPENDENCIES:
- FMDB
- MOLAuthenticatingURLSession
- MOLCertificate
- MOLCodesignChecker
- MOLFCMClient
- MOLXPCConnection
- OCMock
SPEC REPOS:
https://github.com/cocoapods/specs.git:
- FMDB
- MOLAuthenticatingURLSession
- MOLCertificate
- MOLCodesignChecker
- MOLFCMClient
- MOLXPCConnection
- OCMock
SPEC CHECKSUMS:
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
MOLAuthenticatingURLSession: c238aa1c9a7b1077eb39a6f40204bfe76a7d204e
MOLCertificate: e9e88a396c57032cab847f51a46e20c730cd752a
MOLCodesignChecker: b0d5db9d2f9bd94e0fd093891a5d40e5ad77cbc0
MOLFCMClient: 2bfbacd45cc11e1ca3c077e97b80401c4e4a54f1
MOLXPCConnection: c27af5cb1c43b18319698b0e568a8ddc2fc1e306
OCMock: 4ab4577fc941af31f4a0398f6e7e230cf21fc72a
PODFILE CHECKSUM: d03767a9915896232523962c98d9ff7294aec2b7
COCOAPODS: 1.7.5

View File

@@ -6,11 +6,11 @@
[doc-status-link]: https://santa.readthedocs.io/en/latest/?badge=latest
<p align="center">
<img src="./Source/SantaGUI/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
<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
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.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C2DD22F0E95000EE2541"
BuildableName = "Santa.app"
BlueprintName = "Santa"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1100"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
debugAsWhichUser = "root"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "C779C4E522F0F51400EE2541"
BuildableName = "com.google.santa.daemon"
BlueprintName = "com.google.santa.daemon"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Santa.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -18,11 +18,11 @@ objc_library(
name = "SNTBlockMessage_SantaGUI",
srcs = ["SNTBlockMessage.m"],
hdrs = ["SNTBlockMessage.h"],
defines = ["SANTAGUI"],
deps = [
":SNTConfigurator",
":SNTStoredEvent",
],
defines = ["SANTAGUI"],
)
objc_library(
@@ -84,6 +84,27 @@ objc_library(
hdrs = ["SNTLogging.h"],
)
cc_library(
name = "SNTPrefixTree",
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
deps = [":SNTLogging"],
)
cc_library(
name = "SNTPrefixTreeKernel",
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = [
"-std=c++11",
"-mkernel",
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
],
defines = ["KERNEL"],
deps = [":SNTLoggingKernel"],
)
objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
@@ -117,7 +138,10 @@ objc_library(
name = "SNTXPCBundleServiceInterface",
srcs = ["SNTXPCBundleServiceInterface.m"],
hdrs = ["SNTXPCBundleServiceInterface.h"],
deps = [":SNTStoredEvent"],
deps = [
":SNTStoredEvent",
"@MOLXPCConnection",
],
)
objc_library(
@@ -125,6 +149,7 @@ objc_library(
srcs = ["SNTXPCControlInterface.m"],
hdrs = ["SNTXPCControlInterface.h"],
deps = [
":SNTConfigurator",
":SNTStoredEvent",
":SNTXPCUnprivilegedControlInterface",
"@MOLXPCConnection",
@@ -179,3 +204,9 @@ santa_unit_test(
]),
deps = [":SNTFileInfo"],
)
santa_unit_test(
name = "SNTPrefixTreeTest",
srcs = ["SNTPrefixTreeTest.mm"],
deps = ["SNTPrefixTree"],
)

View File

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

View File

@@ -74,7 +74,6 @@ typedef NS_ENUM(NSInteger, SNTRuleTableError) {
SNTRuleTableErrorEmptyRuleArray,
SNTRuleTableErrorInsertOrReplaceFailed,
SNTRuleTableErrorInvalidRule,
SNTRuleTableErrorMissingRequiredRule,
SNTRuleTableErrorRemoveFailed
};
@@ -93,6 +92,6 @@ typedef NS_ENUM(NSInteger, SNTEventLogType) {
};
static const char *kKextPath = "/Library/Extensions/santa-driver.kext";
static const char *kSantaDPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santad";
static const char *kSantaCtlPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santactl";
static const char *kSantaAppPath = "/Library/Extensions/santa-driver.kext/Contents/Resources/Santa.app";
static const char *kSantaDPath = "/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon";
static const char *kSantaCtlPath = "/Applications/Santa.app/Contents/MacOS/santactl";
static const char *kSantaAppPath = "/Applications/Santa.app";

View File

@@ -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 whitelist
/// 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,15 @@
///
@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;
#pragma mark - GUI Settings
///

View File

@@ -66,6 +66,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,6 +76,8 @@ static NSString *const kEventLogPath = @"EventLogPath";
static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration";
static NSString *const kEnableSystemExtension = @"EnableSystemExtension";
// The keys managed by a sync server or mobileconfig.
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kEnableTransitiveWhitelistingKey = @"EnableTransitiveWhitelisting";
@@ -112,6 +115,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kWhitelistRegexKey : re,
kBlacklistRegexKey : re,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey: number,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
@@ -135,6 +139,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kEventLogType : string,
kEventLogPath : string,
kEnableMachineIDDecoration : number,
kEnableSystemExtension : number,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
@@ -301,6 +306,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self syncAndConfigStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSystemExtension {
return [self configStateSet];
}
#pragma mark Public Interface
- (SNTClientMode)clientMode {
@@ -381,6 +390,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 +515,17 @@ 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;
}
}
#pragma mark Private
///

View File

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

View File

@@ -39,9 +39,9 @@
sut = [[SNTFileInfo alloc] initWithPath:@"../../../../../../../../../../../../../../../bin/ls"];
XCTAssertEqualObjects(sut.path, @"/bin/ls");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/qlmanage"];
XCTAssertEqualObjects(sut.path, @"/System/Library/Frameworks/QuickLook.framework/Versions/A/"
@"Resources/quicklookd.app/Contents/MacOS/qlmanage");
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/AppleFileServer"];
XCTAssertEqualObjects(sut.path, @"/System/Library/CoreServices/AppleFileServer.app/"
@"Contents/MacOS/AppleFileServer");
}
- (void)testSHA1 {

View File

@@ -124,6 +124,12 @@ typedef struct {
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
// For messages that originate from EndpointSecurity, this points to a copy of the message.
void *es_message;
// For messages that originate from EndpointSecurity, this points to an NSArray of the arguments.
void *args_array;
} santa_message_t;
// Used for the kSantaUserClientCacheBucketCount request.

View File

@@ -34,6 +34,10 @@
#else // KERNEL
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
@@ -60,6 +64,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__)
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__)
#ifdef __cplusplus
} // extern C
#endif
#endif // KERNEL
#endif // SANTA__COMMON__LOGGING_H

View File

@@ -45,7 +45,8 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
}
// 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);
}

View File

@@ -12,55 +12,51 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "Source/santa_driver/SantaPrefixTree.h"
#include "Source/common/SNTPrefixTree.h"
#ifdef KERNEL
#include <libkern/locks.h>
#include "Source/common/SNTLogging.h"
#else
#include <mutex>
#include <string.h>
#define LOGD(format, ...) // NOP
#define LOGE(format, ...) // NOP
#define lck_grp_attr_alloc_init() nullptr
#define lck_grp_alloc_init(name, attr) nullptr
#define lck_attr_alloc_init() nullptr
#define lck_rw_lock_shared(l) pthread_rwlock_rdlock(&l)
#define lck_rw_unlock_shared(l) pthread_rwlock_unlock(&l)
#define lck_rw_lock_exclusive(l) pthread_rwlock_wrlock(&l)
#define lck_rw_unlock_exclusive(l) pthread_rwlock_unlock(&l)
#define lck_rw_alloc_init(g, a) new std::shared_mutex
#define lck_mtx_alloc_init(g, a) new std::mutex
#define lck_attr_free(attr) // NOP
#define lck_grp_free(grp) // NOP
#define lck_grp_attr_free(grp_attr) // NOP
#define lck_rw_lock_shared(l) l->lock_shared()
#define lck_rw_unlock_shared(l) l->unlock_shared()
#define lck_rw_lock_exclusive(l) l->lock()
#define lck_rw_unlock_exclusive(l) l->unlock()
#define lck_rw_lock_shared_to_exclusive(l) ({ l->unlock_shared(); false; })
#define lck_rw_lock_exclusive_to_shared(l) l->unlock(); l->lock_shared()
#define lck_rw_lock_shared_to_exclusive(l) ({ pthread_rwlock_unlock(&l); false; })
#define lck_rw_lock_exclusive_to_shared(l) ({ pthread_rwlock_unlock(&l); pthread_rwlock_rdlock(&l); })
#define lck_mtx_lock(l) l->lock()
#define lck_mtx_unlock(l) l->unlock()
#endif // KERNEL
SantaPrefixTree::SantaPrefixTree(uint32_t max_nodes) {
SNTPrefixTree::SNTPrefixTree(uint32_t max_nodes) {
root_ = new SantaPrefixNode();
node_count_ = 0;
max_nodes_ = max_nodes;
#ifdef KERNEL
spt_lock_grp_attr_ = lck_grp_attr_alloc_init();
spt_lock_grp_ = lck_grp_alloc_init("santa-prefix-tree-lock", spt_lock_grp_attr_);
spt_lock_attr_ = lck_attr_alloc_init();
spt_lock_ = lck_rw_alloc_init(spt_lock_grp_, spt_lock_attr_);
spt_add_lock_ = lck_mtx_alloc_init(spt_lock_grp_, spt_lock_attr_);
#else
pthread_rwlock_init(&spt_lock_, nullptr);
spt_add_lock_ = new std::mutex;
#endif
}
IOReturn SantaPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
IOReturn SNTPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
// Serialize requests to AddPrefix. Otherwise one AddPrefix thread could overwrite whole
// branches of another. HasPrefix is still free to read the tree, until AddPrefix needs to
// modify it.
@@ -156,7 +152,7 @@ IOReturn SantaPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
return kIOReturnSuccess;
}
bool SantaPrefixTree::HasPrefix(const char *string) {
bool SNTPrefixTree::HasPrefix(const char *string) {
lck_rw_lock_shared(spt_lock_);
auto found = false;
@@ -184,7 +180,7 @@ bool SantaPrefixTree::HasPrefix(const char *string) {
return found;
}
void SantaPrefixTree::Reset() {
void SNTPrefixTree::Reset() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
@@ -194,7 +190,7 @@ void SantaPrefixTree::Reset() {
lck_rw_unlock_exclusive(spt_lock_);
}
void SantaPrefixTree::PruneNode(SantaPrefixNode *target) {
void SNTPrefixTree::PruneNode(SantaPrefixNode *target) {
if (!target) return;
// For deep trees, a recursive approach will generate too many stack frames. Make a "stack"
@@ -226,13 +222,13 @@ void SantaPrefixTree::PruneNode(SantaPrefixNode *target) {
delete[] stack;
}
SantaPrefixTree::~SantaPrefixTree() {
SNTPrefixTree::~SNTPrefixTree() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
root_ = nullptr;
lck_rw_unlock_exclusive(spt_lock_);
#ifdef KERNEL
#ifdef KERNEL
if (spt_lock_) {
lck_rw_free(spt_lock_, spt_lock_grp_);
spt_lock_ = nullptr;
@@ -242,7 +238,6 @@ SantaPrefixTree::~SantaPrefixTree() {
lck_mtx_free(spt_add_lock_, spt_lock_grp_);
spt_add_lock_ = nullptr;
}
#endif
if (spt_lock_attr_) {
lck_attr_free(spt_lock_attr_);
@@ -258,4 +253,7 @@ SantaPrefixTree::~SantaPrefixTree() {
lck_grp_attr_free(spt_lock_grp_attr_);
spt_lock_grp_attr_ = nullptr;
}
#else
pthread_rwlock_destroy(&spt_lock_);
#endif
}

View File

@@ -22,16 +22,16 @@
#include <libkern/locks.h>
#else
// Support for unit testing.
// Requires c++17 / macOS 10.12.
// TODO(bur): Handle warnings from bumping target version of the tests to 10.12.
#include <shared_mutex>
#include <mutex>
#include <pthread.h>
#include <stdint.h>
#endif // KERNEL
///
/// SantaPrefixTree is a simple prefix tree implementation.
/// Operations are thread safe.
///
class SantaPrefixTree {
class SNTPrefixTree {
public:
// Add a prefix to the tree.
// Optionally pass node_count to get the number of nodes after the add.
@@ -43,8 +43,8 @@ class SantaPrefixTree {
// Reset the tree.
void Reset();
SantaPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
~SantaPrefixTree();
SNTPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
~SNTPrefixTree();
private:
///
@@ -85,19 +85,19 @@ class SantaPrefixTree {
uint32_t max_nodes_;
uint32_t node_count_;
#ifdef KERNEL
#ifdef KERNEL
lck_grp_t *spt_lock_grp_;
lck_grp_attr_t *spt_lock_grp_attr_;
lck_attr_t *spt_lock_attr_;
lck_rw_t *spt_lock_;
lck_mtx_t *spt_add_lock_;
#else // KERNEL
#else // KERNEL
void *spt_lock_grp_;
void *spt_lock_grp_attr_;
void *spt_lock_attr_;
std::shared_mutex *spt_lock_;
pthread_rwlock_t spt_lock_;
std::mutex *spt_add_lock_;
#endif // KERNEL
#endif // KERNEL
};
#endif /* SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H */

View File

@@ -14,22 +14,22 @@
#import <XCTest/XCTest.h>
#include "Source/santa_driver/SantaPrefixTree.h"
#include "Source/common/SNTPrefixTree.h"
@interface SantaPrefixTreeTest : XCTestCase
@interface SNTPrefixTreeTest : XCTestCase
@end
@implementation SantaPrefixTreeTest
@implementation SNTPrefixTreeTest
- (void)testAddAndHas {
auto t = SantaPrefixTree();
auto t = SNTPrefixTree();
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
}
- (void)testReset {
auto t = SantaPrefixTree();
auto t = SNTPrefixTree();
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
t.Reset();
@@ -38,7 +38,7 @@
- (void)testThreading {
uint32_t count = 4096;
auto t = new SantaPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
auto t = new SNTPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; ++i) {

View File

@@ -14,6 +14,8 @@
#import <Foundation/Foundation.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
@class SNTStoredEvent;
/// A block that takes the calculated bundle hash, associated events and hashing time in ms.
@@ -25,7 +27,7 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
/// @param listener The listener to connect back to the SantaGUI.
///
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
///
/// Hash a bundle for an event. The SNTBundleHashBlock will be called with nil parameters if a
@@ -39,6 +41,11 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
///
/// santabundleservice is launched on demand by launchd, call spindown to let santabundleservice know you are done with it.
///
- (void)spindown;
@end
@interface SNTXPCBundleServiceInterface : NSObject
@@ -52,6 +59,12 @@ typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNu
///
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceId;
+ (NSString *)serviceID;
///
/// Retrieve a pre-configured MOLXPCConnection for communicating with santabundleservice.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (MOLXPCConnection *)configuredConnection;
@end

View File

@@ -29,8 +29,15 @@
return r;
}
+ (NSString *)serviceId {
return @"com.google.santabs";
+ (NSString *)serviceID {
return @"com.google.santa.bundleservice";
}
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
privileged:YES];
c.remoteInterface = [self bundleServiceInterface];
return c;
}
@end

View File

@@ -57,12 +57,28 @@
@end
@interface SNTXPCControlInterface : SNTXPCUnprivilegedControlInterface
@interface SNTXPCControlInterface : NSObject
///
/// Internal method used to initialize the control interface
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceID;
+ (void)initializeControlInterface:(NSXPCInterface *)r;
///
/// Returns the SystemExtension ID for this service.
///
+ (NSString *)systemExtensionID;
///
/// Returns an initialized NSXPCInterface for the SNTUnprivilegedDaemonControlXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)controlInterface;
///
/// Retrieve a pre-configured MOLXPCConnection for communicating with santad.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (MOLXPCConnection *)configuredConnection;
@end

View File

@@ -14,20 +14,33 @@
#import "Source/common/SNTXPCControlInterface.h"
#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"
NSString *const kBundleID = @"com.google.santa.daemon";
@implementation SNTXPCControlInterface
+ (NSString *)serviceId {
return @"SantaXPCControl";
+ (NSString *)serviceID {
if ([[SNTConfigurator configurator] enableSystemExtension]) {
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithSelf];
// "teamid.com.google.santa.daemon.xpc"
NSString *t = cs.signingInformation[@"teamid"];
return [NSString stringWithFormat:@"%@.%@.xpc", t, kBundleID];
}
return kBundleID;
}
+ (NSString *)systemExtensionID {
return kBundleID;
}
+ (void)initializeControlInterface:(NSXPCInterface *)r {
[super initializeControlInterface:r];
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(databaseEventsPending:)
argumentIndex:0
@@ -47,7 +60,7 @@
}
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceId]
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceID]
privileged:YES];
c.remoteInterface = [self controlInterface];
return c;

View File

@@ -24,16 +24,10 @@
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message;
- (void)postClientModeNotification:(SNTClientMode)clientmode;
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message;
@end
/// Protocol implemented by SantaGUI and utilized by santabs
@protocol SNTBundleNotifierXPC
- (void)updateCountsForEvent:(SNTStoredEvent *)event
binaryCount:(uint64_t)binaryCount
fileCount:(uint64_t)fileCount
hashedCount:(uint64_t)hashedCount;
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener;
@end
@interface SNTXPCNotifierInterface : NSObject
@@ -44,10 +38,4 @@
///
+ (NSXPCInterface *)notifierInterface;
///
/// @return an initialized NSXPCInterface for the SNTBundleNotifierXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)bundleNotifierInterface;
@end

View File

@@ -20,8 +20,4 @@
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
}
+ (NSXPCInterface *)bundleNotifierInterface {
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTBundleNotifierXPC)];
}
@end

View File

@@ -17,7 +17,6 @@
#import "Source/common/SNTCommonEnums.h"
#import "Source/common/SNTKernelCommon.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
@class SNTRule;
@class SNTStoredEvent;
@@ -78,7 +77,6 @@
/// GUI Ops
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
///
/// Syncd Ops
@@ -88,34 +86,21 @@
///
/// Bundle Ops
///
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
@end
@interface SNTXPCUnprivilegedControlInterface : NSObject
///
/// Returns the MachService ID for this service.
///
+ (NSString *)serviceId;
///
/// Returns an initialized NSXPCInterface for the SNTUnprivilegedDaemonControlXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning
///
+ (NSXPCInterface *)controlInterface;
///
/// Retrieve a pre-configured MOLXPCConnection for communicating with santad.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (MOLXPCConnection *)configuredConnection;
///
/// Internal method used to initialize the control interface
///
+ (void)initializeControlInterface:(NSXPCInterface *)r;
@end

View File

@@ -21,16 +21,7 @@
@implementation SNTXPCUnprivilegedControlInterface
+ (NSString *)serviceId {
return @"SantaUnprivilegedXPCControl";
}
+ (void)initializeControlInterface:(NSXPCInterface *)r {
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(hashBundleBinariesForEvent:reply:)
argumentIndex:1
ofReply:YES];
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
forSelector:@selector(syncBundleEvent:relatedEvents:)
argumentIndex:1
@@ -44,11 +35,4 @@
return r;
}
+ (MOLXPCConnection *)configuredConnection {
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceId]
privileged:YES];
c.remoteInterface = [self controlInterface];
return c;
}
@end

View File

@@ -30,6 +30,7 @@ objc_library(
sdk_frameworks = [
"IOKit",
"SecurityInterface",
"SystemExtensions",
],
deps = [
"//Source/common:SNTBlockMessage_SantaGUI",
@@ -42,11 +43,16 @@ objc_library(
)
macos_application(
name = "SantaGUI",
name = "Santa",
additional_contents = {
"//Source/santactl": "MacOS",
"//Source/santabundleservice": "MacOS",
"//Source/santad:com.google.santa.daemon": "Library/SystemExtensions",
},
app_icons = glob(["Resources/Images.xcassets/**"]),
bundle_id = "com.google.SantaGUI",
bundle_id = "com.google.santa",
bundle_name = "Santa",
infoplists = ["Resources/SantaGUI-Info.plist"],
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
version = "//:version",
visibility = ["//visibility:public"],

View File

@@ -2,31 +2,31 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>NSHumanReadableCopyright</key>
<string>Google, Inc.</string>
<key>CFBundleIdentifier</key>
<string>com.google.SantaGUI</string>
<key>CFBundleName</key>
<string>Santa</string>
<key>CFBundleExecutable</key>
<string>Santa</string>
<key>CFBundleVersion</key>
<string>${SANTA_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${SANTA_VERSION}</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_VERSION_MIN}</string>
<key>LSUIElement</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Santa</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_VERSION_MIN}</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Google LLC.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

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

View File

@@ -12,14 +12,10 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/SantaGUI/SNTAccessibleTextField.h"
#import "Source/santa/SNTAccessibleTextField.h"
@implementation SNTAccessibleTextField
- (BOOL)accessibilityIsIgnored {
return NO;
}
- (NSString *)accessibilityLabel {
if (self.toolTip && self.stringValue) {
return [NSString stringWithFormat:@"%@: %@", self.toolTip, self.stringValue];

View File

@@ -12,21 +12,23 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/SantaGUI/SNTAppDelegate.h"
#import "Source/santa/SNTAppDelegate.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <SystemExtensions/SystemExtensions.h>
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/SantaGUI/SNTAboutWindowController.h"
#import "Source/SantaGUI/SNTNotificationManager.h"
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/santa/SNTNotificationManager.h"
@interface SNTAppDelegate ()
@interface SNTAppDelegate ()<OSSystemExtensionRequestDelegate>
@property SNTAboutWindowController *aboutWindowController;
@property SNTNotificationManager *notificationManager;
@property MOLXPCConnection *daemonListener;
@property MOLXPCConnection *bundleListener;
@end
@implementation SNTAppDelegate
@@ -34,6 +36,10 @@
#pragma mark App Delegate methods
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if (@available(macOS 10.15, *)) {
[self loadSystemExtension];
}
[self setupMenu];
self.notificationManager = [[SNTNotificationManager alloc] init];
@@ -46,21 +52,15 @@
self.daemonListener.invalidationHandler = nil;
[self.daemonListener invalidate];
self.daemonListener = nil;
self.bundleListener.invalidationHandler = nil;
[self.bundleListener invalidate];
self.bundleListener = nil;
}];
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
[self attemptDaemonReconnection];
[self attemptBundleReconnection];
}];
[self createDaemonConnection];
[self createBundleConnection];
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
@@ -69,6 +69,20 @@
return NO;
}
- (void)loadSystemExtension API_AVAILABLE(macos(10.15)) {
if (![[SNTConfigurator configurator] enableSystemExtension]) {
LOGI(@"EnableSystemExtension is disabled");
return;
}
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];
}
#pragma mark Connection handling
- (void)createDaemonConnection {
@@ -90,6 +104,11 @@
};
[self.daemonListener resume];
// This listener will also handle bundle service requests to update the GUI.
// When initializing connections with santabundleservice, the notification manager
// will send along the endpoint so santabundleservice knows where to find us.
self.notificationManager.notificationListener = listener.endpoint;
// Tell daemon to connect back to the above listener.
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
[daemonConn resume];
@@ -108,43 +127,6 @@
[self performSelectorInBackground:@selector(createDaemonConnection) withObject:nil];
}
- (void)createBundleConnection {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
WEAKIFY(self);
// Create listener for return connection from the bundle service.
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.bundleListener = [[MOLXPCConnection alloc] initServerWithListener:listener];
self.bundleListener.privilegedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
self.bundleListener.exportedObject = self.notificationManager;
self.bundleListener.acceptedHandler = ^{
dispatch_semaphore_signal(sema);
};
self.bundleListener.invalidationHandler = ^{
STRONGIFY(self);
[self attemptBundleReconnection];
};
[self.bundleListener resume];
// Tell santabs to connect back to the above listener.
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
[daemonConn resume];
[[daemonConn remoteObjectProxy] setBundleNotificationListener:listener.endpoint];
[daemonConn invalidate];
// Now wait for the connection to come in.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self attemptBundleReconnection];
}
}
- (void)attemptBundleReconnection {
self.bundleListener.invalidationHandler = nil;
[self.bundleListener invalidate];
[self performSelectorInBackground:@selector(createBundleConnection) withObject:nil];
}
#pragma mark Menu Management
- (void)setupMenu {
@@ -161,4 +143,28 @@
[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);
return OSSystemExtensionReplacementActionReplace;
}
- (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

View File

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

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/SantaGUI/SNTMessageWindowController.h"
#import "Source/santa/SNTMessageWindowController.h"
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>
@@ -20,7 +20,7 @@
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/SantaGUI/SNTMessageWindow.h"
#import "Source/santa/SNTMessageWindow.h"
@interface SNTMessageWindowController ()
/// The custom message to display for this event

View File

@@ -15,12 +15,13 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/SantaGUI/SNTMessageWindowController.h"
#import "Source/santa/SNTMessageWindowController.h"
///
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.
///
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate,
SNTNotifierXPC, SNTBundleNotifierXPC>
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate, SNTNotifierXPC>
@property NSXPCListenerEndpoint *notificationListener;
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/SantaGUI/SNTNotificationManager.h"
#import "Source/santa/SNTNotificationManager.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -31,12 +31,6 @@
/// The queue of pending notifications
@property(readonly) NSMutableArray *pendingNotifications;
/// The connection to the bundle service
@property MOLXPCConnection *bundleServiceConnection;
/// A semaphore to block bundle hashing until a connection is established
@property dispatch_semaphore_t bundleServiceSema;
// A serial queue for holding hashBundleBinaries requests
@property dispatch_queue_t hashBundleBinariesQueue;
@@ -50,7 +44,6 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
self = [super init];
if (self) {
_pendingNotifications = [[NSMutableArray alloc] init];
_bundleServiceSema = dispatch_semaphore_create(0);
_hashBundleBinariesQueue = dispatch_queue_create("com.google.santagui.hashbundlebinaries",
DISPATCH_QUEUE_SERIAL);
}
@@ -72,10 +65,10 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
});
}
} else {
// Tear down the bundle service
self.bundleServiceSema = dispatch_semaphore_create(0);
[self.bundleServiceConnection invalidate];
self.bundleServiceConnection = nil;
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
[bc resume];
[[bc remoteObjectProxy] spindown];
[bc invalidate];
[NSApp hide:self];
}
}
@@ -192,50 +185,35 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
}
}
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener {
// Ensure any existing listener is invalidated.
self.bundleServiceConnection.invalidationHandler = nil;
[self.bundleServiceConnection invalidate];
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithListener:listener];
c.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
[c resume];
self.bundleServiceConnection = c;
WEAKIFY(self);
self.bundleServiceConnection.invalidationHandler = ^{
STRONGIFY(self);
if (self.currentWindowController) {
[self updateBlockNotification:self.currentWindowController.event withBundleHash:nil];
}
};
dispatch_semaphore_signal(self.bundleServiceSema);
}
#pragma mark SNTBundleNotifierXPC helper methods
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event {
self.currentWindowController.foundFileCountLabel.stringValue = @"Searching for files...";
// Wait a max of 6 secs for the bundle service. Should the bundle service fall over, it will
// reconnect within 5 secs. Otherwise abandon bundle hashing and display the blockable event.
if (dispatch_semaphore_wait(self.bundleServiceSema,
dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC))) {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
bc.acceptedHandler = ^{
dispatch_semaphore_signal(sema);
};
[bc resume];
// Wait a max of 5 secs for the bundle service
// Otherwise abandon bundle hashing and display the blockable event.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self updateBlockNotification:event withBundleHash:nil];
return;
}
// Let all future requests flow, until the connection is terminated and we go back to waiting.
dispatch_semaphore_signal(self.bundleServiceSema);
[[bc remoteObjectProxy] setNotificationListener:self.notificationListener];
// NSProgress becomes current for this thread. XPC messages vend a child node to the receiver.
[self.currentWindowController.progress becomeCurrentWithPendingUnitCount:100];
// Start hashing. Progress is reported to the root NSProgress (currentWindowController.progress).
[[self.bundleServiceConnection remoteObjectProxy]
hashBundleBinariesForEvent:event
reply:^(NSString *bh, NSArray<SNTStoredEvent *> *events, NSNumber *ms) {
[[bc remoteObjectProxy] hashBundleBinariesForEvent:event
reply:^(NSString *bh,
NSArray<SNTStoredEvent *> *events,
NSNumber *ms) {
// Revert to displaying the blockable event if we fail to calculate the bundle hash
if (!bh) return [self updateBlockNotification:event withBundleHash:nil];
@@ -257,7 +235,10 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
// Update the UI with the bundle hash. Also make the openEventButton available.
[self updateBlockNotification:event withBundleHash:bh];
[bc invalidate];
}];
[self.currentWindowController.progress resignCurrent];
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.system-extension.install</key>
<true/>
</dict>
</plist>

100
Source/santa/main.m Normal file
View File

@@ -0,0 +1,100 @@
/// Copyright 2015 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 <Cocoa/Cocoa.h>
#import <SystemExtensions/SystemExtensions.h>
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAppDelegate.h"
@interface SNTSystemExtensionDelegate : NSObject<OSSystemExtensionRequestDelegate>
@end
@implementation SNTSystemExtensionDelegate
#pragma mark OSSystemExtensionRequestDelegate
- (OSSystemExtensionReplacementAction)request:(OSSystemExtensionRequest *)request
actionForReplacingExtension:(OSSystemExtensionProperties *)old
withExtension:(OSSystemExtensionProperties *)new
API_AVAILABLE(macos(10.15)) {
NSLog(@"SystemExtension \"%@\" request for replacement", request.identifier);
return OSSystemExtensionReplacementActionReplace;
}
- (void)requestNeedsUserApproval:(OSSystemExtensionRequest *)request API_AVAILABLE(macos(10.15)) {
NSLog(@"SystemExtension \"%@\" request needs user approval", request.identifier);
}
- (void)request:(OSSystemExtensionRequest *)request
didFailWithError:(NSError *)error API_AVAILABLE(macos(10.15)) {
NSLog(@"SystemExtension \"%@\" request did fail: %@", request.identifier, error);
exit((int)error.code);
}
- (void)request:(OSSystemExtensionRequest *)request
didFinishWithResult:(OSSystemExtensionRequestResult)result API_AVAILABLE(macos(10.15)) {
NSLog(@"SystemExtension \"%@\" request did finish: %ld", request.identifier, (long)result);
exit(0);
}
@end
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSNumber *sysxOperation;
NSArray *args = [NSProcessInfo processInfo].arguments;
if ([args containsObject:@"--load-system-extension"]) {
sysxOperation = @(1);
} else if ([args containsObject:@"--unload-system-extension"]) {
sysxOperation = @(2);
}
if (sysxOperation) {
if (@available(macOS 10.15, *)) {
NSString *e = [SNTXPCControlInterface systemExtensionID];
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) {
NSLog(@"Requesting SystemExtension deactivation");
req = [OSSystemExtensionRequest deactivationRequestForExtension:e queue:q];
}
if (req) {
SNTSystemExtensionDelegate *ed = [[SNTSystemExtensionDelegate alloc] init];
req.delegate = ed;
[[OSSystemExtensionManager sharedManager] submitRequest:req];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60), q, ^{
exit(1);
});
[[NSRunLoop mainRunLoop] run];
}
} else {
exit(1);
}
}
NSApplication *app = [NSApplication sharedApplication];
SNTAppDelegate *delegate = [[SNTAppDelegate alloc] init];
[app setDelegate:delegate];
[app finishLaunching];
[app run];
}
}

View File

@@ -18,20 +18,15 @@ cc_library(
"SantaDriver.h",
"SantaDriverClient.cc",
"SantaDriverClient.h",
"SantaPrefixTree.cc",
"SantaPrefixTree.h",
"main.cc",
],
copts = [
"-mkernel",
"-fapple-kext",
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/PrivateHeaders",
"-I__BAZEL_XCODE_SDKROOT__/System/Library/Frameworks/Kernel.framework/Headers",
],
defines = [
"KERNEL",
"KERNEL_PRIVATE",
"DRIVER_PRIVATE",
"APPLE",
"NeXT",
"SANTA_VERSION=" + SANTA_VERSION,
@@ -39,6 +34,7 @@ cc_library(
deps = [
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLoggingKernel",
"//Source/common:SNTPrefixTreeKernel",
],
alwayslink = 1,
)
@@ -52,34 +48,8 @@ santa_unit_test(
deps = ["//Source/common:SNTKernelCommon"],
)
cc_library(
name = "SantaPrefixTree_userland_lib",
srcs = ["SantaPrefixTree.cc"],
hdrs = ["SantaPrefixTree.h"],
copts = ["-std=c++1z"],
visibility = ["//visibility:public"],
)
santa_unit_test(
name = "SantaPrefixTreeTest",
srcs = ["SantaPrefixTreeTest.mm"],
copts = ["-std=c++1z"],
minimum_os_version = "10.12",
deps = [
":SantaPrefixTree_userland_lib",
"//Source/common:SNTKernelCommon"
],
)
# Full santa-driver.kext containing all Santa components
macos_kernel_extension(
name = "santa_driver",
additional_contents = {
"//Source/santabs": "XPCServices",
"//Source/SantaGUI": "Resources",
"//Source/santactl": "MacOS",
"//Source/santad": "MacOS",
},
bundle_id = "com.google.santa-driver",
bundle_name = "santa-driver",
infoplists = ["Info.plist"],
@@ -89,17 +59,6 @@ macos_kernel_extension(
deps = [":santa_driver_lib"],
)
# A minimal santa-driver.kext, no other Santa components
macos_kernel_extension(
name = "santa_driver_min",
bundle_id = "com.google.santa-driver",
bundle_name = "santa-driver",
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
version = "//:version",
deps = [":santa_driver_lib"],
)
objc_library(
name = "kernel_tests_lib",
srcs = ["kernel_tests.mm"],
@@ -121,7 +80,7 @@ run_command(
name = "kernel_tests",
srcs = [
":kernel_tests_bin",
":santa_driver_min",
":santa_driver",
],
cmd = """
env

View File

@@ -2,22 +2,20 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>NSHumanReadableCopyright</key>
<string>Google, Inc.</string>
<key>CFBundleIdentifier</key>
<string>com.google.santa-driver</string>
<key>CFBundleName</key>
<string>santa-driver</string>
<key>CFBundleExecutable</key>
<string>santa-driver</string>
<key>CFBundleVersion</key>
<string>${SANTA_VERSION}</string>
<key>CFBundleIdentifier</key>
<string>com.google.santa-driver</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>santa-driver</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>${SANTA_VERSION}</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>IOKitPersonalities</key>
<dict>
<key>SantaDriver</key>
@@ -26,16 +24,18 @@
<string>com.google.santa-driver</string>
<key>IOClass</key>
<string>com_google_SantaDriver</string>
<key>IOMatchCategory</key>
<string>com_google_SantaDriver</string>
<key>IOProviderClass</key>
<string>IOResources</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserClientClass</key>
<string>com_google_SantaDriverClient</string>
<key>IOMatchCategory</key>
<string>com_google_SantaDriver</string>
</dict>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Google LLC.</string>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.bsd</key>

View File

@@ -58,7 +58,7 @@ bool SantaDecisionManager::init() {
root_fsid_ = 0;
// Setup file modification prefix filter.
filemod_prefix_filter_ = new SantaPrefixTree();
filemod_prefix_filter_ = new SNTPrefixTree();
return true;
}

View File

@@ -25,8 +25,8 @@
#include "Source/common/SNTKernelCommon.h"
#include "Source/common/SNTLogging.h"
#include "Source/common/SNTPrefixTree.h"
#include "Source/santa_driver/SantaCache.h"
#include "Source/santa_driver/SantaPrefixTree.h"
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
@@ -347,7 +347,7 @@ class SantaDecisionManager : public OSObject {
SantaCache<santa_vnode_id_t, uint64_t> *vnode_pid_map_;
SantaCache<pid_t, pid_t> *compiler_pid_set_;
SantaPrefixTree *filemod_prefix_filter_;
SNTPrefixTree *filemod_prefix_filter_;
/**
Return the correct cache for a given identifier.

View File

@@ -16,3 +16,14 @@ extern "C" {
__private_extern__ int _kext_apple_cc = __APPLE_CC__ ;
}
#include <IOKit/IOService.h>
#include <IOKit/IOUserClient.h>
// The macOS 10.15 SDK added these Dispatch methods but they aren't
// available on older macOS versions and so prevent kext linking.
// Defining them here as hidden weak no-op's fixes linking and seems to work.
kern_return_t __attribute__((visibility("hidden"))) __attribute__((weak)) OSMetaClassBase::Dispatch(const IORPC rpc) { return KERN_SUCCESS; }
kern_return_t __attribute__((visibility("hidden"))) __attribute__((weak)) OSObject::Dispatch(const IORPC rpc) { return KERN_SUCCESS; }
kern_return_t __attribute__((visibility("hidden"))) __attribute__((weak)) IOService::Dispatch(const IORPC rpc) { return KERN_SUCCESS; }
kern_return_t __attribute__((visibility("hidden"))) __attribute__((weak)) IOUserClient::Dispatch(const IORPC rpc) { return KERN_SUCCESS; }

View File

@@ -1,6 +1,6 @@
licenses(["notice"]) # Apache 2.0
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_xpc_service")
licenses(["notice"]) # Apache 2.0
objc_library(
name = "santabs_lib",
@@ -11,6 +11,7 @@ objc_library(
],
deps = [
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
"//Source/common:SNTXPCBundleServiceInterface",
"//Source/common:SNTXPCNotifierInterface",
"@FMDB",
@@ -19,10 +20,10 @@ objc_library(
],
)
macos_xpc_service(
name = "santabs",
bundle_id = "com.google.santabs",
infoplists = ["Resources/santabs-Info.plist"],
macos_command_line_application(
name = "santabundleservice",
bundle_id = "com.google.santa.bundleservice",
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
version = "//:version",
visibility = ["//visibility:public"],

View File

@@ -5,27 +5,22 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>santabs</string>
<string>santabundleservice</string>
<key>CFBundleExecutable</key>
<string>santabs</string>
<string>santabundleservice</string>
<key>CFBundleIdentifier</key>
<string>com.google.santabs</string>
<string>com.google.santa.bundleservice</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>santabs</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<string>santabundleservice</string>
<key>CFBundleShortVersionString</key>
<string>${SANTA_VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${SANTA_VERSION}</string>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
<string>Application</string>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Google LLC.</string>
</dict>
</plist>

View File

@@ -12,16 +12,17 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santabs/SNTBundleService.h"
#import "Source/santabundleservice/SNTBundleService.h"
#include <stdatomic.h>
#import <CommonCrypto/CommonDigest.h>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <pthread/pthread.h>
#import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCNotifierInterface.h"
@@ -41,51 +42,21 @@
return self;
}
#pragma mark Connection handling
// Create a listener for SantaGUI to connect
- (void)createConnection {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
// Create listener for return connection from SantaGUI.
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[MOLXPCConnection alloc] initServerWithListener:listener];
self.listener.unprivilegedInterface = self.listener.privilegedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
self.listener.exportedObject = self;
self.listener.acceptedHandler = ^{
dispatch_semaphore_signal(sema);
};
// Exit when SantaGUI is done with us.
self.listener.invalidationHandler = ^{
exit(0);
};
[self.listener resume];
// Tell SantaGUI to connect back to the above listener.
[[self.notifierConnection remoteObjectProxy] setBundleServiceListener:listener.endpoint];
// Now wait for the connection to come in.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self attemptReconnection];
}
}
- (void)attemptReconnection {
[self performSelectorOnMainThread:@selector(createConnection) withObject:nil waitUntilDone:NO];
}
#pragma mark SNTBundleServiceXPC Methods
// Connect to the SantaGUI
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener {
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener {
dispatch_async(dispatch_get_main_queue(), ^{
[self.notifierConnection invalidate];
self.notifierConnection = nil;
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithListener:listener];
c.remoteInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
c.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
c.acceptedHandler = ^{
LOGI(@"Connected to Santa.app");
};
[c resume];
self.notifierConnection = c;
[self createConnection];
});
}
@@ -143,6 +114,11 @@
});
}
- (void)spindown {
LOGI(@"Spinning down");
exit(0);
}
#pragma mark Internal Methods
/**

View File

@@ -13,15 +13,24 @@
/// limitations under the License.
#import <Foundation/Foundation.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import "Source/santabs/SNTBundleService.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
#import "Source/santabundleservice/SNTBundleService.h"
int main(int argc, const char *argv[]) {
MOLXPCConnection *c =
[[MOLXPCConnection alloc] initServerWithListener:[NSXPCListener serviceListener]];
c.privilegedInterface = c.unprivilegedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
c.exportedObject = [[SNTBundleService alloc] init];
[c resume];
@autoreleasepool {
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
MOLXPCConnection *c =
[[MOLXPCConnection alloc] initServerWithName:[SNTXPCBundleServiceInterface serviceID]];
c.privilegedInterface = c.unprivilegedInterface =
[SNTXPCBundleServiceInterface bundleServiceInterface];
c.exportedObject = [[SNTBundleService alloc] init];
[c resume];
[[NSRunLoop mainRunLoop] run];
}
}

View File

@@ -71,7 +71,7 @@ objc_library(
macos_command_line_application(
name = "santactl",
bundle_id = "com.google.santactl",
bundle_id = "com.google.santa.ctl",
infoplists = ["Info.plist"],
minimum_os_version = "10.9",
version = "//:version",

View File

@@ -17,7 +17,7 @@
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/common/SNTXPCBundleServiceInterface.h"
#import "Source/santactl/SNTCommand.h"
#import "Source/santactl/SNTCommandController.h"
@@ -35,7 +35,7 @@ REGISTER_COMMAND_NAME(@"bundleinfo")
}
+ (BOOL)requiresDaemonConn {
return YES;
return NO;
}
+ (NSString *)shortHelpText {
@@ -60,10 +60,13 @@ REGISTER_COMMAND_NAME(@"bundleinfo")
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.fileBundlePath = fi.bundlePath;
[[self.daemonConn remoteObjectProxy]
hashBundleBinariesForEvent:se
reply:^(NSString *hash, NSArray<SNTStoredEvent *> *events,
NSNumber *time) {
MOLXPCConnection *bc = [SNTXPCBundleServiceInterface configuredConnection];
[bc resume];
[[bc remoteObjectProxy] hashBundleBinariesForEvent:se
reply:^(NSString *hash,
NSArray<SNTStoredEvent *> *events,
NSNumber *time) {
printf("Hashing time: %llu ms\n", time.unsignedLongLongValue);
printf("%lu events found\n", events.count);
printf("BundleHash: %s\n", hash.UTF8String);
@@ -72,6 +75,7 @@ REGISTER_COMMAND_NAME(@"bundleinfo")
printf("BundleID: %s \n\tSHA-256: %s \n\tPath: %s\n",
event.fileBundleID.UTF8String, event.fileSHA256.UTF8String, event.filePath.UTF8String);
}
[[bc remoteObjectProxy] spindown];
exit(0);
}];
}

View File

@@ -84,14 +84,18 @@ REGISTER_COMMAND_NAME(@"status")
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
SNTConfigurator *configurator = [SNTConfigurator configurator];
// Kext status
__block uint64_t rootCacheCount = -1, nonRootCacheCount = -1;
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
rootCacheCount = rootCache;
nonRootCacheCount = nonRootCache;
dispatch_group_leave(group);
}];
if (![configurator enableSystemExtension]) {
dispatch_group_enter(group);
[[self.daemonConn remoteObjectProxy] cacheCounts:^(uint64_t rootCache, uint64_t nonRootCache) {
rootCacheCount = rootCache;
nonRootCacheCount = nonRootCache;
dispatch_group_leave(group);
}];
}
// Database counts
__block int64_t eventCount = -1, binaryRuleCount = -1, certRuleCount = -1;
@@ -175,7 +179,7 @@ REGISTER_COMMAND_NAME(@"status")
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
NSMutableDictionary *stats = [@{
@"daemon" : @{
@"driver_connected" : @(driverConnected),
@"mode" : clientMode ?: @"null",
@@ -185,10 +189,6 @@ REGISTER_COMMAND_NAME(@"status")
@"watchdog_cpu_peak" : @(cpuPeak),
@"watchdog_ram_peak" : @(ramPeak),
},
@"kernel" : @{
@"root_cache_count" : @(rootCacheCount),
@"non_root_cache_count": @(nonRootCacheCount),
},
@"database" : @{
@"binary_rules" : @(binaryRuleCount),
@"certificate_rules" : @(certRuleCount),
@@ -205,7 +205,13 @@ REGISTER_COMMAND_NAME(@"status")
@"bundle_scanning" : @(enableBundles),
@"transitive_whitelisting" : @(transitiveWhitelistingEnabled),
},
};
} mutableCopy];
if (![configurator enableSystemExtension]) {
stats[@"kernel"] = @{
@"root_cache_count" : @(rootCacheCount),
@"non_root_cache_count": @(nonRootCacheCount),
};
}
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
@@ -218,9 +224,11 @@ 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);
printf(">>> Kernel Info\n");
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
if (![configurator enableSystemExtension]) {
printf(">>> Kernel Info\n");
printf(" %-25s | %lld\n", "Root cache count", rootCacheCount);
printf(" %-25s | %lld\n", "Non-root cache count", nonRootCacheCount);
}
printf(">>> Database Info\n");
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);

View File

@@ -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,6 +71,10 @@ REGISTER_COMMAND_NAME(@"version")
}
- (NSString *)santaKextVersion {
if ([[SNTConfigurator configurator] enableSystemExtension]) {
return @"un-needed (SystemExtension being used)";
}
NSDictionary *loadedKexts = CFBridgingRelease(
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef) @[ @(USERCLIENT_ID) ],
(__bridge CFArrayRef) @[ @"CFBundleVersion" ]));

View File

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

View File

@@ -507,15 +507,15 @@ static void reachabilityHandler(
// Start listening for network state changes on a background thread
- (void)startReachability {
dispatch_async(dispatch_get_main_queue(), ^{
if (_reachability) return;
if (self->_reachability) return;
const char *nodename = [[SNTConfigurator configurator] syncBaseURL].host.UTF8String;
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, nodename);
self->_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, nodename);
SCNetworkReachabilityContext context = {
.info = (__bridge_retained void *)self,
.release = (void (*)(const void *))CFBridgingRelease,
};
if (SCNetworkReachabilitySetCallback(_reachability, reachabilityHandler, &context)) {
SCNetworkReachabilitySetDispatchQueue(_reachability,
if (SCNetworkReachabilitySetCallback(self->_reachability, reachabilityHandler, &context)) {
SCNetworkReachabilitySetDispatchQueue(self->_reachability,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
} else {
[self stopReachability];
@@ -526,10 +526,10 @@ static void reachabilityHandler(
// Stop listening for network state changes
- (void)stopReachability {
dispatch_async(dispatch_get_main_queue(), ^{
if (_reachability) {
SCNetworkReachabilitySetDispatchQueue(_reachability, NULL);
if (_reachability) CFRelease(_reachability);
_reachability = NULL;
if (self->_reachability) {
SCNetworkReachabilitySetDispatchQueue(self->_reachability, NULL);
if (self->_reachability) CFRelease(self->_reachability);
self->_reachability = NULL;
}
});
}

View File

@@ -26,7 +26,7 @@
/**
Initialize this stage. Designated initializer.
@param syncState A holder for state used across requests
@param state A holder for state used across requests
*/
- (nullable instancetype)initWithState:(nonnull SNTCommandSyncState *)state NS_DESIGNATED_INITIALIZER;

View File

@@ -130,10 +130,10 @@
/**
Perform a data request and capture the returned data, response and error objects.
@param request, The request to perform
@param timeout, The number of seconds to wait before cancelling the request
@param response, Return the response details
@param error, Return the error details
@param request The request to perform
@param timeout The number of seconds to wait before cancelling the request
@param response Return the response details
@param error Return the error details
@returns data, The HTTP body of the response
*/
- (NSData *)performRequest:(NSURLRequest *)request

View File

@@ -2,19 +2,17 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.google.santa.ctl</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>NSHumanReadableCopyright</key>
<string>Google, Inc.</string>
<key>CFBundleIdentifier</key>
<string>com.google.santactl</string>
<key>CFBundleName</key>
<string>santactl</string>
<key>CFBundleShortVersionString</key>
<string>${SANTA_VERSION}</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>${SANTA_VERSION}</string>
<key>CSFlags</key>
<string>kill</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Google, LLC.</string>
</dict>
</plist>

View File

@@ -1,8 +1,8 @@
licenses(["notice"]) # Apache 2.0
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_command_line_application")
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_bundle")
load("//:helper.bzl", "santa_unit_test")
licenses(["notice"]) # Apache 2.0
objc_library(
name = "santad_lib",
srcs = [
@@ -12,6 +12,11 @@ objc_library(
"DataLayer/SNTEventTable.m",
"DataLayer/SNTRuleTable.h",
"DataLayer/SNTRuleTable.m",
"EventProviders/SNTDriverManager.h",
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
"EventProviders/SNTEndpointSecurityManager.mm",
"EventProviders/SNTEventProvider.h",
"Logs/SNTEventLog.h",
"Logs/SNTEventLog.m",
"Logs/SNTFileEventLog.h",
@@ -26,8 +31,6 @@ objc_library(
"SNTDaemonControlController.m",
"SNTDatabaseController.h",
"SNTDatabaseController.m",
"SNTDriverManager.h",
"SNTDriverManager.m",
"SNTExecutionController.h",
"SNTExecutionController.m",
"SNTNotificationQueue.h",
@@ -38,6 +41,10 @@ objc_library(
"SNTSyncdQueue.m",
"main.m",
],
sdk_dylibs = [
"EndpointSecurity",
"bsm",
],
sdk_frameworks = [
"DiskArbitration",
"IOKit",
@@ -51,6 +58,7 @@ objc_library(
"//Source/common:SNTFileInfo",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCControlInterface",
@@ -62,10 +70,12 @@ objc_library(
],
)
macos_command_line_application(
name = "santad",
bundle_id = "com.google.santad",
infoplists = ["Resources/santad-Info.plist"],
macos_bundle(
name = "com.google.santa.daemon",
bundle_extension = "systemextension",
bundle_id = "com.google.santa.daemon",
infoplists = ["Info.plist"],
linkopts = ["-execute"],
minimum_os_version = "10.9",
version = "//:version",
visibility = ["//visibility:public"],
@@ -81,12 +91,15 @@ santa_unit_test(
"DataLayer/SNTEventTable.m",
"DataLayer/SNTRuleTable.h",
"DataLayer/SNTRuleTable.m",
"EventProviders/SNTDriverManager.h",
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
"EventProviders/SNTEndpointSecurityManager.mm",
"EventProviders/SNTEventProvider.h",
"Logs/SNTEventLog.h",
"Logs/SNTEventLog.m",
"SNTDatabaseController.h",
"SNTDatabaseController.m",
"SNTDriverManager.h",
"SNTDriverManager.m",
"SNTExecutionController.h",
"SNTExecutionController.m",
"SNTExecutionControllerTest.m",
@@ -97,6 +110,10 @@ santa_unit_test(
"SNTSyncdQueue.h",
"SNTSyncdQueue.m",
],
sdk_dylibs = [
"EndpointSecurity",
"bsm",
],
deps = [
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
@@ -105,6 +122,7 @@ santa_unit_test(
"//Source/common:SNTFileInfo",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SNTRule",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncdInterface",

View File

@@ -30,8 +30,8 @@ static const NSUInteger kTransitiveRuleCullingThreshold = 500000;
static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
@interface SNTRuleTable ()
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@property MOLCodesignChecker *santadCSInfo;
@property MOLCodesignChecker *launchdCSInfo;
@property NSDate *lastTransitiveRuleCulling;
@property NSDictionary *criticalSystemBinaries;
@property(readonly) NSArray *criticalSystemBinaryPaths;
@@ -42,10 +42,51 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
- (NSArray *)criticalSystemBinaryPaths {
return @[
@"/usr/libexec/trustd", @"/usr/sbin/securityd", @"/usr/libexec/xpcproxy",
@"/usr/sbin/ocspd", @"/usr/lib/dyld"
@"/usr/sbin/ocspd", @"/usr/lib/dyld",
@"/Applications/Santa.app/Contents/MacOS/Santa",
@"/Applications/Santa.app/Contents/MacOS/santactl",
@"/Applications/Santa.app/Contents/MacOS/santabundleservice",
// This entry is for on <10.15 - on 10.15+ the binary is actually executed
// from a system-controlled path but will only ever be executed by
// the OS anyway.
@"/Applications/Santa.app/Contents/Library/SystemExtensions/com.google.santa.daemon.systemextension/Contents/MacOS/com.google.santa.daemon",
];
}
- (void)setupSystemCriticalBinaries {
NSMutableDictionary *bins = [NSMutableDictionary dictionary];
for (NSString *path in self.criticalSystemBinaryPaths) {
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
MOLCodesignChecker *csInfo = [binInfo codesignCheckerWithError:NULL];
// Make sure the critical system binary is signed by the same chain as launchd/self
BOOL systemBin = NO;
if ([csInfo signingInformationMatches:self.launchdCSInfo]) {
systemBin = YES;
} else if (![csInfo signingInformationMatches:self.santadCSInfo]) {
LOGE(@"Unable to validate critical system binary. "
@"pid 1: %@, santad: %@ and %@: %@ do not match.",
self.launchdCSInfo.leafCertificate,
self.santadCSInfo.leafCertificate, path, csInfo.leafCertificate);
continue;
}
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.decision = SNTEventStateAllowBinary;
cd.decisionExtra = systemBin ? @"critical system binary" : @"santa binary";
cd.sha256 = binInfo.SHA256;
// Not needed, but nice for logging.
cd.certSHA256 = csInfo.leafCertificate.SHA256;
cd.certCommonName = csInfo.leafCertificate.commonName;
bins[binInfo.SHA256] = cd;
}
self.criticalSystemBinaries = bins;
}
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Lock this database from other processes
[[db executeQuery:@"PRAGMA locking_mode = EXCLUSIVE;"] close];
@@ -72,18 +113,6 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
newVersion = 2;
}
// Save hashes of the signing certs for launchd and santad.
// Used to ensure rules for them are not removed.
self.santadCertSHA = [[[[MOLCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
MOLCodesignChecker *launchdCSInfo = [[MOLCodesignChecker alloc] initWithPID:1];
self.launchdCertSHA = launchdCSInfo.leafCertificate.SHA256;
// Ensure the certificates used to sign the running launchd/santad are whitelisted.
// If they weren't previously and the database is not new, log an error.
int ruleCount = [db intForQuery:@"SELECT COUNT(*)"
@"FROM rules "
@"WHERE (shasum=? OR shasum=?) AND state=? AND type=2",
self.santadCertSHA, self.launchdCertSHA, @(SNTRuleStateWhitelist)];
if (version < 3) {
// Add timestamp column for tracking age of transitive rules.
@@ -91,41 +120,12 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
newVersion = 3;
}
if (ruleCount != 2) {
if (version > 0) LOGE(@"Started without launchd/santad certificate rules in place!");
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
self.santadCertSHA, @(SNTRuleStateWhitelist), @(SNTRuleTypeCertificate)];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
self.launchdCertSHA, @(SNTRuleStateWhitelist), @(SNTRuleTypeCertificate)];
}
// Save signing info for launchd and santad. Used to ensure they are always allowed.
self.santadCSInfo = [[MOLCodesignChecker alloc] initWithSelf];
self.launchdCSInfo = [[MOLCodesignChecker alloc] initWithPID:1];
// Setup critical system binaries
// TODO(tburgin): Add the Santa components to this feature and remove the santadCertSHA rule.
NSMutableDictionary *bins = [NSMutableDictionary dictionary];
for (NSString *path in self.criticalSystemBinaryPaths) {
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
MOLCodesignChecker *csInfo = [binInfo codesignCheckerWithError:NULL];
// Make sure the critical system binary is signed by the same chain as launchd.
if ([csInfo signingInformationMatches:launchdCSInfo]) {
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.decision = SNTEventStateAllowBinary;
cd.decisionExtra = @"critical system binary";
cd.sha256 = binInfo.SHA256;
// Not needed, but nice for logging.
cd.certSHA256 = csInfo.leafCertificate.SHA256;
cd.certCommonName = csInfo.leafCertificate.commonName;
bins[binInfo.SHA256] = cd;
} else {
LOGE(@"Unable to validate critical system binary. pid 1: %@ and %@: %@ do not match.",
launchdCSInfo.leafCertificate, path, csInfo.leafCertificate);
}
}
self.criticalSystemBinaries = bins;
[self setupSystemCriticalBinaries];
return newVersion;
}
@@ -212,6 +212,16 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
[rs close];
}];
// Allow binaries signed by the "Software Signing" cert used to sign launchd
// if no existing rule has matched.
if (!rule && [certificateSHA256 isEqual:self.launchdCSInfo.leafCertificate.SHA256]) {
rule = [[SNTRule alloc] initWithShasum:certificateSHA256
state:SNTRuleStateWhitelist
type:SNTRuleTypeCertificate
customMsg:nil
timestamp:0];
}
return rule;
}
@@ -227,21 +237,6 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
__block BOOL failed = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, SNTRuleTypeCertificate];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", SNTRuleStateWhitelist];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count < 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad certificates.");
[self fillError:error code:SNTRuleTableErrorMissingRequiredRule message:nil];
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}
@@ -363,9 +358,6 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
case SNTRuleTableErrorRemoveFailed:
d[NSLocalizedDescriptionKey] = @"A database error occurred while deleting a rule";
break;
case SNTRuleTableErrorMissingRequiredRule:
d[NSLocalizedDescriptionKey] = @"A required rule was requested to be deleted";
break;
}
if (message) d[NSLocalizedFailureReasonErrorKey] = message;

View File

@@ -19,11 +19,6 @@
#import "Source/common/SNTRule.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
@interface SNTRuleTable (Testing)
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
/// This test case actually tests SNTRuleTable and SNTRule
@interface SNTRuleTableTest : XCTestCase
@property SNTRuleTable *sut;
@@ -37,8 +32,6 @@
self.dbq = [[FMDatabaseQueue alloc] init];
self.sut = [[SNTRuleTable alloc] initWithDatabaseQueue:self.dbq];
// xctest is unsigned in Xcode 10.1.
self.sut.santadCertSHA = [[MOLCodesignChecker alloc] initWithPID:1].leafCertificate.SHA256;
}
- (SNTRule *)_exampleBinaryRule {
@@ -71,31 +64,14 @@
}
- (void)testAddRulesClean {
// Assert that insert without 'self' and launchd cert hashes fails
NSError *error;
XCTAssertFalse([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES error:&error]);
XCTAssertEqual(error.code, SNTRuleTableErrorMissingRequiredRule);
// Now add a binary rule without clean slate
error = nil;
// Add a binary rule without clean slate
NSError *error = nil;
XCTAssertTrue([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO error:&error]);
XCTAssertNil(error);
// Now add a cert rule + the required rules as a clean slate,
// assert that the binary rule was removed
SNTRule *r1 = [[SNTRule alloc] init];
r1.shasum = self.sut.launchdCertSHA;
r1.state = SNTRuleStateWhitelist;
r1.type = SNTRuleTypeCertificate;
SNTRule *r2 = [[SNTRule alloc] init];
r2.shasum = self.sut.santadCertSHA;
r2.state = SNTRuleStateWhitelist;
r2.type = SNTRuleTypeCertificate;
// Now add a cert rule with a clean slate, assert that the binary rule was removed
error = nil;
XCTAssertTrue(([self.sut addRules:@[ [self _exampleCertRule], r1, r2 ]
cleanSlate:YES
error:&error]));
XCTAssertTrue(([self.sut addRules:@[ [self _exampleCertRule] ] cleanSlate:YES error:&error]));
XCTAssertEqual([self.sut binaryRuleCount], 0);
XCTAssertNil(error);
}

View File

@@ -15,13 +15,14 @@
#import <Foundation/Foundation.h>
#include "Source/common/SNTKernelCommon.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
@class SNTNotificationMessage;
///
/// Manages the connection between daemon and kernel.
///
@interface SNTDriverManager : NSObject
@interface SNTDriverManager : NSObject<SNTEventProvider>
///
/// Handles locating and connecting to the driver. If driver is not loaded, will
@@ -30,6 +31,11 @@
///
- (instancetype)init;
///
/// Unloads the driver.
///
+ (void)unloadDriver;
///
/// Handles requests from the kernel using the given block.
/// @note Loops indefinitely unless there is an error trying to read data from the data queue.
@@ -45,7 +51,7 @@
///
/// Sends a response to a query back to the kernel.
///
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeId;
- (kern_return_t)postAction:(santa_action_t)action forMessage:(santa_message_t)sm;
///
/// Get the number of binaries in the kernel's caches.

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/EventProviders/SNTDriverManager.h"
#import <IOKit/IODataQueueClient.h>
#import <IOKit/kext/KextManager.h>
@@ -24,6 +24,8 @@
@interface SNTDriverManager ()
@property io_connect_t connection;
@property(readwrite) BOOL connectionEstablished;
@property(nonatomic, readonly) dispatch_queue_t dmAuthQueue;
@property(nonatomic, readonly) dispatch_queue_t dmNotifiyQueue;
@end
@implementation SNTDriverManager
@@ -33,6 +35,14 @@
- (instancetype)init {
self = [super init];
if (self) {
_dmAuthQueue =
dispatch_queue_create("com.google.santa.daemon.dm_auth", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(_dmAuthQueue,
dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0));
_dmNotifiyQueue =
dispatch_queue_create("com.google.santa.daemon.dm_notify", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(_dmNotifiyQueue, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0));
CFDictionaryRef classToMatch = IOServiceMatching(USERCLIENT_CLASS);
if (!classToMatch) {
LOGE(@"Failed to create matching dictionary");
@@ -52,6 +62,10 @@
IOServiceClose(_connection);
}
+ (void)unloadDriver {
KextManagerUnloadKextWithIdentifier(CFSTR(USERCLIENT_ID));
}
#pragma mark Driver Waiting
// Helper function used with IOServiceAddMatchingNotification which expects
@@ -80,14 +94,14 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
DriverAppearedBlock block = ^(io_object_t object) {
// This calls `initWithTask`, `attach` and `start` in `SantaDriverClient`
kern_return_t kr = IOServiceOpen(object, mach_task_self(), 0, &_connection);
kern_return_t kr = IOServiceOpen(object, mach_task_self(), 0, &self->_connection);
if (kr != kIOReturnSuccess) {
LOGE(@"Failed to open santa-driver service: 0x%X", kr);
exit(1);
}
// Call `open` in `SantaDriverClient`
kr = IOConnectCallMethod(_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
kr = IOConnectCallMethod(self->_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
if (kr == kIOReturnExclusiveAccess) {
LOGE(@"A client is already connected");
exit(2);
@@ -100,7 +114,7 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
IOObjectRelease(iterator);
IONotificationPortDestroy(notificationPort);
_connectionEstablished = YES;
self->_connectionEstablished = YES;
};
LOGI(@"Waiting for Santa driver to become available");
@@ -164,7 +178,23 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
uint32_t dataSize = sizeof(vdata);
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
callback(vdata);
switch (type) {
case QUEUETYPE_DECISION: {
dispatch_async(self.dmAuthQueue, ^{
callback(vdata);
});
break;
}
case QUEUETYPE_LOG: {
dispatch_async(self.dmNotifiyQueue, ^{
callback(vdata);
});
break;
}
default: {
break;
}
}
} else {
LOGE(@"Error dequeuing data for type %d: 0x%X", type, kr);
exit(2);
@@ -178,7 +208,8 @@ static void driverAppearedHandler(void *info, io_iterator_t iterator) {
#pragma mark Outgoing messages
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeId {
- (kern_return_t)postAction:(santa_action_t)action forMessage:(santa_message_t)sm {
santa_vnode_id_t vnodeId = sm.vnode_id;
switch (action) {
case ACTION_RESPOND_ALLOW:
return IOConnectCallStructMethod(_connection,
@@ -279,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;
@@ -303,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);
}
}
}

View File

@@ -1,4 +1,4 @@
/// Copyright 2015 Google Inc. All rights reserved.
/// Copyright 2019 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.
@@ -12,16 +12,10 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import "Source/SantaGUI/SNTAppDelegate.h"
#include "Source/common/SNTKernelCommon.h"
#include "Source/santad/EventProviders/SNTEventProvider.h"
int main(int argc, const char *argv[]) {
@autoreleasepool {
NSApplication *app = [NSApplication sharedApplication];
SNTAppDelegate *delegate = [[SNTAppDelegate alloc] init];
[app setDelegate:delegate];
[app finishLaunching];
[app run];
}
}
@interface SNTEndpointSecurityManager : NSObject<SNTEventProvider>
@end

View File

@@ -0,0 +1,455 @@
/// Copyright 2019 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/SNTEndpointSecurityManager.h"
#include "Source/common/SNTPrefixTree.h"
#import "Source/common/SNTLogging.h"
#include <EndpointSecurity/EndpointSecurity.h>
#include <bsm/libbsm.h>
#include <libproc.h>
#include <atomic>
// Gleaned from https://opensource.apple.com/source/xnu/xnu-4903.241.1/bsd/sys/proc_internal.h
#define PID_MAX 99999
@interface SNTEndpointSecurityManager ()
@property(nonatomic) es_client_t *client;
@property(nonatomic) SNTPrefixTree *prefixTree;
@property(nonatomic, copy) void (^decisionCallback)(santa_message_t);
@property(nonatomic, copy) void (^logCallback)(santa_message_t);
@property(nonatomic, readonly) dispatch_queue_t esAuthQueue;
@property(nonatomic, readonly) dispatch_queue_t esNotifyQueue;
@property(nonatomic, readonly) pid_t selfPID;
@end
@implementation SNTEndpointSecurityManager {
std::atomic<bool> _compilerPIDs[PID_MAX];
}
- (instancetype)init API_AVAILABLE(macos(10.15)) {
self = [super init];
if (self) {
[self establishClient];
_prefixTree = new SNTPrefixTree();
_esAuthQueue =
dispatch_queue_create("com.google.santa.daemon.es_auth", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(_esAuthQueue,
dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0));
_esNotifyQueue =
dispatch_queue_create("com.google.santa.daemon.es_notify", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(_esNotifyQueue,
dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0));
_selfPID = getpid();
}
return self;
}
- (void)dealloc API_AVAILABLE(macos(10.15)) {
if (_client) {
es_unsubscribe_all(_client);
es_delete_client(_client);
}
if (_prefixTree) delete _prefixTree;
}
- (void)establishClient API_AVAILABLE(macos(10.15)) {
while (!self.client) {
es_client_t *client = NULL;
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
// 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.
// TODO(bur/rah): For ES log denies from NOTIFY messages instead of AUTH.
if (m->action.notify.result.auth == ES_AUTH_RESULT_DENY) return;
// Continue log this event
break;
}
case ES_EVENT_TYPE_NOTIFY_CLOSE: {
// Ignore unmodified files
if (!m->event.close.modified) return;
// Create a transitive rule if the file was modified by a running compiler
if (pid && pid < PID_MAX && self->_compilerPIDs[pid].load()) {
santa_message_t sm = {};
BOOL truncated = [self populateBufferFromESFile:m->event.close.target
buffer:sm.path
size:sizeof(sm.path)];
if (truncated) {
LOGE(@"CLOSE: error creating transitive rule, the path is truncated: path=%s pid=%d",
sm.path, pid);
break;
}
sm.action = ACTION_NOTIFY_WHITELIST;
sm.pid = pid;
LOGI(@"CLOSE: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
self.decisionCallback(sm);
}
// Continue log this event
break;
}
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()) {
santa_message_t sm = {};
BOOL truncated = [self populateRenamedNewPathFromESMessage:m->event.rename
buffer:sm.path
size:sizeof(sm.path)];
if (truncated) {
LOGE(@"RENAME: error creating transitive rule, the path is truncated: path=%s pid=%d",
sm.path, pid);
break;
}
sm.action = ACTION_NOTIFY_WHITELIST;
sm.pid = pid;
LOGI(@"RENAME: creating a transitive rule: path=%s pid=%d", sm.path, sm.pid);
self.decisionCallback(sm);
}
// Continue log this event
break;
}
case ES_EVENT_TYPE_NOTIFY_EXIT: {
// Update the set of running compiler PIDs
if (pid && pid < PID_MAX) self->_compilerPIDs[pid].store(false);
// Do not log exits
return;
}
default: {
break;
}
}
switch (m->action_type) {
case ES_ACTION_TYPE_AUTH: {
// Create a timer to deny the execution 2 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.
auto responded = std::make_shared<std::atomic<bool>>(false);
dispatch_after(dispatch_time(m->deadline, NSEC_PER_SEC * -2), 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));
});
// Copy the message and return control back to ES
es_message_t *mc = es_copy_message(m);
dispatch_async(self.esAuthQueue, ^{
[self messageHandler:mc];
responded->store(true);
es_free_message(mc);
});
break;
}
case ES_ACTION_TYPE_NOTIFY: {
// Don't log fileop events from com.google.santa.daemon
if (self.selfPID == pid && m->event_type != ES_EVENT_TYPE_NOTIFY_EXEC) return;
// Copy the message and return control back to ES
es_message_t *mc = es_copy_message(m);
dispatch_async(self.esNotifyQueue, ^{
[self messageHandler:mc];
es_free_message(mc);
});
break;
}
default: {
break;
}
}
});
switch (ret) {
case ES_NEW_CLIENT_RESULT_SUCCESS:
LOGI(@"Connected to EndpointSecurity");
self.client = client;
return;
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
LOGE(@"Sleeping for 30s before restarting.");
sleep(30);
exit(ret);
default:
LOGE(@"Unable to create es client: %d. Sleeping for a minute.", ret);
sleep(60);
continue;
}
}
}
- (void)messageHandler:(es_message_t *)m API_AVAILABLE(macos(10.15)) {
santa_message_t sm = {};
sm.es_message = (void *)m;
es_process_t *targetProcess = NULL;
es_file_t *targetFile = NULL;
void (^callback)(santa_message_t);
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_EXEC: {
sm.action = ACTION_REQUEST_BINARY;
targetFile = m->event.exec.target->executable;
targetProcess = m->event.exec.target;
callback = self.decisionCallback;
break;
}
case ES_EVENT_TYPE_NOTIFY_EXEC: {
sm.action = ACTION_NOTIFY_EXEC;
targetFile = m->event.exec.target->executable;
targetProcess = m->event.exec.target;
// TODO(rah): Profile this, it might need to be improved.
uint32_t argCount = es_exec_arg_count(&(m->event.exec));
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
for (int i = 0; i < argCount; ++i) {
es_string_token_t arg = es_exec_arg(&(m->event.exec), i);
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_NOTIFY_CLOSE: {
sm.action = ACTION_NOTIFY_WRITE;
targetFile = m->event.close.target;
targetProcess = m->process;
callback = self.logCallback;
break;
}
case ES_EVENT_TYPE_NOTIFY_UNLINK: {
sm.action = ACTION_NOTIFY_DELETE;
targetFile = m->event.unlink.target;
targetProcess = m->process;
callback = self.logCallback;
break;
}
case ES_EVENT_TYPE_NOTIFY_LINK: {
sm.action = ACTION_NOTIFY_LINK;
targetFile = m->event.link.source;
targetProcess = m->process;
NSString *p = @(m->event.link.target_dir->path.data);
p = [p stringByAppendingPathComponent:@(m->event.link.target_filename.data)];
[self populateBufferFromString:p.UTF8String
length:p.length
buffer:sm.newpath
size:sizeof(sm.newpath)];
callback = self.logCallback;
break;
}
case ES_EVENT_TYPE_NOTIFY_RENAME: {
sm.action = ACTION_NOTIFY_RENAME;
targetFile = m->event.rename.source;
targetProcess = m->process;
[self populateRenamedNewPathFromESMessage:m->event.rename
buffer:sm.newpath
size:sizeof(sm.newpath)];
callback = self.logCallback;
break;
}
default:
LOGE(@"Unknown es message: %d", m->event_type);
return;
}
// Deny auth exec events if the path doesn't fit in the santa message.
// TODO(bur/rah): Add support for larger paths.
if ([self populateBufferFromESFile:targetFile buffer:sm.path size:sizeof(sm.path)]
&& m->event_type == ES_EVENT_TYPE_AUTH_EXEC) {
LOGE(@"path is truncated, deny: %s", sm.path);
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_DENY, false);
return;
}
// Filter file op events matching the prefix tree.
if (!(m->event_type == ES_EVENT_TYPE_AUTH_EXEC || m->event_type == ES_EVENT_TYPE_NOTIFY_EXEC) &&
self.prefixTree->HasPrefix(sm.path)) {
return;
}
sm.vnode_id.fsid = targetFile->stat.st_dev;
sm.vnode_id.fileid = targetFile->stat.st_ino;
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.ppid = targetProcess->original_ppid;
proc_name((m->event_type == ES_EVENT_TYPE_AUTH_EXEC) ? sm.ppid : sm.pid, sm.pname, 1024);
callback(sm);
}
- (void)listenForDecisionRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
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);
// 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
// and cached, so we force the cache to flush again.
[self flushCacheNonRootOnly:YES];
}
- (void)listenForLogRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
while (!self.connectionEstablished) usleep(100000); // 100ms
self.logCallback = callback;
es_event_type_t events[] = {
ES_EVENT_TYPE_NOTIFY_EXEC,
ES_EVENT_TYPE_NOTIFY_CLOSE,
ES_EVENT_TYPE_NOTIFY_LINK,
ES_EVENT_TYPE_NOTIFY_RENAME,
ES_EVENT_TYPE_NOTIFY_UNLINK,
};
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);
}
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
API_AVAILABLE(macos(10.15)) {
es_respond_result_t ret;
switch (action) {
case ACTION_RESPOND_ALLOW_COMPILER:
if (sm.pid >= PID_MAX) {
LOGE(@"Unable to watch compiler pid=%d >= pid_max=%d", sm.pid, PID_MAX);
} else {
LOGD(@"Watching compiler pid=%d path=%s", sm.pid, sm.path);
self->_compilerPIDs[sm.pid].store(true);
}
// Allow the exec, but don't cache the decision so subsequent execs of the compiler get
// marked appropriately.
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, false);
break;
case ACTION_RESPOND_ALLOW:
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_DENY:
case ACTION_RESPOND_TOOLONG:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_DENY, false);
break;
case ACTION_RESPOND_ACK:
return ES_RESPOND_RESULT_SUCCESS;
default:
ret = ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT;
}
return ret;
}
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly API_AVAILABLE(macos(10.15)) {
if (!self.connectionEstablished) return YES; // if not connected, there's nothing to flush.
return es_clear_cache(self.client) == ES_CLEAR_CACHE_RESULT_SUCCESS;
}
- (void)fileModificationPrefixFilterAdd:(NSArray *)filters {
for (NSString *filter in filters) {
self.prefixTree->AddPrefix(filter.fileSystemRepresentation);
}
}
- (void)fileModificationPrefixFilterReset {
self.prefixTree->Reset();
}
- (NSArray<NSNumber *> *)cacheCounts {
return nil;
}
- (NSArray<NSNumber *> *)cacheBucketCount {
return nil;
}
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
return ACTION_UNSET;
}
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeId {
return KERN_FAILURE;
}
- (BOOL)connectionEstablished {
return self.client != nil;
}
#pragma mark helpers
// Returns YES if the path was truncated.
// The populated path will be NUL terminated.
- (BOOL)populateBufferFromESFile:(es_file_t *)file buffer:(char *)buffer size:(size_t)size {
return [self populateBufferFromString:file->path.data
length:file->path.length
buffer:buffer
size:size];
}
// Returns YES if the path was truncated.
// The populated path will be NUL terminated.
- (BOOL)populateBufferFromString:(const char *)string
length:(size_t)length
buffer:(char *)buffer
size:(size_t)size {
if (length++ > size) length = size;
return strlcpy(buffer, string, length) >= length;
}
- (BOOL)populateRenamedNewPathFromESMessage:(es_event_rename_t)mv
buffer:(char *)buffer
size:(size_t)size {
BOOL truncated = NO;
switch(mv.destination_type) {
case ES_DESTINATION_TYPE_NEW_PATH: {
NSString *p = @(mv.destination.new_path.dir->path.data);
p = [p stringByAppendingPathComponent:
@(mv.destination.new_path.filename.data)];
truncated = [self populateBufferFromString:p.UTF8String
length:p.length
buffer:buffer
size:size];
break;
}
case ES_DESTINATION_TYPE_EXISTING_FILE: {
truncated = [self populateBufferFromESFile:mv.destination.existing_file
buffer:buffer
size:size];
break;
}
}
return truncated;
}
@end

View File

@@ -0,0 +1,33 @@
/// Copyright 2019 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/common/SNTKernelCommon.h"
@protocol SNTEventProvider <NSObject>
- (void)listenForDecisionRequests:(void (^)(santa_message_t message))callback;
- (void)listenForLogRequests:(void (^)(santa_message_t message))callback;
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm;
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly;
- (void)fileModificationPrefixFilterAdd:(NSArray *)filters;
- (void)fileModificationPrefixFilterReset;
- (NSArray<NSNumber *> *)cacheCounts;
- (NSArray<NSNumber *> *)cacheBucketCount;
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID;
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeId;
@property(readonly) BOOL connectionEstablished;
@end

View File

@@ -2,17 +2,23 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>com.google.santa.daemon</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>NSHumanReadableCopyright</key>
<string>Google, Inc.</string>
<key>CFBundleIdentifier</key>
<string>com.google.santad</string>
<key>CFBundleName</key>
<string>santad</string>
<key>CFBundleVersion</key>
<string>${SANTA_VERSION}</string>
<key>CFBundlePackageType</key>
<string>SYSX</string>
<key>CFBundleShortVersionString</key>
<string>${SANTA_VERSION}</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Google, LLC.</string>
<key>NSSystemExtensionUsageDescription</key>
<string>Santa knows who is naughty and nice.</string>
<key>CFBundleExecutable</key>
<string>com.google.santa.daemon</string>
</dict>
</plist>

View File

@@ -15,6 +15,7 @@
#import "Source/santad/Logs/SNTSyslogEventLog.h"
#import <libproc.h>
#include <EndpointSecurity/EndpointSecurity.h>
#import "Source/common/SNTCachedDecision.h"
#import "Source/common/SNTConfigurator.h"
@@ -181,7 +182,12 @@
}
if (logArgs) {
[self addArgsForPid:message.pid toString:outLog];
if (message.args_array) {
NSArray *args = CFBridgingRelease(message.args_array);
[outLog appendFormat:@"|args=%@", [args componentsJoinedByString:@" "]];
} else {
[self addArgsForPid:message.pid toString:outLog];
}
}
if ([[SNTConfigurator configurator] enableMachineIDDecoration]) {

View File

@@ -27,18 +27,20 @@
#import "Source/santad/SNTCompilerController.h"
#import "Source/santad/SNTDaemonControlController.h"
#import "Source/santad/SNTDatabaseController.h"
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/SNTExecutionController.h"
#import "Source/santad/SNTNotificationQueue.h"
#import "Source/santad/SNTSyncdQueue.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/EventProviders/SNTDriverManager.h"
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
#import "Source/santad/Logs/SNTFileEventLog.h"
#import "Source/santad/Logs/SNTSyslogEventLog.h"
@interface SNTApplication ()
@property DASessionRef diskArbSession;
@property SNTDriverManager *driverManager;
@property id<SNTEventProvider> eventProvider;
@property SNTEventLog *eventLog;
@property SNTExecutionController *execController;
@property SNTCompilerController *compilerController;
@@ -52,10 +54,19 @@
- (instancetype)init {
self = [super init];
if (self) {
// Locate and connect to driver
_driverManager = [[SNTDriverManager alloc] init];
SNTConfigurator *configurator = [SNTConfigurator configurator];
if (!_driverManager) {
// Choose an event logger.
// Locate and connect to driver / SystemExtension
if ([configurator enableSystemExtension]) {
LOGI(@"Using EndpointSecurity as event provider.");
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
} else {
LOGI(@"Using Kauth as event provider.");
_eventProvider = [[SNTDriverManager alloc] init];
}
if (!_eventProvider) {
LOGE(@"Failed to connect to driver, exiting.");
return nil;
}
@@ -72,8 +83,7 @@
return nil;
}
// Choose an event logger.
SNTConfigurator *configurator = [SNTConfigurator configurator];
switch ([configurator eventLogType]) {
case SNTEventLogTypeSyslog:
_eventLog = [[SNTSyslogEventLog alloc] init];
@@ -83,12 +93,14 @@
break;
}
// The filter is reset when santad disconnects from the driver.
// Add the default filters.
[_driverManager 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.
[_driverManager 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];
@@ -116,27 +128,33 @@
forKeyPath:NSStringFromSelector(@selector(blacklistPathRegex))
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 =
[[SNTDaemonControlController alloc] initWithDriverManager:_driverManager
[[SNTDaemonControlController alloc] initWithEventProvider:_eventProvider
notificationQueue:self.notQueue
syncdQueue:syncdQueue
eventLog:_eventLog];
_controlConnection =
[[MOLXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceId]];
[[MOLXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceID]];
_controlConnection.privilegedInterface = [SNTXPCControlInterface controlInterface];
_controlConnection.unprivilegedInterface = [SNTXPCUnprivilegedControlInterface controlInterface];
_controlConnection.exportedObject = dc;
[_controlConnection resume];
// Initialize the transitive whitelisting controller object.
_compilerController = [[SNTCompilerController alloc] initWithDriverManager:_driverManager
_compilerController = [[SNTCompilerController alloc] initWithEventProvider:_eventProvider
eventLog:_eventLog];
// Initialize the binary checker object
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
_execController = [[SNTExecutionController alloc] initWithEventProvider:_eventProvider
ruleTable:ruleTable
eventTable:eventTable
notifierQueue:self.notQueue
@@ -160,75 +178,53 @@
}
- (void)beginListeningForDecisionRequests {
dispatch_queue_t exec_queue = dispatch_queue_create(
"com.google.santad.execution_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(
exec_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[self.driverManager listenForDecisionRequests:^(santa_message_t message) {
@autoreleasepool {
dispatch_async(exec_queue, ^{
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
exit(0);
}
case ACTION_REQUEST_BINARY: {
[_execController validateBinaryWithMessage:message];
break;
}
case ACTION_NOTIFY_WHITELIST: {
// Determine if we should add a transitive whitelisting rule for this new file.
// Requires that writing process was a compiler and that new file is executable.
[self.compilerController createTransitiveRule:message];
break;
}
default: {
LOGE(@"Received decision request without a valid action: %d", message.action);
exit(1);
}
}
});
[self.eventProvider listenForDecisionRequests:^(santa_message_t message) {
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
exit(0);
}
case ACTION_REQUEST_BINARY: {
[self->_execController validateBinaryWithMessage:message];
break;
}
case ACTION_NOTIFY_WHITELIST: {
// Determine if we should add a transitive whitelisting rule for this new file.
// Requires that writing process was a compiler and that new file is executable.
[self.compilerController createTransitiveRule:message];
break;
}
default: {
LOGE(@"Received decision request without a valid action: %d", message.action);
exit(1);
}
}
}];
}
- (void)beginListeningForLogRequests {
dispatch_queue_t log_queue = dispatch_queue_create(
"com.google.santad.log_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(
log_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
// Limit number of threads the queue can create.
dispatch_semaphore_t concurrencyLimiter = dispatch_semaphore_create(15);
[self.driverManager listenForLogRequests:^(santa_message_t message) {
@autoreleasepool {
dispatch_semaphore_wait(concurrencyLimiter, DISPATCH_TIME_FOREVER);
dispatch_async(log_queue, ^{
switch (message.action) {
case ACTION_NOTIFY_DELETE:
case ACTION_NOTIFY_EXCHANGE:
case ACTION_NOTIFY_LINK:
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[_eventLog logFileModification:message];
}
break;
}
case ACTION_NOTIFY_EXEC: {
[_eventLog logAllowedExecution:message];
break;
}
default:
LOGE(@"Received log request without a valid action: %d", message.action);
break;
[self.eventProvider listenForLogRequests:^(santa_message_t message) {
switch (message.action) {
case ACTION_NOTIFY_DELETE:
case ACTION_NOTIFY_EXCHANGE:
case ACTION_NOTIFY_LINK:
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if (!path) break;
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self->_eventLog logFileModification:message];
}
dispatch_semaphore_signal(concurrencyLimiter);
});
break;
}
case ACTION_NOTIFY_EXEC: {
[self->_eventLog logAllowedExecution:message];
break;
}
default:
LOGE(@"Received log request without a valid action: %d", message.action);
break;
}
}];
}
@@ -270,7 +266,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
if (![props[@"DAVolumeMountable"] boolValue]) return;
[app.eventLog logDiskDisappeared:props];
[app.driverManager flushCacheNonRootOnly:YES];
[app.eventProvider flushCacheNonRootOnly:YES];
}
- (void)startSyncd {
@@ -322,7 +318,15 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
if (!new && !old) return;
if (![new.pattern isEqualToString:old.pattern]) {
LOGI(@"Changed [white|black]list regex, flushing cache");
[self.driverManager flushCacheNonRootOnly:NO];
[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);
}
}
}
@@ -330,7 +334,7 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
- (void)clientModeDidChange:(SNTClientMode)clientMode {
if (clientMode == SNTClientModeLockdown) {
LOGI(@"Changed client mode, flushing cache.");
[self.driverManager flushCacheNonRootOnly:NO];
[self.eventProvider flushCacheNonRootOnly:NO];
}
[[self.notQueue.notifierConnection remoteObjectProxy] postClientModeNotification:clientMode];
}

View File

@@ -23,7 +23,7 @@
// Designated initializer takes a SNTEventLog instance so that we can
// call saveDecisionDetails: to create a fake cached decision for transitive
// rule creation requests that are still pending.
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(SNTDriverManager *)driverManager
eventLog:(SNTEventLog *)eventLog;
// Whenever an executable file is closed or renamed whitelist the resulting file.

View File

@@ -21,22 +21,22 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTRule.h"
#import "Source/santad/SNTDatabaseController.h"
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
#import "Source/santad/Logs/SNTEventLog.h"
@interface SNTCompilerController ()
@property SNTDriverManager *driverManager;
@property id<SNTEventProvider> eventProvider;
@property SNTEventLog *eventLog;
@end
@implementation SNTCompilerController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
eventLog:(SNTEventLog *)eventLog {
self = [super init];
if (self) {
_driverManager = driverManager;
_eventProvider = eventProvider;
_eventLog = eventLog;
}
return self;
@@ -92,7 +92,7 @@
}
// Remove the temporary allow rule in the kernel decision cache.
[self.driverManager removeCacheEntryForVnodeID:message.vnode_id];
[self.eventProvider removeCacheEntryForVnodeID:message.vnode_id];
// Remove the "pending" decision info from SNTEventLog.
[self removeFakeDecision:message];
}

View File

@@ -26,7 +26,7 @@
///
@interface SNTDaemonControlController : NSObject<SNTDaemonControlXPC>
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(SNTDriverManager *)driverManager
notificationQueue:(SNTNotificationQueue *)notQueue
syncdQueue:(SNTSyncdQueue *)syncdQueue
eventLog:(SNTEventLog *)eventLog;

View File

@@ -27,12 +27,12 @@
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/common/SNTXPCSyncdInterface.h"
#import "Source/santad/SNTDatabaseController.h"
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/SNTNotificationQueue.h"
#import "Source/santad/SNTPolicyProcessor.h"
#import "Source/santad/SNTSyncdQueue.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
#import "Source/santad/Logs/SNTEventLog.h"
// Globals used by the santad watchdog thread
@@ -45,14 +45,14 @@ double watchdogRAMPeak = 0;
@property NSString *_syncXsrfToken;
@property SNTPolicyProcessor *policyProcessor;
@property SNTEventLog *eventLog;
@property SNTDriverManager *driverManager;
@property id<SNTEventProvider> eventProvider;
@property SNTNotificationQueue *notQueue;
@property SNTSyncdQueue *syncdQueue;
@end
@implementation SNTDaemonControlController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
notificationQueue:(SNTNotificationQueue *)notQueue
syncdQueue:(SNTSyncdQueue *)syncdQueue
eventLog:(SNTEventLog *)eventLog {
@@ -60,7 +60,7 @@ double watchdogRAMPeak = 0;
if (self) {
_policyProcessor = [[SNTPolicyProcessor alloc] initWithRuleTable:
[SNTDatabaseController ruleTable]];
_driverManager = driverManager;
_eventProvider = eventProvider;
_notQueue = notQueue;
_syncdQueue = syncdQueue;
_eventLog = eventLog;
@@ -71,24 +71,24 @@ double watchdogRAMPeak = 0;
#pragma mark Kernel ops
- (void)cacheCounts:(void (^)(uint64_t, uint64_t))reply {
NSArray<NSNumber *> *counts = [self.driverManager cacheCounts];
NSArray<NSNumber *> *counts = [self.eventProvider cacheCounts];
reply([counts[0] unsignedLongLongValue], [counts[1] unsignedLongLongValue]);
}
- (void)cacheBucketCount:(void (^)(NSArray *))reply {
reply([self.driverManager cacheBucketCount]);
reply([self.eventProvider cacheBucketCount]);
}
- (void)flushCache:(void (^)(BOOL))reply {
reply([self.driverManager flushCacheNonRootOnly:NO]);
reply([self.eventProvider flushCacheNonRootOnly:NO]);
}
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply {
reply([self.driverManager checkCache:vnodeID]);
reply([self.eventProvider checkCache:vnodeID]);
}
- (void)driverConnectionEstablished:(void (^)(BOOL))reply {
reply(self.driverManager.connectionEstablished);
reply(self.eventProvider.connectionEstablished);
}
#pragma mark Database ops
@@ -121,7 +121,7 @@ double watchdogRAMPeak = 0;
// The actual cache flushing happens after the new rules have been added to the database.
if (flushCache) {
LOGI(@"Flushing decision cache");
[self.driverManager flushCacheNonRootOnly:NO];
[self.eventProvider flushCacheNonRootOnly:NO];
}
reply(error);
@@ -153,8 +153,7 @@ double watchdogRAMPeak = 0;
certificateSHA256:(NSString *)certificateSHA256
reply:(void (^)(SNTEventState))reply {
reply([self.policyProcessor decisionForFilePath:filePath
fileSHA256:fileSHA256
certificateSHA256:certificateSHA256].decision);
fileSHA256:fileSHA256].decision);
}
#pragma mark Config Ops
@@ -253,14 +252,6 @@ double watchdogRAMPeak = 0;
self.notQueue.notifierConnection = c;
}
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener {
MOLXPCConnection *bs = [[MOLXPCConnection alloc] initClientWithServiceName:@"com.google.santabs"];
bs.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
[bs resume];
[[bs remoteObjectProxy] setBundleNotificationListener:listener];
[bs invalidate];
}
#pragma mark syncd Ops
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener {
@@ -293,35 +284,12 @@ double watchdogRAMPeak = 0;
reply();
}
#pragma mark Bundle ops
///
/// This method is only used for santactl's bundleinfo command. For blocked executions, SantaGUI
/// calls on santabs directly.
///
/// Hash a bundle for an event. The SNTBundleHashBlock will be called with nil parameters if a
/// failure or cancellation occurs.
///
/// @param event The event that includes the fileBundlePath to be hashed.
/// @param reply A SNTBundleHashBlock to be executed upon completion or cancellation.
///
/// @note If there is a current NSProgress when called this method will report back it's progress.
///
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
reply:(SNTBundleHashBlock)reply {
MOLXPCConnection *bs =
[[MOLXPCConnection alloc] initClientWithServiceName:[SNTXPCBundleServiceInterface serviceId]];
bs.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
[bs resume];
[[bs remoteObjectProxy] hashBundleBinariesForEvent:event reply:reply];
}
///
/// Used by SantaGUI sync the offending event and potentially all the related events,
/// if the sync server has not seen them before.
///
/// @param event The offending event, fileBundleHash & fileBundleBinaryCount need to be populated.
/// @param relatedEvents Nexted bundle events.
/// @param events Next bundle events.
///
- (void)syncBundleEvent:(SNTStoredEvent *)event
relatedEvents:(NSArray<SNTStoredEvent *> *)events {

View File

@@ -16,6 +16,7 @@
#import "Source/common/SNTCommonEnums.h"
#include "Source/common/SNTKernelCommon.h"
#include "Source/santad/EventProviders/SNTEventProvider.h"
@class MOLCodesignChecker;
@class SNTDriverManager;
@@ -35,7 +36,7 @@
///
@interface SNTExecutionController : NSObject
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierQueue:(SNTNotificationQueue *)notifierQueue

View File

@@ -30,12 +30,12 @@
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTRule.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/SNTNotificationQueue.h"
#import "Source/santad/SNTPolicyProcessor.h"
#import "Source/santad/SNTSyncdQueue.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/EventProviders/SNTEventProvider.h"
#import "Source/santad/Logs/SNTEventLog.h"
// A binary is considered large at ~30MB. Large binaries take longer to hash and consequently
@@ -45,7 +45,7 @@
static size_t kLargeBinarySize = 30 * 1024 * 1024;
@interface SNTExecutionController ()
@property SNTDriverManager *driverManager;
@property id<SNTEventProvider> eventProvider;
@property SNTEventLog *eventLog;
@property SNTEventTable *eventTable;
@property SNTNotificationQueue *notifierQueue;
@@ -60,7 +60,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
#pragma mark Initializers
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
- (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierQueue:(SNTNotificationQueue *)notifierQueue
@@ -68,7 +68,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
eventLog:(SNTEventLog *)eventLog {
self = [super init];
if (self) {
_driverManager = driverManager;
_eventProvider = eventProvider;
_ruleTable = ruleTable;
_eventTable = eventTable;
_notifierQueue = notifierQueue;
@@ -91,20 +91,20 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
// Get info about the file. If we can't get this info, allow execution and log an error.
if (unlikely(message.path == NULL)) {
LOGE(@"Path for vnode_id is NULL: %llu/%llu", message.vnode_id.fsid, message.vnode_id.fileid);
[_driverManager postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:message.vnode_id];
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
return;
}
NSError *fileInfoError;
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@(message.path) error:&fileInfoError];
if (unlikely(!binInfo)) {
LOGE(@"Failed to read file %@: %@", @(message.path), fileInfoError.localizedDescription);
[_driverManager postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:message.vnode_id];
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
return;
}
// PrinterProxy workaround, see description above the method for more details.
if ([self printerProxyWorkaround:binInfo]) {
[_driverManager postToKernelAction:ACTION_RESPOND_DENY forVnodeID:message.vnode_id];
[self.eventProvider postAction:ACTION_RESPOND_DENY forMessage:message];
return;
}
@@ -112,52 +112,34 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
if (binInfo.fileSize > kLargeBinarySize) {
LOGD(@"%@ is larger than %zu. Letting santa-driver know we are working on it.",
binInfo.path, kLargeBinarySize);
[_driverManager postToKernelAction:ACTION_RESPOND_ACK forVnodeID:message.vnode_id];
}
// 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;
[self.eventProvider postAction:ACTION_RESPOND_ACK forMessage:message];
}
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;
// 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) {
[_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;
}
// 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.
if (action == ACTION_RESPOND_ALLOW || action == ACTION_RESPOND_ALLOW_COMPILER) {
[_eventLog cacheDecision:cd];
}
// Send the decision to the kernel.
[_driverManager postToKernelAction:action forVnodeID:message.vnode_id];
[self.eventProvider postAction:action forMessage:message];
// Log to database if necessary.
if (cd.decision != SNTEventStateAllowBinary &&
@@ -171,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);
@@ -202,7 +184,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
se.quarantineAgentBundleID = binInfo.quarantineAgentBundleID;
dispatch_async(_eventQueue, ^{
[_eventTable addStoredEvent:se];
[self.eventTable addStoredEvent:se];
});
// If binary was blocked, do the needful
@@ -219,7 +201,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
// So the server has something to show the user straight away, initiate an event
// upload for the blocked binary rather than waiting for the next sync.
dispatch_async(_eventQueue, ^{
[_syncdQueue addEvents:@[se] isFromBundle:NO];
[self.syncdQueue addEvents:@[se] isFromBundle:NO];
});
}
@@ -237,9 +219,9 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
if (detailURL) {
[msg appendFormat:@"More info:\n%@\n\n", detailURL.absoluteString];
}
[self printMessage:msg toTTYForPID:message.ppid];
if (ttyPath) [self printMessage:msg toTTY:ttyPath];
[_notifierQueue addEvent:se customMessage:cd.customMsg];
[self.notifierQueue addEvent:se customMessage:cd.customMsg];
}
}
}
@@ -259,7 +241,7 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
is invalidated when the file is closed which will trigger a brand new request coming from the
kernel.
@param fi, SNTFileInfo object for the binary being executed.
@param fi SNTFileInfo object for the binary being executed.
@return YES if the workaround was applied, NO otherwise.
*/
- (BOOL)printerProxyWorkaround:(SNTFileInfo *)fi {
@@ -286,19 +268,21 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
return NO;
}
- (void)printMessage:(NSString *)msg toTTYForPID:(pid_t)pid {
if (pid < 2) return; // don't bother even looking for launchd.
- (NSString *)ttyPathForPID:(pid_t)pid {
if (pid < 2) return nil; // don't bother even looking for launchd.
struct proc_bsdinfo taskInfo = {};
if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &taskInfo, sizeof(taskInfo)) < 1) {
return;
}
if (proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &taskInfo, sizeof(taskInfo)) < 1) return nil;
// 16-bytes here is for future-proofing. Currently kern.tty.ptmx_max is
// limited to 999 so 12 bytes should be enough.
char devPath[16] = "/dev/";
snprintf(devPath, 16, "/dev/%s", devname(taskInfo.e_tdev, S_IFCHR));
int fd = open(devPath, O_WRONLY | O_NOCTTY);
return @(devPath);
}
- (void)printMessage:(NSString *)msg toTTY:(NSString *)path {
int fd = open(path.UTF8String, O_WRONLY | O_NOCTTY);
write(fd, msg.UTF8String, msg.length);
close(fd);
}

View File

@@ -22,9 +22,9 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTRule.h"
#import "Source/santad/SNTDriverManager.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
#import "Source/santad/EventProviders/SNTDriverManager.h"
@interface SNTExecutionControllerTest : XCTestCase
@property id mockConfigurator;
@@ -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,11 +60,13 @@
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]);
self.sut = [[SNTExecutionController alloc] initWithDriverManager:self.mockDriverManager
self.sut = [[SNTExecutionController alloc] initWithEventProvider:self.mockDriverManager
ruleTable:self.mockRuleDatabase
eventTable:self.mockEventDatabase
notifierQueue:nil
@@ -75,7 +76,7 @@
/// Return a pre-configured santa_message_ t for testing with.
- (santa_message_t)getMessage {
santa_message_t message = {0};
santa_message_t message = {};
message.pid = 12;
message.ppid = 1;
message.vnode_id = [self getVnodeId];
@@ -108,8 +109,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testBinaryBlacklistRule {
@@ -123,8 +123,8 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
}
- (void)testCertificateWhitelistRule {
@@ -141,8 +141,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testCertificateBlacklistRule {
@@ -159,8 +158,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
}
- (void)testBinaryWhitelistCompilerRule {
@@ -175,8 +173,8 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW_COMPILER
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW_COMPILER
forMessage:[self getMessage]]);
}
- (void)testBinaryWhitelistCompilerRuleDisabled {
@@ -191,8 +189,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testBinaryWhitelistTransitiveRule {
@@ -207,8 +204,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testBinaryWhitelistTransitiveRuleDisabled {
@@ -224,8 +220,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
}
- (void)testDefaultDecision {
@@ -234,13 +229,11 @@
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeMonitor);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
OCMExpect([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
}
- (void)testOutOfScope {
@@ -248,14 +241,12 @@
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testMissingShasum {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
}
- (void)testPageZero {
@@ -263,8 +254,7 @@
OCMStub([self.mockFileInfo isMissingPageZero]).andReturn(YES);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:[self getVnodeId]]);
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
}
@end

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