Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1d008af0a | ||
|
|
5db56e01f5 | ||
|
|
726c49bec5 | ||
|
|
ae5db5dde7 | ||
|
|
2671807f0e | ||
|
|
70c8626016 | ||
|
|
436c472a49 | ||
|
|
ed5be6b062 | ||
|
|
a38f24728a | ||
|
|
4af026356f | ||
|
|
c6e1bb5618 | ||
|
|
e64d2e7ad4 | ||
|
|
3d393e9aa4 | ||
|
|
b8f3122ee9 | ||
|
|
8acfa6591e | ||
|
|
25b75b0e1b | ||
|
|
cb01b77f84 | ||
|
|
61582a0324 | ||
|
|
a17b5d51a4 | ||
|
|
447ea8674b | ||
|
|
c5eec850e1 | ||
|
|
1870631150 | ||
|
|
20ed1659c1 | ||
|
|
258de3efba | ||
|
|
394fd5fab9 | ||
|
|
53b7ef86ed | ||
|
|
423479771e | ||
|
|
933271826b | ||
|
|
880170ea7d | ||
|
|
e58ec37881 | ||
|
|
dece50dd10 | ||
|
|
9db9fc6009 | ||
|
|
f38c030805 | ||
|
|
d8060d3af9 | ||
|
|
34b4090b42 | ||
|
|
c6ca3d64b3 | ||
|
|
4913426631 | ||
|
|
455a1c76c3 | ||
|
|
e5a5f6f9fb | ||
|
|
7ef88d06a5 | ||
|
|
bc82d7988b | ||
|
|
545fa858e4 | ||
|
|
71c917649e | ||
|
|
3781556cf5 | ||
|
|
765d10a7c3 | ||
|
|
3583113381 | ||
|
|
46cd60e579 | ||
|
|
8198e59736 | ||
|
|
c5f0f5d177 |
1
.bazelrc
@@ -1 +1,2 @@
|
||||
build --apple_generate_dsym --define=apple.propagate_embedded_extra_outputs=yes
|
||||
build --host_force_python=PY2
|
||||
|
||||
6
.gitignore
vendored
@@ -1,3 +1,9 @@
|
||||
.DS_Store
|
||||
default.profraw
|
||||
*.provisionprofile
|
||||
bazel-*
|
||||
Pods
|
||||
Santa.xcodeproj/xcuserdata
|
||||
Santa.xcodeproj/project.xcworkspace
|
||||
Santa.xcworkspace/xcuserdata
|
||||
Santa.xcworkspace/xcshareddata
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
---
|
||||
os: osx
|
||||
osx_image: xcode11
|
||||
language: objective-c
|
||||
sudo: false
|
||||
|
||||
|
||||
41
BUILD
@@ -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,9 +122,9 @@ 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
|
||||
@@ -134,7 +143,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 +156,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",
|
||||
|
||||
@@ -9,20 +9,28 @@
|
||||
# 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
|
||||
|
||||
if [ $(uname -r | cut -d'.' -f1) -ge 19 ]; then
|
||||
# Running on 10.15+
|
||||
echo "Santa postinstall: running on 10.15+"
|
||||
/bin/rm -rf /Library/Extensions/santa-driver.kext
|
||||
/bin/rm -f /Library/LaunchDaemons/com.google.santad.plist
|
||||
else
|
||||
# Running on <10.15
|
||||
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
|
||||
fi
|
||||
|
||||
# Load the bundle service
|
||||
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santa.bundleservice.plist
|
||||
|
||||
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
|
||||
if [[ -z "$user" ]]; then
|
||||
/Applications/Santa.app/Contents/MacOS/Santa --load-system-extension
|
||||
exit 0
|
||||
fi
|
||||
/bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.santa.plist
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -6,22 +6,24 @@
|
||||
|
||||
[[ $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
|
||||
|
||||
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
|
||||
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santa
|
||||
|
||||
exit 0
|
||||
|
||||
26
Conf/com.google.santa.bundleservice.plist
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,6 +33,8 @@ 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.santa
|
||||
|
||||
# Cleanup cruft from old versions
|
||||
/bin/launchctl remove com.google.santasync >/dev/null 2>&1
|
||||
@@ -40,31 +45,37 @@ GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
/bin/rm -rf /Library/Extensions/santa-driver.kext 2>&1
|
||||
|
||||
# Copy new files.
|
||||
/bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions
|
||||
/bin/mkdir -p /var/db/santa
|
||||
|
||||
/bin/cp -r ${BINARIES}/Santa.app /Applications
|
||||
|
||||
/bin/mkdir -p /usr/local/bin
|
||||
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
|
||||
/bin/ln -s /Applications/Santa.app/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
|
||||
|
||||
if [ ! -d /var/db/santa ] ; then
|
||||
/bin/mkdir /var/db/santa
|
||||
fi
|
||||
|
||||
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/cp ${CONF}/com.google.santagui.plist /Library/LaunchAgents
|
||||
/bin/cp ${CONF}/com.google.santa.plist /Library/LaunchAgents
|
||||
/bin/cp ${CONF}/com.google.santa.bundleservice.plist /Library/LaunchDaemons
|
||||
/bin/cp ${CONF}/com.google.santa.asl.conf /etc/asl/
|
||||
/bin/cp ${CONF}/com.google.santa.newsyslog.conf /etc/newsyslog.d/
|
||||
|
||||
# Reload syslogd to pick up ASL configuration change.
|
||||
/usr/bin/killall -HUP syslogd
|
||||
|
||||
# Load kext.
|
||||
/sbin/kextload /Library/Extensions/santa-driver.kext
|
||||
# Only copy the kext and load santad if running pre-10.15
|
||||
if [ $(uname -r | cut -d'.' -f1) -lt 19 ]; then
|
||||
/bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions
|
||||
/bin/cp ${CONF}/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
else
|
||||
/Applications/Santa.app/Contents/MacOS/Santa --load-system-extension
|
||||
fi
|
||||
|
||||
# Load santad and scheduled sync jobs.
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
# Load the bundle service
|
||||
/bin/launchctl load /Library/LaunchDaemons/com.google.santa.bundleservice.plist
|
||||
|
||||
# Load GUI agent if someone is logged in.
|
||||
[[ -n "$GUI_USER" ]] && \
|
||||
if [[ -n "$GUI_USER" ]]; then
|
||||
/bin/launchctl asuser ${GUI_USER} \
|
||||
/bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
|
||||
/bin/launchctl load -w /Library/LaunchAgents/com.google.santa.plist
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -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
@@ -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
@@ -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.4.3)
|
||||
|
||||
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: 43565190abc78977ad44a61c0d20d7f0784d35ab
|
||||
|
||||
PODFILE CHECKSUM: d03767a9915896232523962c98d9ff7294aec2b7
|
||||
|
||||
COCOAPODS: 1.7.5
|
||||
@@ -6,7 +6,7 @@
|
||||
[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
|
||||
@@ -22,7 +22,7 @@ Santa is a project of Google's Macintosh Operations Team.
|
||||
# Docs
|
||||
|
||||
The Santa docs are stored in the
|
||||
[Docs](https://github.com/google/santa/blob/master/Docs) directory. A Read the
|
||||
[Docs](https://github.com/google/santa/blob/master/docs) directory. A Read the
|
||||
Docs instance is available here: https://santa.readthedocs.io.
|
||||
|
||||
The docs include deployment options, details on how parts of Santa work and
|
||||
|
||||
1665
Santa.xcodeproj/project.pbxproj
Normal file
78
Santa.xcodeproj/xcshareddata/xcschemes/Santa.xcscheme
Normal 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>
|
||||
79
Santa.xcodeproj/xcshareddata/xcschemes/santad.xcscheme
Normal 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>
|
||||
10
Santa.xcworkspace/contents.xcworkspacedata
generated
Normal 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>
|
||||
@@ -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(
|
||||
@@ -179,3 +203,9 @@ santa_unit_test(
|
||||
]),
|
||||
deps = [":SNTFileInfo"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SNTPrefixTreeTest",
|
||||
srcs = ["SNTPrefixTreeTest.mm"],
|
||||
deps = ["SNTPrefixTree"],
|
||||
)
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,6 +39,13 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
dispatch_once(&pred, ^{
|
||||
binaryName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([binaryName isEqualToString:@"com.google.santa.daemon"]) {
|
||||
useSyslog = YES;
|
||||
pthread_key_create(&syslogKey, syslogClientDestructor);
|
||||
}
|
||||
}
|
||||
|
||||
// If debug logging is enabled, the process must be restarted.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
|
||||
logLevel = LOG_LEVEL_DEBUG;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,20 +14,31 @@
|
||||
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
|
||||
#import <MOLCodesignChecker/MOLCodesignChecker.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.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 (@available(macOS 10.15, *)) {
|
||||
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 +58,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,8 +20,4 @@
|
||||
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)bundleNotifierInterface {
|
||||
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTBundleNotifierXPC)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
@@ -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>
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
@@ -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"
|
||||
|
||||
@@ -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];
|
||||
@@ -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,16 @@
|
||||
#pragma mark App Delegate methods
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
LOGI(@"Requesting SystemExtension activation");
|
||||
NSString *e = [SNTXPCControlInterface systemExtensionID];
|
||||
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
||||
OSSystemExtensionRequest *req = [OSSystemExtensionRequest activationRequestForExtension:e
|
||||
queue:q];
|
||||
req.delegate = self;
|
||||
[[OSSystemExtensionManager sharedManager] submitRequest:req];
|
||||
}
|
||||
|
||||
[self setupMenu];
|
||||
self.notificationManager = [[SNTNotificationManager alloc] init];
|
||||
|
||||
@@ -46,21 +58,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 {
|
||||
@@ -90,6 +96,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 +119,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 +135,33 @@
|
||||
[NSApp setMainMenu:mainMenu];
|
||||
}
|
||||
|
||||
#pragma mark OSSystemExtensionRequestDelegate
|
||||
|
||||
- (OSSystemExtensionReplacementAction)request:(OSSystemExtensionRequest *)request
|
||||
actionForReplacingExtension:(OSSystemExtensionProperties *)old
|
||||
withExtension:(OSSystemExtensionProperties *)new
|
||||
API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request for replacement", request.identifier);
|
||||
#ifdef DEBUG
|
||||
return OSSystemExtensionReplacementActionReplace;
|
||||
#else
|
||||
return [old.bundleVersion isEqualToString:new.bundleVersion]
|
||||
? OSSystemExtensionReplacementActionCancel : OSSystemExtensionReplacementActionReplace;
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)requestNeedsUserApproval:(OSSystemExtensionRequest *)request API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request needs user approval", request.identifier);
|
||||
}
|
||||
|
||||
- (void)request:(OSSystemExtensionRequest *)request
|
||||
didFailWithError:(NSError *)error API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request did fail: %@", request.identifier, error);
|
||||
}
|
||||
|
||||
- (void)request:(OSSystemExtensionRequest *)request
|
||||
didFinishWithResult:(OSSystemExtensionRequestResult)result API_AVAILABLE(macos(10.15)) {
|
||||
LOGI(@"SystemExtension \"%@\" request did finish: %ld", request.identifier, (long)result);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,48 +185,35 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener {
|
||||
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];
|
||||
}
|
||||
self.bundleServiceConnection.invalidationHandler = nil;
|
||||
[self.bundleServiceConnection invalidate];
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
@@ -255,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];
|
||||
}
|
||||
|
||||
8
Source/santa/Santa.app.entitlements
Normal 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>
|
||||
95
Source/santa/main.m
Normal file
@@ -0,0 +1,95 @@
|
||||
/// 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/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) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -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,27 +48,8 @@ santa_unit_test(
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SantaPrefixTreeTest",
|
||||
srcs = [
|
||||
"SantaPrefixTree.cc",
|
||||
"SantaPrefixTree.h",
|
||||
"SantaPrefixTreeTest.mm",
|
||||
],
|
||||
copts = ["-std=c++1z"],
|
||||
minimum_os_version = "10.12",
|
||||
deps = ["//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"],
|
||||
@@ -82,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"],
|
||||
@@ -114,7 +80,7 @@ run_command(
|
||||
name = "kernel_tests",
|
||||
srcs = [
|
||||
":kernel_tests_bin",
|
||||
":santa_driver_min",
|
||||
":santa_driver",
|
||||
],
|
||||
cmd = """
|
||||
env
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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"],
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -330,6 +330,8 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
if ([error.domain isEqualToString:@"com.google.molcodesignchecker"]) {
|
||||
return @"Yes, but signing is not consistent for all architectures";
|
||||
}
|
||||
case CSSMERR_TP_CERT_REVOKED:
|
||||
return @"Yes, but the signing certificate was revoked";
|
||||
default: {
|
||||
return [NSString stringWithFormat:@"Yes, but failed to validate (%ld)", error.code];
|
||||
}
|
||||
@@ -595,12 +597,17 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
NSDictionary *cert = signingChain[index];
|
||||
|
||||
// Check if we should skip over this item based on outputFilters.
|
||||
BOOL filterMatch = self.outputFilters.count == 0;
|
||||
for (NSString *key in self.outputFilters) {
|
||||
NSString *value = cert[key];
|
||||
NSString *value = cert[key] ?: @"";
|
||||
NSRegularExpression *regex = self.outputFilters[key];
|
||||
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) return;
|
||||
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) continue;
|
||||
filterMatch = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filterMatch) return;
|
||||
|
||||
// Filter out the info we want now, in case JSON output
|
||||
for (NSString *key in self.outputKeyList) {
|
||||
outputDict[key] = cert[key];
|
||||
@@ -609,17 +616,22 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
// Check if we should skip over this item based on outputFilters. We do this before collecting
|
||||
// output info because there's a chance that we can bail out early if a filter doesn't match.
|
||||
// However we also don't want to recompute info, so we save any values that we plan to show.
|
||||
BOOL filterMatch = self.outputFilters.count == 0;
|
||||
for (NSString *key in self.outputFilters) {
|
||||
NSString *value = self.propertyMap[key](self, fileInfo);
|
||||
NSString *value = self.propertyMap[key](self, fileInfo) ?: @"";
|
||||
NSRegularExpression *regex = self.outputFilters[key];
|
||||
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) return;
|
||||
if (![regex firstMatchInString:value options:0 range:NSMakeRange(0, value.length)]) continue;
|
||||
// If this is a value we want to show, store it in the output dictionary.
|
||||
// This does a linear search on an array, but it's a small array.
|
||||
if ([self.outputKeyList containsObject:key]) {
|
||||
if (value.length && [self.outputKeyList containsObject:key]) {
|
||||
outputDict[key] = value;
|
||||
}
|
||||
filterMatch = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filterMatch) return;
|
||||
|
||||
// Then fill the outputDict with the rest of the missing values.
|
||||
for (NSString *key in self.outputKeyList) {
|
||||
if (outputDict[key]) continue; // ignore keys that we've already set due to a filter
|
||||
|
||||
@@ -86,12 +86,15 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
|
||||
// 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 (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
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 +178,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 +188,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 +204,14 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"bundle_scanning" : @(enableBundles),
|
||||
@"transitive_whitelisting" : @(transitiveWhitelistingEnabled),
|
||||
},
|
||||
};
|
||||
} mutableCopy];
|
||||
if (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
stats[@"kernel"] = @{
|
||||
@"root_cache_count" : @(rootCacheCount),
|
||||
@"non_root_cache_count": @(nonRootCacheCount),
|
||||
};
|
||||
}
|
||||
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
@@ -218,9 +224,12 @@ 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 (@available(macOS 10.15, *)) {
|
||||
} else {
|
||||
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);
|
||||
|
||||
@@ -70,6 +70,10 @@ REGISTER_COMMAND_NAME(@"version")
|
||||
}
|
||||
|
||||
- (NSString *)santaKextVersion {
|
||||
if (@available(macOS 10.15, *)) {
|
||||
return @"un-needed (SystemExtension being used)";
|
||||
}
|
||||
|
||||
NSDictionary *loadedKexts = CFBridgingRelease(
|
||||
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef) @[ @(USERCLIENT_ID) ],
|
||||
(__bridge CFArrayRef) @[ @"CFBundleVersion" ]));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
@@ -41,10 +41,52 @@ static const NSUInteger kTransitiveRuleExpirationSeconds = 6 * 30 * 24 * 3600;
|
||||
|
||||
- (NSArray *)criticalSystemBinaryPaths {
|
||||
return @[
|
||||
@"/usr/libexec/trustd", @"/usr/sbin/securityd", @"/usr/libexec/xpcproxy", @"/usr/sbin/ocspd"
|
||||
@"/usr/libexec/trustd", @"/usr/sbin/securityd", @"/usr/libexec/xpcproxy",
|
||||
@"/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];
|
||||
@@ -71,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.
|
||||
@@ -90,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;
|
||||
}
|
||||
@@ -211,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;
|
||||
}
|
||||
|
||||
@@ -226,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"];
|
||||
}
|
||||
@@ -362,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -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,
|
||||
@@ -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
|
||||
454
Source/santad/EventProviders/SNTEndpointSecurityManager.mm
Normal file
@@ -0,0 +1,454 @@
|
||||
/// 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);
|
||||
[args addObject:[[NSString alloc] initWithBytes:arg.data
|
||||
length:arg.length
|
||||
encoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
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
|
||||
33
Source/santad/EventProviders/SNTEventProvider.h
Normal 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
|
||||
@@ -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>
|
||||
@@ -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]) {
|
||||
|
||||
@@ -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,16 @@
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Locate and connect to driver
|
||||
_driverManager = [[SNTDriverManager alloc] init];
|
||||
// Locate and connect to driver / SystemExtension
|
||||
if (@available(macOS 10.15, *)) {
|
||||
LOGI(@"Using EndpointSecurity as event provider.");
|
||||
_eventProvider = [[SNTEndpointSecurityManager alloc] init];
|
||||
} else {
|
||||
LOGI(@"Using Kauth as event provider.");
|
||||
_eventProvider = [[SNTDriverManager alloc] init];
|
||||
}
|
||||
|
||||
if (!_driverManager) {
|
||||
if (!_eventProvider) {
|
||||
LOGE(@"Failed to connect to driver, exiting.");
|
||||
return nil;
|
||||
}
|
||||
@@ -85,10 +93,10 @@
|
||||
|
||||
// The filter is reset when santad disconnects from the driver.
|
||||
// Add the default filters.
|
||||
[_driverManager fileModificationPrefixFilterAdd:@[ @"/.", @"/dev/" ]];
|
||||
[_eventProvider fileModificationPrefixFilterAdd:@[ @"/.", @"/dev/" ]];
|
||||
|
||||
// TODO(bur): Add KVO handling for fileChangesPrefixFilters.
|
||||
[_driverManager fileModificationPrefixFilterAdd:[configurator fileChangesPrefixFilters]];
|
||||
[_eventProvider fileModificationPrefixFilterAdd:[configurator fileChangesPrefixFilters]];
|
||||
|
||||
self.notQueue = [[SNTNotificationQueue alloc] init];
|
||||
SNTSyncdQueue *syncdQueue = [[SNTSyncdQueue alloc] init];
|
||||
@@ -119,24 +127,24 @@
|
||||
|
||||
// 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 +168,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 +256,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 +308,7 @@ 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +316,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];
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -253,14 +253,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 +285,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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,11 +112,11 @@ 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];
|
||||
[self.eventProvider postAction:ACTION_RESPOND_ACK forMessage:message];
|
||||
}
|
||||
|
||||
// If the binary is a critical system binary, don't check its signature. The binary was validated
|
||||
// by santad at startup.
|
||||
// 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) {
|
||||
@@ -152,12 +152,15 @@ static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
// Save decision details for logging the execution later. For transitive rules, we also use
|
||||
// the shasum stored in the decision details to update the rule's timestamp whenever an
|
||||
// ACTION_NOTIFY_EXEC message related to the transitive rule is received.
|
||||
NSString *ttyPath;
|
||||
if (action == ACTION_RESPOND_ALLOW || action == ACTION_RESPOND_ALLOW_COMPILER) {
|
||||
[_eventLog cacheDecision:cd];
|
||||
} else {
|
||||
ttyPath = [self ttyPathForPID:message.ppid];
|
||||
}
|
||||
|
||||
// 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 &&
|
||||
@@ -202,7 +205,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 +222,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 +240,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 +262,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 +289,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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -65,7 +65,7 @@
|
||||
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 +75,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 +108,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 +122,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 +140,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 +157,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 +172,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 +188,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 +203,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 +219,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 +228,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 +240,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 +253,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
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
[self dispatchBlockOnSyncdQueue:^{
|
||||
[self.syncdConnection.remoteObjectProxy
|
||||
postBundleEventToSyncServer:event reply:^(SNTBundleEventAction action) {
|
||||
// Remove the backoff entry for the inital block event. The same event will be included in
|
||||
// Remove the backoff entry for the initial block event. The same event will be included in
|
||||
// the related events synced using addEvents:isFromBundle:.
|
||||
if (action == SNTBundleEventActionSendEvents) {
|
||||
[self.uploadBackoff removeObjectForKey:event.fileBundleHash];
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.application-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)com.google.santa.daemon</string>
|
||||
<key>com.apple.developer.team-identifier</key>
|
||||
<string>EQHXZ8M8AV</string>
|
||||
<key>com.apple.developer.endpoint-security.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -14,14 +14,15 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/santad/EventProviders/SNTDriverManager.h"
|
||||
#import "Source/santad/SNTApplication.h"
|
||||
|
||||
#include <mach/task.h>
|
||||
#include <pthread/pthread.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#import "Source/santad/SNTApplication.h"
|
||||
|
||||
extern uint64_t watchdogCPUEvents;
|
||||
extern uint64_t watchdogRAMEvents;
|
||||
extern double watchdogCPUPeak;
|
||||
@@ -88,20 +89,40 @@ void *watchdogThreadFunction(__unused void *idata) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
LOGI(@"com.google.santa.daemon is running from an unexpected path: cleaning up");
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
[fm removeItemAtPath:@"/Library/LaunchDaemons/com.google.santad.plist" error:NULL];
|
||||
[SNTDriverManager unloadDriver];
|
||||
[fm removeItemAtPath:@"/Library/Extensions/santa-driver.kext" error:NULL];
|
||||
NSTask *t = [[NSTask alloc] init];
|
||||
t.launchPath = @"/bin/launchctl";
|
||||
t.arguments = @[ @"remove", @"com.google.santad" ];
|
||||
[t launch];
|
||||
[t waitUntilExit];
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@autoreleasepool {
|
||||
// Do not wait on child processes
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
|
||||
NSProcessInfo *pi = [NSProcessInfo processInfo];
|
||||
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"-v"]) {
|
||||
if ([pi.arguments containsObject:@"-v"]) {
|
||||
printf("%s\n", [infoDict[@"CFBundleVersion"] UTF8String]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
|
||||
|
||||
// Handle the case of macOS < 10.15 updating to >= 10.15.
|
||||
if (@available(macOS 10.15, *)) {
|
||||
if ([pi.arguments.firstObject isEqualToString:@(kSantaDPath)]) cleanup();
|
||||
}
|
||||
|
||||
SNTApplication *s = [[SNTApplication alloc] init];
|
||||
[s start];
|
||||
|
||||
|
||||