mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5a5f6f9fb | ||
|
|
7ef88d06a5 | ||
|
|
bc82d7988b | ||
|
|
545fa858e4 | ||
|
|
71c917649e | ||
|
|
3781556cf5 | ||
|
|
765d10a7c3 | ||
|
|
3583113381 | ||
|
|
46cd60e579 | ||
|
|
8198e59736 | ||
|
|
c5f0f5d177 | ||
|
|
ebc93954be | ||
|
|
cb4d2984b3 | ||
|
|
4c2018ef67 | ||
|
|
06d8295d0a | ||
|
|
ef8e9975e9 | ||
|
|
31509f4b9c | ||
|
|
497c1f393f | ||
|
|
8334a245c7 | ||
|
|
e8826a2941 | ||
|
|
ef040c1e7d | ||
|
|
dc692c8256 | ||
|
|
e9c7bfc087 | ||
|
|
22c72625c8 | ||
|
|
65a2212890 | ||
|
|
0a7c08cafc | ||
|
|
831a32160b | ||
|
|
b186419e54 | ||
|
|
1dc579c00f | ||
|
|
abdd6c319a | ||
|
|
5dd93fadfa | ||
|
|
e6fcbf59df | ||
|
|
9fd04ed301 | ||
|
|
e4b5f595ce | ||
|
|
212b02589b | ||
|
|
42c3631995 | ||
|
|
2695355dd2 | ||
|
|
db0cd861d6 | ||
|
|
57d6a962de | ||
|
|
91608d7366 | ||
|
|
7d4f1ffc45 | ||
|
|
ba539bb555 | ||
|
|
d9ecbf06c0 | ||
|
|
01df4623c7 | ||
|
|
c9cb91a22e | ||
|
|
1f9d60aecc | ||
|
|
52c5b5aade | ||
|
|
2d98173c51 | ||
|
|
5e3f13be70 | ||
|
|
90b894b88a | ||
|
|
6dc7387881 | ||
|
|
b14b017d72 | ||
|
|
d0ede18bf4 | ||
|
|
6d223aea03 | ||
|
|
f7986b0a05 | ||
|
|
629e70287c |
2
.bazelrc
Normal file
2
.bazelrc
Normal file
@@ -0,0 +1,2 @@
|
||||
build --apple_generate_dsym --define=apple.propagate_embedded_extra_outputs=yes
|
||||
build --host_force_python=PY2
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,12 +1,3 @@
|
||||
.DS_Store
|
||||
Build
|
||||
santa-*
|
||||
!santa-driver
|
||||
!*.md
|
||||
Pods
|
||||
Santa.xcodeproj/xcuserdata
|
||||
Santa.xcodeproj/project.xcworkspace
|
||||
Santa.xcworkspace/xcuserdata
|
||||
Santa.xcworkspace/xcshareddata
|
||||
Source/DevelopmentTeam.xcconfig
|
||||
default.profraw
|
||||
bazel-*
|
||||
|
||||
16
.travis.yml
16
.travis.yml
@@ -1,14 +1,14 @@
|
||||
---
|
||||
os: osx
|
||||
osx_image: xcode10.2
|
||||
language: objective-c
|
||||
cache:
|
||||
- bundler
|
||||
- cocoapods
|
||||
sudo: false
|
||||
osx_image: xcode9.3
|
||||
|
||||
before_install:
|
||||
- gem install cocoapods xcpretty
|
||||
- pod setup >/dev/null
|
||||
addons:
|
||||
homebrew:
|
||||
taps: bazelbuild/tap
|
||||
packages: bazelbuild/tap/bazel
|
||||
|
||||
script:
|
||||
- xcodebuild -workspace Santa.xcworkspace -scheme All -derivedDataPath build build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}
|
||||
- bazel build :release --show_progress_rate_limit=30.0 -c opt --apple_generate_dsym --color=no --verbose_failures --sandbox_debug
|
||||
- bazel test :unit_tests --show_progress_rate_limit=30.0 --test_output=errors --color=no --verbose_failures --sandbox_debug
|
||||
|
||||
158
BUILD
Normal file
158
BUILD
Normal file
@@ -0,0 +1,158 @@
|
||||
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")
|
||||
|
||||
# The version label for mac_* rules.
|
||||
apple_bundle_version(
|
||||
name = "version",
|
||||
build_version = SANTA_VERSION,
|
||||
short_version_string = SANTA_VERSION,
|
||||
)
|
||||
|
||||
# Used to detect optimized builds
|
||||
config_setting(
|
||||
name = "opt_build",
|
||||
values = {"compilation_mode": "opt"},
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Loading/Unloading/Reloading
|
||||
################################################################################
|
||||
run_command(
|
||||
name = "unload",
|
||||
cmd = """
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.google.santad.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
|
||||
""",
|
||||
)
|
||||
|
||||
run_command(
|
||||
name = "load",
|
||||
cmd = """
|
||||
sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
launchctl load /Library/LaunchAgents/com.google.santagui.plist
|
||||
""",
|
||||
)
|
||||
|
||||
run_command(
|
||||
name = "reload",
|
||||
srcs = ["//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
|
||||
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
|
||||
rm -rf /tmp/bazel_santa_reload
|
||||
echo "Time to stop being naughty"
|
||||
""",
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Release rules - used to create a release tarball
|
||||
################################################################################
|
||||
genrule(
|
||||
name = "release",
|
||||
srcs = [
|
||||
"//Source/santa_driver",
|
||||
"Conf/install.sh",
|
||||
"Conf/uninstall.sh",
|
||||
"Conf/com.google.santad.plist",
|
||||
"Conf/com.google.santagui.plist",
|
||||
"Conf/com.google.santa.asl.conf",
|
||||
"Conf/com.google.santa.newsyslog.conf",
|
||||
"Conf/Package/Makefile",
|
||||
"Conf/Package/postinstall",
|
||||
"Conf/Package/preinstall",
|
||||
],
|
||||
outs = ["santa-" + SANTA_VERSION + ".tar.gz"],
|
||||
cmd = select({
|
||||
"//conditions:default": """
|
||||
echo "ERROR: Trying to create a release tarball without optimization."
|
||||
echo "Please add '-c opt' flag to bazel invocation"
|
||||
""",
|
||||
":opt_build": """
|
||||
# Extract santa_driver.zip
|
||||
for SRC in $(SRCS); do
|
||||
if [[ $$(basename $${SRC}) == "santa_driver.zip" ]]; then
|
||||
mkdir -p $(@D)/binaries
|
||||
unzip -q $${SRC} -d $(@D)/binaries >/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
# Copy config files
|
||||
for SRC in $(SRCS); do
|
||||
if [[ "$$(dirname $${SRC})" == *"Conf" ]]; then
|
||||
mkdir -p $(@D)/conf
|
||||
cp $${SRC} $(@D)/conf/
|
||||
fi
|
||||
done
|
||||
|
||||
# Gather together the dSYMs. Throw an error if no dSYMs were found
|
||||
for SRC in $(SRCS); do
|
||||
case $${SRC} in
|
||||
*santa-driver.kext.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santa-driver.kext.dSYM
|
||||
;;
|
||||
*santad.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santad.dSYM
|
||||
;;
|
||||
*santactl.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santactl.dSYM
|
||||
;;
|
||||
*santabs.xpc.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/santabs.xpc.dSYM
|
||||
;;
|
||||
*Santa.app.dSYM*Info.plist)
|
||||
mkdir -p $(@D)/dsym
|
||||
cp -LR $$(dirname $$(dirname $${SRC})) $(@D)/dsym/Santa.app.dSYM
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Cause a build failure if the dSYMs are missing.
|
||||
if [[ ! -d "$(@D)/dsym" ]]; then
|
||||
echo "dsym dir missing: Did you forget to use --apple_generate_dsym?"
|
||||
echo "This flag is required for the 'release' target."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update all the timestamps to now. Bazel avoids timestamps to allow
|
||||
# builds to be hermetic and cacheable but for releases we want the
|
||||
# timestamps to be more-or-less correct.
|
||||
find $(@D)/{binaries,conf,dsym} -exec touch {} \;
|
||||
|
||||
# Create final output tar
|
||||
tar -C $(@D) -czpf $(@) binaries dsym conf
|
||||
""",
|
||||
}),
|
||||
heuristic_label_expansion = 0,
|
||||
)
|
||||
|
||||
test_suite(
|
||||
name = "unit_tests",
|
||||
tests = [
|
||||
"//Source/common:SNTFileInfoTest",
|
||||
"//Source/santa_driver:SantaCacheTest",
|
||||
"//Source/santa_driver:SantaPrefixTreeTest",
|
||||
"//Source/santactl:SNTCommandFileInfoTest",
|
||||
"//Source/santactl:SNTCommandSyncTest",
|
||||
"//Source/santad:SNTEventTableTest",
|
||||
"//Source/santad:SNTExecutionControllerTest",
|
||||
"//Source/santad:SNTRuleTableTest",
|
||||
],
|
||||
)
|
||||
@@ -17,6 +17,7 @@ sleep 1
|
||||
/bin/rm /usr/sbin/santactl
|
||||
/bin/launchctl remove com.google.santasync
|
||||
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist
|
||||
/bin/rm -rf /Applications/Santa.app
|
||||
|
||||
sleep 1
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<string>com.google.santagui</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/Santa.app/Contents/MacOS/Santa</string>
|
||||
<string>/Library/Extensions/santa-driver.kext/Contents/Resources/Santa.app/Contents/MacOS/Santa</string>
|
||||
<string>--syslog</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Minimal Configuration -->
|
||||
<key>ClientMode</key>
|
||||
<integer>1</integer>
|
||||
|
||||
<!-- For documentation of other keys, see the following URL:
|
||||
https://github.com/google/santa/wiki/Configuration-Keys -->
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -5,44 +5,53 @@ if [[ $EUID -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "binaries" ]]; then
|
||||
SOURCE="."
|
||||
elif [[ -d "../binaries" ]]; then
|
||||
SOURCE=".."
|
||||
else
|
||||
echo "Can't find binaries, run install.sh from inside the conf directory" 1>&2
|
||||
exit 1
|
||||
if [[ -z "${BINARIES}" || -z "${CONF}" ]]; then
|
||||
if [[ -d "binaries" ]]; then
|
||||
BINARIES="${PWD}/binaries"
|
||||
CONF="${PWD}/conf"
|
||||
elif [[ -d "../binaries" ]]; then
|
||||
BINARIES="${PWD}/../binaries"
|
||||
CONF="${PWD}/../conf"
|
||||
else
|
||||
echo "Can't find binaries, run install.sh from inside the conf directory" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine if anyone is logged into the GUI
|
||||
GUI_USER=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
|
||||
# Unload santad and scheduled sync job.
|
||||
/bin/launchctl remove com.google.santad >/dev/null 2>&1
|
||||
|
||||
# Unload kext.
|
||||
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
|
||||
|
||||
# Determine if anyone is logged into the GUI
|
||||
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 /Library/LaunchAgents/com.google.santagui.plist
|
||||
/bin/launchctl asuser ${GUI_USER} /bin/launchctl remove com.google.santagui
|
||||
|
||||
# Cleanup cruft from old versions
|
||||
/bin/launchctl remove com.google.santasync >/dev/null 2>&1
|
||||
/bin/rm /Library/LaunchDaemons/com.google.santasync.plist >/dev/null 2>&1
|
||||
/bin/rm /usr/libexec/santad >/dev/null 2>&1
|
||||
/bin/rm /usr/sbin/santactl >/dev/null 2>&1
|
||||
/bin/rm -rf /Applications/Santa.app 2>&1
|
||||
/bin/rm -rf /Library/Extensions/santa-driver.kext 2>&1
|
||||
|
||||
# Copy new files.
|
||||
/bin/cp -r ${SOURCE}/binaries/santa-driver.kext /Library/Extensions
|
||||
/bin/cp -r ${SOURCE}/binaries/Santa.app /Applications
|
||||
mkdir -p /usr/local/bin
|
||||
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
|
||||
/bin/cp -r ${BINARIES}/santa-driver.kext /Library/Extensions
|
||||
/bin/mkdir -p /usr/local/bin
|
||||
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin 2>/dev/null
|
||||
|
||||
/bin/cp ${SOURCE}/conf/com.google.santad.plist /Library/LaunchDaemons
|
||||
/bin/cp ${SOURCE}/conf/com.google.santagui.plist /Library/LaunchAgents
|
||||
/bin/cp ${SOURCE}/conf/com.google.santa.asl.conf /etc/asl/
|
||||
/bin/cp ${SOURCE}/conf/com.google.santa.newsyslog.conf /etc/newsyslog.d/
|
||||
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.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
|
||||
@@ -55,6 +64,7 @@ mkdir -p /usr/local/bin
|
||||
|
||||
# Load GUI agent if someone is logged in.
|
||||
[[ -n "$GUI_USER" ]] && \
|
||||
/bin/launchctl asuser ${GUI_USER} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
|
||||
/bin/launchctl asuser ${GUI_USER} \
|
||||
/bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -21,6 +21,7 @@ user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
/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
|
||||
|
||||
#uncomment to remove the config file and all databases, log files
|
||||
#/bin/rm -rf /var/db/santa
|
||||
#/bin/rm -f /var/log/santa*
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
|
||||
|
||||
# Building
|
||||
|
||||
Santa makes use of [rake](https://ruby.github.io/rake/) for building and testing Santa. All of the [releases](https://github.com/google/santa/releases) are made using this same process. Santa's releases are codesigned with Google's KEXT signing certificate. This allows Santa to be loaded with SIP fully enabled.
|
||||
|
||||
#### Cloning
|
||||
|
||||
Clone the source and change into the directory.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/google/santa
|
||||
cd santa
|
||||
```
|
||||
|
||||
The above command will default to using the `master` branch. If you wanted to build, run or test a specific version of Santa use this command.
|
||||
|
||||
```sh
|
||||
git checkout <version, i.e. 0.9.19>
|
||||
```
|
||||
|
||||
#### Building
|
||||
|
||||
Build a debug version of Santa. This keeps all the debug symbols, adds additional logs and does not optimize the compiled output. For speed sensitive tests make sure to benchmark a release version too.
|
||||
|
||||
```sh
|
||||
rake build:debug
|
||||
```
|
||||
|
||||
Build a release version of Santa.
|
||||
|
||||
```sh
|
||||
rake build:release
|
||||
```
|
||||
|
||||
Both of these just output the binaries that makeup Santa in the default Xcode build location. To actually run what was built, see the next section.
|
||||
|
||||
#### Running
|
||||
|
||||
On macOS 10.11+ System Integrity Protection (SIP) prevents loading of kernel extensions that are not signed by an Apple KEXT signing certificate. To be able to load and test a non-release version of Santa, SIP will have to be configured to allow non-Apple KEXT signing certificates.
|
||||
|
||||
__This is only to be done a machine that is actively developing, unloading and loading kernel extensions.__
|
||||
|
||||
1. Boot into Recovery Mode: Reboot and hold down `command+r`
|
||||
2. From the utilities menu select `Terminal`
|
||||
3. Disable the KEXT feature of SIP: `csrutil enable --without kext`
|
||||
4. Reboot
|
||||
|
||||
You should now be able to load and run a non-release version of Santa.
|
||||
|
||||
Build and run a debug version of Santa.
|
||||
|
||||
```sh
|
||||
rake reload:debug
|
||||
```
|
||||
|
||||
Build and run a release version of Santa.
|
||||
|
||||
```sh
|
||||
rake reload:release
|
||||
```
|
||||
|
||||
#### Debugging
|
||||
|
||||
Xcode and lldb can be used to debug Santa, just like any other project. Instead of clicking the play button to launch and attach to a process, you can attach to an already running, or soon to by running, component of Santa. To do this select the Debug menu and choose `Attach to Process by PID or Name… `. Below are the four components of Santa and who to debug the process as.
|
||||
|
||||
Note: santa-driver (the kernel extension) cannot be debugged by attaching with Xcode.
|
||||
|
||||
Note: Xcode can attach to santad without interruption, however any breakpoints in the decision making codepath can deadlock the machine. Using lldb directly to attach to santad will deadlock the machine.
|
||||
|
||||
| process | user |
|
||||
| -------- | ---- |
|
||||
| santad | root |
|
||||
| Santa* | me |
|
||||
| santactl | me |
|
||||
| santabs | root |
|
||||
|
||||
Xcode will then wait for the process to start. Issue this command to restart all the Santa processes in debug mode.
|
||||
|
||||
*The Santa (GUI) process is the only component of Santa that can be launched and debugged from Xcode directly. All the other components are launched with privileges and/or are scoped to an XPC service that launchd scopes to a hosting bundle. Thus the need for the `Attach to Process by PID or Name…` technique. See the [ipc](../details/ipc.md) document for for details.
|
||||
|
||||
```sh
|
||||
rake reload:debug
|
||||
```
|
||||
|
||||
Now the process is attached in Xcode and you can debug your day away.
|
||||
|
||||
#### Tests
|
||||
|
||||
Run all the logic / unit tests
|
||||
|
||||
```sh
|
||||
rake tests:logic
|
||||
```
|
||||
|
||||
Run all of santa-driver kernel extension tests
|
||||
|
||||
```sh
|
||||
rake tests:kernel
|
||||
```
|
||||
|
||||
#### Releases
|
||||
|
||||
Creates a release build of Santa with a version based of the newest tag. Also saves the dsym files for each component of Santa. This makes debugging and interpreting future crashes or kernel panics much easier.
|
||||
|
||||
```sh
|
||||
rake dist
|
||||
```
|
||||
4
Fuzzing/libFuzzer/.gitignore
vendored
Normal file
4
Fuzzing/libFuzzer/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bin
|
||||
llvm-*.src
|
||||
llvm-*.src.tar.xz
|
||||
|
||||
109
Fuzzing/libFuzzer/build.sh
Executable file
109
Fuzzing/libFuzzer/build.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
LLVM_VERSION='5.0.1'
|
||||
LLVM_COMPILERRT_TARBALL_NAME="llvm-${LLVM_VERSION}.src.tar.xz"
|
||||
LLVM_COMPILERRT_SRC_FOLDER_NAME=`echo "${LLVM_COMPILERRT_TARBALL_NAME}" | cut -d '.' -f 1-4`
|
||||
LLVM_COMPILERRT_TARBALL_URL="http://releases.llvm.org/${LLVM_VERSION}/${LLVM_COMPILERRT_TARBALL_NAME}"
|
||||
|
||||
LIBFUZZER_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
LOG_FILE=`mktemp`
|
||||
|
||||
main() {
|
||||
echo "libFuzzer build script"
|
||||
|
||||
echo " > Checking dependencies..."
|
||||
checkDependencies || return 1
|
||||
|
||||
echo " > Entering libFuzzer folder..."
|
||||
cd "${LIBFUZZER_FOLDER}" > /dev/null 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "Failed to enter the libFuzzer folder: ${LIBFUZZER_FOLDER}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${LLVM_COMPILERRT_TARBALL_NAME}" ] ; then
|
||||
echo " > Downloading the LLVM tarball..."
|
||||
curl "${LLVM_COMPILERRT_TARBALL_URL}" -o "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to download the LLVM tarball"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo " > An existing LLVM tarball was found"
|
||||
fi
|
||||
|
||||
if [ -d "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" ] ; then
|
||||
echo " > Deleting existing LLVM folder..."
|
||||
rm -rf "${LLVM_COMPILERRT_SRC_FOLDER_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to delete the existing source folder"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " > Extracting the LLVM tarball..."
|
||||
tar xf "${LLVM_COMPILERRT_TARBALL_NAME}" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
rm "${LLVM_COMPILERRT_TARBALL_NAME}" "${LLVM_COMPILERRT_SRC_FOLDER_NAME}"
|
||||
dumpLogFile "Failed to extract the LLVM tarball"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -d "bin" ] ; then
|
||||
echo " > Deleting existing bin folder..."
|
||||
rm -rf "bin" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to delete the existing bin folder"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir "bin" > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to create the bin folder"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " > Building libFuzzer..."
|
||||
( cd "bin" && "../${LLVM_COMPILERRT_SRC_FOLDER_NAME}/lib/Fuzzer/build.sh" ) > "${LOG_FILE}" 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
dumpLogFile "Failed to build the library"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "\nFinished building libFuzzer\n"
|
||||
rm "${LOG_FILE}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checkDependencies() {
|
||||
executable_list=( "clang++" "curl" "tar" )
|
||||
|
||||
for executable in "${executable_list[@]}" ; do
|
||||
which "${executable}" > /dev/null 2>&1
|
||||
if [ $? -ne 0 ] ; then
|
||||
echo "The following program was not found: ${executable}"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
dumpLogFile() {
|
||||
if [ $# -eq 1 ] ; then
|
||||
local message="$1"
|
||||
else
|
||||
local message="An error has occurred"
|
||||
fi
|
||||
|
||||
printf "${message}\n"
|
||||
printf "Log file follows\n===\n"
|
||||
cat "${LOG_FILE}"
|
||||
printf "\n===\n"
|
||||
rm "${LOG_FILE}"
|
||||
}
|
||||
|
||||
main $@
|
||||
exit $?
|
||||
3
Fuzzing/santacache/.gitignore
vendored
Normal file
3
Fuzzing/santacache/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
santacache.dSYM
|
||||
santacache
|
||||
|
||||
BIN
Fuzzing/santacache/santacache_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santacache/santacache_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
41
Fuzzing/santacache/src/main.cpp
Normal file
41
Fuzzing/santacache/src/main.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include <SantaCache.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
static SantaCache<uint64_t, uint64_t> decision_cache(5000, 2);
|
||||
|
||||
std::uint64_t fields[2] = {};
|
||||
|
||||
if (size > 16) {
|
||||
std::cout << "Invalid size! Start with -max_len=16\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::memcpy(fields, data, size);
|
||||
|
||||
decision_cache.set(fields[0], fields[1]);
|
||||
auto returned_value = decision_cache.get(fields[0]);
|
||||
|
||||
if (returned_value != fields[1]) {
|
||||
std::cout << fields[0] << ", " << fields[1] << " -> " << returned_value << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
16
Fuzzing/santactl/santactl_fuzzer_seed_corpus/example01
Normal file
16
Fuzzing/santactl/santactl_fuzzer_seed_corpus/example01
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"rules": [
|
||||
{
|
||||
"rule_type": "BINARY",
|
||||
"policy": "BLACKLIST",
|
||||
"sha256": "2dc104631939b4bdf5d6bccab76e166e37fe5e1605340cf68dab919df58b8eda",
|
||||
"custom_msg": "blacklist firefox"
|
||||
},
|
||||
{
|
||||
"rule_type": "CERTIFICATE",
|
||||
"policy": "BLACKLIST",
|
||||
"sha256": "e7726cf87cba9e25139465df5bd1557c8a8feed5c7dd338342d8da0959b63c8d",
|
||||
"custom_msg": "blacklist dash app certificate"
|
||||
}
|
||||
]
|
||||
}
|
||||
62
Fuzzing/santactl/src/main.mm
Normal file
62
Fuzzing/santactl/src/main.mm
Normal file
@@ -0,0 +1,62 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <SNTCommandSyncRuleDownload.h>
|
||||
#include <SNTCommandSyncState.h>
|
||||
#include <SNTCommandSyncConstants.h>
|
||||
#include <SNTRule.h>
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
NSData *buffer = [NSData dataWithBytes:static_cast<const void *>(data) length:size];
|
||||
if (!buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
|
||||
if (!response) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (![response isKindOfClass:[NSDictionary class]]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (![response objectForKey:kRules]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SNTCommandSyncState *state = [[SNTCommandSyncState alloc] init];
|
||||
if (!state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SNTCommandSyncRuleDownload *obj = [[SNTCommandSyncRuleDownload alloc] initWithState:state];
|
||||
if (!obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (NSDictionary *ruleDict in response[kRules]) {
|
||||
SNTRule *rule = [obj ruleFromDictionary:ruleDict];
|
||||
if (rule) {
|
||||
std::cerr << "Rule: " << [[rule description] UTF8String] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
Fuzzing/santad/santad_checkCacheForVnodeID_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santad/santad_checkCacheForVnodeID_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
@@ -0,0 +1 @@
|
||||
К'.p▒└G╗М┐║ЙSЮ╝и▌РУерЭxt1iАЫШ9ы*H╩4R"═©$-├Уww╙+Р╝╘[┼иу╧oС┬ОwRpЗя≤х°е
|
||||
BIN
Fuzzing/santad/santad_databaseRuleAddRules_fuzzer_seed_corpus/example01
Executable file
BIN
Fuzzing/santad/santad_databaseRuleAddRules_fuzzer_seed_corpus/example01
Executable file
Binary file not shown.
55
Fuzzing/santad/src/checkCacheForVnodeID.mm
Normal file
55
Fuzzing/santad/src/checkCacheForVnodeID.mm
Normal file
@@ -0,0 +1,55 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
if (size > 16) {
|
||||
std::cerr << "Invalid buffer size of " << size
|
||||
<< " (should be <= 16)" << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
santa_vnode_id_t vnodeID = {};
|
||||
std::memcpy(&vnodeID, data, size);
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
|
||||
[[daemonConn remoteObjectProxy] checkCacheForVnodeID:vnodeID
|
||||
withReply:^(santa_action_t action) {
|
||||
if (action == ACTION_RESPOND_ALLOW) {
|
||||
std::cerr << "File exists in [whitelist] kernel cache" << std::endl;;
|
||||
} else if (action == ACTION_RESPOND_DENY) {
|
||||
std::cerr << "File exists in [blacklist] kernel cache" << std::endl;;
|
||||
} else if (action == ACTION_UNSET) {
|
||||
std::cerr << "File does not exist in cache" << std::endl;;
|
||||
}
|
||||
}];
|
||||
|
||||
return 0;
|
||||
}
|
||||
51
Fuzzing/santad/src/databaseRemoveEventsWithIDs.mm
Normal file
51
Fuzzing/santad/src/databaseRemoveEventsWithIDs.mm
Normal file
@@ -0,0 +1,51 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
auto *eventId = reinterpret_cast<const std::uint64_t *>(data);
|
||||
std::size_t eventIdCount = size / sizeof(std::uint64_t);
|
||||
if (eventIdCount == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
|
||||
NSMutableSet *eventIds = [NSMutableSet setWithCapacity:eventIdCount];
|
||||
for (std::size_t i = 0; i < eventIdCount; i++) {
|
||||
auto id = [NSNumber numberWithInteger:eventId[i]];
|
||||
[eventIds addObject:id];
|
||||
}
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:[eventIds allObjects]];
|
||||
return 0;
|
||||
}
|
||||
73
Fuzzing/santad/src/databaseRuleAddRules.mm
Normal file
73
Fuzzing/santad/src/databaseRuleAddRules.mm
Normal file
@@ -0,0 +1,73 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct InputData {
|
||||
std::uint32_t cleanSlate;
|
||||
std::uint32_t state;
|
||||
std::uint32_t type;
|
||||
char hash[33];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) {
|
||||
if (size > sizeof(InputData)) {
|
||||
std::cerr << "Invalid buffer size of " << size
|
||||
<< " (should be <= " << sizeof(InputData)
|
||||
<< ")" << std::endl;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
InputData input_data = {};
|
||||
std::memcpy(&input_data, data, size);
|
||||
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.state = (SNTRuleState) input_data.state;
|
||||
newRule.type = (SNTRuleType) input_data.type;
|
||||
newRule.shasum = @(input_data.hash);
|
||||
newRule.customMsg = @"";
|
||||
|
||||
MOLXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
daemonConn.invalidationHandler = ^{
|
||||
printf("An error occurred communicating with the daemon, is it running?\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRules:@[newRule]
|
||||
cleanSlate:NO
|
||||
reply:^(NSError *error) {
|
||||
if (!error) {
|
||||
if (newRule.state == SNTRuleStateRemove) {
|
||||
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
} else {
|
||||
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
return 0;
|
||||
}
|
||||
59
Podfile
59
Podfile
@@ -1,59 +0,0 @@
|
||||
platform :osx, "10.9"
|
||||
|
||||
inhibit_all_warnings!
|
||||
|
||||
target :Santa do
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLXPCConnection'
|
||||
end
|
||||
|
||||
target :santad do
|
||||
pod 'FMDB'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLXPCConnection'
|
||||
target :santabs do
|
||||
pod 'FMDB'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLXPCConnection'
|
||||
end
|
||||
end
|
||||
|
||||
target :santactl do
|
||||
pod 'FMDB'
|
||||
pod 'MOLAuthenticatingURLSession'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLFCMClient', '~> 1.3'
|
||||
pod 'MOLXPCConnection'
|
||||
end
|
||||
|
||||
target :LogicTests do
|
||||
pod 'FMDB'
|
||||
pod 'MOLAuthenticatingURLSession'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLXPCConnection'
|
||||
pod 'OCMock'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
target.build_configurations.each do |config|
|
||||
if config.name != 'Release' then
|
||||
break
|
||||
end
|
||||
|
||||
# This is necessary to get FMDB to not NSLog stuff.
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ''
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] <<= "NDEBUG=1"
|
||||
|
||||
# Enable more compiler optimizations.
|
||||
config.build_settings['GCC_OPTIMIZATION_LEVEL'] = 'fast'
|
||||
config.build_settings['LLVM_LTO'] = 'YES'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
46
Podfile.lock
46
Podfile.lock
@@ -1,46 +0,0 @@
|
||||
PODS:
|
||||
- FMDB (2.7.2):
|
||||
- FMDB/standard (= 2.7.2)
|
||||
- FMDB/standard (2.7.2)
|
||||
- MOLAuthenticatingURLSession (2.4):
|
||||
- MOLCertificate (~> 1.8)
|
||||
- MOLCertificate (1.8)
|
||||
- MOLCodesignChecker (1.10):
|
||||
- MOLCertificate (~> 1.8)
|
||||
- MOLFCMClient (1.7):
|
||||
- MOLAuthenticatingURLSession (~> 2.4)
|
||||
- MOLXPCConnection (1.1):
|
||||
- MOLCodesignChecker (~> 1.9)
|
||||
- OCMock (3.4.1)
|
||||
|
||||
DEPENDENCIES:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- MOLFCMClient (~> 1.3)
|
||||
- MOLXPCConnection
|
||||
- OCMock
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- MOLFCMClient
|
||||
- MOLXPCConnection
|
||||
- OCMock
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FMDB: 6198a90e7b6900cfc046e6bc0ef6ebb7be9236aa
|
||||
MOLAuthenticatingURLSession: c238aa1c9a7b1077eb39a6f40204bfe76a7d204e
|
||||
MOLCertificate: c999513316d511c69f290fbf313dfe8dca4ad592
|
||||
MOLCodesignChecker: b0d5db9d2f9bd94e0fd093891a5d40e5ad77cbc0
|
||||
MOLFCMClient: ee45348909351f232e2759c580329072ae7e02d4
|
||||
MOLXPCConnection: de9d5535928f59766a768384e411077b83ec2f9c
|
||||
OCMock: 2cd0716969bab32a2283ff3a46fd26a8c8b4c5e3
|
||||
|
||||
PODFILE CHECKSUM: ddca043a7ace9ec600c108621c56d13a50d17236
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
243
README.md
243
README.md
@@ -1,54 +1,82 @@
|
||||
Santa
|
||||
[](https://travis-ci.org/google/santa)
|
||||
[](https://santa.readthedocs.io/en/latest/?badge=latest)
|
||||
=====
|
||||
# Santa [![Build Status][build-status-img]][build-status-link] [![Documentation Status][doc-status-img]][doc-status-link]
|
||||
|
||||
[build-status-img]: https://travis-ci.org/google/santa.png?branch=master
|
||||
[build-status-link]: https://travis-ci.org/google/santa
|
||||
[doc-status-img]: https://readthedocs.org/projects/santa/badge/?version=latest
|
||||
[doc-status-link]: https://santa.readthedocs.io/en/latest/?badge=latest
|
||||
|
||||
<p align="center">
|
||||
<a href="#santa--">
|
||||
<img src="./Source/SantaGUI/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
</a>
|
||||
<img src="./Source/SantaGUI/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
</p>
|
||||
|
||||
Santa is a binary whitelisting/blacklisting system for macOS. It consists of
|
||||
a kernel extension that monitors for executions, a userland daemon that makes
|
||||
execution decisions based on the contents of a SQLite database, a GUI agent that
|
||||
notifies the user in case of a block decision and a command-line utility for
|
||||
managing the system and synchronizing the database with a server.
|
||||
|
||||
Santa is not yet at 1.0. We're writing more tests, fixing bugs, working on TODOs
|
||||
and finishing up a security audit.
|
||||
Santa is a binary whitelisting/blacklisting system for macOS. It consists of a
|
||||
kernel extension that monitors for executions, a userland daemon that makes
|
||||
execution decisions based on the contents of a SQLite database, a GUI agent
|
||||
that notifies the user in case of a block decision and a command-line utility
|
||||
for managing the system and synchronizing the database with a server.
|
||||
|
||||
It is named Santa because it keeps track of binaries that are naughty or nice.
|
||||
|
||||
Santa is a project of Google's Macintosh Operations Team.
|
||||
|
||||
Docs
|
||||
========
|
||||
The Santa docs are stored in the [Docs](https://github.com/google/santa/blob/master/Docs) directory. A Read the Docs instance is available here: https://santa.readthedocs.io.
|
||||
# Docs
|
||||
|
||||
Admin-Related Features
|
||||
========
|
||||
The Santa docs are stored in the
|
||||
[Docs](https://github.com/google/santa/blob/master/docs) directory. A Read the
|
||||
Docs instance is available here: https://santa.readthedocs.io.
|
||||
|
||||
* Multiple modes: In the default MONITOR mode, all binaries except
|
||||
those marked as blacklisted will be allowed to run, whilst being logged and recorded in the events database. In LOCKDOWN mode, only whitelisted binaries are
|
||||
allowed to run.
|
||||
The docs include deployment options, details on how parts of Santa work and
|
||||
instructions for developing Santa itself.
|
||||
|
||||
* Event logging: When the kext is loaded, all binary launches are logged.
|
||||
When in either mode, all unknown or denied binaries are stored in the database to enable later aggregation.
|
||||
# Get Help
|
||||
|
||||
* Certificate-based rules, with override levels: Instead of relying on a binary's hash (or 'fingerprint'), executables can be whitelisted/blacklisted by their signing
|
||||
certificate. You can therefore trust/block all binaries by a given publisher that were signed with that cert across version updates. A
|
||||
binary can only be whitelisted by its certificate if its signature validates
|
||||
correctly, but a rule for a binary's fingerprint will override a decision for a
|
||||
certificate; i.e. you can whitelist a certificate while blacklisting a binary
|
||||
signed with that certificate, or vice-versa.
|
||||
If you have questions or otherwise need help getting started,
|
||||
the [santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is a
|
||||
great place.
|
||||
|
||||
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature to that found in Managed Client (the precursor to configuration profiles, which used the same implementation mechanism), Application Launch Restrictions via the mcxalr binary. This implementation carries the added benefit of being configurable via regex, and not relying on LaunchServices. As detailed in the wiki, when evaluating rules this holds the lowest precedence.
|
||||
If you believe you have a bug, feel free to report [an
|
||||
issue](https://github.com/google/santa/isues) and we'll respond as soon as we
|
||||
can.
|
||||
|
||||
* Failsafe cert rules: You cannot put in a deny rule that would block the certificate used to sign launchd, a.k.a. pid 1, and therefore all components used in macOS. The binaries in every OS update (and in some cases entire new versions) are therefore auto-whitelisted. This does not affect binaries from Apple's App Store, which use various certs that change regularly for common apps. Likewise, you cannot blacklist Santa itself, and Santa uses a distinct separate cert than other Google apps.
|
||||
|
||||
Intentions and Expectations
|
||||
===========================
|
||||
# Admin-Related Features
|
||||
|
||||
* Multiple modes: In the default MONITOR mode, all binaries except those marked
|
||||
as blacklisted will be allowed to run, whilst being logged and recorded in
|
||||
the events database. In LOCKDOWN mode, only whitelisted binaries are allowed
|
||||
to run.
|
||||
|
||||
* Event logging: When the kext is loaded, all binary launches are logged. When
|
||||
in either mode, all unknown or denied binaries are stored in the database to
|
||||
enable later aggregation.
|
||||
|
||||
* Certificate-based rules, with override levels: Instead of relying on a
|
||||
binary's hash (or 'fingerprint'), executables can be whitelisted/blacklisted
|
||||
by their signing certificate. You can therefore trust/block all binaries by a
|
||||
given publisher that were signed with that cert across version updates. A
|
||||
binary can only be whitelisted by its certificate if its signature validates
|
||||
correctly, but a rule for a binary's fingerprint will override a decision for
|
||||
a certificate; i.e. you can whitelist a certificate while blacklisting a
|
||||
binary signed with that certificate, or vice-versa.
|
||||
|
||||
* Path-based rules (via NSRegularExpression/ICU): This allows a similar feature
|
||||
to that found in Managed Client (the precursor to configuration profiles,
|
||||
which used the same implementation mechanism), Application Launch
|
||||
Restrictions via the mcxalr binary. This implementation carries the added
|
||||
benefit of being configurable via regex, and not relying on LaunchServices.
|
||||
As detailed in the wiki, when evaluating rules this holds the lowest
|
||||
precedence.
|
||||
|
||||
* Failsafe cert rules: You cannot put in a deny rule that would block the
|
||||
certificate used to sign launchd, a.k.a. pid 1, and therefore all components
|
||||
used in macOS. The binaries in every OS update (and in some cases entire new
|
||||
versions) are therefore auto-whitelisted. This does not affect binaries from
|
||||
Apple's App Store, which use various certs that change regularly for common
|
||||
apps. Likewise, you cannot blacklist Santa itself, and Santa uses a distinct
|
||||
separate cert than other Google apps.
|
||||
|
||||
# Intentions and Expectations
|
||||
|
||||
No single system or process will stop *all* attacks, or provide 100% security.
|
||||
Santa is written with the intention of helping protect users from themselves.
|
||||
People often download malware and trust it, giving the malware credentials, or
|
||||
@@ -57,118 +85,99 @@ centrally managed component, Santa can help stop the spread of malware among a
|
||||
large fleet of machines. Independently, Santa can aid in analyzing what is
|
||||
running on your computer.
|
||||
|
||||
Santa is part of a defense-in-depth strategy, and you should continue to protect
|
||||
hosts in whatever other ways you see fit.
|
||||
Santa is part of a defense-in-depth strategy, and you should continue to
|
||||
protect hosts in whatever other ways you see fit.
|
||||
|
||||
Get Help
|
||||
========
|
||||
If you have questions or otherwise need help getting started, the
|
||||
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is a
|
||||
great place. Please consult the [wiki](https://github.com/google/santa/wiki) and [issues](https://github.com/google/santa/issues) as well.
|
||||
# Security and Performance-Related Features
|
||||
|
||||
Security and Performance-Related Features
|
||||
============
|
||||
* In-kernel caching: whitelisted binaries are cached in the kernel so the
|
||||
processing required to make a request is only done if the binary
|
||||
isn't already cached.
|
||||
processing required to make a request is only done if the binary isn't
|
||||
already cached.
|
||||
|
||||
* Userland components validate each other: each of the userland components (the
|
||||
daemon, the GUI agent and the command-line utility) communicate with each other
|
||||
using XPC and check that their signing certificates are identical before any
|
||||
communication is accepted.
|
||||
daemon, the GUI agent and the command-line utility) communicate with each
|
||||
other using XPC and check that their signing certificates are identical
|
||||
before any communication is accepted.
|
||||
|
||||
* Kext uses only KPIs: the kernel extension only uses provided kernel
|
||||
programming interfaces to do its job. This means that the kext code should
|
||||
continue to work across OS versions.
|
||||
programming interfaces to do its job. This means that the kext code should
|
||||
continue to work across OS versions.
|
||||
|
||||
Known Issues
|
||||
============
|
||||
Santa is not yet at 1.0 and we have some known issues to be aware of:
|
||||
# Known Issues
|
||||
|
||||
* Santa only blocks execution (execve and variants), it doesn't protect against
|
||||
dynamic libraries loaded with dlopen, libraries on disk that have been replaced, or
|
||||
libraries loaded using `DYLD_INSERT_LIBRARIES`. As of version 0.9.1 we *do* address [__PAGEZERO missing issues](b87482e) that were exploited in some versions of macOS. We are working on also protecting against similar avenues of attack.
|
||||
dynamic libraries loaded with dlopen, libraries on disk that have been
|
||||
replaced, or libraries loaded using `DYLD_INSERT_LIBRARIES`. As of version
|
||||
0.9.1 we *do* address [__PAGEZERO missing issues](b87482e) that were
|
||||
exploited in some versions of macOS. We are working on also protecting
|
||||
against similar avenues of attack.
|
||||
|
||||
* Kext communication security: the kext will only accept a connection from a
|
||||
single client at a time and said client must be running as root. We haven't yet
|
||||
found a good way to ensure the kext only accepts connections from a valid client.
|
||||
single client at a time and said client must be running as root. We haven't
|
||||
yet found a good way to ensure the kext only accepts connections from a valid
|
||||
client.
|
||||
|
||||
* Database protection: the SQLite database is installed with permissions so that
|
||||
only the root user can read/write it. We're considering approaches to secure
|
||||
this further.
|
||||
|
||||
* Sync client: The `santactl` command-line client includes a flag to synchronize with a management server, which uploads events that have occurred on the
|
||||
machine and downloads new rules. There are several open-source servers you can sync with:
|
||||
|
||||
* [Upvote](https://github.com/google/upvote) - An AppEngine-based server that implements social voting to make managing a large fleet easier.
|
||||
* [Moroz](https://github.com/groob/moroz) - A simple golang server that serves hardcoded rules from simple configuration files.
|
||||
* [Zentral](https://github.com/zentralopensource/zentral/wiki) - A centralized service that pulls data from multiple sources and deploy configurations to multiple services.
|
||||
* Database protection: the SQLite database is installed with permissions so
|
||||
that only the root user can read/write it. We're considering approaches to
|
||||
secure this further.
|
||||
|
||||
* Scripts: Santa is currently written to ignore any execution that isn't a
|
||||
binary. This is because after weighing the administration cost vs the benefit,
|
||||
we found it wasn't worthwhile. Additionally, a number of applications make use
|
||||
of temporary generated scripts, which we can't possibly whitelist and not doing
|
||||
so would cause problems. We're happy to revisit this (or at least make it an
|
||||
option) if it would be useful to others.
|
||||
binary. This is because after weighing the administration cost vs the
|
||||
benefit, we found it wasn't worthwhile. Additionally, a number of
|
||||
applications make use of temporary generated scripts, which we can't possibly
|
||||
whitelist and not doing so would cause problems. We're happy to revisit this
|
||||
(or at least make it an option) if it would be useful to others.
|
||||
|
||||
* Documentation: This is currently limited.
|
||||
# Sync Servers
|
||||
|
||||
* Tests: There aren't enough of them.
|
||||
* The `santactl` command-line client includes a flag to synchronize with a
|
||||
management server, which uploads events that have occurred on the machine and
|
||||
downloads new rules. There are several open-source servers you can sync with:
|
||||
|
||||
Screenshots
|
||||
===========
|
||||
* [Upvote](https://github.com/google/upvote) - An AppEngine-based server
|
||||
that implements social voting to make managing a large fleet easier.
|
||||
* [Moroz](https://github.com/groob/moroz) - A simple golang server that
|
||||
serves hardcoded rules from simple configuration files.
|
||||
* [Zentral](https://github.com/zentralopensource/zentral/wiki) - A
|
||||
centralized service that pulls data from multiple sources and deploy
|
||||
configurations to multiple services.
|
||||
|
||||
A tool like Santa doesn't really lend itself to screenshots, so here's a video instead.
|
||||
* Alternatively, `santactl` can configure rules locally (without a sync
|
||||
server).
|
||||
|
||||
<p align="center">
|
||||
<img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif" alt="Santa Block Video" />
|
||||
</p>
|
||||
# Screenshots
|
||||
|
||||
Building
|
||||
========
|
||||
A tool like Santa doesn't really lend itself to screenshots, so here's a video
|
||||
instead.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/google/santa
|
||||
cd santa
|
||||
<p align="center"> <img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif"
|
||||
alt="Santa Block Video" /> </p>
|
||||
|
||||
# Build a debug build. This will install any necessary CocoaPods, create the
|
||||
# workspace and build, outputting the full log only if an error occurred.
|
||||
# If CocoaPods is not installed, you'll be prompted to install it.
|
||||
#
|
||||
# For other build/install/run options, run rake without any arguments
|
||||
rake build:debug
|
||||
```
|
||||
|
||||
Note: the Xcode project is setup to use any installed "Mac Developer" certificate
|
||||
and for security-reasons parts of Santa will not operate properly if not signed.
|
||||
|
||||
For more details on building see the [building.md](https://github.com/google/santa/blob/master/Docs/development/building.md) document.
|
||||
|
||||
Kext Signing
|
||||
============
|
||||
# Kext Signing
|
||||
Kernel extensions on macOS 10.9 and later must be signed using an Apple-provided
|
||||
Developer ID certificate with a kernel extension flag. Without it, the only way
|
||||
to load an extension is to enable kext-dev-mode or disable SIP, depending on the
|
||||
OS version.
|
||||
to load an extension is to enable kext-dev-mode or disable SIP, depending on
|
||||
the OS version.
|
||||
|
||||
There are two possible solutions for this, for distribution purposes:
|
||||
|
||||
1) Use a [pre-built, pre-signed version](https://github.com/google/santa/releases)
|
||||
of the kext that we supply. Each time changes are made to the kext code we will
|
||||
update the pre-built version that you can make use of. This doesn't prevent you
|
||||
from making changes to the non-kext parts of Santa and distributing those.
|
||||
If you make changes to the kext and make a pull request, we can merge them in
|
||||
and distribute a new version of the pre-signed kext.
|
||||
1) Use a [pre-built, pre-signed
|
||||
version](https://github.com/google/santa/releases) of the kext that we supply.
|
||||
Each time changes are made to the kext code we will update the pre-built
|
||||
version that you can make use of. This doesn't prevent you from making changes
|
||||
to the non-kext parts of Santa and distributing those. If you make changes to
|
||||
the kext and make a pull request, we can merge them in and distribute a new
|
||||
version of the pre-signed kext.
|
||||
|
||||
2) Apply for your own [kext signing certificate](https://developer.apple.com/contact/kext/).
|
||||
Apple will only grant this for broad distribution within an organization, they
|
||||
won't issue them just for testing purposes.
|
||||
2) Apply for your own [kext signing
|
||||
certificate](https://developer.apple.com/contact/kext/). Apple will only grant
|
||||
this for broad distribution within an organization, they won't issue them just
|
||||
for testing purposes.
|
||||
|
||||
Contributing
|
||||
============
|
||||
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
|
||||
# Contributing
|
||||
Patches to this project are very much welcome. Please see the
|
||||
[CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
|
||||
file.
|
||||
|
||||
Disclaimer
|
||||
==========
|
||||
# Disclaimer
|
||||
This is **not** an official Google product.
|
||||
|
||||
214
Rakefile
214
Rakefile
@@ -1,214 +0,0 @@
|
||||
require 'openssl'
|
||||
|
||||
WORKSPACE = 'Santa.xcworkspace'
|
||||
DEFAULT_SCHEME = 'All'
|
||||
OUTPUT_PATH = 'Build'
|
||||
BINARIES = ['Santa.app', 'santa-driver.kext']
|
||||
DSYMS = ['Santa.app.dSYM', 'santa-driver.kext.dSYM', 'santad.dSYM', 'santactl.dSYM']
|
||||
XCPRETTY_DEFAULTS = '-sc'
|
||||
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
|
||||
DEVTEAM_FILE = 'Source/DevelopmentTeam.xcconfig'
|
||||
DEVTEAM_CERT_CN = 'Mac Developer'
|
||||
$DISABLE_XCPRETTY = false
|
||||
|
||||
task :default do
|
||||
system("rake -sT")
|
||||
end
|
||||
|
||||
def xcodebuild(opts)
|
||||
command = "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts}"
|
||||
if not $DISABLE_XCPRETTY
|
||||
command << " | xcpretty #{XCPRETTY_DEFAULTS} && exit ${PIPESTATUS[0]}"
|
||||
end
|
||||
|
||||
if system command
|
||||
puts "\e[32mPass\e[0m"
|
||||
else
|
||||
raise "\e[31mFail\e[0m"
|
||||
end
|
||||
end
|
||||
|
||||
def xcodebuilddir
|
||||
if not $xcode_build_dir
|
||||
output = `xcodebuild #{XCODEBUILD_DEFAULTS} -scheme All -showBuildSettings`
|
||||
if match = output.match(/BUILD_DIR = (.*)/)
|
||||
$xcode_build_dir = match.captures.first
|
||||
puts "Found Xcode build dir #{$xcode_build_dir}"
|
||||
end
|
||||
end
|
||||
$xcode_build_dir
|
||||
end
|
||||
|
||||
task :init do
|
||||
unless File.exists?(WORKSPACE) and File.exists?('Pods')
|
||||
puts "Pods missing, running 'pod install'"
|
||||
system "pod install" or raise "CocoaPods is not installed. Install with 'sudo gem install cocoapods'"
|
||||
end
|
||||
unless system 'xcpretty -v >/dev/null 2>&1'
|
||||
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
|
||||
$DISABLE_XCPRETTY = true
|
||||
end
|
||||
cert_pem = `security find-certificate -p -c '#{DEVTEAM_CERT_CN}'`
|
||||
cert = OpenSSL::X509::Certificate.new cert_pem
|
||||
team_id = cert.subject.to_a.find {|f| f[0] == "OU"}[1]
|
||||
File.open(DEVTEAM_FILE, 'w') { |f|
|
||||
f.puts("// This file is auto-generated. Do not edit manually")
|
||||
f.puts("DEVELOPMENT_TEAM = #{team_id}")
|
||||
}
|
||||
end
|
||||
|
||||
task :remove_existing do
|
||||
system 'sudo rm -rf /Library/Extensions/santa-driver.kext'
|
||||
system 'sudo rm -rf /Applications/Santa.app'
|
||||
end
|
||||
|
||||
desc "Clean"
|
||||
task :clean => :init do
|
||||
puts "Cleaning"
|
||||
FileUtils.rm_rf(OUTPUT_PATH)
|
||||
xcodebuild("-scheme All clean")
|
||||
end
|
||||
|
||||
# Build
|
||||
namespace :build do
|
||||
desc "Build: Debug"
|
||||
task :debug do
|
||||
Rake::Task['build:build'].invoke("Debug")
|
||||
end
|
||||
|
||||
desc "Build: Release"
|
||||
task :release do
|
||||
Rake::Task['build:build'].invoke("Release")
|
||||
end
|
||||
|
||||
task :build, [:configuration] => :init do |t, args|
|
||||
config = args[:configuration]
|
||||
puts "Building with configuration: #{config}"
|
||||
xcodebuild("-scheme All -configuration #{config} build")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Install
|
||||
namespace :install do
|
||||
desc "Install: Debug"
|
||||
task :debug do
|
||||
Rake::Task['install:install'].invoke("Debug")
|
||||
end
|
||||
|
||||
desc "Install: Release"
|
||||
task :release do
|
||||
Rake::Task['install:install'].invoke("Release")
|
||||
end
|
||||
|
||||
task :install, [:configuration] do |t, args|
|
||||
config = args[:configuration]
|
||||
system 'sudo cp conf/com.google.santad.plist /Library/LaunchDaemons'
|
||||
system 'sudo cp conf/com.google.santagui.plist /Library/LaunchAgents'
|
||||
system 'sudo cp conf/com.google.santa.asl.conf /etc/asl'
|
||||
system 'sudo cp conf/com.google.santa.newsyslog.conf /etc/newsyslog.d/'
|
||||
system '/usr/bin/killall -HUP syslogd'
|
||||
Rake::Task['build:build'].invoke(config)
|
||||
puts "Installing with configuration: #{config}"
|
||||
Rake::Task['remove_existing'].invoke()
|
||||
system "sudo cp -r #{xcodebuilddir}/#{config}/santa-driver.kext /Library/Extensions"
|
||||
system "sudo cp -r #{xcodebuilddir}/#{config}/Santa.app /Applications"
|
||||
end
|
||||
end
|
||||
|
||||
# Dist
|
||||
task :dist do
|
||||
desc "Create distribution folder"
|
||||
|
||||
Rake::Task['clean'].invoke()
|
||||
Rake::Task['build:build'].invoke("Release")
|
||||
|
||||
dist_path = "santa-#{`defaults read #{xcodebuilddir}/Release/santa-driver.kext/Contents/Info.plist CFBundleVersion`.strip}"
|
||||
|
||||
FileUtils.rm_rf(dist_path)
|
||||
|
||||
FileUtils.mkdir_p("#{dist_path}/binaries")
|
||||
FileUtils.mkdir_p("#{dist_path}/conf")
|
||||
FileUtils.mkdir_p("#{dist_path}/dsym")
|
||||
|
||||
BINARIES.each do |x|
|
||||
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{dist_path}/binaries")
|
||||
end
|
||||
|
||||
DSYMS.each do |x|
|
||||
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{dist_path}/dsym")
|
||||
end
|
||||
|
||||
|
||||
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{dist_path}/conf")}
|
||||
|
||||
puts "Distribution folder #{dist_path} created"
|
||||
end
|
||||
|
||||
# Tests
|
||||
namespace :tests do
|
||||
desc "Tests: Logic"
|
||||
task :logic => [:init] do
|
||||
puts "Running logic tests"
|
||||
xcodebuild("-scheme LogicTests test")
|
||||
end
|
||||
|
||||
desc "Tests: Kernel"
|
||||
task :kernel do
|
||||
Rake::Task['build:debug'].invoke()
|
||||
system "sudo #{xcodebuilddir}/Debug/KernelTests"
|
||||
end
|
||||
end
|
||||
|
||||
# Load/Unload
|
||||
task :unload_daemon do
|
||||
puts "Unloading daemon"
|
||||
system "sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist 2>/dev/null"
|
||||
end
|
||||
|
||||
task :unload_kext do
|
||||
puts "Unloading kernel extension"
|
||||
system "sudo kextunload -b com.google.santa-driver 2>/dev/null"
|
||||
end
|
||||
|
||||
task :unload_gui do
|
||||
puts "Unloading GUI agent"
|
||||
system "launchctl unload /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
|
||||
end
|
||||
|
||||
desc "Unload"
|
||||
task :unload => [:unload_daemon, :unload_kext, :unload_gui]
|
||||
|
||||
task :load_daemon do
|
||||
puts "Loading daemon"
|
||||
system "sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist"
|
||||
end
|
||||
|
||||
task :load_kext do
|
||||
puts "Loading kernel extension"
|
||||
system "sudo kextload /Library/Extensions/santa-driver.kext"
|
||||
end
|
||||
|
||||
task :load_gui do
|
||||
puts "Loading GUI agent"
|
||||
system "launchctl load /Library/LaunchAgents/com.google.santagui.plist 2>/dev/null"
|
||||
end
|
||||
|
||||
desc "Load"
|
||||
task :load => [:load_kext, :load_daemon, :load_gui]
|
||||
|
||||
namespace :reload do
|
||||
desc "Reload: Debug"
|
||||
task :debug do
|
||||
Rake::Task['unload'].invoke()
|
||||
Rake::Task['install:debug'].invoke()
|
||||
Rake::Task['load'].invoke()
|
||||
end
|
||||
|
||||
desc "Reload: Release"
|
||||
task :release do
|
||||
Rake::Task['unload'].invoke()
|
||||
Rake::Task['install:release'].invoke()
|
||||
Rake::Task['load'].invoke()
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,84 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
|
||||
BuildableName = "All"
|
||||
BlueprintName = "All"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES"
|
||||
enableAddressSanitizer = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
|
||||
BuildableName = "All"
|
||||
BlueprintName = "All"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
|
||||
BuildableName = "KernelTests"
|
||||
BlueprintName = "KernelTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
|
||||
BuildableName = "KernelTests"
|
||||
BlueprintName = "KernelTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
debugAsWhichUser = "root"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
|
||||
BuildableName = "KernelTests"
|
||||
BlueprintName = "KernelTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
|
||||
BuildableName = "KernelTests"
|
||||
BlueprintName = "KernelTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
|
||||
BuildableName = "LogicTests.xctest"
|
||||
BlueprintName = "LogicTests"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
|
||||
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"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
|
||||
BuildableName = "Santa.app"
|
||||
BlueprintName = "Santa"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
|
||||
BuildableName = "Santa.app"
|
||||
BlueprintName = "Santa"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
|
||||
BuildableName = "Santa.app"
|
||||
BlueprintName = "Santa"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
|
||||
BuildableName = "santa-driver.kext"
|
||||
BlueprintName = "santa-driver"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
|
||||
BuildableName = "santa-driver.kext"
|
||||
BlueprintName = "santa-driver"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,93 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
|
||||
BuildableName = "santactl"
|
||||
BlueprintName = "santactl"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
|
||||
BuildableName = "santactl"
|
||||
BlueprintName = "santactl"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
|
||||
BuildableName = "santactl"
|
||||
BlueprintName = "santactl"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
|
||||
BuildableName = "santactl"
|
||||
BlueprintName = "santactl"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0910"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
|
||||
BuildableName = "santad"
|
||||
BlueprintName = "santad"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
|
||||
BuildableName = "santad"
|
||||
BlueprintName = "santad"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
debugAsWhichUser = "root"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
|
||||
BuildableName = "santad"
|
||||
BlueprintName = "santad"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
|
||||
BuildableName = "santad"
|
||||
BlueprintName = "santad"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
1
Santa.xcworkspace/contents.xcworkspacedata
generated
1
Santa.xcworkspace/contents.xcworkspacedata
generated
@@ -1 +0,0 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:Santa.xcodeproj'/><FileRef location='group:Pods/Pods.xcodeproj'/></Workspace>
|
||||
54
Source/SantaGUI/BUILD
Normal file
54
Source/SantaGUI/BUILD
Normal file
@@ -0,0 +1,54 @@
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
exports_files([
|
||||
"Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-256.png",
|
||||
])
|
||||
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
|
||||
|
||||
objc_library(
|
||||
name = "SantaGUI_lib",
|
||||
srcs = [
|
||||
"SNTAboutWindowController.h",
|
||||
"SNTAboutWindowController.m",
|
||||
"SNTAccessibleTextField.h",
|
||||
"SNTAccessibleTextField.m",
|
||||
"SNTAppDelegate.h",
|
||||
"SNTAppDelegate.m",
|
||||
"SNTMessageWindow.h",
|
||||
"SNTMessageWindow.m",
|
||||
"SNTMessageWindowController.h",
|
||||
"SNTMessageWindowController.m",
|
||||
"SNTNotificationManager.h",
|
||||
"SNTNotificationManager.m",
|
||||
"main.m",
|
||||
],
|
||||
data = [
|
||||
"Resources/AboutWindow.xib",
|
||||
"Resources/MessageWindow.xib",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"IOKit",
|
||||
"SecurityInterface",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SNTBlockMessage_SantaGUI",
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"@MOLCodesignChecker",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
macos_application(
|
||||
name = "SantaGUI",
|
||||
app_icons = glob(["Resources/Images.xcassets/**"]),
|
||||
bundle_id = "com.google.SantaGUI",
|
||||
bundle_name = "Santa",
|
||||
infoplists = ["Resources/SantaGUI-Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":SantaGUI_lib"],
|
||||
)
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTAboutWindowController">
|
||||
@@ -13,10 +13,10 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="1577"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@@ -29,12 +29,13 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="VVj-gU-bzy">
|
||||
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
|
||||
<color key="textColor" red="0.1869618941" green="0.1869618941" blue="0.1869618941" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uh6-q0-RzL">
|
||||
<rect key="frame" x="18" y="65" width="444" height="60"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="title">Santa is an application whitelisting system for macOS.
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
<capability name="system font weights other than Regular or Bold" minToolsVersion="7.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
|
||||
@@ -20,10 +18,10 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Santa Blocked Execution" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
|
||||
<window title="Santa Blocked Execution" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
|
||||
<windowStyleMask key="styleMask" utility="YES"/>
|
||||
<rect key="contentRect" x="167" y="107" width="540" height="479"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="1578"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="1577"/>
|
||||
<view key="contentView" id="Iwq-Lx-rLv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="540" height="479"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@@ -42,14 +40,14 @@
|
||||
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="vl5-A8-O0H"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR" customClass="SNTAccessibleTextField">
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="43" y="369" width="454" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" refusesFirstResponder="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -92,7 +90,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Name" id="E7T-9h-ofr">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -117,7 +115,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -168,7 +166,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="SHA-256" id="X4W-9e-eIu">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
@@ -214,7 +212,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -242,7 +240,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
|
||||
<rect key="frame" x="154" y="33" width="112" height="25"/>
|
||||
<rect key="frame" x="154" y="34" width="112" height="23"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
|
||||
</constraints>
|
||||
@@ -266,7 +264,7 @@ DQ
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="H1b-Ui-CYo">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -287,7 +285,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL" userLabel="Dismiss Button">
|
||||
<rect key="frame" x="278" y="33" width="110" height="25"/>
|
||||
<rect key="frame" x="278" y="34" width="110" height="23"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
|
||||
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
|
||||
@@ -313,7 +311,7 @@ DQ
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -337,7 +335,7 @@ DQ
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="Calculating..." id="yJa-yL-X9a">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
@@ -354,7 +352,7 @@ DQ
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1000 related binaries" id="AVM-vB-hB8">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -374,7 +372,7 @@ DQ
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
@@ -410,8 +408,8 @@ DQ
|
||||
<rect key="frame" x="229" y="408" width="82" height="41"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" refusesFirstResponder="YES" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
|
||||
<font key="font" metaFont="systemUltraLight" size="34"/>
|
||||
<color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
|
||||
@@ -9,20 +9,24 @@
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google, Inc.</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.${PRODUCT_NAME:rfc1034identifier}GUI</string>
|
||||
<string>com.google.SantaGUI</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>Santa</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<string>Santa</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<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>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTAboutWindowController.h"
|
||||
#import "Source/SantaGUI/SNTAboutWindowController.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
|
||||
@implementation SNTAboutWindowController
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTAccessibleTextField.h"
|
||||
#import "Source/SantaGUI/SNTAccessibleTextField.h"
|
||||
|
||||
@implementation SNTAccessibleTextField
|
||||
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTAppDelegate.h"
|
||||
#import "Source/SantaGUI/SNTAppDelegate.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTAboutWindowController.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTNotificationManager.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/SantaGUI/SNTAboutWindowController.h"
|
||||
#import "Source/SantaGUI/SNTNotificationManager.h"
|
||||
|
||||
@interface SNTAppDelegate ()
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@@ -79,7 +79,7 @@
|
||||
// Create listener for return connection from daemon.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.daemonListener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.daemonListener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.daemonListener.privilegedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.daemonListener.exportedObject = self.notificationManager;
|
||||
self.daemonListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
@@ -116,7 +116,7 @@
|
||||
// Create listener for return connection from the bundle service.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.bundleListener = [[MOLXPCConnection alloc] initServerWithListener:listener];
|
||||
self.bundleListener.exportedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
self.bundleListener.privilegedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
self.bundleListener.exportedObject = self.notificationManager;
|
||||
self.bundleListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTMessageWindow.h"
|
||||
#import "Source/SantaGUI/SNTMessageWindow.h"
|
||||
|
||||
@implementation SNTMessageWindow
|
||||
|
||||
|
||||
@@ -12,16 +12,15 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTMessageWindowController.h"
|
||||
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
#import "Source/SantaGUI/SNTMessageWindowController.h"
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
|
||||
#import "SNTBlockMessage.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTMessageWindow.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTBlockMessage.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/SantaGUI/SNTMessageWindow.h"
|
||||
|
||||
@interface SNTMessageWindowController ()
|
||||
/// The custom message to display for this event
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "SNTMessageWindowController.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
#import "Source/common/SNTXPCNotifierInterface.h"
|
||||
#import "Source/SantaGUI/SNTMessageWindowController.h"
|
||||
|
||||
///
|
||||
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.
|
||||
|
||||
@@ -12,16 +12,16 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTNotificationManager.h"
|
||||
#import "Source/SantaGUI/SNTNotificationManager.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTBlockMessage.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "Source/common/SNTBlockMessage.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTNotificationManager ()
|
||||
|
||||
@@ -193,6 +193,10 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
}
|
||||
|
||||
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener {
|
||||
// Ensure any existing listener is invalidated.
|
||||
self.bundleServiceConnection.invalidationHandler = nil;
|
||||
[self.bundleServiceConnection invalidate];
|
||||
|
||||
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithListener:listener];
|
||||
c.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
[c resume];
|
||||
@@ -204,8 +208,6 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
if (self.currentWindowController) {
|
||||
[self updateBlockNotification:self.currentWindowController.event withBundleHash:nil];
|
||||
}
|
||||
self.bundleServiceConnection.invalidationHandler = nil;
|
||||
[self.bundleServiceConnection invalidate];
|
||||
};
|
||||
|
||||
dispatch_semaphore_signal(self.bundleServiceSema);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "SNTAppDelegate.h"
|
||||
#import "Source/SantaGUI/SNTAppDelegate.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@autoreleasepool {
|
||||
|
||||
181
Source/common/BUILD
Normal file
181
Source/common/BUILD
Normal file
@@ -0,0 +1,181 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load("//:helper.bzl", "santa_unit_test")
|
||||
|
||||
objc_library(
|
||||
name = "SNTBlockMessage",
|
||||
srcs = ["SNTBlockMessage.m"],
|
||||
hdrs = ["SNTBlockMessage.h"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTBlockMessage_SantaGUI",
|
||||
srcs = ["SNTBlockMessage.m"],
|
||||
hdrs = ["SNTBlockMessage.h"],
|
||||
deps = [
|
||||
":SNTConfigurator",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
defines = ["SANTAGUI"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTCachedDecision",
|
||||
srcs = ["SNTCachedDecision.m"],
|
||||
hdrs = ["SNTCachedDecision.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTKernelCommon",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "SNTCommonEnums",
|
||||
hdrs = ["SNTCommonEnums.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTConfigurator",
|
||||
srcs = ["SNTConfigurator.m"],
|
||||
hdrs = ["SNTConfigurator.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTLogging",
|
||||
":SNTStrengthify",
|
||||
":SNTSystemInfo",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTDropRootPrivs",
|
||||
srcs = ["SNTDropRootPrivs.m"],
|
||||
hdrs = ["SNTDropRootPrivs.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTFileInfo",
|
||||
srcs = ["SNTFileInfo.m"],
|
||||
hdrs = ["SNTFileInfo.h"],
|
||||
deps = [
|
||||
"@FMDB",
|
||||
"@MOLCodesignChecker",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "SNTKernelCommon",
|
||||
hdrs = ["SNTKernelCommon.h"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "SNTLoggingKernel",
|
||||
hdrs = ["SNTLogging.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTLogging",
|
||||
srcs = ["SNTLogging.m"],
|
||||
hdrs = ["SNTLogging.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTRule",
|
||||
srcs = ["SNTRule.m"],
|
||||
hdrs = ["SNTRule.h"],
|
||||
deps = [":SNTCommonEnums"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTStoredEvent",
|
||||
srcs = ["SNTStoredEvent.m"],
|
||||
hdrs = ["SNTStoredEvent.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
"@MOLCertificate",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "SNTStrengthify",
|
||||
hdrs = ["SNTStrengthify.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTSystemInfo",
|
||||
srcs = ["SNTSystemInfo.m"],
|
||||
hdrs = ["SNTSystemInfo.h"],
|
||||
sdk_frameworks = ["IOKit"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCBundleServiceInterface",
|
||||
srcs = ["SNTXPCBundleServiceInterface.m"],
|
||||
hdrs = ["SNTXPCBundleServiceInterface.h"],
|
||||
deps = [":SNTStoredEvent"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCControlInterface",
|
||||
srcs = ["SNTXPCControlInterface.m"],
|
||||
hdrs = ["SNTXPCControlInterface.h"],
|
||||
deps = [
|
||||
":SNTStoredEvent",
|
||||
":SNTXPCUnprivilegedControlInterface",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCNotifierInterface",
|
||||
srcs = ["SNTXPCNotifierInterface.m"],
|
||||
hdrs = ["SNTXPCNotifierInterface.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTXPCBundleServiceInterface",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCSyncdInterface",
|
||||
srcs = ["SNTXPCSyncdInterface.m"],
|
||||
hdrs = ["SNTXPCSyncdInterface.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTStoredEvent",
|
||||
],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SNTXPCUnprivilegedControlInterface",
|
||||
srcs = ["SNTXPCUnprivilegedControlInterface.m"],
|
||||
hdrs = ["SNTXPCUnprivilegedControlInterface.h"],
|
||||
deps = [
|
||||
":SNTCommonEnums",
|
||||
":SNTKernelCommon",
|
||||
":SNTRule",
|
||||
":SNTStoredEvent",
|
||||
":SNTXPCBundleServiceInterface",
|
||||
"@MOLCertificate",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SNTFileInfoTest",
|
||||
srcs = ["SNTFileInfoTest.m"],
|
||||
resources = [
|
||||
"testdata/bad_pagezero",
|
||||
"testdata/missing_pagezero",
|
||||
],
|
||||
structured_resources = glob([
|
||||
"testdata/BundleExample.app/**",
|
||||
"testdata/DirectoryBundle/**",
|
||||
]),
|
||||
deps = [":SNTFileInfo"],
|
||||
)
|
||||
@@ -12,11 +12,11 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTBlockMessage.h"
|
||||
#import "Source/common/SNTBlockMessage.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTBlockMessage
|
||||
|
||||
@@ -26,10 +26,24 @@
|
||||
@"body {"
|
||||
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
|
||||
@" font-size: 13px;"
|
||||
@" color: #666;"
|
||||
@" color: %@;"
|
||||
@" text-align: center;"
|
||||
@"}"
|
||||
|
||||
// Supported in beta WebKit. Not sure if it is dynamic when used with NSAttributedString.
|
||||
@"@media (prefers-color-scheme: dark) {"
|
||||
@" body {"
|
||||
@" color: #ddd;"
|
||||
@" }"
|
||||
@"}"
|
||||
@"</style></head><body>";
|
||||
|
||||
// Support Dark Mode. Note, the returned NSAttributedString is static and does not update when
|
||||
// the OS switches modes.
|
||||
NSString *mode = [NSUserDefaults.standardUserDefaults stringForKey:@"AppleInterfaceStyle"];
|
||||
BOOL dark = [mode isEqualToString:@"Dark"];
|
||||
htmlHeader = [NSString stringWithFormat:htmlHeader, dark ? @"#ddd" : @"#333"];
|
||||
|
||||
NSString *htmlFooter = @"</body></html>";
|
||||
|
||||
NSString *message;
|
||||
@@ -51,7 +65,7 @@
|
||||
|
||||
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];
|
||||
|
||||
#ifdef NSAppKitVersionNumber10_0
|
||||
#ifdef SANTAGUI
|
||||
NSData *htmlData = [fullHTML dataUsingEncoding:NSUTF8StringEncoding];
|
||||
return [[NSAttributedString alloc] initWithHTML:htmlData documentAttributes:NULL];
|
||||
#else
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTKernelCommon.h"
|
||||
|
||||
///
|
||||
/// Store information about executions from decision making for later logging.
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "Source/common/SNTCachedDecision.h"
|
||||
|
||||
@implementation SNTCachedDecision
|
||||
@end
|
||||
@@ -33,6 +33,9 @@ typedef NS_ENUM(NSInteger, SNTRuleState) {
|
||||
SNTRuleStateBlacklist = 2,
|
||||
SNTRuleStateSilentBlacklist = 3,
|
||||
SNTRuleStateRemove = 4,
|
||||
|
||||
SNTRuleStateWhitelistCompiler = 5,
|
||||
SNTRuleStateWhitelistTransitive = 6,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SNTClientMode) {
|
||||
@@ -58,6 +61,9 @@ typedef NS_ENUM(NSInteger, SNTEventState) {
|
||||
SNTEventStateAllowBinary = 1 << 25,
|
||||
SNTEventStateAllowCertificate = 1 << 26,
|
||||
SNTEventStateAllowScope = 1 << 27,
|
||||
SNTEventStateAllowCompiler = 1 << 28,
|
||||
SNTEventStateAllowTransitive = 1 << 29,
|
||||
SNTEventStateAllowPendingTransitive = 1 << 30,
|
||||
|
||||
// Block and Allow masks
|
||||
SNTEventStateBlock = 0xFF << 16,
|
||||
@@ -89,3 +95,4 @@ 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";
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
/// Singleton that provides an interface for managing configuration values on disk
|
||||
@@ -72,6 +72,57 @@
|
||||
///
|
||||
@property(readonly, nonatomic) NSRegularExpression *fileChangesRegex;
|
||||
|
||||
///
|
||||
/// A list of ignore prefixes which are checked in-kernel.
|
||||
/// This is more performant than FileChangesRegex when ignoring whole directory trees.
|
||||
///
|
||||
/// For example adding a prefix of "/private/tmp/" will turn off file change log generation
|
||||
/// in-kernel for that entire tree. Since they are ignored by the kernel, they never reach santad
|
||||
/// and are not seen by the fileChangesRegex. Note the trailing "/", without it any file or
|
||||
/// directory starting with "/private/tmp" would be ignored.
|
||||
///
|
||||
/// By default "/." and "/dev/" are added.
|
||||
///
|
||||
/// Memory in the kernel is precious. A total of MAXPATHLEN (1024) nodes are allowed.
|
||||
/// Using all 1024 nodes will result in santa-driver allocating ~2MB of wired memory.
|
||||
/// An ASCII character uses 1 node. An UTF-8 encoded Unicode character uses 1-4 nodes.
|
||||
/// Prefixes are added to the running config in-order, one by one. The prefix will be ignored if
|
||||
/// (the running config's current size) + (the prefix's size) totals up to more than 1024 nodes.
|
||||
/// The running config is stored in a prefix tree.
|
||||
/// Prefixes that share prefixes are effectively de-duped; their shared node sized components only
|
||||
/// take up 1 node. For example these 3 prefixes all have a common prefix of "/private/".
|
||||
/// They will only take up 21 nodes instead of 39.
|
||||
///
|
||||
/// "/private/tmp/"
|
||||
/// "/private/var/"
|
||||
/// "/private/new/"
|
||||
///
|
||||
/// -> [t] -> [m] -> [p] -> [/]
|
||||
///
|
||||
/// [/] -> [p] -> [r] -> [i] -> [v] -> [a] -> [t] -> [e] -> [/] -> [v] -> [a] -> [r] -> [/]
|
||||
///
|
||||
/// -> [n] -> [e] -> [w] -> [/]
|
||||
///
|
||||
/// Prefixes with Unicode characters work similarly. Assuming a UTF-8 encoding these two prefixes
|
||||
/// are actually the same for the first 3 nodes. They take up 7 nodes instead of 10.
|
||||
///
|
||||
/// "/🤘"
|
||||
/// "/🖖"
|
||||
///
|
||||
/// -> [0xa4] -> [0x98]
|
||||
///
|
||||
/// [/] -> [0xf0] -> [0x9f]
|
||||
///
|
||||
/// -> [0x96] -> [0x96]
|
||||
///
|
||||
/// To disable file change logging completely add "/".
|
||||
/// TODO(bur): Make this default if no FileChangesRegex is set.
|
||||
///
|
||||
/// Filters are only applied on santad startup.
|
||||
/// TODO(bur): Support add / remove of filters while santad is running.
|
||||
///
|
||||
@property(readonly, nonatomic) NSArray *fileChangesPrefixFilters;
|
||||
|
||||
///
|
||||
/// Enable __PAGEZERO protection, defaults to YES
|
||||
/// If this flag is set to NO, 32-bit binaries that are missing
|
||||
@@ -196,7 +247,16 @@
|
||||
/// If YES, enables bundle detection for blocked events. This property is not stored on disk.
|
||||
/// Its value is set by a sync server that supports bundles. Defaults to NO.
|
||||
///
|
||||
@property BOOL bundlesEnabled;
|
||||
@property BOOL enableBundles;
|
||||
|
||||
#pragma mark Transitive Whitelisting Settings
|
||||
|
||||
///
|
||||
/// If YES, binaries marked with SNTRuleStateWhitelistCompiler rules are allowed to transitively
|
||||
/// whitelist any executables that they produce. If NO, SNTRuleStateWhitelistCompiler rules are
|
||||
/// interpreted as if they were simply SNTRuleStateWhitelist rules. Defaults to NO.
|
||||
///
|
||||
@property BOOL enableTransitiveWhitelisting;
|
||||
|
||||
#pragma mark Server Auth Settings
|
||||
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTSystemInfo.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStrengthify.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@interface SNTConfigurator ()
|
||||
/// A NSUserDefaults object set to use the com.google.santa suite.
|
||||
@@ -68,6 +68,7 @@ static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
|
||||
static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
|
||||
|
||||
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
|
||||
static NSString *const kFileChangesPrefixFiltersKey = @"FileChangesPrefixFilters";
|
||||
|
||||
static NSString *const kEventLogType = @"EventLogType";
|
||||
static NSString *const kEventLogPath = @"EventLogPath";
|
||||
@@ -76,6 +77,7 @@ static NSString *const kEnableMachineIDDecoration = @"EnableMachineIDDecoration"
|
||||
|
||||
// The keys managed by a sync server or mobileconfig.
|
||||
static NSString *const kClientModeKey = @"ClientMode";
|
||||
static NSString *const kEnableTransitiveWhitelistingKey = @"EnableTransitiveWhitelisting";
|
||||
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
|
||||
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
|
||||
|
||||
@@ -92,8 +94,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
Class date = [NSDate class];
|
||||
Class string = [NSString class];
|
||||
Class data = [NSData class];
|
||||
Class array = [NSArray class];
|
||||
_syncServerKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
kFullSyncLastSuccess : date,
|
||||
@@ -102,7 +106,9 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
};
|
||||
_forcedConfigKeyTypes = @{
|
||||
kClientModeKey : number,
|
||||
kEnableTransitiveWhitelistingKey : number,
|
||||
kFileChangesRegexKey : re,
|
||||
kFileChangesPrefixFiltersKey : array,
|
||||
kWhitelistRegexKey : re,
|
||||
kBlacklistRegexKey : re,
|
||||
kEnablePageZeroProtectionKey : number,
|
||||
@@ -195,6 +201,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFileChangesPrefixFiltersKey {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
|
||||
return [self configStateSet];
|
||||
}
|
||||
@@ -287,6 +297,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableTransitiveWhitelisting {
|
||||
return [self syncAndConfigStateSet];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
@@ -311,6 +325,18 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)enableTransitiveWhitelisting {
|
||||
NSNumber *n = self.syncState[kEnableTransitiveWhitelistingKey];
|
||||
if (n) {
|
||||
return [n boolValue];
|
||||
}
|
||||
return [self.configState[kEnableTransitiveWhitelistingKey] boolValue];
|
||||
}
|
||||
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled {
|
||||
[self updateSyncStateForKey:kEnableTransitiveWhitelistingKey value:@(enabled)];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)whitelistPathRegex {
|
||||
return self.syncState[kWhitelistRegexKey] ?: self.configState[kWhitelistRegexKey];
|
||||
}
|
||||
@@ -331,8 +357,20 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return self.configState[kFileChangesRegexKey];
|
||||
}
|
||||
|
||||
- (NSArray *)fileChangesPrefixFilters {
|
||||
NSArray *filters = self.configState[kFileChangesPrefixFiltersKey];
|
||||
for (id filter in filters) {
|
||||
if (![filter isKindOfClass:[NSString class]]) {
|
||||
LOGE(@"Ignoring FileChangesPrefixFilters: array contains a non-string %@", filter);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
- (NSURL *)syncBaseURL {
|
||||
NSString *urlString = self.configState[kSyncBaseURLKey];
|
||||
if (![urlString hasSuffix:@"/"]) urlString = [urlString stringByAppendingString:@"/"];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
if (urlString && !url) LOGW(@"SyncBaseURL is not a valid URL!");
|
||||
return url;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTDropRootPrivs.h"
|
||||
#import "Source/common/SNTDropRootPrivs.h"
|
||||
|
||||
BOOL DropRootPrivileges() {
|
||||
if (getuid() == 0 || geteuid() == 0 || getgid() == 0 || getegid() == 0) {
|
||||
|
||||
@@ -12,9 +12,10 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTFileInfo.h"
|
||||
#import "Source/common/SNTFileInfo.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <fmdb/FMDB.h>
|
||||
#import <MOLCodesignChecker/MOLCodesignChecker.h>
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
@@ -24,7 +25,6 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#import <fmdb/FMDB.h>
|
||||
|
||||
// Simple class to hold the data of a mach_header and the offset within the file
|
||||
// in which that header was found.
|
||||
|
||||
@@ -14,13 +14,23 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "SNTFileInfo.h"
|
||||
#import "Source/common/SNTFileInfo.h"
|
||||
|
||||
@interface SNTFileInfoTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation SNTFileInfoTest
|
||||
|
||||
- (NSString *)directoryBundle {
|
||||
NSString *rp = [[NSBundle bundleForClass:[self class]] resourcePath];
|
||||
return [rp stringByAppendingPathComponent:@"testdata/DirectoryBundle"];
|
||||
}
|
||||
|
||||
- (NSString *)bundleExample {
|
||||
NSString *rp = [[NSBundle bundleForClass:[self class]] resourcePath];
|
||||
return [rp stringByAppendingPathComponent:@"testdata/BundleExample.app"];
|
||||
}
|
||||
|
||||
- (void)testPathStandardizing {
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/Applications/Safari.app"];
|
||||
XCTAssertNotNil(sut);
|
||||
@@ -120,8 +130,7 @@
|
||||
}
|
||||
|
||||
- (void)testBundle {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
|
||||
ofType:@"app"];
|
||||
NSString *path = [self bundleExample];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
|
||||
XCTAssertNotNil([sut bundle]);
|
||||
@@ -134,19 +143,18 @@
|
||||
}
|
||||
|
||||
- (void)testAncestorBundle {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
|
||||
ofType:@"app"];
|
||||
NSString *path = [self bundleExample];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
sut.useAncestorBundle = YES;
|
||||
|
||||
XCTAssertNotNil([sut bundle]);
|
||||
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
|
||||
XCTAssertNotNil([sut bundleVersion]);
|
||||
XCTAssertNotNil([sut bundleShortVersionString]);
|
||||
|
||||
NSString *ancestorBundlePath = path;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
|
||||
}
|
||||
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
|
||||
@@ -159,15 +167,14 @@
|
||||
|
||||
XCTAssertNotNil([sut bundle]);
|
||||
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
|
||||
XCTAssertNotNil([sut bundleVersion]);
|
||||
XCTAssertNotNil([sut bundleShortVersionString]);
|
||||
XCTAssertEqualObjects([sut bundlePath], path);
|
||||
}
|
||||
|
||||
- (void)testDirectoryBundleIsNotAncestor {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"DirectoryBundle"
|
||||
ofType:@""];
|
||||
NSString *path = [self directoryBundle];
|
||||
NSString *directoryBundle = @"/tmp/DirectoryBundle";
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
[fm removeItemAtPath:directoryBundle error:NULL];
|
||||
@@ -186,8 +193,7 @@
|
||||
}
|
||||
|
||||
- (void)testBundleCacheReset {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"BundleExample"
|
||||
ofType:@"app"];
|
||||
NSString *path = [self bundleExample];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
|
||||
XCTAssertNotNil([sut bundle]);
|
||||
@@ -202,12 +208,12 @@
|
||||
|
||||
XCTAssertNotNil([sut bundle]);
|
||||
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.LogicTests");
|
||||
XCTAssertEqualObjects([sut bundleIdentifier], @"com.google.santa.UnitTest.SNTFileInfoTest");
|
||||
XCTAssertNotNil([sut bundleVersion]);
|
||||
XCTAssertNotNil([sut bundleShortVersionString]);
|
||||
|
||||
NSString *ancestorBundlePath = path;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ancestorBundlePath = [ancestorBundlePath stringByDeletingLastPathComponent];
|
||||
}
|
||||
XCTAssertEqualObjects([sut bundlePath], ancestorBundlePath);
|
||||
@@ -33,12 +33,16 @@
|
||||
enum SantaDriverMethods {
|
||||
kSantaUserClientOpen,
|
||||
kSantaUserClientAllowBinary,
|
||||
kSantaUserClientAllowCompiler,
|
||||
kSantaUserClientDenyBinary,
|
||||
kSantaUserClientAcknowledgeBinary,
|
||||
kSantaUserClientClearCache,
|
||||
kSantaUserClientRemoveCacheEntry,
|
||||
kSantaUserClientCacheCount,
|
||||
kSantaUserClientCheckCache,
|
||||
kSantaUserClientCacheBucketCount,
|
||||
kSantaUserClientFilemodPrefixFilterAdd,
|
||||
kSantaUserClientFilemodPrefixFilterReset,
|
||||
|
||||
// Any methods supported by the driver should be added above this line to
|
||||
// ensure this remains the count of methods.
|
||||
@@ -47,7 +51,7 @@ enum SantaDriverMethods {
|
||||
|
||||
typedef enum {
|
||||
QUEUETYPE_DECISION,
|
||||
QUEUETYPE_LOG
|
||||
QUEUETYPE_LOG,
|
||||
} santa_queuetype_t;
|
||||
|
||||
// Enum defining actions that can be passed down the IODataQueue and in
|
||||
@@ -64,6 +68,10 @@ typedef enum {
|
||||
ACTION_RESPOND_DENY = 21,
|
||||
ACTION_RESPOND_TOOLONG = 22,
|
||||
ACTION_RESPOND_ACK = 23,
|
||||
ACTION_RESPOND_ALLOW_COMPILER = 24,
|
||||
// The following response is stored only in the kernel decision cache.
|
||||
// It is removed by SNTCompilerController
|
||||
ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE = 25,
|
||||
|
||||
// NOTIFY
|
||||
ACTION_NOTIFY_EXEC = 30,
|
||||
@@ -72,13 +80,17 @@ typedef enum {
|
||||
ACTION_NOTIFY_LINK = 33,
|
||||
ACTION_NOTIFY_EXCHANGE = 34,
|
||||
ACTION_NOTIFY_DELETE = 35,
|
||||
ACTION_NOTIFY_WHITELIST = 36,
|
||||
|
||||
// ERROR
|
||||
ACTION_ERROR = 99,
|
||||
} santa_action_t;
|
||||
|
||||
#define RESPONSE_VALID(x) \
|
||||
(x == ACTION_RESPOND_ALLOW || x == ACTION_RESPOND_DENY)
|
||||
(x == ACTION_RESPOND_ALLOW || \
|
||||
x == ACTION_RESPOND_DENY || \
|
||||
x == ACTION_RESPOND_ALLOW_COMPILER || \
|
||||
x == ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE)
|
||||
|
||||
// Struct to manage vnode IDs
|
||||
typedef struct santa_vnode_id_t {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTLogging.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
|
||||
#import <asl.h>
|
||||
#import <pthread.h>
|
||||
@@ -24,7 +24,10 @@ static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
|
||||
#endif
|
||||
|
||||
void syslogClientDestructor(void *arg) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
asl_close((aslclient)arg);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
@@ -58,8 +61,11 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
if (useSyslog) {
|
||||
aslclient client = (aslclient)pthread_getspecific(syslogKey);
|
||||
if (client == NULL) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
client = asl_open(NULL, "com.google.santa", 0);
|
||||
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
|
||||
#pragma clang diagnostic pop
|
||||
pthread_setspecific(syslogKey, client);
|
||||
}
|
||||
|
||||
@@ -84,7 +90,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
break;
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName.UTF8String, s.UTF8String);
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
[s appendString:@"\n"];
|
||||
size_t len = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
/// Represents a Rule.
|
||||
@@ -41,12 +41,32 @@
|
||||
///
|
||||
@property(copy) NSString *customMsg;
|
||||
|
||||
///
|
||||
/// The time when this rule was last retrieved from the rules database, if rule is transitive.
|
||||
/// Stored as number of seconds since 00:00:00 UTC on 1 January 2001.
|
||||
///
|
||||
@property(readonly) NSUInteger timestamp;
|
||||
|
||||
///
|
||||
/// Designated initializer.
|
||||
///
|
||||
- (instancetype)initWithShasum:(NSString *)shasum
|
||||
state:(SNTRuleState)state
|
||||
type:(SNTRuleType)type
|
||||
customMsg:(NSString *)customMsg
|
||||
timestamp:(NSUInteger)timestamp;
|
||||
|
||||
///
|
||||
/// Initialize with a default timestamp: current time if rule state is transitive, 0 otherwise.
|
||||
///
|
||||
- (instancetype)initWithShasum:(NSString *)shasum
|
||||
state:(SNTRuleState)state
|
||||
type:(SNTRuleType)type
|
||||
customMsg:(NSString *)customMsg;
|
||||
|
||||
///
|
||||
/// Sets timestamp of rule to the current time.
|
||||
///
|
||||
- (void)resetTimestamp;
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,24 +12,47 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTRule.h"
|
||||
#import "Source/common/SNTRule.h"
|
||||
|
||||
@interface SNTRule()
|
||||
@property(readwrite) NSUInteger timestamp;
|
||||
@end
|
||||
|
||||
@implementation SNTRule
|
||||
|
||||
- (instancetype)initWithShasum:(NSString *)shasum
|
||||
state:(SNTRuleState)state
|
||||
type:(SNTRuleType)type
|
||||
customMsg:(NSString *)customMsg {
|
||||
customMsg:(NSString *)customMsg
|
||||
timestamp:(NSUInteger)timestamp {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_shasum = shasum;
|
||||
_state = state;
|
||||
_type = type;
|
||||
_customMsg = customMsg;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithShasum:(NSString *)shasum
|
||||
state:(SNTRuleState)state
|
||||
type:(SNTRuleType)type
|
||||
customMsg:(NSString *)customMsg {
|
||||
self = [self initWithShasum:shasum
|
||||
state:state
|
||||
type:type
|
||||
customMsg:customMsg
|
||||
timestamp:0];
|
||||
// Initialize timestamp to current time if rule is transitive.
|
||||
if (self && state == SNTRuleStateWhitelistTransitive) {
|
||||
[self resetTimestamp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
#pragma clang diagnostic push
|
||||
@@ -46,6 +69,7 @@
|
||||
ENCODE(@(self.state), @"state");
|
||||
ENCODE(@(self.type), @"type");
|
||||
ENCODE(self.customMsg, @"custommsg");
|
||||
ENCODE(@(self.timestamp), @"timestamp");
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
@@ -55,6 +79,7 @@
|
||||
_state = [DECODE(NSNumber, @"state") intValue];
|
||||
_type = [DECODE(NSNumber, @"type") intValue];
|
||||
_customMsg = DECODE(NSString, @"custommsg");
|
||||
_timestamp = [DECODE(NSNumber, @"timestamp") unsignedIntegerValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -80,8 +105,14 @@
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %ld, Type: %ld",
|
||||
self.shasum, self.state, self.type];
|
||||
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %ld, Type: %ld, Timestamp: %lu",
|
||||
self.shasum, self.state, self.type, (unsigned long)self.timestamp];
|
||||
}
|
||||
|
||||
# pragma mark Last-access Timestamp
|
||||
|
||||
- (void)resetTimestamp {
|
||||
self.timestamp = (NSUInteger)[[NSDate date] timeIntervalSinceReferenceDate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
/// Represents an event stored in the database.
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTSystemInfo.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
|
||||
@implementation SNTSystemInfo
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
#import "Source/common/SNTXPCBundleServiceInterface.h"
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCBundleServiceInterface
|
||||
|
||||
|
||||
@@ -12,120 +12,57 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
@class MOLXPCConnection;
|
||||
#import "Source/common/SNTXPCUnprivilegedControlInterface.h"
|
||||
|
||||
///
|
||||
/// Protocol implemented by santad and utilized by santactl
|
||||
/// Protocol implemented by santad and utilized by santactl (privileged operations)
|
||||
///
|
||||
@protocol SNTDaemonControlXPC
|
||||
@protocol SNTDaemonControlXPC <SNTUnprivilegedDaemonControlXPC>
|
||||
|
||||
///
|
||||
/// Kernel ops
|
||||
///
|
||||
- (void)cacheCounts:(void (^)(uint64_t count))reply;
|
||||
- (void)flushCache:(void (^)(BOOL))reply;
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
|
||||
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// Database ops
|
||||
///
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
|
||||
- (void)databaseRuleAddRules:(NSArray *)rules
|
||||
cleanSlate:(BOOL)cleanSlate
|
||||
reply:(void (^)(NSError *error))reply;
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply;
|
||||
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
|
||||
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
|
||||
- (void)databaseRuleForBinarySHA256:(NSString *)binarySHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTRule *))reply;
|
||||
///
|
||||
/// Decision ops
|
||||
///
|
||||
|
||||
///
|
||||
/// @param filePath A Path to the file, can be nil.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
///
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply;
|
||||
|
||||
///
|
||||
/// Config ops
|
||||
///
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply;
|
||||
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)(void))reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)(void))reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)(void))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)(void))reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)(void))reply;
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply;
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setEnableBundles:(BOOL)bundlesEnabled reply:(void (^)(void))reply;
|
||||
- (void)setEnableTransitiveWhitelisting:(BOOL)enabled reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)(void))reply;
|
||||
|
||||
///
|
||||
/// Bundle Ops
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
|
||||
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCControlInterface : NSObject
|
||||
@interface SNTXPCControlInterface : SNTXPCUnprivilegedControlInterface
|
||||
|
||||
///
|
||||
/// Returns the MachService ID for this service.
|
||||
/// Internal method used to initialize the control interface
|
||||
///
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
///
|
||||
/// Returns an initialized NSXPCInterface for the SNTDaemonControlXPC 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;
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r;
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "SNTRule.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCControlInterface
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
return @"SantaXPCControl";
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
|
||||
+ (void)initializeControlInterface:(NSXPCInterface *)r {
|
||||
[super initializeControlInterface:r];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(databaseEventsPending:)
|
||||
@@ -37,16 +37,11 @@
|
||||
forSelector:@selector(databaseRuleAddRules:cleanSlate:reply:)
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
}
|
||||
|
||||
[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
|
||||
ofReply:NO];
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
|
||||
[self initializeControlInterface:r];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
#import "Source/common/SNTXPCNotifierInterface.h"
|
||||
|
||||
@implementation SNTXPCNotifierInterface
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
#import "Source/common/SNTXPCSyncdInterface.h"
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCSyncdInterface
|
||||
|
||||
|
||||
121
Source/common/SNTXPCUnprivilegedControlInterface.h
Normal file
121
Source/common/SNTXPCUnprivilegedControlInterface.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/// 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 <Foundation/Foundation.h>
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTKernelCommon.h"
|
||||
#import "Source/common/SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
@class MOLXPCConnection;
|
||||
|
||||
///
|
||||
/// Protocol implemented by santad and utilized by santactl (unprivileged operations)
|
||||
///
|
||||
@protocol SNTUnprivilegedDaemonControlXPC
|
||||
|
||||
///
|
||||
/// Kernel ops
|
||||
///
|
||||
- (void)cacheCounts:(void (^)(uint64_t rootCache, uint64_t nonRootCache))reply;
|
||||
- (void)cacheBucketCount:(void (^)(NSArray *))reply;
|
||||
- (void)checkCacheForVnodeID:(santa_vnode_id_t)vnodeID withReply:(void (^)(santa_action_t))reply;
|
||||
- (void)driverConnectionEstablished:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// Database ops
|
||||
///
|
||||
- (void)databaseRuleCounts:(void (^)(int64_t binary,
|
||||
int64_t certificate,
|
||||
int64_t compiler,
|
||||
int64_t transitive))reply;
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply;
|
||||
|
||||
///
|
||||
/// Decision ops
|
||||
///
|
||||
|
||||
///
|
||||
/// @param filePath A Path to the file, can be nil.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
///
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply;
|
||||
|
||||
///
|
||||
/// Config ops
|
||||
///
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)enableBundles:(void (^)(BOOL))reply;
|
||||
- (void)enableTransitiveWhitelisting:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
|
||||
///
|
||||
/// 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
|
||||
54
Source/common/SNTXPCUnprivilegedControlInterface.m
Normal file
54
Source/common/SNTXPCUnprivilegedControlInterface.m
Normal file
@@ -0,0 +1,54 @@
|
||||
/// 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 "Source/common/SNTXPCUnprivilegedControlInterface.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
|
||||
@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
|
||||
ofReply:NO];
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTUnprivilegedDaemonControlXPC)];
|
||||
[self initializeControlInterface:r];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
+ (MOLXPCConnection *)configuredConnection {
|
||||
MOLXPCConnection *c = [[MOLXPCConnection alloc] initClientWithName:[self serviceId]
|
||||
privileged:YES];
|
||||
c.remoteInterface = [self controlInterface];
|
||||
return c;
|
||||
}
|
||||
|
||||
@end
|
||||
142
Source/santa_driver/BUILD
Normal file
142
Source/santa_driver/BUILD
Normal file
@@ -0,0 +1,142 @@
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load(
|
||||
"@build_bazel_rules_apple//apple:macos.bzl",
|
||||
"macos_command_line_application",
|
||||
"macos_kernel_extension",
|
||||
)
|
||||
load("//:helper.bzl", "run_command", "santa_unit_test")
|
||||
load("//:version.bzl", "SANTA_VERSION")
|
||||
|
||||
cc_library(
|
||||
name = "santa_driver_lib",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaDecisionManager.cc",
|
||||
"SantaDecisionManager.h",
|
||||
"SantaDriver.cc",
|
||||
"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,
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SNTKernelCommon",
|
||||
"//Source/common:SNTLoggingKernel",
|
||||
],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SantaCacheTest",
|
||||
srcs = [
|
||||
"SantaCache.h",
|
||||
"SantaCacheTest.mm",
|
||||
],
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "SantaPrefixTree_userland_lib",
|
||||
srcs = ["SantaPrefixTree.cc"],
|
||||
hdrs = ["SantaPrefixTree.h"],
|
||||
copts = ["-std=c++1z"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
santa_unit_test(
|
||||
name = "SantaPrefixTreeTest",
|
||||
srcs = ["SantaPrefixTreeTest.mm"],
|
||||
copts = ["-std=c++1z"],
|
||||
minimum_os_version = "10.12",
|
||||
deps = [
|
||||
":SantaPrefixTree_userland_lib",
|
||||
"//Source/common:SNTKernelCommon"
|
||||
],
|
||||
)
|
||||
|
||||
# Full santa-driver.kext containing all Santa components
|
||||
macos_kernel_extension(
|
||||
name = "santa_driver",
|
||||
additional_contents = {
|
||||
"//Source/santabs": "XPCServices",
|
||||
"//Source/SantaGUI": "Resources",
|
||||
"//Source/santactl": "MacOS",
|
||||
"//Source/santad": "MacOS",
|
||||
},
|
||||
bundle_id = "com.google.santa-driver",
|
||||
bundle_name = "santa-driver",
|
||||
infoplists = ["Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
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"],
|
||||
sdk_frameworks = [
|
||||
"Foundation",
|
||||
"IOKit",
|
||||
],
|
||||
deps = ["//Source/common:SNTKernelCommon"],
|
||||
)
|
||||
|
||||
macos_command_line_application(
|
||||
name = "kernel_tests_bin",
|
||||
bundle_id = "com.google.santa.KernelTests",
|
||||
minimum_os_version = "10.9",
|
||||
deps = [":kernel_tests_lib"],
|
||||
)
|
||||
|
||||
run_command(
|
||||
name = "kernel_tests",
|
||||
srcs = [
|
||||
":kernel_tests_bin",
|
||||
":santa_driver_min",
|
||||
],
|
||||
cmd = """
|
||||
env
|
||||
function sigint() {
|
||||
echo "\nInterrupted, unloading driver."
|
||||
sudo kextunload -b com.google.santa-driver >/dev/null
|
||||
exit 1
|
||||
}
|
||||
unzip -o $${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa_driver/santa_driver.zip >/dev/null
|
||||
echo "Launching Kernel Tests as root. You may be prompted for your sudo password."
|
||||
trap sigint INT
|
||||
sudo $${BUILD_WORKSPACE_DIRECTORY}/bazel-bin/Source/santa_driver/kernel_tests_bin
|
||||
echo "Tests complete."
|
||||
if kextstat | grep com.google.santa-driver; then
|
||||
sudo kextunload -b com.google.santa-driver >/dev/null
|
||||
fi
|
||||
""",
|
||||
)
|
||||
@@ -9,15 +9,15 @@
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google, Inc.</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>com.google.santa-driver</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<string>santa-driver</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<string>santa-driver</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>TO.BE.FILLED</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>IOKitPersonalities</key>
|
||||
<dict>
|
||||
<key>SantaDriver</key>
|
||||
@@ -32,6 +32,8 @@
|
||||
<string>IOKit</string>
|
||||
<key>IOUserClientClass</key>
|
||||
<string>com_google_SantaDriverClient</string>
|
||||
<key>IOMatchCategory</key>
|
||||
<string>com_google_SantaDriver</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OSBundleLibraries</key>
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <IOKit/IOLib.h>
|
||||
@@ -29,10 +29,9 @@
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
// TODO(rah): Consider templatizing these.
|
||||
#define panic(args...) printf(args); printf("\n"); abort()
|
||||
#define IOMalloc malloc
|
||||
#define IOMallocAligned(sz, alignment) malloc(sz);
|
||||
#define IOFree(addr, sz) free(addr)
|
||||
#define IOFreeAligned(addr, sz) free(addr)
|
||||
#define OSTestAndSet OSAtomicTestAndSet
|
||||
#define OSTestAndClear(bit, addr) OSAtomicTestAndClear(bit, addr) == 0
|
||||
@@ -74,11 +73,12 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
Cannot be higher than 64 to try and ensure buckets don't overflow.
|
||||
*/
|
||||
SantaCache(uint64_t maximum_size = 10000, uint8_t per_bucket = 5) {
|
||||
if (unlikely(per_bucket > maximum_size)) per_bucket = maximum_size;
|
||||
if (unlikely(per_bucket < 1)) per_bucket = 1;
|
||||
if (unlikely(per_bucket > 64)) per_bucket = 64;
|
||||
max_size_ = maximum_size;
|
||||
bucket_count_ = 1 << (32 - __builtin_clz(((uint32_t)max_size_ / per_bucket) - 1));
|
||||
buckets_ = (struct bucket *)IOMalloc(bucket_count_ * sizeof(struct bucket));
|
||||
bucket_count_ = (1 << (32 - __builtin_clz((((uint32_t)max_size_ / per_bucket) - 1) ?: 1)));
|
||||
buckets_ = (struct bucket *)IOMallocAligned(bucket_count_ * sizeof(struct bucket), 2);
|
||||
bzero(buckets_, bucket_count_ * sizeof(struct bucket));
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
*/
|
||||
~SantaCache() {
|
||||
clear();
|
||||
IOFree(buckets_, bucket_count_ * sizeof(struct bucket));
|
||||
IOFreeAligned(buckets_, bucket_count_ * sizeof(struct bucket));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,12 +115,12 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key, The key.
|
||||
@param value, The value with parameterized type.
|
||||
@param key The key.
|
||||
@param value The value with parameterized type.
|
||||
|
||||
@return true if the value was set.
|
||||
*/
|
||||
bool set(KeyT key, ValueT value) {
|
||||
bool set(const KeyT& key, const ValueT& value) {
|
||||
return set(key, value, {}, false);
|
||||
}
|
||||
|
||||
@@ -130,22 +130,22 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key, The key.
|
||||
@param value, The value with parameterized type.
|
||||
@param previous_value, the new value will only be set if this
|
||||
@param key The key.
|
||||
@param value The value with parameterized type.
|
||||
@param previous_value the new value will only be set if this
|
||||
parameter is equal to the existing value in the cache.
|
||||
This allows set to become a CAS operation.
|
||||
|
||||
@return true if the value was set
|
||||
*/
|
||||
bool set(KeyT key, ValueT value, ValueT previous_value) {
|
||||
bool set(const KeyT& key, const ValueT& value, const ValueT& previous_value) {
|
||||
return set(key, value, previous_value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
An alias for `set(key, zero_)`
|
||||
*/
|
||||
inline void remove(KeyT key) {
|
||||
inline void remove(const KeyT& key) {
|
||||
set(key, zero_);
|
||||
}
|
||||
|
||||
@@ -236,16 +236,17 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
@note If the cache is full when this is called, this will
|
||||
empty the cache before inserting the new value.
|
||||
|
||||
@param key, The key
|
||||
@param value, The value with parameterized type
|
||||
@param previous_value, If has_prev_value is true, the new value will only
|
||||
@param key The key
|
||||
@param value The value with parameterized type
|
||||
@param previous_value If has_prev_value is true, the new value will only
|
||||
be set if this parameter is equal to the existing value in the cache.
|
||||
This allows set to become a CAS operation.
|
||||
@param has_prev_value, Pass true if previous_value should be used.
|
||||
@param has_prev_value Pass true if previous_value should be used.
|
||||
|
||||
@return true if the entry was set, false if it was not
|
||||
*/
|
||||
bool set(KeyT key, ValueT value, ValueT previous_value, bool has_prev_value) {
|
||||
bool set(const KeyT& key, const ValueT& value,
|
||||
const ValueT& previous_value, bool has_prev_value) {
|
||||
struct bucket *bucket = &buckets_[hash(key)];
|
||||
lock(bucket);
|
||||
struct entry *entry = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
@@ -302,6 +303,7 @@ template<typename KeyT, typename ValueT> class SantaCache {
|
||||
// Allocate a new entry, set the key and value, then put this new entry at
|
||||
// the head of this bucket's linked list.
|
||||
struct entry *new_entry = (struct entry *)IOMallocAligned(sizeof(struct entry), 2);
|
||||
bzero(new_entry, sizeof(struct entry));
|
||||
new_entry->key = key;
|
||||
new_entry->value = value;
|
||||
new_entry->next = (struct entry *)((uintptr_t)bucket->head - 1);
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "SantaCache.h"
|
||||
#include "Source/santa_driver/SantaCache.h"
|
||||
|
||||
@interface SantaCacheTest : XCTestCase
|
||||
@end
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
- (void)testCacheResetAtLimit {
|
||||
auto sut = SantaCache<uint64_t, uint64_t>(5);
|
||||
|
||||
|
||||
sut.set(1, 42);
|
||||
sut.set(2, 42);
|
||||
sut.set(3, 42);
|
||||
@@ -12,15 +12,23 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDecisionManager.h"
|
||||
#include "Source/santa_driver/SantaDecisionManager.h"
|
||||
|
||||
// This is a made-up KAUTH_FILEOP constant which represents a
|
||||
// KAUTH_VNODE_WRITE_DATA event that gets passed to SantaDecisionManager's
|
||||
// FileOpCallback method. The KAUTH_FILEOP_* constants are defined in
|
||||
// sys/kauth.h and run from 1--7. KAUTH_VNODE_WRITE_DATA is already defined as
|
||||
// 4 so it overlaps with the other KAUTH_FILEOP_* constants and can't be used.
|
||||
// We define KAUTH_FILEOP_WRITE as something much greater than 7.
|
||||
#define KAUTH_FILEOP_WRITE 100
|
||||
|
||||
#define super OSObject
|
||||
OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
|
||||
|
||||
#pragma mark Object Lifecycle
|
||||
|
||||
template<> uint64_t SantaCacheHasher<santa_vnode_id_t>(santa_vnode_id_t const& s) {
|
||||
return (SantaCacheHasher<uint64_t>(s.fsid) << 1) ^ SantaCacheHasher<uint64_t>(s.fileid);
|
||||
template<> uint64_t SantaCacheHasher<santa_vnode_id_t>(santa_vnode_id_t const& t) {
|
||||
return (SantaCacheHasher<uint64_t>(t.fsid) << 1) ^ SantaCacheHasher<uint64_t>(t.fileid);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::init() {
|
||||
@@ -33,8 +41,10 @@ bool SantaDecisionManager::init() {
|
||||
decision_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
log_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
|
||||
|
||||
decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(10000, 2);
|
||||
root_decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(5000, 2);
|
||||
non_root_decision_cache_ = new SantaCache<santa_vnode_id_t, uint64_t>(500, 2);
|
||||
vnode_pid_map_ = new SantaCache<santa_vnode_id_t, uint64_t>(2000, 5);
|
||||
compiler_pid_set_ = new SantaCache<pid_t, pid_t>(500, 5);
|
||||
|
||||
decision_dataqueue_ = IOSharedDataQueue::withEntries(
|
||||
kMaxDecisionQueueEvents, sizeof(santa_message_t));
|
||||
@@ -45,14 +55,21 @@ bool SantaDecisionManager::init() {
|
||||
if (!log_dataqueue_) return kIOReturnNoMemory;
|
||||
|
||||
client_pid_ = 0;
|
||||
root_fsid_ = 0;
|
||||
|
||||
// Setup file modification prefix filter.
|
||||
filemod_prefix_filter_ = new SantaPrefixTree();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
delete decision_cache_;
|
||||
delete root_decision_cache_;
|
||||
delete non_root_decision_cache_;
|
||||
delete vnode_pid_map_;
|
||||
|
||||
StopPidMonitorThreads();
|
||||
|
||||
if (decision_dataqueue_lock_) {
|
||||
lck_mtx_free(decision_dataqueue_lock_, sdm_lock_grp_);
|
||||
decision_dataqueue_lock_ = nullptr;
|
||||
@@ -81,6 +98,8 @@ void SantaDecisionManager::free() {
|
||||
OSSafeReleaseNULL(decision_dataqueue_);
|
||||
OSSafeReleaseNULL(log_dataqueue_);
|
||||
|
||||
delete filemod_prefix_filter_;
|
||||
|
||||
super::free();
|
||||
}
|
||||
|
||||
@@ -91,6 +110,17 @@ void SantaDecisionManager::ConnectClient(pid_t pid) {
|
||||
|
||||
client_pid_ = pid;
|
||||
|
||||
// Determine root fsid
|
||||
vfs_context_t ctx = vfs_context_create(nullptr);
|
||||
if (ctx) {
|
||||
vnode_t root = vfs_rootvnode();
|
||||
if (root) {
|
||||
root_fsid_ = GetVnodeIDForVnode(ctx, root).fsid;
|
||||
vnode_put(root);
|
||||
}
|
||||
vfs_context_rele(ctx);
|
||||
}
|
||||
|
||||
// Any decisions made while the daemon wasn't
|
||||
// connected should be cleared
|
||||
ClearCache();
|
||||
@@ -125,6 +155,10 @@ void SantaDecisionManager::DisconnectClient(bool itDied, pid_t pid) {
|
||||
kMaxLogQueueEvents, sizeof(santa_message_t));
|
||||
lck_mtx_unlock(log_dataqueue_lock_);
|
||||
}
|
||||
|
||||
// Reset the filter.
|
||||
// On startup the daemon will add prefixes as needed.
|
||||
FilemodPrefixFilterReset();
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::ClientConnected() const {
|
||||
@@ -195,27 +229,131 @@ kern_return_t SantaDecisionManager::StopListener() {
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
# pragma mark Monitoring PIDs
|
||||
|
||||
// Arguments that are passed to pid_monitor thread.
|
||||
typedef struct {
|
||||
pid_t pid; // process to monitor
|
||||
SantaDecisionManager *sdm; // reference to SantaDecisionManager
|
||||
} pid_monitor_info;
|
||||
|
||||
// Function executed in its own thread used to monitor a compiler process for
|
||||
// termination and then remove the process pid from cache of compiler pids.
|
||||
static void pid_monitor(void *param, __unused wait_result_t wait_result) {
|
||||
pid_monitor_info *info = (pid_monitor_info *)param;
|
||||
if (info && info->sdm) {
|
||||
uint32_t sleep_time = info->sdm->PidMonitorSleepTimeMilliseconds();
|
||||
while (!info->sdm->PidMonitorThreadsShouldExit()) {
|
||||
proc_t proc = proc_find(info->pid);
|
||||
if (!proc) break;
|
||||
proc_rele(proc);
|
||||
IOSleep(sleep_time);
|
||||
}
|
||||
info->sdm->ForgetCompilerPid(info->pid);
|
||||
info->sdm->DecrementPidMonitorThreadCount();
|
||||
}
|
||||
thread_terminate(current_thread());
|
||||
}
|
||||
|
||||
// TODO(nguyenphillip): Look at moving pid monitoring out of SDM entirely,
|
||||
// maybe by creating a dedicated class to do this that SDM could then query.
|
||||
void SantaDecisionManager::MonitorCompilerPidForExit(pid_t pid) {
|
||||
// Don't start any new threads if compiler_pid_set_ doesn't exist.
|
||||
if (!compiler_pid_set_) return;
|
||||
auto info = new pid_monitor_info;
|
||||
info->pid = pid;
|
||||
info->sdm = this;
|
||||
thread_t thread = THREAD_NULL;
|
||||
IncrementPidMonitorThreadCount();
|
||||
if (KERN_SUCCESS != kernel_thread_start(pid_monitor, (void *)info, &thread)) {
|
||||
LOGE("couldn't start pid monitor thread");
|
||||
DecrementPidMonitorThreadCount();
|
||||
}
|
||||
thread_deallocate(thread);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ForgetCompilerPid(pid_t pid) {
|
||||
if (compiler_pid_set_) compiler_pid_set_->remove(pid);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::PidMonitorThreadsShouldExit() const {
|
||||
return compiler_pid_set_ == nullptr;
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::StopPidMonitorThreads() {
|
||||
// Each pid_monitor thread checks for the existence of compiler_pid_set_.
|
||||
// As soon as they see that it's gone, they should terminate and decrement
|
||||
// SantaDecisionManager's pid_monitor_thread_count. When this count decreases
|
||||
// to zero all threads have finished.
|
||||
auto temp = compiler_pid_set_;
|
||||
compiler_pid_set_ = nullptr;
|
||||
delete temp;
|
||||
|
||||
// Sleep time between checks starts at 10 ms, but increases to 5 sec after
|
||||
// 10 sec have passed without the thread count dropping to 0.
|
||||
unsigned int sleep_time_milliseconds = 10;
|
||||
unsigned int total_wait_time = 0;
|
||||
|
||||
while (pid_monitor_thread_count_ > 0) {
|
||||
if (sleep_time_milliseconds == 10) {
|
||||
total_wait_time += sleep_time_milliseconds;
|
||||
if (total_wait_time >= 10000) {
|
||||
sleep_time_milliseconds = 5000;
|
||||
LOGD("Waited %d ms for pid monitor threads to quit, switching sleep"
|
||||
"time to %d ms", total_wait_time, sleep_time_milliseconds);
|
||||
}
|
||||
}
|
||||
IOSleep(sleep_time_milliseconds);
|
||||
}
|
||||
LOGD("Pid monitor threads stopped.");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SantaDecisionManager::PidMonitorSleepTimeMilliseconds() const {
|
||||
return kPidMonitorSleepTimeMilliseconds;
|
||||
}
|
||||
|
||||
#pragma mark Cache Management
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *SantaDecisionManager::CacheForIdentifier(
|
||||
const santa_vnode_id_t identifier) {
|
||||
return (identifier.fsid == root_fsid_) ? root_decision_cache_ : non_root_decision_cache_;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
santa_vnode_id_t identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
switch (decision) {
|
||||
case ACTION_REQUEST_BINARY:
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
decision_cache->set(identifier, (uint64_t)ACTION_REQUEST_BINARY << 56, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
decision_cache_->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
decision_cache->set(identifier, (uint64_t)ACTION_RESPOND_ACK << 56,
|
||||
((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
case ACTION_RESPOND_DENY: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
if (!decision_cache_->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache_->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
if (!decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: {
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
uint64_t val = ((uint64_t)decision << 56) | (microsecs & 0xFFFFFFFFFFFFFF);
|
||||
decision_cache->set(identifier, val, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -225,21 +363,26 @@ void SantaDecisionManager::AddToCache(
|
||||
|
||||
void SantaDecisionManager::RemoveFromCache(santa_vnode_id_t identifier) {
|
||||
if (unlikely(identifier.fsid == 0 && identifier.fileid == 0)) return;
|
||||
decision_cache_->remove(identifier);
|
||||
CacheForIdentifier(identifier)->remove(identifier);
|
||||
wakeup((void *)identifier.unsafe_simple_id());
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::CacheCount() const {
|
||||
return decision_cache_->count();
|
||||
uint64_t SantaDecisionManager::RootCacheCount() const {
|
||||
return root_decision_cache_->count();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache() {
|
||||
decision_cache_->clear();
|
||||
uint64_t SantaDecisionManager::NonRootCacheCount() const {
|
||||
return non_root_decision_cache_->count();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache(bool non_root_only) {
|
||||
if (!non_root_only) root_decision_cache_->clear();
|
||||
non_root_decision_cache_->clear();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::CacheBucketCount(
|
||||
uint16_t *per_bucket_counts, uint16_t *array_size, uint64_t *start_bucket) {
|
||||
decision_cache_->bucket_counts(per_bucket_counts, array_size, start_bucket);
|
||||
root_decision_cache_->bucket_counts(per_bucket_counts, array_size, start_bucket);
|
||||
}
|
||||
|
||||
#pragma mark Decision Fetching
|
||||
@@ -248,7 +391,9 @@ santa_action_t SantaDecisionManager::GetFromCache(santa_vnode_id_t identifier) {
|
||||
auto result = ACTION_UNSET;
|
||||
uint64_t decision_time = 0;
|
||||
|
||||
uint64_t cache_val = decision_cache_->get(identifier);
|
||||
auto decision_cache = CacheForIdentifier(identifier);
|
||||
|
||||
uint64_t cache_val = decision_cache->get(identifier);
|
||||
if (cache_val == 0) return result;
|
||||
|
||||
// Decision is stored in upper 8 bits, timestamp in remaining 56.
|
||||
@@ -259,7 +404,7 @@ santa_action_t SantaDecisionManager::GetFromCache(santa_vnode_id_t identifier) {
|
||||
if (result == ACTION_RESPOND_DENY) {
|
||||
auto expiry_time = decision_time + (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
if (expiry_time < GetCurrentUptime()) {
|
||||
decision_cache_->remove(identifier);
|
||||
decision_cache->remove(identifier);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
}
|
||||
@@ -406,6 +551,34 @@ void SantaDecisionManager::DecrementListenerInvocations() {
|
||||
OSDecrementAtomic(&listener_invocations_);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::IncrementPidMonitorThreadCount() {
|
||||
OSIncrementAtomic(&pid_monitor_thread_count_);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::DecrementPidMonitorThreadCount() {
|
||||
OSDecrementAtomic(&pid_monitor_thread_count_);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::IsCompilerProcess(pid_t pid) {
|
||||
for (;;) {
|
||||
// Find the parent pid.
|
||||
proc_t proc = proc_find(pid);
|
||||
if (!proc) return false;
|
||||
pid_t ppid = proc_ppid(proc);
|
||||
proc_rele(proc);
|
||||
// Quit if process is launchd or has no parent.
|
||||
if (ppid == 0 || pid == ppid) break;
|
||||
pid_t val = compiler_pid_set_->get(pid);
|
||||
// If pid was in compiler_pid_set_ then make sure that it has the same
|
||||
// parent pid as when it was set.
|
||||
if (val) return val == ppid;
|
||||
// If pid not in the set, then quit unless we want to check ancestors.
|
||||
if (!kCheckCompilerAncestors) break;
|
||||
pid = ppid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#pragma mark Callbacks
|
||||
|
||||
int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
@@ -420,7 +593,9 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
auto returnedAction = FetchDecision(cred, vp, vnode_id);
|
||||
|
||||
switch (returnedAction) {
|
||||
case ACTION_RESPOND_ALLOW: {
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
case ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE: {
|
||||
auto proc = vfs_context_proc(ctx);
|
||||
if (proc) {
|
||||
pid_t pid = proc_pid(proc);
|
||||
@@ -428,6 +603,15 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
// pid_t is 32-bit; pid is in upper 32 bits, ppid in lower.
|
||||
uint64_t val = ((uint64_t)pid << 32) | (ppid & 0xFFFFFFFF);
|
||||
vnode_pid_map_->set(vnode_id, val);
|
||||
if (returnedAction == ACTION_RESPOND_ALLOW_COMPILER && ppid != 0) {
|
||||
// Do some additional bookkeeping for compilers:
|
||||
// We associate the pid with a compiler so that when we see it later
|
||||
// in the context of a KAUTH_FILEOP event, we'll recognize it.
|
||||
compiler_pid_set_->set(pid, ppid);
|
||||
// And start polling for the compiler process termination, so that we
|
||||
// can remove the pid from our cache of compiler pids.
|
||||
MonitorCompilerPidForExit(pid);
|
||||
}
|
||||
}
|
||||
return KAUTH_RESULT_ALLOW;
|
||||
}
|
||||
@@ -448,40 +632,87 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
void SantaDecisionManager::FileOpCallback(
|
||||
const kauth_action_t action, const vnode_t vp,
|
||||
const char *path, const char *new_path) {
|
||||
if (!ClientConnected() || proc_selfpid() == client_pid_) return;
|
||||
if (!ClientConnected()) return;
|
||||
|
||||
if (vp) {
|
||||
// KAUTH_FILEOP_CLOSE implies KAUTH_FILEOP_CLOSE_MODIFIED, so remove it from the cache.
|
||||
if (action == KAUTH_FILEOP_CLOSE) {
|
||||
auto context = vfs_context_create(nullptr);
|
||||
RemoveFromCache(GetVnodeIDForVnode(context, vp));
|
||||
vfs_context_rele(context);
|
||||
}
|
||||
|
||||
// Don't log santad fileops.
|
||||
if (proc_selfpid() == client_pid_) return;
|
||||
|
||||
if (vp && action == KAUTH_FILEOP_EXEC) {
|
||||
auto context = vfs_context_create(nullptr);
|
||||
auto vnode_id = GetVnodeIDForVnode(context, vp);
|
||||
vfs_context_rele(context);
|
||||
|
||||
if (action == KAUTH_FILEOP_EXEC) {
|
||||
auto message = NewMessage(nullptr);
|
||||
message->vnode_id = vnode_id;
|
||||
message->action = ACTION_NOTIFY_EXEC;
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
uint64_t val = vnode_pid_map_->get(vnode_id);
|
||||
if (val) {
|
||||
// pid_t is 32-bit, so pid is in upper 32 bits, ppid in lower.
|
||||
message->pid = (val >> 32);
|
||||
message->ppid = (val & ~0xFFFFFFFF00000000);
|
||||
}
|
||||
PostToLogQueue(message);
|
||||
delete message;
|
||||
return;
|
||||
auto message = NewMessage(nullptr);
|
||||
message->vnode_id = vnode_id;
|
||||
message->action = ACTION_NOTIFY_EXEC;
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
|
||||
// The vnode scope gets posix_spawn pid and ppid properly. The fileop scope does not.
|
||||
// Get pid and ppid cached during vnode execution.
|
||||
uint64_t val = vnode_pid_map_->get(vnode_id);
|
||||
if (val) {
|
||||
// pid_t is 32-bit, so pid is in upper 32 bits, ppid in lower.
|
||||
message->pid = (val >> 32);
|
||||
message->ppid = (val & ~0xFFFFFFFF00000000);
|
||||
}
|
||||
|
||||
PostToLogQueue(message);
|
||||
delete message;
|
||||
return;
|
||||
}
|
||||
|
||||
// For transitive whitelisting decisions, we must check for KAUTH_FILEOP_CLOSE events from a
|
||||
// known compiler process. But we must also check for KAUTH_FILEOP_RENAME events because clang
|
||||
// under Xcode 9 will, if the output file already exists, write to a temp file, delete the
|
||||
// existing file, then rename the temp file, without ever closing it. So in this scenario,
|
||||
// the KAUTH_FILEOP_RENAME is the only chance we have of whitelisting the output.
|
||||
if (action == KAUTH_FILEOP_CLOSE || (action == KAUTH_FILEOP_RENAME && new_path)) {
|
||||
auto message = NewMessage(nullptr);
|
||||
if (IsCompilerProcess(message->pid)) {
|
||||
// Fill out the rest of the message details and send it to the decision queue.
|
||||
auto context = vfs_context_create(nullptr);
|
||||
vnode_t real_vp = vp;
|
||||
// We have to manually look up the vnode pointer from new_path for KAUTH_FILEOP_RENAME.
|
||||
if (!real_vp && new_path && ERR_SUCCESS == vnode_lookup(new_path, 0, &real_vp, context)) {
|
||||
vnode_put(real_vp);
|
||||
}
|
||||
if (real_vp) message->vnode_id = GetVnodeIDForVnode(context, real_vp);
|
||||
vfs_context_rele(context);
|
||||
message->action = ACTION_NOTIFY_WHITELIST;
|
||||
const char *real_path = (action == KAUTH_FILEOP_CLOSE) ? path : new_path;
|
||||
strlcpy(message->path, real_path, sizeof(message->path));
|
||||
proc_name(message->pid, message->pname, sizeof(message->pname));
|
||||
PostToDecisionQueue(message);
|
||||
// Add a temporary allow rule to the decision cache for this vnode_id
|
||||
// while SNTCompilerController decides whether or not to add a
|
||||
// permanent rule for the new file to the rules database. This is
|
||||
// because checking if the file is a Mach-O binary and hashing it might
|
||||
// not finish before an attempt to execute it.
|
||||
AddToCache(message->vnode_id, ACTION_RESPOND_ALLOW_PENDING_TRANSITIVE, 0);
|
||||
}
|
||||
delete message;
|
||||
// Don't need to do anything else for FILEOP_CLOSE, but FILEOP_RENAME should fall through.
|
||||
if (action == KAUTH_FILEOP_CLOSE) return;
|
||||
}
|
||||
|
||||
// Filter out modifications to locations that are definitely
|
||||
// not useful or made by santad.
|
||||
if (!strprefix(path, "/.") && !strprefix(path, "/dev")) {
|
||||
if (!filemod_prefix_filter_->HasPrefix(path)) {
|
||||
auto message = NewMessage(nullptr);
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
|
||||
proc_name(message->pid, message->pname, sizeof(message->pname));
|
||||
|
||||
switch (action) {
|
||||
case KAUTH_FILEOP_CLOSE:
|
||||
case KAUTH_FILEOP_WRITE:
|
||||
// This is actually a KAUTH_VNODE_WRITE_DATA event.
|
||||
message->action = ACTION_NOTIFY_WRITE;
|
||||
break;
|
||||
case KAUTH_FILEOP_RENAME:
|
||||
@@ -524,6 +755,13 @@ extern "C" int fileop_scope_callback(
|
||||
char *new_path = nullptr;
|
||||
|
||||
switch (action) {
|
||||
case KAUTH_FILEOP_CLOSE:
|
||||
// We only care about KAUTH_FILEOP_CLOSE events where the closed file
|
||||
// was modified.
|
||||
if (!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED))
|
||||
return KAUTH_RESULT_DEFER;
|
||||
// Intentional fallthrough to get vnode reference.
|
||||
[[fallthrough]];
|
||||
case KAUTH_FILEOP_DELETE:
|
||||
case KAUTH_FILEOP_EXEC:
|
||||
vp = reinterpret_cast<vnode_t>(arg0);
|
||||
@@ -563,7 +801,7 @@ extern "C" int vnode_scope_callback(
|
||||
// We only care about regular files.
|
||||
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
|
||||
|
||||
if ((action & KAUTH_VNODE_EXECUTE) && !(action & KAUTH_VNODE_ACCESS)) {
|
||||
if ((action & KAUTH_VNODE_EXECUTE) && !(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
sdm->IncrementListenerInvocations();
|
||||
int result = sdm->VnodeCallback(credential,
|
||||
reinterpret_cast<vfs_context_t>(arg0),
|
||||
@@ -573,14 +811,16 @@ extern "C" int vnode_scope_callback(
|
||||
return result;
|
||||
} else if (action & KAUTH_VNODE_WRITE_DATA || action & KAUTH_VNODE_APPEND_DATA) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
if (!(action & KAUTH_VNODE_ACCESS)) {
|
||||
if (!(action & KAUTH_VNODE_ACCESS)) { // NOLINT
|
||||
auto vnode_id = sdm->GetVnodeIDForVnode(reinterpret_cast<vfs_context_t>(arg0), vp);
|
||||
sdm->RemoveFromCache(vnode_id);
|
||||
}
|
||||
char path[MAXPATHLEN];
|
||||
int pathlen = MAXPATHLEN;
|
||||
vn_getpath(vp, path, &pathlen);
|
||||
sdm->FileOpCallback(KAUTH_FILEOP_CLOSE, vp, path, nullptr);
|
||||
// KAUTH_VNODE_WRITE_DATA events are translated into fake KAUTH_FILEOP_WRITE
|
||||
// events so that we can handle them in the FileOpCallback function.
|
||||
sdm->FileOpCallback(KAUTH_FILEOP_WRITE, vp, path, nullptr);
|
||||
sdm->DecrementListenerInvocations();
|
||||
}
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOMemoryDescriptor.h>
|
||||
#include <IOKit/IOSharedDataQueue.h>
|
||||
#include <libkern/c++/OSDictionary.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include "SantaCache.h"
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "SNTLogging.h"
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#include "Source/santa_driver/SantaCache.h"
|
||||
#include "Source/santa_driver/SantaPrefixTree.h"
|
||||
|
||||
///
|
||||
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
|
||||
@@ -79,19 +79,42 @@ class SantaDecisionManager : public OSObject {
|
||||
kern_return_t StartListener();
|
||||
|
||||
/**
|
||||
Stops the kauth listeners. After stopping new callback requests, waits
|
||||
Stops the kauth listeners. After stopping new callback requests, waits
|
||||
until all current invocations have finished before clearing the cache and
|
||||
returning.
|
||||
*/
|
||||
kern_return_t StopListener();
|
||||
|
||||
/**
|
||||
This spins off a new thread for each process that we monitor. Generally the
|
||||
threads should be short-lived, since they die as soon as their associated
|
||||
compiler process dies.
|
||||
*/
|
||||
void MonitorCompilerPidForExit(pid_t pid);
|
||||
|
||||
/// Remove the given pid from cache of compiler pids.
|
||||
void ForgetCompilerPid(pid_t pid);
|
||||
|
||||
/// Returns true when SantaDecisionManager wants monitor threads to exit.
|
||||
bool PidMonitorThreadsShouldExit() const;
|
||||
|
||||
/**
|
||||
Stops the pid monitor threads. Waits until all threads have stopped before
|
||||
returning. This also frees the compiler_pid_set_. Returns true if all
|
||||
threads exited cleanly. Returns false if timed out while waiting.
|
||||
*/
|
||||
bool StopPidMonitorThreads();
|
||||
|
||||
/// Returns how long pid monitor should sleep between termination checks.
|
||||
uint32_t PidMonitorSleepTimeMilliseconds() const;
|
||||
|
||||
/// Adds a decision to the cache, with a timestamp.
|
||||
void AddToCache(santa_vnode_id_t identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs = GetCurrentUptime());
|
||||
|
||||
/**
|
||||
Fetches a response from the cache, first checking to see if the entry
|
||||
Fetches a response from the cache, first checking to see if the entry
|
||||
has expired.
|
||||
*/
|
||||
santa_action_t GetFromCache(santa_vnode_id_t identifier);
|
||||
@@ -100,11 +123,14 @@ class SantaDecisionManager : public OSObject {
|
||||
void RemoveFromCache(santa_vnode_id_t identifier);
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
uint64_t CacheCount() const;
|
||||
|
||||
/// Clears the cache.
|
||||
void ClearCache();
|
||||
uint64_t RootCacheCount() const;
|
||||
uint64_t NonRootCacheCount() const;
|
||||
|
||||
/**
|
||||
Clears the cache(s). If non_root_only is true, only the non-root cache
|
||||
is cleared.
|
||||
*/
|
||||
void ClearCache(bool non_root_only = false);
|
||||
|
||||
/**
|
||||
Fills out the per_bucket_counts array with the number of items in each bucket in the
|
||||
@@ -125,6 +151,33 @@ class SantaDecisionManager : public OSObject {
|
||||
/// Decrements the count of active callbacks pending.
|
||||
void DecrementListenerInvocations();
|
||||
|
||||
/// Increments the count of active pid monitor threads.
|
||||
void IncrementPidMonitorThreadCount();
|
||||
|
||||
/// Decrements the count of active pid monitor threads.
|
||||
void DecrementPidMonitorThreadCount();
|
||||
|
||||
/**
|
||||
Determine if pid belongs to a compiler process. When
|
||||
kCheckCompilerAncestors is set to true, this also checks all ancestor
|
||||
processes of the pid.
|
||||
*/
|
||||
bool IsCompilerProcess(pid_t pid);
|
||||
|
||||
/**
|
||||
Add a file modification prefix filter.
|
||||
*/
|
||||
inline IOReturn FilemodPrefixFilterAdd(const char *prefix, uint64_t *node_count = nullptr) {
|
||||
return filemod_prefix_filter_->AddPrefix(prefix, node_count);
|
||||
}
|
||||
|
||||
/**
|
||||
Reset the file modification prefix filter tree.
|
||||
*/
|
||||
inline void FilemodPrefixFilterReset() {
|
||||
filemod_prefix_filter_->Reset();
|
||||
}
|
||||
|
||||
/**
|
||||
Fetches the vnode_id for a given vnode.
|
||||
|
||||
@@ -203,6 +256,16 @@ class SantaDecisionManager : public OSObject {
|
||||
*/
|
||||
static const uint32_t kMaxLogQueueEvents = 2048;
|
||||
|
||||
/// How long pid monitor thread should sleep between termination checks.
|
||||
static const uint32_t kPidMonitorSleepTimeMilliseconds = 1000;
|
||||
|
||||
/**
|
||||
When set to true, Santa will check all ancestors of a process to determine
|
||||
if it is a compiler.
|
||||
TODO(nguyenphillip): this setting (and others above) should be configurable.
|
||||
*/
|
||||
static const bool kCheckCompilerAncestors = false;
|
||||
|
||||
/**
|
||||
Fetches a response from the daemon. Handles both daemon death
|
||||
and failure to post messages to the daemon.
|
||||
@@ -279,8 +342,24 @@ class SantaDecisionManager : public OSObject {
|
||||
return (uint64_t)((sec * 1000000) + usec);
|
||||
}
|
||||
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *root_decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *non_root_decision_cache_;
|
||||
SantaCache<santa_vnode_id_t, uint64_t> *vnode_pid_map_;
|
||||
SantaCache<pid_t, pid_t> *compiler_pid_set_;
|
||||
|
||||
SantaPrefixTree *filemod_prefix_filter_;
|
||||
|
||||
/**
|
||||
Return the correct cache for a given identifier.
|
||||
|
||||
@param identifier The identifier
|
||||
@return SantaCache* The cache to use
|
||||
*/
|
||||
SantaCache<santa_vnode_id_t, uint64_t>* CacheForIdentifier(const santa_vnode_id_t identifier);
|
||||
|
||||
// This is the file system ID of the root filesystem,
|
||||
// used to determine which cache to use for requests
|
||||
uint64_t root_fsid_;
|
||||
|
||||
lck_grp_t *sdm_lock_grp_;
|
||||
lck_grp_attr_t *sdm_lock_grp_attr_;
|
||||
@@ -295,6 +374,7 @@ class SantaDecisionManager : public OSObject {
|
||||
uint32_t failed_log_queue_requests_;
|
||||
|
||||
int32_t listener_invocations_;
|
||||
int32_t pid_monitor_thread_count_ = 0;
|
||||
|
||||
pid_t client_pid_;
|
||||
|
||||
@@ -308,13 +388,13 @@ class SantaDecisionManager : public OSObject {
|
||||
/**
|
||||
The kauth callback function for the Vnode scope
|
||||
|
||||
@param actor's credentials
|
||||
@param data that was passed when the listener was registered
|
||||
@param action that was requested
|
||||
@param VFS context
|
||||
@param Vnode being operated on
|
||||
@param Parent Vnode. May be nullptr.
|
||||
@param Pointer to an errno-style error.
|
||||
@param credential actor's credentials
|
||||
@param idata data that was passed when the listener was registered
|
||||
@param action action that was requested
|
||||
@param arg0 VFS context
|
||||
@param arg1 Vnode being operated on
|
||||
@param arg2 Parent Vnode. May be nullptr.
|
||||
@param arg3 Pointer to an errno-style error.
|
||||
*/
|
||||
extern "C" int vnode_scope_callback(
|
||||
kauth_cred_t credential,
|
||||
@@ -328,13 +408,13 @@ extern "C" int vnode_scope_callback(
|
||||
/**
|
||||
The kauth callback function for the FileOp scope
|
||||
|
||||
@param actor's credentials
|
||||
@param data that was passed when the listener was registered
|
||||
@param action that was requested
|
||||
@param depends on action, usually the vnode ref.
|
||||
@param depends on action.
|
||||
@param depends on action, usually 0.
|
||||
@param depends on action, usually 0.
|
||||
@param credential actor's credentials
|
||||
@param idata data that was passed when the listener was registered
|
||||
@param action action that was requested
|
||||
@param arg0 depends on action, usually the vnode ref.
|
||||
@param arg1 depends on action.
|
||||
@param arg2 depends on action, usually 0.
|
||||
@param arg3 depends on action, usually 0.
|
||||
*/
|
||||
extern "C" int fileop_scope_callback(
|
||||
kauth_cred_t credential,
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDriver.h"
|
||||
#include "Source/santa_driver/SantaDriver.h"
|
||||
|
||||
#define super IOService
|
||||
#define SantaDriver com_google_SantaDriver
|
||||
@@ -18,8 +18,8 @@
|
||||
#include <IOKit/IOService.h>
|
||||
#include <libkern/OSKextLib.h>
|
||||
|
||||
#include "SNTLogging.h"
|
||||
#include "SantaDecisionManager.h"
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#include "Source/santa_driver/SantaDecisionManager.h"
|
||||
|
||||
///
|
||||
/// The driver class, which provides the start/stop functions and holds
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDriverClient.h"
|
||||
#include "Source/santa_driver/SantaDriverClient.h"
|
||||
|
||||
#define super IOUserClient
|
||||
#define SantaDriverClient com_google_SantaDriverClient
|
||||
@@ -145,6 +145,19 @@ IOReturn SantaDriverClient::allow_binary(
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::allow_compiler(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(*vnode_id, ACTION_RESPOND_ALLOW_COMPILER);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::deny_binary(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
@@ -176,7 +189,21 @@ IOReturn SantaDriverClient::clear_cache(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
me->decisionManager->ClearCache();
|
||||
const bool non_root_only = static_cast<const bool>(arguments->scalarInput[0]);
|
||||
me->decisionManager->ClearCache(non_root_only);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::remove_cache_entry(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
if (arguments->structureInputSize != sizeof(santa_vnode_id_t)) return kIOReturnInvalid;
|
||||
santa_vnode_id_t *vnode_id = (santa_vnode_id_t *)arguments->structureInput;
|
||||
if (vnode_id->fsid == 0 || vnode_id->fileid == 0) return kIOReturnInvalid;
|
||||
me->decisionManager->RemoveFromCache(*vnode_id);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
@@ -186,7 +213,8 @@ IOReturn SantaDriverClient::cache_count(
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
arguments->scalarOutput[0] = me->decisionManager->CacheCount();
|
||||
arguments->scalarOutput[0] = me->decisionManager->RootCacheCount();
|
||||
arguments->scalarOutput[1] = me->decisionManager->NonRootCacheCount();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
@@ -220,6 +248,24 @@ IOReturn SantaDriverClient::cache_bucket_count(
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::filemod_prefix_filter_add(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const char *prefix = reinterpret_cast<const char *>(arguments->structureInput);
|
||||
return me->decisionManager->FilemodPrefixFilterAdd(prefix, arguments->scalarOutput);
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::filemod_prefix_filter_reset(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
me->decisionManager->FilemodPrefixFilterReset();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
#pragma mark Method Resolution
|
||||
|
||||
IOReturn SantaDriverClient::externalMethod(
|
||||
@@ -234,13 +280,17 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
// Function ptr, input scalar count, input struct size, output scalar count, output struct size
|
||||
{ &SantaDriverClient::open, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::allow_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::allow_compiler, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::acknowledge_binary, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 1, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::remove_cache_entry, 0, sizeof(santa_vnode_id_t), 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 2, 0 },
|
||||
{ &SantaDriverClient::check_cache, 0, sizeof(santa_vnode_id_t), 1, 0 },
|
||||
{ &SantaDriverClient::cache_bucket_count, 0, sizeof(santa_bucket_count_t),
|
||||
0, sizeof(santa_bucket_count_t) },
|
||||
{ &SantaDriverClient::filemod_prefix_filter_add, 0, sizeof(const char[MAXPATHLEN]), 1, 0 },
|
||||
{ &SantaDriverClient::filemod_prefix_filter_reset, 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
if (selector > static_cast<UInt32>(kSantaUserClientNMethods)) {
|
||||
@@ -20,9 +20,9 @@
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "SantaDecisionManager.h"
|
||||
#include "SantaDriver.h"
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
#include "Source/santa_driver/SantaDecisionManager.h"
|
||||
#include "Source/santa_driver/SantaDriver.h"
|
||||
|
||||
///
|
||||
/// This class is instantiated by IOKit when a new client process attempts to
|
||||
@@ -74,7 +74,7 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
OSObject *target, void *reference) override;
|
||||
|
||||
///
|
||||
/// The userpsace callable methods are below. Each method corresponds
|
||||
/// The userspace callable methods are below. Each method corresponds
|
||||
/// to an entry in SantaDriverMethods.
|
||||
///
|
||||
|
||||
@@ -84,7 +84,11 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
|
||||
/// The daemon calls this to allow a binary.
|
||||
static IOReturn allow_binary(
|
||||
OSObject *target, void *reference,IOExternalMethodArguments *arguments);
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to allow a compiler binary.
|
||||
static IOReturn allow_compiler(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to deny a binary.
|
||||
static IOReturn deny_binary(
|
||||
@@ -99,6 +103,10 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
static IOReturn clear_cache(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon call this to remove a single cache entry.
|
||||
static IOReturn remove_cache_entry(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to find out how many items are in the cache
|
||||
static IOReturn cache_count(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
@@ -113,6 +121,14 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
static IOReturn cache_bucket_count(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to add filemod prefix filters at daemon startup.
|
||||
static IOReturn filemod_prefix_filter_add(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to reset the prefix filter tree.
|
||||
static IOReturn filemod_prefix_filter_reset(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
private:
|
||||
com_google_SantaDriver *myProvider;
|
||||
SantaDecisionManager *decisionManager;
|
||||
261
Source/santa_driver/SantaPrefixTree.cc
Normal file
261
Source/santa_driver/SantaPrefixTree.cc
Normal file
@@ -0,0 +1,261 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#include "Source/santa_driver/SantaPrefixTree.h"
|
||||
|
||||
#ifdef KERNEL
|
||||
#include <libkern/locks.h>
|
||||
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#else
|
||||
#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_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_mtx_lock(l) l->lock()
|
||||
#define lck_mtx_unlock(l) l->unlock()
|
||||
#endif // KERNEL
|
||||
|
||||
SantaPrefixTree::SantaPrefixTree(uint32_t max_nodes) {
|
||||
root_ = new SantaPrefixNode();
|
||||
node_count_ = 0;
|
||||
max_nodes_ = max_nodes;
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
IOReturn SantaPrefixTree::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.
|
||||
lck_mtx_lock(spt_add_lock_);
|
||||
|
||||
// Don't allow an empty prefix.
|
||||
if (prefix[0] == '\0') return kIOReturnBadArgument;
|
||||
|
||||
LOGD("Trying to add prefix: %s", prefix);
|
||||
|
||||
// Enforce max tree depth.
|
||||
size_t len = strnlen(prefix, max_nodes_);
|
||||
|
||||
// Grab a shared lock until a new branch is required.
|
||||
lck_rw_lock_shared(spt_lock_);
|
||||
|
||||
SantaPrefixNode *node = root_;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
// If there is a node in the path that is considered a prefix, stop adding.
|
||||
// For our purposes we only care about the shortest path that matches.
|
||||
if (node->isPrefix) break;
|
||||
|
||||
// Only process a byte at a time.
|
||||
uint8_t value = prefix[i];
|
||||
|
||||
// Create the child if it does not exist.
|
||||
if (!node->children[value]) {
|
||||
// Upgrade the shared lock.
|
||||
// If the upgrade fails, the shared lock is released.
|
||||
if (!lck_rw_lock_shared_to_exclusive(spt_lock_)) {
|
||||
// Grab a new exclusive lock.
|
||||
lck_rw_lock_exclusive(spt_lock_);
|
||||
}
|
||||
|
||||
// Is there enough room for the rest of the prefix?
|
||||
if ((node_count_ + (len - i)) > max_nodes_) {
|
||||
LOGE("Prefix tree is full, can not add: %s", prefix);
|
||||
|
||||
if (node_count) *node_count = node_count_;
|
||||
lck_rw_unlock_exclusive(spt_lock_);
|
||||
lck_mtx_unlock(spt_add_lock_);
|
||||
return kIOReturnNoResources;
|
||||
}
|
||||
|
||||
// Create the rest of the prefix.
|
||||
while (i < len) {
|
||||
value = prefix[i++];
|
||||
|
||||
SantaPrefixNode *new_node = new SantaPrefixNode();
|
||||
node->children[value] = new_node;
|
||||
++node_count_;
|
||||
|
||||
node = new_node;
|
||||
}
|
||||
|
||||
// This is the end, mark the node as a prefix.
|
||||
LOGD("Added prefix: %s", prefix);
|
||||
|
||||
node->isPrefix = true;
|
||||
|
||||
// Downgrade the exclusive lock
|
||||
lck_rw_lock_exclusive_to_shared(spt_lock_);
|
||||
} else if (i + 1 == len) {
|
||||
// If the child does exist and it is the end...
|
||||
// Set the new, higher prefix and prune the now dead nodes.
|
||||
|
||||
if (!lck_rw_lock_shared_to_exclusive(spt_lock_)) {
|
||||
lck_rw_lock_exclusive(spt_lock_);
|
||||
}
|
||||
|
||||
PruneNode(node->children[value]);
|
||||
|
||||
SantaPrefixNode *new_node = new SantaPrefixNode();
|
||||
new_node->isPrefix = true;
|
||||
|
||||
node->children[value] = new_node;
|
||||
++node_count_;
|
||||
|
||||
LOGD("Added prefix: %s", prefix);
|
||||
|
||||
lck_rw_lock_exclusive_to_shared(spt_lock_);
|
||||
}
|
||||
|
||||
// Get ready for the next iteration.
|
||||
node = node->children[value];
|
||||
}
|
||||
|
||||
if (node_count) *node_count = node_count_;
|
||||
|
||||
lck_rw_unlock_shared(spt_lock_);
|
||||
lck_mtx_unlock(spt_add_lock_);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
bool SantaPrefixTree::HasPrefix(const char *string) {
|
||||
lck_rw_lock_shared(spt_lock_);
|
||||
|
||||
auto found = false;
|
||||
|
||||
SantaPrefixNode *node = root_;
|
||||
|
||||
// A well formed tree will always break this loop. Even if string doesn't terminate.
|
||||
const char *p = string;
|
||||
while (*p) {
|
||||
// Only process a byte at a time.
|
||||
node = node->children[(uint8_t)*p++];
|
||||
|
||||
// If it doesn't exist in the tree, no match.
|
||||
if (!node) break;
|
||||
|
||||
// If it does exist, is it a prefix?
|
||||
if (node->isPrefix) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lck_rw_unlock_shared(spt_lock_);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void SantaPrefixTree::Reset() {
|
||||
lck_rw_lock_exclusive(spt_lock_);
|
||||
|
||||
PruneNode(root_);
|
||||
root_ = new SantaPrefixNode();
|
||||
node_count_ = 0;
|
||||
|
||||
lck_rw_unlock_exclusive(spt_lock_);
|
||||
}
|
||||
|
||||
void SantaPrefixTree::PruneNode(SantaPrefixNode *target) {
|
||||
if (!target) return;
|
||||
|
||||
// For deep trees, a recursive approach will generate too many stack frames. Make a "stack"
|
||||
// and walk the tree.
|
||||
auto stack = new SantaPrefixNode *[node_count_ + 1];
|
||||
if (!stack) {
|
||||
LOGE("Unable to prune tree!");
|
||||
|
||||
return;
|
||||
}
|
||||
auto count = 0;
|
||||
|
||||
// Seed the "stack" with a starting node.
|
||||
stack[count++] = target;
|
||||
|
||||
// Start at the target node and walk the tree to find and delete all the sub-nodes.
|
||||
while (count) {
|
||||
auto node = stack[--count];
|
||||
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
if (!node->children[i]) continue;
|
||||
stack[count++] = node->children[i];
|
||||
}
|
||||
|
||||
delete node;
|
||||
--node_count_;
|
||||
}
|
||||
|
||||
delete[] stack;
|
||||
}
|
||||
|
||||
SantaPrefixTree::~SantaPrefixTree() {
|
||||
lck_rw_lock_exclusive(spt_lock_);
|
||||
PruneNode(root_);
|
||||
root_ = nullptr;
|
||||
lck_rw_unlock_exclusive(spt_lock_);
|
||||
|
||||
#ifdef KERNEL
|
||||
if (spt_lock_) {
|
||||
lck_rw_free(spt_lock_, spt_lock_grp_);
|
||||
spt_lock_ = nullptr;
|
||||
}
|
||||
|
||||
if (spt_add_lock_) {
|
||||
lck_mtx_free(spt_add_lock_, spt_lock_grp_);
|
||||
spt_add_lock_ = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (spt_lock_attr_) {
|
||||
lck_attr_free(spt_lock_attr_);
|
||||
spt_lock_attr_ = nullptr;
|
||||
}
|
||||
|
||||
if (spt_lock_grp_) {
|
||||
lck_grp_free(spt_lock_grp_);
|
||||
spt_lock_grp_ = nullptr;
|
||||
}
|
||||
|
||||
if (spt_lock_grp_attr_) {
|
||||
lck_grp_attr_free(spt_lock_grp_attr_);
|
||||
spt_lock_grp_attr_ = nullptr;
|
||||
}
|
||||
}
|
||||
103
Source/santa_driver/SantaPrefixTree.h
Normal file
103
Source/santa_driver/SantaPrefixTree.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/// Copyright 2018 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.
|
||||
|
||||
#ifndef SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H
|
||||
#define SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H
|
||||
|
||||
#include <IOKit/IOReturn.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifdef KERNEL
|
||||
#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>
|
||||
#endif // KERNEL
|
||||
|
||||
///
|
||||
/// SantaPrefixTree is a simple prefix tree implementation.
|
||||
/// Operations are thread safe.
|
||||
///
|
||||
class SantaPrefixTree {
|
||||
public:
|
||||
// Add a prefix to the tree.
|
||||
// Optionally pass node_count to get the number of nodes after the add.
|
||||
IOReturn AddPrefix(const char *, uint64_t *node_count = nullptr);
|
||||
|
||||
// Check if the tree has a prefix for string.
|
||||
bool HasPrefix(const char *string);
|
||||
|
||||
// Reset the tree.
|
||||
void Reset();
|
||||
|
||||
SantaPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
|
||||
~SantaPrefixTree();
|
||||
|
||||
private:
|
||||
///
|
||||
/// SantaPrefixNode is a wrapper class that represents one byte.
|
||||
/// 1 node can represent a whole ASCII character.
|
||||
/// For example a pointer to the 'A' node will be stored at children[0x41].
|
||||
/// It takes 1-4 nodes to represent a UTF-8 encoded Unicode character.
|
||||
///
|
||||
/// The path for "/🤘" would look like this:
|
||||
/// children[0x2f] -> children[0xf0] -> children[0x9f] -> children[0xa4] -> children[0x98]
|
||||
///
|
||||
/// The path for "/dev" is:
|
||||
/// children[0x2f] -> children[0x64] -> children[0x65] -> children[0x76]
|
||||
///
|
||||
/// Lookups of children are O(1).
|
||||
///
|
||||
/// Having the nodes represented by a smaller width, such as a nibble (1/2 byte), would
|
||||
/// drastically decrease the memory footprint but would double required dereferences.
|
||||
///
|
||||
/// TODO(bur): Potentially convert this into a full on radix tree.
|
||||
///
|
||||
class SantaPrefixNode {
|
||||
public:
|
||||
bool isPrefix;
|
||||
SantaPrefixNode *children[256];
|
||||
};
|
||||
|
||||
// PruneNode will remove the passed in node from the tree.
|
||||
// The passed in node and all subnodes will be deleted.
|
||||
// It is the caller's responsibility to reset the pointer to this node (held by the parent).
|
||||
// If the tree is in use grab the exclusive lock.
|
||||
void PruneNode(SantaPrefixNode *);
|
||||
|
||||
SantaPrefixNode *root_;
|
||||
|
||||
// Each node takes up ~2k, assuming MAXPATHLEN is 1024 max out at ~2MB.
|
||||
static const uint32_t kDefaultMaxNodes = MAXPATHLEN;
|
||||
uint32_t max_nodes_;
|
||||
uint32_t node_count_;
|
||||
|
||||
#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
|
||||
void *spt_lock_grp_;
|
||||
void *spt_lock_grp_attr_;
|
||||
void *spt_lock_attr_;
|
||||
std::shared_mutex *spt_lock_;
|
||||
std::mutex *spt_add_lock_;
|
||||
#endif // KERNEL
|
||||
};
|
||||
|
||||
#endif /* SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H */
|
||||
70
Source/santa_driver/SantaPrefixTreeTest.mm
Normal file
70
Source/santa_driver/SantaPrefixTreeTest.mm
Normal file
@@ -0,0 +1,70 @@
|
||||
/// Copyright 2018 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "Source/santa_driver/SantaPrefixTree.h"
|
||||
|
||||
@interface SantaPrefixTreeTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation SantaPrefixTreeTest
|
||||
|
||||
- (void)testAddAndHas {
|
||||
auto t = SantaPrefixTree();
|
||||
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
|
||||
t.AddPrefix("/private/var/tmp/");
|
||||
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
|
||||
}
|
||||
|
||||
- (void)testReset {
|
||||
auto t = SantaPrefixTree();
|
||||
t.AddPrefix("/private/var/tmp/");
|
||||
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
|
||||
t.Reset();
|
||||
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
|
||||
}
|
||||
|
||||
- (void)testThreading {
|
||||
uint32_t count = 4096;
|
||||
auto t = new SantaPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
|
||||
|
||||
NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
[UUIDs addObject:[NSUUID UUID].UUIDString];
|
||||
}
|
||||
|
||||
// Create a bunch of background noise.
|
||||
dispatch_async(dispatch_get_global_queue(0, 0), ^{
|
||||
dispatch_apply(UINT64_MAX, dispatch_get_global_queue(0, 0), ^(size_t i) {
|
||||
t->HasPrefix([UUIDs[i % count] UTF8String]);
|
||||
});
|
||||
});
|
||||
|
||||
// Fill up the tree.
|
||||
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
|
||||
if (t->AddPrefix([UUIDs[i] UTF8String]) != kIOReturnSuccess) {
|
||||
XCTFail();
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure every leaf byte is found.
|
||||
dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t i) {
|
||||
if (!t->HasPrefix([UUIDs[i] UTF8String])) {
|
||||
XCTFail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -13,6 +13,7 @@
|
||||
/// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOKit/IODataQueueClient.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/kext/KextManager.h>
|
||||
|
||||
@@ -28,7 +29,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <vector>
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "Source/common/SNTKernelCommon.h"
|
||||
|
||||
///
|
||||
/// Kernel Extension Tests
|
||||
@@ -99,26 +100,60 @@
|
||||
return @(buf);
|
||||
}
|
||||
|
||||
/// Return the path to the version of ld being used by clang.
|
||||
- (NSString *)ldPath {
|
||||
static NSString *path;
|
||||
if (!path) {
|
||||
NSTask *xcrun = [self taskWithPath:@"/usr/bin/xcrun"];
|
||||
xcrun.arguments = @[@"-f", @"ld"];
|
||||
xcrun.standardOutput = [NSPipe pipe];
|
||||
@try {
|
||||
[xcrun launch];
|
||||
[xcrun waitUntilExit];
|
||||
} @catch (NSException *exception) {
|
||||
return nil;
|
||||
}
|
||||
if (xcrun.terminationStatus != 0) return nil;
|
||||
NSData *data = [[xcrun.standardOutput fileHandleForReading] readDataToEndOfFile];
|
||||
if (!data) return nil;
|
||||
path = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
#pragma mark - Driver Helpers
|
||||
|
||||
/// Call in-kernel function: |kSantaUserClientAllowBinary| or |kSantaUserClientDenyBinary|
|
||||
/// passing the |vnodeID|.
|
||||
- (void)postToKernelAction:(santa_action_t)action forVnodeID:(santa_vnode_id_t)vnodeid {
|
||||
if (action == ACTION_RESPOND_DENY) {
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientDenyBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
} else if (action == ACTION_RESPOND_ACK) {
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
} else {
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAllowBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
switch (action) {
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAllowBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_DENY:
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientDenyBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW_COMPILER:
|
||||
IOConnectCallStructMethod(self.connection, kSantaUserClientAllowCompiler,
|
||||
&vnodeid, sizeof(vnodeid), 0, 0);
|
||||
break;
|
||||
default:
|
||||
TFAILINFO("postToKernelAction:forVnodeID: received unknown action type: %d", action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Call in-kernel function: |kSantaUserClientClearCache|
|
||||
- (void)flushCache {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, 0, 0, 0, 0);
|
||||
uint64_t nonRootOnly = 0;
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientClearCache, &nonRootOnly, 1, 0, 0);
|
||||
}
|
||||
|
||||
#pragma mark - Connection Tests
|
||||
@@ -297,6 +332,7 @@
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSString *target =
|
||||
[[fm currentDirectoryPath] stringByAppendingPathComponent:@"invalidatecachetest"];
|
||||
[fm removeItemAtPath:target error:nil];
|
||||
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
|
||||
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
@@ -378,6 +414,7 @@
|
||||
|
||||
// Create temporary file
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"invalidacachetest_tmp" error:nil]) {
|
||||
TFAILINFO("Failed to create temp file");
|
||||
}
|
||||
@@ -547,6 +584,189 @@
|
||||
TPASS();
|
||||
}
|
||||
|
||||
- (void)testPendingTransitiveRules {
|
||||
TSTART("Adds pending transitive whitelist rules");
|
||||
|
||||
NSString *ldPath = [self ldPath];
|
||||
if (!ldPath) {
|
||||
TFAILINFO("Couldn't get path to ld");
|
||||
}
|
||||
|
||||
// Clear out cached decisions from any previous tests.
|
||||
[self flushCache];
|
||||
|
||||
__block int ldCount = 0;
|
||||
__block int helloCount = 0;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (!strcmp(ldPath.UTF8String, msg.path)) {
|
||||
ldCount++;
|
||||
return ACTION_RESPOND_ALLOW_COMPILER;
|
||||
} else if (!strcmp("/private/tmp/hello", msg.path)) {
|
||||
helloCount++;
|
||||
return ACTION_RESPOND_DENY;
|
||||
}
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
// Write source file to /tmp/hello.c
|
||||
FILE *out = fopen("/tmp/hello.c", "wb");
|
||||
fprintf(out, "#include <stdio.h>\nint main(void) { printf(\"Hello, world!\\n\"); }");
|
||||
fclose(out);
|
||||
// Then compile it with clang and ld, the latter of which has been marked as a compiler.
|
||||
NSTask *clang = [self taskWithPath:@"/usr/bin/clang"];
|
||||
clang.arguments = @[@"-o", @"/private/tmp/hello", @"/private/tmp/hello.c"];
|
||||
[clang launch];
|
||||
[clang waitUntilExit];
|
||||
// Make sure that our version of ld marked as compiler was run. This assumes that
|
||||
// "xcode-select -p" returns "/Applications/Xcode.app/Contents/Developer"
|
||||
if (ldCount != 1) {
|
||||
TFAILINFO("Didn't record run of ld");
|
||||
}
|
||||
// Check if we can now run /private/tmp/hello. If working correctly, there will already be
|
||||
// a pending transitive rule in the cache, so no decision request will be sent to listener.
|
||||
// If for some reason a decision request is sent, then binary will be denied.
|
||||
NSTask *hello = [self taskWithPath:@"/private/tmp/hello"];
|
||||
@try {
|
||||
[hello launch];
|
||||
[hello waitUntilExit];
|
||||
} @catch (NSException *exception) {
|
||||
TFAILINFO("could not launch /private/tmp/hello: %s", exception.reason.UTF8String);
|
||||
}
|
||||
// Check that the listener was not consulted for the decision.
|
||||
if (helloCount > 0) {
|
||||
TFAILINFO("pending decision for /private/tmp/hello was not in cache");
|
||||
}
|
||||
|
||||
// Clean up
|
||||
remove("/tmp/hello");
|
||||
remove("/tmp/hello.c");
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
- (void)testNoTransitiveRules {
|
||||
TSTART("No transitive rule generated by non-compiler");
|
||||
|
||||
NSString *ldPath = [self ldPath];
|
||||
if (!ldPath) {
|
||||
TFAILINFO("Couldn't get path to ld");
|
||||
}
|
||||
|
||||
// Clear out cached decisions from any previous tests.
|
||||
[self flushCache];
|
||||
|
||||
__block int ldCount = 0;
|
||||
__block int helloCount = 0;
|
||||
self.handlerBlock = ^santa_action_t(santa_message_t msg) {
|
||||
if (!strcmp(ldPath.UTF8String, msg.path)) {
|
||||
ldCount++;
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
} else if (!strcmp("/private/tmp/hello", msg.path)) {
|
||||
helloCount++;
|
||||
return ACTION_RESPOND_DENY;
|
||||
}
|
||||
return ACTION_RESPOND_ALLOW;
|
||||
};
|
||||
// Write source file to /tmp/hello.c
|
||||
FILE *out = fopen("/tmp/hello.c", "wb");
|
||||
fprintf(out, "#include <stdio.h>\nint main(void) { printf(\"Hello, world!\\n\"); }");
|
||||
fclose(out);
|
||||
// Then compile it with clang and ld, neither of which have been marked as a compiler.
|
||||
NSTask *clang = [self taskWithPath:@"/usr/bin/clang"];
|
||||
clang.arguments = @[@"-o", @"/private/tmp/hello", @"/private/tmp/hello.c"];
|
||||
@try {
|
||||
[clang launch];
|
||||
[clang waitUntilExit];
|
||||
} @catch (NSException *exception) {
|
||||
TFAILINFO("Couldn't launch clang");
|
||||
}
|
||||
// Make sure that our version of ld was run. This assumes that "xcode-select -p"
|
||||
// returns "/Applications/Xcode.app/Contents/Developer"
|
||||
if (ldCount != 1) {
|
||||
TFAILINFO("Didn't record run of ld");
|
||||
}
|
||||
// Check that we cannot run /private/tmp/hello.
|
||||
NSTask *hello = [self taskWithPath:@"/private/tmp/hello"];
|
||||
@try {
|
||||
[hello launch];
|
||||
[hello waitUntilExit];
|
||||
TFAILINFO("Should not have been able to launch /private/tmp/hello");
|
||||
} @catch (NSException *exception) {
|
||||
// All good
|
||||
}
|
||||
// Check that there wasn't a decision for /private/tmp/hello in the cache.
|
||||
if (helloCount != 1) {
|
||||
TFAILINFO("decision for /private/tmp/hello found in cache");
|
||||
}
|
||||
|
||||
// Clean up
|
||||
remove("/tmp/hello");
|
||||
remove("/tmp/hello.c");
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
- (void)testFilemodPrefixFilter {
|
||||
TSTART("Testing filemod prefix filter");
|
||||
|
||||
NSString *filter =
|
||||
@"Albert Einstein, in his theory of special relativity, determined that the laws of physics "
|
||||
@"are the same for all non-accelerating observers, and he showed that the speed of light "
|
||||
@"within a vacuum is the same no matter the speed at which an observer travels.";
|
||||
|
||||
// Create a buffer that has 1024 bytes of characters, non-terminating.
|
||||
char buffer[MAXPATHLEN + 1]; // +1 is for the null byte from strlcpy(). It falls off the ledge.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (![filter getFileSystemRepresentation:buffer + (i * filter.length) maxLength:MAXPATHLEN]) {
|
||||
TFAILINFO("Invalid filemod prefix filter: %s", filter.UTF8String);
|
||||
}
|
||||
}
|
||||
strlcpy(&buffer[1016], "E = mc²", 9); // Fill in the last 8 bytes.
|
||||
|
||||
uint64_t n = 0;
|
||||
uint32_t n_len = 1;
|
||||
|
||||
// The filter should currently be empty. It is reset when the client disconnects.
|
||||
// Fill up the 1024 node capacity.
|
||||
kern_return_t ret =
|
||||
IOConnectCallMethod(self.connection, kSantaUserClientFilemodPrefixFilterAdd, NULL, 0, buffer,
|
||||
sizeof(const char[MAXPATHLEN]), &n, &n_len, NULL, NULL);
|
||||
|
||||
if (ret != kIOReturnSuccess || n != 1024) {
|
||||
TFAILINFO("Failed to fill the prefix filter: got %llu nodes expected 1024", n);
|
||||
}
|
||||
|
||||
// Make sure it will enforce capacity.
|
||||
const char *too_much = "B";
|
||||
ret = IOConnectCallMethod(self.connection, kSantaUserClientFilemodPrefixFilterAdd, NULL, 0,
|
||||
too_much, sizeof(const char[MAXPATHLEN]), &n, &n_len, NULL, NULL);
|
||||
if (ret != kIOReturnNoResources || n != 1024) {
|
||||
TFAILINFO("Failed enforce capacity: got %llu nodes expected 1024", n);
|
||||
}
|
||||
|
||||
// Make sure it will prune.
|
||||
const char *ignore_it_all = "A";
|
||||
ret = IOConnectCallMethod(self.connection, kSantaUserClientFilemodPrefixFilterAdd, NULL, 0,
|
||||
ignore_it_all, sizeof(const char[MAXPATHLEN]), &n, &n_len, NULL, NULL);
|
||||
// Expect 1 "A" node.
|
||||
if (ret != kIOReturnSuccess || n != 1) {
|
||||
TFAILINFO("Failed to prune the prefix filter: got %llu nodes expected 1", n);
|
||||
}
|
||||
|
||||
// Reset.
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientFilemodPrefixFilterReset, NULL, 0,
|
||||
NULL, NULL);
|
||||
|
||||
// And fill it back up again.
|
||||
ret = IOConnectCallMethod(self.connection, kSantaUserClientFilemodPrefixFilterAdd, NULL, 0,
|
||||
buffer, sizeof(const char[MAXPATHLEN]), &n, &n_len, NULL, NULL);
|
||||
|
||||
if (ret != kIOReturnSuccess || n != 1024) {
|
||||
TFAILINFO("Failed to fill the prefix filter: got %llu nodes expected 1024", n);
|
||||
}
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
#pragma mark - Main
|
||||
|
||||
- (void)unloadDaemon {
|
||||
@@ -620,6 +840,9 @@
|
||||
[self clearCacheTests];
|
||||
[self blocksDeniedTracedBinaries];
|
||||
[self testLargeBinary];
|
||||
[self testPendingTransitiveRules];
|
||||
[self testNoTransitiveRules];
|
||||
[self testFilemodPrefixFilter];
|
||||
|
||||
printf("\n-> Performance tests:\n");
|
||||
[self testCachePerformance];
|
||||
@@ -638,8 +861,6 @@ int main(int argc, const char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
chdir([[[NSBundle mainBundle] bundlePath] UTF8String]);
|
||||
|
||||
SantaKernelTests *skt = [[SantaKernelTests alloc] init];
|
||||
printf("\nSanta Kernel Tests\n==================\n\n");
|
||||
printf("-> Loading tests:\n");
|
||||
18
Source/santa_driver/main.cc
Normal file
18
Source/santa_driver/main.cc
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#define STR_EXPAND(tok) #tok
|
||||
#define STR(tok) STR_EXPAND(tok)
|
||||
|
||||
extern "C" {
|
||||
|
||||
extern kern_return_t _start(kmod_info_t *ki, void *data);
|
||||
extern kern_return_t _stop(kmod_info_t *ki, void *data);
|
||||
|
||||
__attribute__((visibility("default"))) \
|
||||
KMOD_EXPLICIT_DECL(com.google.santa-driver, STR(SANTA_VERSION), _start, _stop)
|
||||
|
||||
__private_extern__ kmod_start_func_t *_realmain = 0;
|
||||
__private_extern__ kmod_stop_func_t *_antimain = 0;
|
||||
|
||||
__private_extern__ int _kext_apple_cc = __APPLE_CC__ ;
|
||||
}
|
||||
30
Source/santabs/BUILD
Normal file
30
Source/santabs/BUILD
Normal file
@@ -0,0 +1,30 @@
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_xpc_service")
|
||||
|
||||
objc_library(
|
||||
name = "santabs_lib",
|
||||
srcs = [
|
||||
"SNTBundleService.h",
|
||||
"SNTBundleService.m",
|
||||
"main.m",
|
||||
],
|
||||
deps = [
|
||||
"//Source/common:SNTFileInfo",
|
||||
"//Source/common:SNTXPCBundleServiceInterface",
|
||||
"//Source/common:SNTXPCNotifierInterface",
|
||||
"@FMDB",
|
||||
"@MOLCodesignChecker",
|
||||
"@MOLXPCConnection",
|
||||
],
|
||||
)
|
||||
|
||||
macos_xpc_service(
|
||||
name = "santabs",
|
||||
bundle_id = "com.google.santabs",
|
||||
infoplists = ["Resources/santabs-Info.plist"],
|
||||
minimum_os_version = "10.9",
|
||||
version = "//:version",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":santabs_lib"],
|
||||
)
|
||||
@@ -7,21 +7,21 @@
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>santabs</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<string>santabs</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<string>com.google.santabs</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<string>santabs</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>${SANTA_VERSION}</string>
|
||||
<key>XPCService</key>
|
||||
<dict>
|
||||
<key>ServiceType</key>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user