Compare commits

...

105 Commits
0.8.2 ... 0.8.7

Author SHA1 Message Date
Russell Hancox
32707fb501 santa-driver: Fix rare panic in CacheCheck where lock upgrade fails.
lck_rw_lock_shared_to_exclusive can return false if a previous reader upgraded. The result is the lock being unlocked and the panic is caused when unlocking a lock that isn't locked.
2015-05-20 11:13:19 -04:00
Russell Hancox
d72547e187 Project: Simplify package download URL generation in pkg Makefile 2015-05-18 18:36:30 -04:00
Russell Hancox
9150ddffb1 Project: Fix broken curl command in pkg Makefile 2015-05-18 17:52:33 -04:00
Russell Hancox
d5c1d66c2f KernelTests: Update tests for dataqueue usage changes 2015-05-18 16:39:27 -04:00
Russell Hancox
536b8969ed santactl/sync: LogUpload - logsToUpload is part of the same class, use self. 2015-05-18 16:31:43 -04:00
Russell Hancox
0db3b6d955 santactl/sync: Split out Log Upload request generation and Rule Download rule parsing from main request methods. 2015-05-18 14:33:21 -04:00
Russell Hancox
78bb9a1bd6 common: Correct comment on default loglevels 2015-05-18 13:06:31 -04:00
Russell Hancox
567e0b6431 santad: If exiting because of a failed dequeue, log the return code at ERROR level. 2015-05-18 13:06:31 -04:00
Russell Hancox
f2f27c5675 santa-driver: Up ACTION_REQUEST_SHUTDOWN from 60->90 2015-05-18 13:06:31 -04:00
Russell Hancox
5a7ac2287b santa-driver: Stop defining MAX_PATH_LEN, use MAXPATHLEN instead.
I can't recall why I did this.
2015-05-18 13:06:31 -04:00
Russell Hancox
f82da21b75 santactl/sync: Bug from 8cd9898, call completion handler even if no rules are downloaded. 2015-05-15 10:38:26 -04:00
Russell Hancox
969a5ef94e santactl/sync: Don't release SecAsn1Coder until we're done with its data. 2015-05-14 17:35:06 -04:00
Russell Hancox
fd7ad07193 santactl/sync: Handle DER decoding failures more gracefully. 2015-05-14 17:01:49 -04:00
Russell Hancox
3f5400b264 santa-driver: Split FetchDecision, notify daemon of missed executions, reorganize some methods. 2015-05-14 17:01:49 -04:00
Russell Hancox
466b5ed491 santa-driver: Make SantaDecisionManager::AddToCache default the microsecs parameter to the current uptime 2015-05-14 17:01:48 -04:00
Russell Hancox
25f1b71f10 santa-driver: Move dataqueue to SantaDecisionManager rather than
recreating it on every connect.
2015-05-14 17:01:48 -04:00
Russell Hancox
d1295f97b9 santa-driver: Rename owning_pid/proc to client_pid/proc. Minor style
cleanup
2015-05-14 17:01:48 -04:00
Russell Hancox
f5eb274aa0 santa-driver: remove unnecessary dataqueue lock 2015-05-14 17:01:48 -04:00
Russell Hancox
58b9dab74f santa-driver: SDM should call super::init 2015-05-14 17:01:48 -04:00
Russell Hancox
9f6b6d10dc santa-driver: Make room in santa_message_t->path for the terminator so we don't miss a character. 2015-05-14 17:01:47 -04:00
Russell Hancox
57f6e516c2 santa-driver: Mark all overriden methods with 'override' 2015-05-14 17:01:42 -04:00
Russell Hancox
8cd9898cf3 santactl/sync: Don't send empty rules array to daemon. 2015-05-11 16:27:02 -04:00
Russell Hancox
d53b04213a santa-driver: Remove empty spacing at end of line 2015-05-08 14:58:16 -04:00
Russell Hancox
ac99bd1070 santad: Add tests for SNTRuleTable 2015-05-08 14:57:53 -04:00
Russell Hancox
30df44df96 santad: Correctly delete corrupt events. Add test for this. 2015-05-08 14:57:37 -04:00
Russell Hancox
fc55b86f30 santad: Switch to uint32_t for table version numbers 2015-05-08 14:56:20 -04:00
Russell Hancox
59ffb67554 santad: Reject addRules requests with empty/nil array. Also switch to NSUInteger for rule counts. 2015-05-08 14:55:28 -04:00
Russell Hancox
d46b156b85 santa-driver: vnode_getattr sometimes panics if a vfs_context isn't available (when used with osxfuse for instance). 2015-05-07 18:24:29 -04:00
Russell Hancox
6492e70599 santactl: Instead of rejecting authentication challenges and trying again, cancel the whole request. 2015-05-07 17:56:47 -04:00
Russell Hancox
bc5d0f8685 santa-driver: Don't allow StopListener to return until both kauth scopes are done 2015-05-06 14:57:33 -04:00
Russell Hancox
838da16da1 santad: Delete events that fail to unarchive 2015-05-06 14:44:09 -04:00
Russell Hancox
6e242bf98d SantaGUI: Change Line to custom NSBox 2015-05-05 17:44:06 -04:00
Russell Hancox
be1e66c29d Project: Enable more warnings and then fix them. 2015-05-01 17:40:39 -04:00
Russell Hancox
57866308e3 santad: Consider scripts that are part of installer packages as in-scope. 2015-04-30 18:37:04 -04:00
Russell Hancox
63bc8fca2d santad: Don't post GUI notification for SILENT_BLACKLIST rules. 2015-04-30 18:36:43 -04:00
Russell Hancox
408712f00f santad: Don't log when client mode is not set in defaultDecision, as that can't actually happen. 2015-04-30 18:36:20 -04:00
Russell Hancox
8cb6046f94 GUI: Add parent process name, only show part of the shasum, resize window. 2015-04-30 18:24:54 -04:00
Russell Hancox
297fb4cb68 Add parent process name collection and upload 2015-04-30 18:21:13 -04:00
Russell Hancox
1501d413f0 Project: Add install.sh script and package Makefile
Adds an install.sh script that can be run from the distribution tarball
and a Luggage package makefile
2015-04-30 14:12:02 -04:00
Russell Hancox
e747ace0f3 santactl/binaryinfo: Add bundle details to file info 2015-04-30 12:34:31 -04:00
Russell Hancox
6b96f36b2b SantaGUI: Re-create AboutWindowController each time it's needed so that More Info button state is correct 2015-04-22 15:59:04 -04:00
Russell Hancox
f16fa691b5 santactl: include zlib.h instead of import 2015-04-21 16:43:09 -04:00
Russell Hancox
4fd5e1139f Project: Style clean-ups 2015-04-21 14:29:30 -04:00
Russell Hancox
0b33079833 Merge pull request #16 from marczak/readme-up
Updated README to clarify intentions and expectations
2015-04-21 09:14:50 -07:00
Edward Marczak
6069ed5801 Update README.md 2015-04-21 12:14:05 -04:00
Edward Marczak
c2a9061ea2 Updated README to clarify expectations. 2015-04-21 11:24:58 -04:00
Russell Hancox
ee963d62a4 Project: Update README to mention dyld issue 2015-04-21 10:02:32 -04:00
Russell Hancox
c12adbc8e6 Project: Update schemes to make Xcode be quiet. 2015-04-20 18:35:10 -04:00
Russell Hancox
e6b20bcce6 Project: update Podfile.lock 2015-04-20 18:07:33 -04:00
Russell Hancox
10333bba01 santa-driver: Change file-write cache check, use FileOp scope for most writes and use hasdirtyblks to catch flushed but still-open files. 2015-04-20 18:07:22 -04:00
Russell Hancox
437764e6fc Conf: Undo adding Username/Groupname to santasync launchd, it doesn't work properly 2015-04-13 16:41:47 -04:00
Russell Hancox
460dd6aa8b Project: Stop using xctool, use xcpretty to make xcodebuild output nice instead. 2015-04-10 16:37:24 -04:00
Russell Hancox
0a511468e3 Conf: Run scheduled santactl/sync runs as nobody 2015-04-10 16:06:56 -04:00
Russell Hancox
96517573e7 santactl/sync: rename SyncStatus->SyncState, add cleanSync option that can be requested by client or server. 2015-04-10 12:39:22 -04:00
Russell Hancox
c996921c22 GUI: Move window centering to the fadeIn method so it's only called when the window is displayed 2015-04-10 10:07:53 -04:00
Russell Hancox
8365e00a50 Sync: Decision should be uploaded as a string, not an int. Rename serial_no to serial_num 2015-04-09 10:02:21 -04:00
Russell Hancox
a629e6cff1 Clean-up: NSString length is quicker than isEqual 2015-04-09 10:01:44 -04:00
Russell Hancox
cbb786c6d1 Kext: Check fSharedMemory and fDataQueue before trying to release them in terminate() 2015-04-09 10:01:04 -04:00
Russell Hancox
49b169ec36 SNTFileWatcher: unset source event handler correctly 2015-04-08 14:22:24 -04:00
Russell Hancox
41d1d7e3de SNTFileWatcher: fix some dispatch bugs, move eventHandler property to
class extension, add tests.
2015-04-08 12:29:41 -04:00
Russell Hancox
323a38dc21 Project: Clean-up block style, order of public/private in C++ headers 2015-04-08 12:29:22 -04:00
Russell Hancox
c37f1eb006 SNTConfigurator: remove auto-reloading code, move to file watching
class.
2015-04-08 12:28:58 -04:00
Russell Hancox
b7b2b5b630 santactl/status: Add daemon status, currently just the mode. 2015-04-01 16:15:35 -04:00
Russell Hancox
2486cfdcff santactl/sync: Update logging 2015-04-01 16:15:14 -04:00
Russell Hancox
4231781178 Project: Update CocoaPods 2015-03-31 18:13:37 -04:00
Russell Hancox
7ba886ed18 SNTConfigurator: Fix broken MachineOwner parsing, part 2 2015-03-31 17:29:11 -04:00
Russell Hancox
8096701fbd SNTConfigurator: Fix broken MachineID/MachineOwner parsing 2015-03-31 17:26:02 -04:00
Russell Hancox
16531d18c8 santa-driver: Kill daemon if PostToQueue is failing too much. 2015-03-31 16:14:32 -04:00
Russell Hancox
ef0cc2fffd santactl/sync: Put constant API strings in a separate file 2015-03-31 15:56:47 -04:00
Russell Hancox
f2dc7fb4b0 SNTConfigurator: Only reject and re-save client mode in santad. 2015-03-31 15:49:49 -04:00
Russell Hancox
707e9a11d4 SantaGUI: Let background move window, part 2. 2015-03-31 12:20:43 -04:00
Russell Hancox
aef3d57dcf SantaGUI: Ensure configuration is loaded before first message arrives, let window be moved, close window properly when opening event URL 2015-03-30 18:45:06 -04:00
Russell Hancox
cfb38068f8 santa-driver: DisconnectClient should return straight away if client is not properly connected 2015-03-30 18:37:36 -04:00
Russell Hancox
ca19d9fde7 SNTConfigurator: Do initial file monitoring async 2015-03-30 18:37:18 -04:00
Russell Hancox
e19aab36bd SantaGUI: Close window when opening web pages 2015-03-27 16:56:13 -04:00
Russell Hancox
111540f0a8 LogicTests: Update some Stub calls to Expect 2015-03-27 15:39:13 -04:00
Russell Hancox
88897477b6 santad: Now that SNTConfigurator auto-reloads, stop tracking clientMode separately. Also reload logAllDecisions if req'd. 2015-03-27 15:38:55 -04:00
Russell Hancox
a9d6e42d5a SNTConfigurator: Have configuration auto-reload if file on disk changes 2015-03-27 15:38:10 -04:00
Russell Hancox
8b5720b291 santad: Update comments in DriverManager 2015-03-27 15:37:08 -04:00
Russell Hancox
2d9f392efc santa-driver: Ensure fSDM and fDataQueue are NULL'd ASAP. 2015-03-27 15:36:24 -04:00
Russell Hancox
76844eb77d santa-driver: lock data queue mutex when retaining/releasing queue 2015-03-27 15:35:50 -04:00
Russell Hancox
2db996f8e0 SantaGUI: Add configurable About Window and Event Detail buttons 2015-03-27 15:35:16 -04:00
Russell Hancox
6c27ac60a1 SNTConfigurator: if MachineID hasn't been overridden get one from IOKit 2015-03-26 15:08:26 -04:00
Russell Hancox
d4c4b26c3b santactl/sync: Fix MachineOwnerPlist* config methods, don't crash if machine owner missing 2015-03-26 08:24:53 -04:00
Russell Hancox
50614f589c santactl/sync: Update Rule download API 2015-03-25 17:17:41 -04:00
Russell Hancox
0292d4e956 GUI: Handle missing custom message in event notifications 2015-03-16 17:29:27 -04:00
Russell Hancox
4e1e4cde3b santactl/sync: Fix broken Log Upload, re-add gzip compression 2015-03-16 17:05:49 -04:00
Russell Hancox
c86f0e7c80 Project: unload kext using bundle ID 2015-03-16 15:09:38 -04:00
Russell Hancox
77b8edda79 Project: enable 'deep' static analysis for all builds except for LogicTests and KernelTests 2015-03-16 15:04:18 -04:00
Russell Hancox
f3d098c521 Project: Add LogicTests scheme that can be used for logic tests without building all binaries 2015-03-16 15:03:46 -04:00
Russell Hancox
0afe465ac5 santactl: Rename machineIDOverride to machineID 2015-03-13 18:18:09 -04:00
Russell Hancox
472558a03c santad: Add LogAllEvents option 2015-03-13 18:17:09 -04:00
Russell Hancox
dfef7d8567 SantaGUI: Fix broken predicate for pending notifications 2015-03-13 17:33:18 -04:00
Russell Hancox
925903e07d SantaGUI: Move publisher certs button, make publisher info autosize width 2015-03-13 17:29:31 -04:00
Russell Hancox
a43c0ee295 SantaGUI: swap publisher/sha256 fields in message window 2015-03-13 16:57:25 -04:00
Russell Hancox
1e82b5abc6 Missed some newlines in printf statements 2015-03-13 16:56:38 -04:00
Russell Hancox
7502dbdec6 Project: Enable NDEBUG for FMDB in release builds. 2015-03-13 15:35:21 -04:00
Russell Hancox
217ad25531 santactl: move more commands from LOG* to printf 2015-03-13 12:48:49 -04:00
Russell Hancox
7c3b533679 santactl: Update help strings 2015-03-13 12:47:28 -04:00
Russell Hancox
2c4ba45988 santa-driver: Move from IO*Lock to lck_* 2015-03-12 14:34:52 -04:00
Russell Hancox
26ee0a68d1 santactl: Separate out version info from status, move user-only commands from LOG* to printf 2015-03-12 14:31:49 -04:00
Russell Hancox
27eb2e9cff santactl: Remove old todo about establishing connection 2015-03-11 21:42:15 -04:00
Russell Hancox
9431d954b5 SNTXPCConnection: invalidate connection if verification doesn't complete 2015-03-11 21:41:53 -04:00
Russell Hancox
1a2d8b55f8 SNTSystemInfo: NSHost is awful, use gethostname() instead 2015-03-11 21:24:56 -04:00
Russell Hancox
d27a26ca50 santad: Make driver-waiting an exponential backoff up to 16s 2015-03-11 19:14:41 -04:00
102 changed files with 2768 additions and 1231 deletions

View File

@@ -2,9 +2,7 @@
language: objective-c
before_install:
- gem install cocoapods
- brew update
- brew upgrade xctool
- gem install cocoapods xcpretty
script:
- xctool -workspace Santa.xcworkspace -scheme All build test CODE_SIGN_IDENTITY=''
- xcodebuild -workspace Santa.xcworkspace -scheme All build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}

102
Conf/Package/Makefile Normal file
View File

@@ -0,0 +1,102 @@
#
# Package Makefile for Santa
# Requires TheLuggage (github.com/unixorn/luggage) to be installed
#
# Will generate a package based on the latest release. You can replace
# the PACKAGE_VERSION variable with a specific variable instead if you wish.
#
LUGGAGE:=/usr/local/share/luggage/luggage.make
include ${LUGGAGE}
TITLE:=santa
REVERSE_DOMAIN:=com.google
# Get latest Release version using the GitHub API. Each release is bound to a
# git tag, which should always be a semantic version number. The most recent
# release is always first in the API result.
PACKAGE_VERSION:=$(shell curl -fs https://api.github.com/repos/google/santa/releases |\
python -c 'import json, sys; print json.load(sys.stdin)[0]["tag_name"]' 2>/dev/null)
# Get the download URL for the latest Release. Each release should have a
# tarball named santa-$version.tar.bz2 containing all of the files associated
# with that release. The tarball layout is:
#
# santa-$version.tar.bz2
# +--santa-$version
# |-- binaries
# | |-- santa-driver.kext
# | |-- Santa.app
# | |-- santad
# | +-- santactl
# |-- conf
# | |-- install.sh
# | |-- com.google.santad.plist
# | |-- com.google.santasync.plist
# | |-- com.google.santagui.plist
# | +-- com.google.santa.asl.conf
# +--dsym
# |-- santa-driver.kext.dSYM
# |-- Santa.app.dSYM
# |-- santad.dSYM
# +-- santactl.dSYM
PACKAGE_DOWNLOAD_URL:="https://github.com/google/santa/releases/download/${PACKAGE_VERSION}/santa-${PACKAGE_VERSION}.tar.bz2"
PAYLOAD:=pack-usr-libexec-santad \
pack-usr-sbin-santactl \
pack-Library-Extensions-santa-driver.kext \
pack-applications-Santa.app \
pack-Library-LaunchDaemons-com.google.santad.plist \
pack-Library-LaunchDaemons-com.google.santasync.plist \
pack-Library-LaunchAgents-com.google.santagui.plist \
pack-etc-asl-com.google.santa.asl.conf \
pack-script-preinstall \
pack-script-postinstall
santad: download
santactl: download
santa-driver.kext: download
Santa.app: download
com.google.santad.plist: download
com.google.santagui.plist: download
com.google.santasync.plist: download
com.google.santa.asl.conf: download
download:
$(if $(PACKAGE_VERSION),, $(error GitHub API returned unexpected result. Wait a while and try again))
@curl -fL ${PACKAGE_DOWNLOAD_URL} | tar xvj --strip=2
@rm -rf *.dSYM
pack-usr-libexec-santad: santad l_usr
@sudo mkdir -p ${WORK_D}/usr/libexec
@sudo chown root:wheel ${WORK_D}/usr/libexec
@sudo chmod 755 ${WORK_D}/usr/libexec
@sudo install -m 755 -o root -g wheel santad ${WORK_D}/usr/libexec
pack-etc-asl-com.google.santa.asl.conf: com.google.santa.asl.conf l_private_etc
@sudo mkdir -p ${WORK_D}/private/etc/asl
@sudo chown root:wheel ${WORK_D}/private/etc/asl
@sudo chmod 755 ${WORK_D}/private/etc/asl
@sudo install -m 644 -o root -g wheel com.google.santa.asl.conf ${WORK_D}/private/etc/asl
pack-Library-Extensions-santa-driver.kext: santa-driver.kext l_Library
@sudo mkdir -p ${WORK_D}/Library/Extensions
@sudo ${DITTO} --noqtn santa-driver.kext ${WORK_D}/Library/Extensions/santa-driver.kext
@sudo chown -R root:wheel ${WORK_D}/Library/Extensions/santa-driver.kext
@sudo chmod -R 755 ${WORK_D}/Library/Extensions/santa-driver.kext
clean: myclean
myclean:
@rm -rf *.dSYM
@rm -rf Santa.app
@rm -rf santa-driver.kext
@rm -f santad
@rm -f santactl
@rm -f config.plist
@rm -f com.google.santa.asl.conf
@rm -f com.google.santad.plist
@rm -f com.google.santagui.plist
@rm -f com.google.santasync.plist
@rm -f install.sh

25
Conf/Package/postinstall Normal file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
# Load the kernel extension, santad, sync client
# If a user is logged in, also load the GUI agent.
# If the target volume is not /, do nothing
[[ $3 != "/" ]] && exit 0
# Restart syslogd to pick up ASL configuration change
/usr/bin/killall -HUP syslogd
/sbin/kextload /Library/Extensions/santa-driver.kext
sleep 1
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santad.plist
/bin/launchctl load -w /Library/LaunchDaemons/com.google.santasync.plist
sleep 1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -z "$user" ]] && exit 0
/bin/launchctl asuser ${user} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
exit 0

21
Conf/Package/preinstall Normal file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# Unload the kernel extension, santad, sync client
# If a user is logged in, also unload the GUI agent.
# If the target volume is not /, do nothing
[[ $3 != "/" ]] && exit 0
/bin/launchctl remove com.google.santad
/bin/launchctl remove com.google.santasync
sleep 1
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
sleep 1
user=$(/usr/bin/stat -f '%u' /dev/console)
[[ -n "$user" ]] && /bin/launchctl asuser ${user} /bin/launchctl remove com.google.santagui
exit 0

55
Conf/install.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root" 1>&2
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
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
/bin/launchctl remove com.google.santasync >/dev/null 2>&1
# Unload kext.
/sbin/kextunload -b com.google.santa-driver >/dev/null 2>&1
# 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
# Copy new files.
/bin/cp ${SOURCE}/binaries/santad /usr/libexec
/bin/cp ${SOURCE}/binaries/santactl /usr/sbin
/bin/cp -r ${SOURCE}/binaries/santa-driver.kext /Library/Extensions
/bin/cp -r ${SOURCE}/binaries/Santa.app /Applications
/bin/cp ${SOURCE}/conf/com.google.{santad,santasync}.plist /Library/LaunchDaemons
/bin/cp ${SOURCE}/conf/com.google.santagui.plist /Library/LaunchAgents
/bin/cp ${SOURCE}/conf/com.google.santa.asl.conf /etc/asl/
# Reload syslogd to pick up ASL configuration change.
/usr/bin/killall -HUP syslogd
# Load kext.
/sbin/kextload /Library/Extensions/santa-driver.kext
# Load santad and scheduled sync jobs.
/bin/launchctl load /Library/LaunchDaemons/com.google.santad.plist
/bin/launchctl load /Library/LaunchDaemons/com.google.santasync.plist
# Load GUI agent if someone is logged in.
[[ -n "$GUI_USER" ]] && \
/bin/launchctl asuser ${GUI_USER} /bin/launchctl load /Library/LaunchAgents/com.google.santagui.plist
exit 0

12
Podfile
View File

@@ -4,6 +4,18 @@ inhibit_all_warnings!
target :santad do
pod 'FMDB'
post_install do |rep|
rep.project.targets.each do |target|
target.build_configurations.each do |config|
if config.name != 'Release' then
break
end
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ''
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] <<= "NDEBUG=1"
end
end
end
end
target :LogicTests do

View File

@@ -11,7 +11,7 @@ DEPENDENCIES:
- OCMock
SPEC CHECKSUMS:
FMDB: 0efa188cf0dd1ce82c27a478cd5f5fa245308677
OCMock: ecdd510b73ef397f2f97274785c1e87fd147c49f
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
OCMock: a10ea9f0a6e921651f96f78b6faee95ebc813b92
COCOAPODS: 0.35.0
COCOAPODS: 0.36.3

View File

@@ -46,11 +46,21 @@ server.
programming interfaces to do its job. This means that the kext code should
continue to work across OS versions.
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 allowing unknown software to exfiltrate more data about your system. As a centrally managed component, Santa can help stop the spread of malware among a larger fleet of machines. Additionally, Santa can aid in analyzing what is running in your fleet.
Santa is part of a defense-in-depth strategy, and you should continue to protect hosts in whatever other ways you see fit.
Known Issues
============
Santa is not yet a 1.0 and we have some known issues to be aware of:
* 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. We are working on also protecting
against these 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.
@@ -80,7 +90,6 @@ option) if it would be useful to others.
Building
========
```sh
git clone https://github.com/google/santa
cd santa
@@ -98,7 +107,6 @@ and for security-reasons parts of Santa will not operate properly if not signed.
Kext Signing
============
10.9 requires a special Developer ID certificate to sign kernel extensions and
if the kext is not signed with one of these special certificates a warning will
be shown when loading the kext for the first time. In 10.10 this is a hard error
@@ -124,11 +132,9 @@ kext-dev mode, instructions for which can be found on the Apple developer site.
Contributing
============
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
file.
Disclaimer
==========
This is **not** an official Google product.

View File

@@ -1,46 +1,24 @@
require 'timeout'
WORKSPACE = 'Santa.xcworkspace'
DEFAULT_SCHEME = 'All'
OUTPUT_PATH = 'Build'
DIST_PATH = 'Dist'
BINARIES = ['Santa.app', 'santa-driver.kext', 'santad', 'santactl']
XCTOOL_DEFAULTS = "-workspace #{WORKSPACE} -scheme #{DEFAULT_SCHEME}"
XCODE_DEFAULTS = "-workspace #{WORKSPACE} -scheme #{DEFAULT_SCHEME} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
WORKSPACE = 'Santa.xcworkspace'
DEFAULT_SCHEME = 'All'
OUTPUT_PATH = 'Build'
DIST_PATH = 'Dist'
BINARIES = ['Santa.app', 'santa-driver.kext', 'santad', 'santactl']
XCPRETTY_DEFAULTS = '-sc'
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
task :default do
system("rake -sT")
end
def xctool_available
return system 'xctool --version >/dev/null 2>&1'
end
def run_and_output_on_fail(cmd)
output=`#{cmd} 2>&1`
if not $?.success?
raise output
end
end
def run_and_output_with_color(cmd)
output=`#{cmd} 2>&1`
has_output = false
output.scan(/((Test Suite|Test Case|Executed).*)$/) do |match|
has_output = true
out = match[0]
if out.include?("passed")
puts "\e[32m#{out}\e[0m"
elsif out.include?("failed")
puts "\e[31m#{out}\e[0m"
else
puts out
end
end
if not has_output
raise output
def xcodebuild(opts)
if system "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts} | " \
"xcpretty #{XCPRETTY_DEFAULTS} && " \
"exit ${PIPESTATUS[0]}"
puts "\e[32mPass\e[0m"
else
raise "\e[31mFail\e[0m"
end
end
@@ -49,6 +27,9 @@ task :init do
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'"
end
end
task :remove_existing do
@@ -61,7 +42,7 @@ end
desc "Clean"
task :clean => :init do
puts "Cleaning"
system "xcodebuild #{XCODE_DEFAULTS} clean"
xcodebuild("-scheme All clean")
FileUtils.rm_rf(OUTPUT_PATH)
FileUtils.rm_rf(DIST_PATH)
end
@@ -81,11 +62,7 @@ namespace :build do
task :build, [:configuration] => :init do |t, args|
config = args[:configuration]
puts "Building with configuration: #{config}"
if xctool_available
system "xctool #{XCTOOL_DEFAULTS} -configuration #{config} build"
else
system "xcodebuild #{XCODE_DEFAULTS} -configuration #{config} build"
end
xcodebuild("-scheme All -configuration #{config} build")
end
end
@@ -135,7 +112,7 @@ task :dist do
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}.dSYM", "#{DIST_PATH}/dsym")
end
Dir.glob("Conf/*") {|x| FileUtils.cp(x, "#{DIST_PATH}/conf")}
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{DIST_PATH}/conf")}
puts "Distribution folder created"
end
@@ -145,11 +122,7 @@ namespace :tests do
desc "Tests: Logic"
task :logic => [:init] do
puts "Running logic tests"
if xctool_available
system "xctool #{XCTOOL_DEFAULTS} test"
else
system "xcodebuild #{XCODE_DEFAULTS} test"
end
xcodebuild("-scheme LogicTests test")
end
desc "Tests: Kernel"
@@ -178,7 +151,7 @@ end
task :unload_kext do
puts "Unloading kernel extension"
system "sudo kextunload /santa-driver.kext 2>/dev/null"
system "sudo kextunload -b com.google.santa-driver 2>/dev/null"
end
task :unload_gui do

View File

@@ -69,7 +69,7 @@
0D3AFBF018FB4C6C0087BCEE /* SNTDriverManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */; };
0D3AFBF618FB4C7E0087BCEE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D385DB7180DE4A900418BC6 /* Cocoa.framework */; };
0D3AFBF818FB4C870087BCEE /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0D416401191974F1006A356A /* SNTCommandSyncStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D416400191974F1006A356A /* SNTCommandSyncStatus.m */; };
0D416401191974F1006A356A /* SNTCommandSyncState.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D416400191974F1006A356A /* SNTCommandSyncState.m */; };
0D41640519197AD7006A356A /* SNTCommandSyncEventUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D41640419197AD7006A356A /* SNTCommandSyncEventUpload.m */; };
0D41DAD41A7C28C800A890FE /* SNTEventTableTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */; };
0D42D2B519D1D98A00955F08 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B419D1D98A00955F08 /* SNTSystemInfo.m */; };
@@ -97,6 +97,10 @@
0D7FFD4B1A017D4B00F34435 /* SNTDERDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7FFD4A1A017D4B00F34435 /* SNTDERDecoder.m */; };
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B719D2042900955F08 /* SNTConfigurator.m */; };
0D827E6719DF3C74006EC811 /* SNTCommandStatus.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827E6619DF3C74006EC811 /* SNTCommandStatus.m */; };
0D8868091AC48A1100B86659 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B419D1D98A00955F08 /* SNTSystemInfo.m */; };
0D88680A1AC48A1200B86659 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B419D1D98A00955F08 /* SNTSystemInfo.m */; };
0D88680C1AC48A1400B86659 /* SNTSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B419D1D98A00955F08 /* SNTSystemInfo.m */; };
0D88680D1AC48A5D00B86659 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0D8C200C180F359A00CE2BF8 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8C200B180F359A00CE2BF8 /* Security.framework */; };
0D8E18CD19107B56000F89B8 /* SNTDaemonControlController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D8E18CC19107B56000F89B8 /* SNTDaemonControlController.m */; };
0D9A7F331759144800035EB5 /* SantaDriver.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D9A7F311759144800035EB5 /* SantaDriver.cc */; };
@@ -108,6 +112,8 @@
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DA73CA11934F8100056D7C4 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DB390991AB1E11400614002 /* SNTCommandVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB390981AB1E11400614002 /* SNTCommandVersion.m */; };
0DB537871AFD36EB00487F92 /* SNTRuleTableTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */; };
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */; };
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
0DC5D86E191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
@@ -142,6 +148,13 @@
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */; };
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */; };
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C71ACF0BFE00B92AAE /* SNTFileWatcherTest.m */; };
0DF395641AB76A7900CBC520 /* NSData+Zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DF395631AB76A7900CBC520 /* NSData+Zlib.m */; };
0DF395661AB76ABC00CBC520 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DF395651AB76ABC00CBC520 /* libz.dylib */; };
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 409232791A51B65D00A04527 /* SNTCommandRule.m */; };
8BFD9B39112F4D16B3D0EFFB /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */; };
E86AE075D7F24FB88FB627C5 /* libPods-santad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A84545E322F475FA0B505D5 /* libPods-santad.a */; };
@@ -241,8 +254,8 @@
0D385DEF180DE51600418BC6 /* SNTNotificationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTNotificationManager.m; sourceTree = "<group>"; };
0D3AFBE618FB32CB0087BCEE /* SNTXPCConnectionTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTXPCConnectionTest.m; sourceTree = "<group>"; };
0D3AFBF718FB4C870087BCEE /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
0D4163FF191974F1006A356A /* SNTCommandSyncStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncStatus.h; sourceTree = "<group>"; };
0D416400191974F1006A356A /* SNTCommandSyncStatus.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncStatus.m; sourceTree = "<group>"; };
0D4163FF191974F1006A356A /* SNTCommandSyncState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncState.h; sourceTree = "<group>"; };
0D416400191974F1006A356A /* SNTCommandSyncState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncState.m; sourceTree = "<group>"; };
0D41640319197AD7006A356A /* SNTCommandSyncEventUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncEventUpload.h; sourceTree = "<group>"; };
0D41640419197AD7006A356A /* SNTCommandSyncEventUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncEventUpload.m; sourceTree = "<group>"; };
0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTEventTableTest.m; sourceTree = "<group>"; };
@@ -288,6 +301,8 @@
0D9A7F411759330500035EB5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = main.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
0DA73C9E1934F8100056D7C4 /* SNTLogging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTLogging.m; sourceTree = "<group>"; };
0DB2B92318085753001C01D9 /* santad-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "santad-Prefix.pch"; sourceTree = "<group>"; };
0DB390981AB1E11400614002 /* SNTCommandVersion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SNTCommandVersion.m; path = version/SNTCommandVersion.m; sourceTree = "<group>"; };
0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRuleTableTest.m; sourceTree = "<group>"; };
0DB8ACBF185662DC00FEF9C7 /* SNTApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTApplication.h; sourceTree = "<group>"; };
0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SNTApplication.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
0DB8ACE41858D73000FEF9C7 /* santad-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "santad-Info.plist"; sourceTree = "<group>"; };
@@ -317,6 +332,14 @@
0DE50F671912716A007B2B0C /* SNTRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRule.m; sourceTree = "<group>"; };
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTExecutionController.h; sourceTree = "<group>"; };
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SNTExecutionController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncConstants.m; sourceTree = "<group>"; };
0DEFB7C11ACB28BC00B92AAE /* SNTCommandSyncConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncConstants.h; sourceTree = "<group>"; };
0DEFB7C21ACDD80100B92AAE /* SNTFileWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTFileWatcher.h; sourceTree = "<group>"; };
0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileWatcher.m; sourceTree = "<group>"; };
0DEFB7C71ACF0BFE00B92AAE /* SNTFileWatcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileWatcherTest.m; sourceTree = "<group>"; };
0DF395621AB76A7900CBC520 /* NSData+Zlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Zlib.h"; sourceTree = "<group>"; };
0DF395631AB76A7900CBC520 /* NSData+Zlib.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Zlib.m"; sourceTree = "<group>"; };
0DF395651AB76ABC00CBC520 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
13A4FE400F3857C0F5831498 /* Pods-LogicTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LogicTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests.debug.xcconfig"; sourceTree = "<group>"; };
409232791A51B65D00A04527 /* SNTCommandRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SNTCommandRule.m; path = rule/SNTCommandRule.m; sourceTree = "<group>"; };
627BB4EC9917DC20E89D718C /* Pods-santad.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santad/Pods-santad.debug.xcconfig"; sourceTree = "<group>"; };
@@ -349,6 +372,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0DF395661AB76ABC00CBC520 /* libz.dylib in Frameworks */,
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */,
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */,
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */,
@@ -359,6 +383,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0D88680D1AC48A5D00B86659 /* IOKit.framework in Frameworks */,
0D6F12D819EC8822006B218E /* SecurityInterface.framework in Frameworks */,
0D8C200C180F359A00CE2BF8 /* Security.framework in Frameworks */,
0D385DB8180DE4A900418BC6 /* Cocoa.framework in Frameworks */,
@@ -397,6 +422,8 @@
0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */,
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */,
0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */,
0DEFB7C71ACF0BFE00B92AAE /* SNTFileWatcherTest.m */,
0DB537861AFD36EB00487F92 /* SNTRuleTableTest.m */,
0D3AFBE618FB32CB0087BCEE /* SNTXPCConnectionTest.m */,
);
path = LogicTests;
@@ -419,14 +446,15 @@
0D35BDA018FD71CE00921A21 /* santactl */ = {
isa = PBXGroup;
children = (
409232751A51914400A04527 /* rule */,
0D35BDA118FD71CE00921A21 /* main.m */,
0D35BDAA18FD7CFD00921A21 /* SNTCommandController.h */,
0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */,
0DCD5FBC1909D4FD006B445C /* binaryinfo */,
0DE4C8A318FF3AFA00466D04 /* flushcache */,
409232751A51914400A04527 /* rule */,
0D827E6819DF4F3F006EC811 /* status */,
0D35BDB618FD84FC00921A21 /* sync */,
0DB390971AB1E0F200614002 /* version */,
0D35BDA318FD71CE00921A21 /* Resources */,
);
name = santactl;
@@ -445,9 +473,13 @@
0D35BDB618FD84FC00921A21 /* sync */ = {
isa = PBXGroup;
children = (
0DF395621AB76A7900CBC520 /* NSData+Zlib.h */,
0DF395631AB76A7900CBC520 /* NSData+Zlib.m */,
0DCD6060191188B1006B445C /* SNTAuthenticatingURLSession.h */,
0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */,
0D35BDB418FD84F600921A21 /* SNTCommandSync.m */,
0DEFB7C11ACB28BC00B92AAE /* SNTCommandSyncConstants.h */,
0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */,
0D41640319197AD7006A356A /* SNTCommandSyncEventUpload.h */,
0D41640419197AD7006A356A /* SNTCommandSyncEventUpload.m */,
0DC5D86F192160180078A5C0 /* SNTCommandSyncLogUpload.h */,
@@ -458,8 +490,8 @@
0DCD605B19117A90006B445C /* SNTCommandSyncPreflight.m */,
0D0A1EC1191998C900B8450F /* SNTCommandSyncRuleDownload.h */,
0D0A1EC2191998C900B8450F /* SNTCommandSyncRuleDownload.m */,
0D4163FF191974F1006A356A /* SNTCommandSyncStatus.h */,
0D416400191974F1006A356A /* SNTCommandSyncStatus.m */,
0D4163FF191974F1006A356A /* SNTCommandSyncState.h */,
0D416400191974F1006A356A /* SNTCommandSyncState.m */,
0D7FFD491A017D4B00F34435 /* SNTDERDecoder.h */,
0D7FFD4A1A017D4B00F34435 /* SNTDERDecoder.m */,
);
@@ -555,6 +587,7 @@
0D91BCB6174E8A7E00131A7D /* Frameworks */ = {
isa = PBXGroup;
children = (
0DF395651AB76ABC00CBC520 /* libz.dylib */,
0DCD5F771909C659006B445C /* SecurityInterface.framework */,
0D3AFBF718FB4C870087BCEE /* IOKit.framework */,
0D8C200B180F359A00CE2BF8 /* Security.framework */,
@@ -599,6 +632,8 @@
0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */,
0DCD6040190ACCB8006B445C /* SNTFileInfo.h */,
0DCD6041190ACCB8006B445C /* SNTFileInfo.m */,
0DEFB7C21ACDD80100B92AAE /* SNTFileWatcher.h */,
0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */,
0D28E5E31926AFE400280F87 /* SNTKernelCommon.h */,
0D28E5E119269B3600280F87 /* SNTLogging.h */,
0DA73C9E1934F8100056D7C4 /* SNTLogging.m */,
@@ -661,6 +696,14 @@
name = DataLayer;
sourceTree = "<group>";
};
0DB390971AB1E0F200614002 /* version */ = {
isa = PBXGroup;
children = (
0DB390981AB1E11400614002 /* SNTCommandVersion.m */,
);
name = version;
sourceTree = "<group>";
};
0DCD5FBC1909D4FD006B445C /* binaryinfo */ = {
isa = PBXGroup;
children = (
@@ -1052,6 +1095,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D88680C1AC48A1400B86659 /* SNTSystemInfo.m in Sources */,
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
0D3AFBF018FB4C6C0087BCEE /* SNTDriverManager.m in Sources */,
0DCD6044190ACCB8006B445C /* SNTFileInfo.m in Sources */,
@@ -1060,10 +1104,13 @@
0DD0D491194F9947005F27EB /* SNTExecutionControllerTest.m in Sources */,
0D3AFBEF18FB4C6C0087BCEE /* SNTExecutionController.m in Sources */,
0D3AFBEC18FB48E70087BCEE /* SNTEventTable.m in Sources */,
0DB537871AFD36EB00487F92 /* SNTRuleTableTest.m in Sources */,
0DCD604D19105433006B445C /* SNTStoredEvent.m in Sources */,
0DCD605819115E57006B445C /* SNTXPCControlInterface.m in Sources */,
0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */,
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */,
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */,
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */,
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */,
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */,
0D3AFBE718FB32CB0087BCEE /* SNTXPCConnectionTest.m in Sources */,
@@ -1090,6 +1137,7 @@
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.m in Sources */,
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */,
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */,
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */,
0DCD6062191188B1006B445C /* SNTAuthenticatingURLSession.m in Sources */,
0DCD605619115D17006B445C /* SNTXPCControlInterface.m in Sources */,
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */,
@@ -1097,16 +1145,18 @@
0DCD605C19117A90006B445C /* SNTCommandSyncPreflight.m in Sources */,
0D41640519197AD7006A356A /* SNTCommandSyncEventUpload.m in Sources */,
0D42D2B919D2042900955F08 /* SNTConfigurator.m in Sources */,
0DF395641AB76A7900CBC520 /* NSData+Zlib.m in Sources */,
0D10BE871A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */,
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */,
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */,
0D416401191974F1006A356A /* SNTCommandSyncStatus.m in Sources */,
0D416401191974F1006A356A /* SNTCommandSyncState.m in Sources */,
0DC5D871192160180078A5C0 /* SNTCommandSyncLogUpload.m in Sources */,
0D35BDA218FD71CE00921A21 /* main.m in Sources */,
0DCD6043190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */,
0D0A1EC3191998C900B8450F /* SNTCommandSyncRuleDownload.m in Sources */,
0D35BDC018FDA5C800921A21 /* SNTCertificate.m in Sources */,
0DB390991AB1E11400614002 /* SNTCommandVersion.m in Sources */,
0D42D2B519D1D98A00955F08 /* SNTSystemInfo.m in Sources */,
0D827E6719DF3C74006EC811 /* SNTCommandStatus.m in Sources */,
0D0A1EC6191AB9B000B8450F /* SNTCommandSyncPostflight.m in Sources */,
@@ -1122,6 +1172,7 @@
0D54E0B11976F8D3000BB59F /* SNTFileInfo.m in Sources */,
0DCA552718C95928002A7DAE /* SNTXPCConnection.m in Sources */,
0D385DF1180DE51600418BC6 /* SNTAppDelegate.m in Sources */,
0D88680A1AC48A1200B86659 /* SNTSystemInfo.m in Sources */,
0DCD605119115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */,
0D385DF2180DE51600418BC6 /* SNTMessageWindowController.m in Sources */,
@@ -1132,6 +1183,7 @@
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */,
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */,
0DA73CA11934F8100056D7C4 /* SNTLogging.m in Sources */,
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1151,6 +1203,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D8868091AC48A1100B86659 /* SNTSystemInfo.m in Sources */,
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */,
0D10BE861A0AABD600C0C944 /* SNTDropRootPrivs.m in Sources */,
0D63DD5C1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
@@ -1160,6 +1213,7 @@
0D1AF477187C7A2C00D3298D /* SNTCertificate.m in Sources */,
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */,
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */,
0D7D01871774F93A005DBAB4 /* SNTDriverManager.m in Sources */,
0D8E18CD19107B56000F89B8 /* SNTDaemonControlController.m in Sources */,
@@ -1241,6 +1295,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
WARNING_CFLAGS = "";
};
name = Debug;
};
@@ -1269,6 +1324,7 @@
GCC_WARN_UNUSED_VARIABLE = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
WARNING_CFLAGS = "";
};
name = Release;
};
@@ -1277,7 +1333,6 @@
baseConfigurationReference = 13A4FE400F3857C0F5831498 /* Pods-LogicTests.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Santa.app/Contents/MacOS/Santa";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
@@ -1318,6 +1373,7 @@
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
@@ -1327,7 +1383,6 @@
baseConfigurationReference = BE74E23CF5A553E5F02462B9 /* Pods-LogicTests.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Santa.app/Contents/MacOS/Santa";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
@@ -1362,6 +1417,7 @@
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
};
name = Release;
@@ -1372,6 +1428,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@@ -1410,6 +1467,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@@ -1443,6 +1501,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@@ -1483,6 +1542,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@@ -1522,6 +1582,11 @@
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
);
};
name = Debug;
};
@@ -1537,6 +1602,11 @@
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
WARNING_CFLAGS = (
"-Wall",
"-Wextra",
"-Wno-unused-parameter",
);
};
name = Release;
};
@@ -1545,6 +1615,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
@@ -1569,7 +1640,10 @@
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
WARNING_CFLAGS = "-Wno-deprecated-register";
WARNING_CFLAGS = (
"-Wno-deprecated-register",
"$(inherit)",
);
WRAPPER_EXTENSION = kext;
};
name = Debug;
@@ -1579,6 +1653,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
@@ -1599,7 +1674,10 @@
MODULE_VERSION = TO.BE.FILLED;
OTHER_LDFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "-Wno-deprecated-register";
WARNING_CFLAGS = (
"-Wno-deprecated-register",
"$(inherit)",
);
WRAPPER_EXTENSION = kext;
};
name = Release;
@@ -1624,6 +1702,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
@@ -1658,6 +1737,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_STATIC_ANALYZER_MODE = deep;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -49,6 +49,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCDC174E8AE600131A7D"
BuildableName = "All"
BlueprintName = "All"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0600"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D0016A1192BCD3C005E7FCD"

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
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
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<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>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D260DAB18B68E12002A0B55"
BuildableName = "LogicTests.xctest"
BlueprintName = "LogicTests"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
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>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D385DB5180DE4A900418BC6"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -39,6 +39,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D91BCB3174E8A7E00131A7D"
BuildableName = "santa-driver.kext"
BlueprintName = "santa-driver"
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"
@@ -66,7 +67,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D35BD9D18FD71CE00921A21"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -49,7 +49,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"
@@ -67,7 +68,8 @@
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable>
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D9A7F3C1759330400035EB5"

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C1514" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SNTAboutWindowController">
<connections>
<outlet property="moreInfoButton" destination="SRu-Kf-vu5" id="Vj2-9Q-05d"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
@@ -15,7 +16,7 @@
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1578"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" 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"/>
@@ -36,19 +37,32 @@
<rect key="frame" x="18" y="65" width="444" height="60"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
<font key="font" metaFont="system"/>
<string key="title">Santa is a binary whitelisting system for Mac OS X.
<string key="title">Santa is an application whitelisting system for Mac OS X.
There are no user-configurable settings.</string>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Udo-BY-n7e">
<rect key="frame" x="196" y="21" width="88" height="32"/>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SRu-Kf-vu5">
<rect key="frame" x="130" y="21" width="111" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="76" id="2Xc-ax-2bV"/>
<constraint firstAttribute="width" constant="99" id="JHv-2J-QSe"/>
</constraints>
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uSw-o1-lWW">
<buttonCell key="cell" type="push" title="More Info..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="6fe-ju-aET">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="openMoreInfoURL:" target="-2" id="dps-TN-rkS"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Udo-BY-n7e">
<rect key="frame" x="240" y="21" width="111" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="99" id="2Xc-ax-2bV"/>
</constraints>
<buttonCell key="cell" type="push" title="Dismiss" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uSw-o1-lWW">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
@@ -58,10 +72,13 @@ There are no user-configurable settings.</string>
</button>
</subviews>
<constraints>
<constraint firstItem="Udo-BY-n7e" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" priority="900" constant="191" id="1T4-DB-Dz8"/>
<constraint firstItem="SRu-Kf-vu5" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="136" id="Ake-nU-qhW"/>
<constraint firstItem="BnL-ZS-kXw" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="Fj1-SG-mzF"/>
<constraint firstAttribute="bottom" secondItem="Udo-BY-n7e" secondAttribute="bottom" constant="28" id="bpF-hC-haN"/>
<constraint firstItem="BnL-ZS-kXw" firstAttribute="centerX" secondItem="Udo-BY-n7e" secondAttribute="centerX" constant="0.5" id="csK-2p-W94"/>
<constraint firstAttribute="bottom" secondItem="SRu-Kf-vu5" secondAttribute="bottom" constant="28" id="fCB-02-SEt"/>
<constraint firstItem="BnL-ZS-kXw" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="kez-S0-6Gg"/>
<constraint firstItem="Udo-BY-n7e" firstAttribute="leading" secondItem="SRu-Kf-vu5" secondAttribute="trailing" constant="11" id="sYO-yY-w9w"/>
</constraints>
</view>
<connections>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
@@ -7,6 +7,7 @@
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
<connections>
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
</connections>
</customObject>
@@ -14,14 +15,14 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
<windowStyleMask key="styleMask" utility="YES"/>
<rect key="contentRect" x="167" y="107" width="550" height="331"/>
<rect key="contentRect" x="167" y="107" width="497" height="356"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="550" height="331"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="356"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
<rect key="frame" x="234" y="261" width="83" height="40"/>
<rect key="frame" x="207" y="286" width="83" height="40"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
<color key="textColor" red="0.18696189413265307" green="0.18696189413265307" blue="0.18696189413265307" alpha="1" colorSpace="calibratedRGB"/>
@@ -29,9 +30,9 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
<rect key="frame" x="25" y="214" width="500" height="17"/>
<rect key="frame" x="22" y="239" width="454" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="496" id="XgJ-EV-tBa"/>
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
</constraints>
<textFieldCell key="cell" 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"/>
@@ -43,9 +44,9 @@
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ">
<rect key="frame" x="175" y="167" width="324" height="17"/>
<rect key="frame" x="165" y="192" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="320" id="xVR-j3-dLw"/>
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
</constraints>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="E7T-9h-ofr">
<font key="font" metaFont="system"/>
@@ -57,58 +58,25 @@
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="175" y="142" width="304" height="17"/>
<rect key="frame" x="165" y="142" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="4hh-R2-86s"/>
<constraint firstAttribute="width" constant="290" id="4hh-R2-86s"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="File SHA-256" id="X4W-9e-eIu">
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="Part of SHA-256" id="X4W-9e-eIu">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.fileSHA256" id="SzX-Ep-rBa"/>
<binding destination="-2" name="value" keyPath="self.shortenedHash" id="xgu-71-9ZT"/>
</connections>
</textField>
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS" userLabel="Publisher Certs">
<rect key="frame" x="340" y="118" width="10" height="15"/>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
<rect key="frame" x="165" y="167" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="10" id="QTm-Iv-m5p"/>
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSFollowLinkFreestandingTemplate" imagePosition="overlaps" alignment="center" refusesFirstResponder="YES" imageScaling="proportionallyDown" inset="2" id="R72-Qy-Xbb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showCertInfo:" target="-2" id="dB0-a3-X31"/>
<binding destination="-2" name="hidden" keyPath="self.publisherInfo" id="fFR-f3-Oiw">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL">
<rect key="frame" x="220" y="33" width="110" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</constraints>
<buttonCell key="cell" type="roundTextured" title="OK" bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
</buttonCell>
<connections>
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
</connections>
</button>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
<rect key="frame" x="175" y="117" width="159" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
<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" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@@ -122,15 +90,23 @@ DQ
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL">
<rect key="frame" x="18" y="92" width="120" height="17"/>
<rect key="frame" x="8" y="92" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="User" id="1ut-uT-hQD">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J">
<rect key="frame" x="8" y="117" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Parent" id="gze-4A-1w5">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5">
<rect key="frame" x="18" y="117" width="120" height="17"/>
<rect key="frame" x="8" y="167" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Publisher" id="yL9-yD-JXX">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -138,7 +114,7 @@ DQ
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H">
<rect key="frame" x="18" y="167" width="120" height="17"/>
<rect key="frame" x="8" y="192" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
</constraints>
@@ -149,17 +125,17 @@ DQ
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y">
<rect key="frame" x="18" y="142" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="SHA-256" id="eKN-Ic-5zy">
<rect key="frame" x="8" y="142" width="120" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Identifier" id="eKN-Ic-5zy">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="h6f-PY-cc0">
<rect key="frame" x="175" y="92" width="368" height="17"/>
<rect key="frame" x="165" y="92" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="364" id="on6-pj-m2k"/>
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<font key="font" metaFont="system"/>
@@ -167,55 +143,147 @@ DQ
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="xe2-U2-WrZ"/>
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="IcM-Lt-xTT">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<box horizontalHuggingPriority="750" title="Box" boxType="separator" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="154" y="92" width="5" height="92"/>
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="146" y="92" width="1" height="117"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
</constraints>
<color key="borderColor" white="0.0" alpha="0.18" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS" userLabel="Publisher Certs">
<rect key="frame" x="40" y="168" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
</constraints>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSInfo" imagePosition="overlaps" alignment="center" refusesFirstResponder="YES" imageScaling="proportionallyDown" inset="2" id="R72-Qy-Xbb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showCertInfo:" target="-2" id="dB0-a3-X31"/>
<binding destination="-2" name="hidden" keyPath="self.publisherInfo" id="fFR-f3-Oiw">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL">
<rect key="frame" x="256" y="33" width="110" height="25"/>
<constraints>
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</constraints>
<buttonCell key="cell" type="roundTextured" title="Dismiss" bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
</buttonCell>
<connections>
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
</connections>
</button>
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
<rect key="frame" x="132" y="33" width="112" height="25"/>
<constraints>
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
</constraints>
<buttonCell key="cell" type="roundTextured" title="Open Event..." bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X1b-TF-1TL">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</buttonCell>
<connections>
<action selector="openEventDetails:" target="-2" id="VhL-ql-rCV"/>
</connections>
</button>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="f1p-GL-O3o">
<rect key="frame" x="165" y="117" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.parentName" id="arL-Mc-4xj">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="h6f-PY-cc0" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="1Nc-gl-xMe"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="f1p-GL-O3o" secondAttribute="bottom" constant="8" id="496-VQ-Fx5"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="-116" id="6Q5-Oo-1cI"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="7K6-bY-Rn6"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="ALv-0v-szi"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="E6D-7P-17g"/>
<constraint firstItem="cJf-k6-OxS" firstAttribute="centerY" secondItem="C3G-wL-u7w" secondAttribute="centerY" id="FdL-ZZ-Vbe"/>
<constraint firstItem="t8c-Fx-e5h" firstAttribute="top" secondItem="Iwq-Lx-rLv" secondAttribute="top" constant="30" id="FuB-GX-0jg"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="G0I-O2-S91"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-45" id="GD2-Ka-deo"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="20" id="IwX-ja-ZIs"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="centerY" secondItem="d9e-Wv-Y5H" secondAttribute="centerY" id="JeD-9X-ULA"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="IwX-ja-ZIs"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" id="JY4-N1-j8e"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="leading" priority="999" id="MVr-jY-GDj"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="SCl-Ky-VmT"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="centerY" secondItem="pc8-G9-4pJ" secondAttribute="centerY" id="SLv-F7-w5k"/>
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="Scq-zQ-Sao"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="KEB-eH-x2Y" secondAttribute="trailing" constant="20" id="Seb-c0-MUL"/>
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="V0a-Py-iEc"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="leading" priority="999" id="Z6G-l9-G4a"/>
<constraint firstAttribute="centerX" secondItem="BbV-3h-mmL" secondAttribute="centerX" id="acs-5J-vQY"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="top" secondItem="eQb-0a-76J" secondAttribute="bottom" constant="8" id="abm-cM-PN0"/>
<constraint firstAttribute="centerX" secondItem="BbV-3h-mmL" secondAttribute="centerX" priority="900" constant="-62.5" id="acs-5J-vQY"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="eQb-0a-76J" secondAttribute="trailing" constant="20" id="b0B-3w-grH"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" priority="999" id="b5A-M7-ZsD"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="centerY" secondItem="PXc-xv-A28" secondAttribute="centerY" id="cHe-pZ-0Oq"/>
<constraint firstItem="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="30" id="dYg-zP-wh2"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="eSz-lz-Fdh"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="top" id="fzY-94-y2n"/>
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" constant="-0.5" id="h3d-Kc-q88"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="fGd-YS-phP"/>
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" id="h3d-Kc-q88"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="7ua-5a-uSd" secondAttribute="trailing" constant="12" id="ioO-NJ-Jqo"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="lvJ-Rk-UT5" secondAttribute="centerY" id="jfs-YI-7Ae"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="jlD-Lo-abc"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="trailing" constant="20" id="kOG-Cj-hFG"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="trailing" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" id="lse-kg-lA2"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="top" secondItem="KEB-eH-x2Y" secondAttribute="bottom" constant="8" id="m2z-1O-ifB"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="pdq-a6-Y73"/>
<constraint firstAttribute="centerX" secondItem="7ua-5a-uSd" secondAttribute="centerX" constant="61" id="phL-j9-rPq"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="20" id="qKi-KT-jzJ"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="C3G-wL-u7w" secondAttribute="bottom" constant="8" id="rwU-fp-qh6"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="C3G-wL-u7w" secondAttribute="bottom" constant="8" id="sG1-gQ-Qoo"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="snd-8T-LjC"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="bottom" secondItem="PXc-xv-A28" secondAttribute="top" constant="-8" id="snd-8T-LjC"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="trailing" constant="20" id="stz-Vm-Kxo"/>
<constraint firstItem="PXc-xv-A28" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="tAa-1s-xVZ"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="width" secondItem="eQb-0a-76J" secondAttribute="width" id="u4p-1B-x5B"/>
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="35" id="ukF-FH-DE8"/>
<constraint firstItem="cJf-k6-OxS" firstAttribute="leading" secondItem="C3G-wL-u7w" secondAttribute="trailing" constant="8" id="wsf-ru-MoA"/>
<constraint firstItem="PXc-xv-A28" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="bottom" constant="8" id="zst-nc-VqA"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="bottom" secondItem="C3G-wL-u7w" secondAttribute="top" constant="-8" id="zst-nc-VqA"/>
</constraints>
</view>
<point key="canvasLocation" x="162" y="710.5"/>
<point key="canvasLocation" x="112.5" y="308"/>
</window>
</objects>
<resources>
<image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
<image name="NSInfo" width="32" height="32"/>
</resources>
</document>

View File

@@ -14,4 +14,8 @@
@interface SNTAboutWindowController : NSWindowController
@property IBOutlet NSButton *moreInfoButton;
- (IBAction)openMoreInfoURL:(id)sender;
@end

View File

@@ -14,10 +14,24 @@
#import "SNTAboutWindowController.h"
#import "SNTConfigurator.h"
@implementation SNTAboutWindowController
- (instancetype)init {
return [super initWithWindowNibName:@"AboutWindow"];
}
- (void)loadWindow {
[super loadWindow];
if (![[SNTConfigurator configurator] moreInfoURL]) {
[self.moreInfoButton removeFromSuperview];
}
}
- (IBAction)openMoreInfoURL:(id)sender {
[[NSWorkspace sharedWorkspace] openURL:[[SNTConfigurator configurator] moreInfoURL]];
[self close];
}
@end

View File

@@ -15,11 +15,14 @@
#import "SNTAppDelegate.h"
#import "SNTAboutWindowController.h"
#import "SNTConfigurator.h"
#import "SNTFileWatcher.h"
#import "SNTNotificationManager.h"
#import "SNTXPCConnection.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;
@property SNTFileWatcher *configFileWatcher;
@property SNTNotificationManager *notificationManager;
@property SNTXPCConnection *listener;
@end
@@ -30,7 +33,12 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self setupMenu];
self.aboutWindowController = [[SNTAboutWindowController alloc] init];
self.configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^{
[[SNTConfigurator configurator] reloadConfigData];
}];
self.notificationManager = [[SNTNotificationManager alloc] init];
NSNotificationCenter *workspaceNotifications = [[NSWorkspace sharedWorkspace] notificationCenter];
@@ -47,6 +55,7 @@
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
self.aboutWindowController = [[SNTAboutWindowController alloc] init];
[self.aboutWindowController showWindow:self];
return NO;
}
@@ -56,9 +65,8 @@
- (void)createConnection {
__weak __typeof(self) weakSelf = self;
self.listener =
[[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
@@ -73,15 +81,12 @@
self.listener.invalidationHandler = nil;
[self.listener invalidate];
self.listener = nil;
NSLog(@"KILLING CONNECTION");
}
- (void)attemptReconnection {
// TODO(rah): Make this smarter.
sleep(10);
[self performSelectorOnMainThread:@selector(createConnection)
withObject:nil
waitUntilDone:NO];
[self performSelectorOnMainThread:@selector(createConnection) withObject:nil waitUntilDone:NO];
}
#pragma mark Menu Management

View File

@@ -26,6 +26,7 @@
- (IBAction)fadeIn:(id)sender {
[self setAlphaValue:0.f];
[self center];
[self makeKeyAndOrderFront:sender];
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.15f];

View File

@@ -47,11 +47,17 @@
///
/// A 'friendly' string representing the certificate information
///
@property(readonly) IBOutlet NSString *publisherInfo;
@property(readonly, nonatomic) NSString *publisherInfo;
///
/// An optional message to display with this block.
///
@property(readonly) IBOutlet NSAttributedString *attributedCustomMessage;
@property(readonly, nonatomic) NSAttributedString *attributedCustomMessage;
///
/// Reference to the "Open Event" button in the XIB. Used to either remove the button
/// if it isn't needed or set its title if it is.
///
@property IBOutlet NSButton *openEventButton;
@end

View File

@@ -17,6 +17,7 @@
#import <SecurityInterface/SFCertificatePanel.h>
#import "SNTCertificate.h"
#import "SNTConfigurator.h"
#import "SNTFileInfo.h"
#import "SNTMessageWindow.h"
#import "SNTStoredEvent.h"
@@ -27,14 +28,26 @@
self = [super initWithWindowNibName:@"MessageWindow"];
if (self) {
_event = event;
_customMessage = message;
[self.window setMovableByWindowBackground:NO];
[self.window setLevel:NSPopUpMenuWindowLevel];
[self.window center];
_customMessage = (message != (NSString *)[NSNull null] ? message : nil);
}
return self;
}
- (void)loadWindow {
[super loadWindow];
[self.window setLevel:NSPopUpMenuWindowLevel];
[self.window setMovableByWindowBackground:YES];
if (![[SNTConfigurator configurator] eventDetailURL]) {
[self.openEventButton removeFromSuperview];
} else {
NSString *eventDetailText = [[SNTConfigurator configurator] eventDetailText];
if (eventDetailText) {
[self.openEventButton setTitle:eventDetailText];
}
}
}
- (IBAction)showWindow:(id)sender {
[(SNTMessageWindow *)self.window fadeIn:sender];
}
@@ -62,16 +75,35 @@
showGroup:YES];
}
- (IBAction)openEventDetails:(id)sender {
SNTConfigurator *config = [SNTConfigurator configurator];
NSString *formatStr = config.eventDetailURL;
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
withString:self.event.fileSHA256];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"
withString:self.event.executingUser];
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%machine_id%"
withString:config.machineID];
[self closeWindow:sender];
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:formatStr]];
}
#pragma mark Generated properties
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if (! [key isEqualToString:@"event"]) {
if (![key isEqualToString:@"event"]) {
return [NSSet setWithObject:@"event"];
} else {
return nil;
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
SNTCertificate *leafCert = [self.event.signingChain firstObject];
@@ -98,7 +130,7 @@
NSString *htmlFooter = @"</body></html>";
NSString *message;
if (self.customMessage && ![self.customMessage isEqual:@""]) {
if ([self.customMessage length] > 0) {
message = self.customMessage;
} else {
message = @"The following application has been blocked from executing<br />"
@@ -111,7 +143,6 @@
NSAttributedString *returnStr = [[NSAttributedString alloc] initWithHTML:htmlData
documentAttributes:NULL];
return returnStr;
}
@end

View File

@@ -20,5 +20,4 @@
///
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate, SNTNotifierXPC>
@end

View File

@@ -54,9 +54,16 @@
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
// See if this binary is already in the list of pending notifications.
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"fileSHA256==%@", event.fileSHA256];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"event.fileSHA256==%@", event.fileSHA256];
if ([[self.pendingNotifications filteredArrayUsingPredicate:predicate] count]) return;
if (!event) {
NSLog(@"Error: Missing event object in message received from daemon!");
return;
}
if (!message) message = (NSString *)[NSNull null];
// Notifications arrive on a background thread but UI updates must happen on the main thread.
// This includes making windows.
[self performSelectorOnMainThread:@selector(postBlockNotificationMainThread:)

View File

@@ -53,59 +53,59 @@
///
/// Access the underlying certificate ref.
///
@property(readonly) SecCertificateRef certRef;
@property(readonly, nonatomic) SecCertificateRef certRef;
///
/// SHA-1 hash of the certificate data.
///
@property(readonly) NSString *SHA1;
@property(readonly, nonatomic) NSString *SHA1;
///
/// SHA-256 hash of the certificate data.
///
@property(readonly) NSString *SHA256;
@property(readonly, nonatomic) NSString *SHA256;
///
/// Certificate data.
///
@property(readonly) NSData *certData;
@property(readonly, nonatomic) NSData *certData;
///
/// Common Name e.g: "Software Signing"
///
@property(readonly) NSString *commonName;
@property(readonly, nonatomic) NSString *commonName;
///
/// Country Name e.g: "US"
///
@property(readonly) NSString *countryName;
@property(readonly, nonatomic) NSString *countryName;
///
/// Organizational Name e.g: "Apple Inc."
///
@property(readonly) NSString *orgName;
@property(readonly, nonatomic) NSString *orgName;
///
/// Organizational Unit Name e.g: "Apple Software"
///
@property(readonly) NSString *orgUnit;
@property(readonly, nonatomic) NSString *orgUnit;
///
/// Issuer details, same fields as above.
///
@property(readonly) NSString *issuerCommonName;
@property(readonly) NSString *issuerCountryName;
@property(readonly) NSString *issuerOrgName;
@property(readonly) NSString *issuerOrgUnit;
@property(readonly, nonatomic) NSString *issuerCommonName;
@property(readonly, nonatomic) NSString *issuerCountryName;
@property(readonly, nonatomic) NSString *issuerOrgName;
@property(readonly, nonatomic) NSString *issuerOrgUnit;
///
/// Validity Not Before
///
@property(readonly) NSDate *validFrom;
@property(readonly, nonatomic) NSDate *validFrom;
///
/// Validity Not After
///
@property(readonly) NSDate *validUntil;
@property(readonly, nonatomic) NSDate *validUntil;
@end

View File

@@ -80,9 +80,9 @@ static NSString *const kCertDataKey = @"certData";
NSData *output = nil;
if (SecTransformSetAttribute(transform,
kSecTransformInputAttributeName,
(__bridge CFDataRef)input,
NULL)) {
kSecTransformInputAttributeName,
(__bridge CFDataRef)input,
NULL)) {
output = CFBridgingRelease(SecTransformExecute(transform, NULL));
}
if (transform) CFRelease(transform);
@@ -126,11 +126,12 @@ static NSString *const kCertDataKey = @"certData";
#pragma mark Equality & description
- (BOOL)isEqual:(SNTCertificate *)other {
- (BOOL)isEqual:(id)other {
if (self == other) return YES;
if (![other isKindOfClass:[SNTCertificate class]]) return NO;
return [self.certData isEqual:other.certData];
SNTCertificate *o = other;
return [self.certData isEqual:o.certData];
}
- (NSUInteger)hash {
@@ -138,10 +139,8 @@ static NSString *const kCertDataKey = @"certData";
}
- (NSString *)description {
return [NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@",
self.orgName,
self.orgUnit,
self.commonName];
return
[NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@", self.orgName, self.orgUnit, self.commonName];
}
#pragma mark NSSecureCoding
@@ -227,7 +226,7 @@ static NSString *const kCertDataKey = @"certData";
}
return nil;
}
@catch (NSException *exception) {
@catch (NSException *e) {
return nil;
}
}

View File

@@ -38,12 +38,12 @@
///
/// Returns the leaf certificate that this binary was signed with
///
@property(readonly) SNTCertificate *leafCertificate;
@property(readonly, nonatomic) SNTCertificate *leafCertificate;
///
/// Returns the on-disk path of this binary.
///
@property(readonly) NSString *binaryPath;
@property(readonly, nonatomic) NSString *binaryPath;
///
/// Designated initializer

View File

@@ -18,7 +18,6 @@
#import "SNTCertificate.h"
/**
* kStaticSigningFlags are the flags used when validating signatures on disk.
*
@@ -38,15 +37,14 @@
* kSecCSDoNotValidateResources: 0.032s
* kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.033s
*/
const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
static const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
/**
* kSigningFlags are the flags used when validating signatures for running binaries.
*
* No special flags needed currently.
*/
const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
static const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
@interface SNTCodesignChecker ()
/// Array of @c SNTCertificate's representing the chain of certs this executable was signed with.
@@ -87,7 +85,7 @@ const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
// Wrap SecCertificateRef objects in SNTCertificate and put in a new NSArray
NSMutableArray *mutableCerts = [[NSMutableArray alloc] initWithCapacity:certs.count];
for (int i = 0; i < certs.count; ++i) {
for (NSUInteger i = 0; i < certs.count; ++i) {
SecCertificateRef certRef = (__bridge SecCertificateRef)certs[i];
SNTCertificate *newCert = [[SNTCertificate alloc] initWithSecCertificateRef:certRef];
[mutableCerts addObject:newCert];
@@ -105,10 +103,10 @@ const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
SecStaticCodeRef codeRef = NULL;
// Get SecStaticCodeRef for binary
if (SecStaticCodeCreateWithPath((__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath
isDirectory:NO],
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
if (SecStaticCodeCreateWithPath(
(__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath isDirectory:NO],
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
@@ -120,12 +118,13 @@ const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
- (instancetype)initWithPID:(pid_t)PID {
SecCodeRef codeRef = NULL;
NSDictionary *attributes = @{(__bridge NSString *)kSecGuestAttributePid: @(PID)};
NSDictionary *attributes = @{ (__bridge NSString *)kSecGuestAttributePid : @(PID) };
if (SecCodeCopyGuestWithAttributes(NULL,
(__bridge CFDictionaryRef)attributes,
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
if (SecCodeCopyGuestWithAttributes(
NULL,
(__bridge CFDictionaryRef)attributes,
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
@@ -170,9 +169,7 @@ const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
}
return [NSString stringWithFormat:@"%@ binary, signed by %@, located at: %@",
binarySource,
self.leafCertificate.orgName,
self.binaryPath];
binarySource, self.leafCertificate.orgName, self.binaryPath];
}
#pragma mark Public accessors
@@ -183,7 +180,7 @@ const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
- (NSString *)binaryPath {
CFURLRef path;
OSStatus status = SecCodeCopyPath(_codeRef, kSecCSDefaultFlags, &path);
OSStatus status = SecCodeCopyPath(self.codeRef, kSecCSDefaultFlags, &path);
NSURL *pathURL = CFBridgingRelease(path);
if (status != errSecSuccess) return nil;
return [pathURL path];

View File

@@ -16,79 +16,123 @@
///
/// Singleton that provides an interface for managing configuration values on disk
/// @note This class is designed as a singleton but that is not enforced.
/// @note This class is designed as a singleton but that is not strictly enforced.
///
@interface SNTConfigurator : NSObject
///
/// The operating mode
///
@property santa_clientmode_t clientMode;
/// Default config file path
extern NSString * const kDefaultConfigFilePath;
# pragma mark - Sync Settings
#pragma mark - Daemon Settings
///
/// The base URL of the sync server
/// The operating mode.
///
@property(readonly) NSURL *syncBaseURL;
@property(nonatomic) santa_clientmode_t clientMode;
///
/// The machine owner
/// Whether or not to log all events, even for whitelisted binaries.
///
@property(readonly) NSString *machineOwner;
@property(nonatomic) BOOL logAllEvents;
#pragma mark - GUI Settings
///
/// If set, this over-rides the default machine ID used for syncing
/// The URL to open when the user clicks "More Info..." when opening Santa.app.
/// If unset, the button will not be displayed.
///
@property(readonly) NSString *machineIDOverride;
@property(readonly, nonatomic) NSURL *moreInfoURL;
# pragma mark Server Auth Settings
///
/// When the user gets a block notification, a button can be displayed which will
/// take them to a web page with more information about that event.
/// This property contains a kind of format string to be turned into the URL to send them to.
/// The following sequences will be replaced in the final URL:
///
/// %file_sha% -- SHA-256 of the file that was blocked.
/// %machine_id% -- ID of the machine.
/// %username% -- executing user.
///
/// @note: This is not an NSURL because the format-string parsing is done elsewhere.
///
/// If this item isn't set, the Open Event button will not be displayed.
///
@property(readonly, nonatomic) NSString *eventDetailURL;
///
/// Related to the above property, this string represents the text to show on the button.
///
@property(readonly, nonatomic) NSString *eventDetailText;
#pragma mark - Sync Settings
///
/// The base URL of the sync server.
///
@property(readonly, nonatomic) NSURL *syncBaseURL;
///
/// The machine owner.
///
@property(readonly, nonatomic) NSString *machineOwner;
///
/// If set, this over-rides the default machine ID used for syncing.
///
@property(readonly, nonatomic) NSString *machineID;
#pragma mark Server Auth Settings
///
/// If set, this is valid PEM containing one or more certificates to be used to evaluate the
/// server's SSL chain, overriding the list of trusted CAs distributed with the OS.
///
@property(readonly) NSData *syncServerAuthRootsData;
@property(readonly, nonatomic) NSData *syncServerAuthRootsData;
///
/// This property is the same as the above but is a file on disk containing the PEM data.
///
@property(readonly) NSString *syncServerAuthRootsFile;
@property(readonly, nonatomic) NSString *syncServerAuthRootsFile;
# pragma mark Client Auth Settings
#pragma mark Client Auth Settings
///
/// If set, this contains the location of a PKCS#12 certificate to be used for sync authentication.
///
@property(readonly) NSString *syncClientAuthCertificateFile;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateFile;
///
/// Contains the password for the pkcs#12 certificate.
///
@property(readonly) NSString *syncClientAuthCertificatePassword;
@property(readonly, nonatomic) NSString *syncClientAuthCertificatePassword;
///
/// If set, this is the Common Name of a certificate in the System keychain to be used for
/// sync authentication. The corresponding private key must also be in the keychain.
///
@property(readonly) NSString *syncClientAuthCertificateCn;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateCn;
///
/// If set, this is the Issuer Name of a certificate in the System keychain to be used for
/// sync authentication. The corresponding private key must also be in the keychain.
///
@property(readonly) NSString *syncClientAuthCertificateIssuer;
@property(readonly, nonatomic) NSString *syncClientAuthCertificateIssuer;
///
/// Retrieve an initialized singleton configurator object using the default file path
/// Retrieve an initialized singleton configurator object using the default file path.
///
+ (instancetype)configurator;
///
/// Designated initializer
/// Designated initializer.
///
/// @param filePath The path to the file to use as a backing store.
///
- (instancetype)initWithFilePath:(NSString *)filePath;
///
/// Re-read config data from disk.
///
- (void)reloadConfigData;
@end

View File

@@ -15,6 +15,7 @@
#import "SNTConfigurator.h"
#import "SNTLogging.h"
#import "SNTSystemInfo.h"
@interface SNTConfigurator ()
@property NSString *configFilePath;
@@ -24,9 +25,17 @@
@implementation SNTConfigurator
/// The hard-coded path to the config file
static NSString * const kConfigFilePath = @"/var/db/santa/config.plist";
NSString * const kDefaultConfigFilePath = @"/var/db/santa/config.plist";
/// The keys in the config file
static NSString * const kClientModeKey = @"ClientMode";
static NSString * const kLogAllEventsKey = @"LogAllEvents";
static NSString * const kMoreInfoURLKey = @"MoreInfoURL";
static NSString * const kEventDetailURLKey = @"EventDetailURL";
static NSString * const kEventDetailTextKey = @"EventDetailText";
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
static NSString * const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString * const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
@@ -34,7 +43,6 @@ static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN"
static NSString * const kClientAuthCertificateIssuerKey = @"ClientAuthCertificateIssuerCN";
static NSString * const kServerAuthRootsDataKey = @"ServerAuthRootsData";
static NSString * const kServerAuthRootsFileKey = @"ServerAuthRootsFile";
static NSString * const kClientModeKey = @"ClientMode";
static NSString * const kMachineOwnerKey = @"MachineOwner";
static NSString * const kMachineIDKey = @"MachineID";
@@ -54,18 +62,56 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self;
}
# pragma mark Singleton retriever
#pragma mark Singleton retriever
+ (instancetype)configurator {
static SNTConfigurator *sharedConfigurator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedConfigurator = [[SNTConfigurator alloc] initWithFilePath:kConfigFilePath];
sharedConfigurator = [[SNTConfigurator alloc] initWithFilePath:kDefaultConfigFilePath];
});
return sharedConfigurator;
}
# pragma mark Public Interface
#pragma mark Public Interface
- (santa_clientmode_t)clientMode {
int cm = [self.configData[kClientModeKey] intValue];
if (cm > CLIENTMODE_UNKNOWN && cm < CLIENTMODE_MAX) {
return (santa_clientmode_t)cm;
} else {
self.configData[kClientModeKey] = @(CLIENTMODE_MONITOR);
return CLIENTMODE_MONITOR;
}
}
- (void)setClientMode:(santa_clientmode_t)newMode {
if (newMode > CLIENTMODE_UNKNOWN && newMode < CLIENTMODE_MAX) {
self.configData[kClientModeKey] = @(newMode);
[self saveConfigToDisk];
}
}
- (BOOL)logAllEvents {
return [self.configData[kLogAllEventsKey] boolValue];
}
- (void)setLogAllEvents:(BOOL)logAllEvents {
self.configData[kLogAllEventsKey] = @(logAllEvents);
[self saveConfigToDisk];
}
- (NSURL *)moreInfoURL {
return [NSURL URLWithString:self.configData[kMoreInfoURLKey]];
}
- (NSString *)eventDetailURL {
return self.configData[kEventDetailURLKey];
}
- (NSString *)eventDetailText {
return self.configData[kEventDetailTextKey];
}
- (NSURL *)syncBaseURL {
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
@@ -96,92 +142,55 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
}
- (NSString *)machineOwner {
NSString *machineOwner;
if (self.configData[kMachineOwnerPlistFileKey] && self.configData[kMachineOwnerPlistKeyKey]) {
NSDictionary *plist =
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineOwnerPlistFileKey]];
return plist[kMachineOwnerPlistKeyKey];
machineOwner = plist[self.configData[kMachineOwnerPlistKeyKey]];
}
if (self.configData[kMachineOwnerKey]) {
return self.configData[kMachineOwnerKey];
machineOwner = self.configData[kMachineOwnerKey];
}
return @"";
if (!machineOwner) machineOwner = @"";
return machineOwner;
}
- (NSString *)machineIDOverride {
- (NSString *)machineID {
NSString *machineId;
if (self.configData[kMachineIDPlistFileKey] && self.configData[kMachineIDPlistKeyKey]) {
NSDictionary *plist =
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineIDPlistFileKey]];
return plist[kMachineIDPlistKeyKey];
machineId = plist[self.configData[kMachineIDPlistKeyKey]];
}
if (self.configData[kMachineIDKey]) {
return self.configData[kMachineIDKey];
machineId = self.configData[kMachineIDKey];
}
return @"";
}
- (santa_clientmode_t)clientMode {
int cm = [self.configData[kClientModeKey] intValue];
if (cm > CLIENTMODE_UNKNOWN && cm < CLIENTMODE_MAX) {
return cm;
} else {
self.configData[kClientModeKey] = @(CLIENTMODE_MONITOR);
return CLIENTMODE_MONITOR;
if ([machineId length] == 0) {
machineId = [SNTSystemInfo hardwareUUID];
}
return machineId;
}
- (void)setClientMode:(santa_clientmode_t)newMode {
if (newMode > CLIENTMODE_UNKNOWN && newMode < CLIENTMODE_MAX) {
[self reloadConfigData];
self.configData[kClientModeKey] = @(newMode);
[self saveConfigToDisk];
}
}
#pragma mark Private
///
/// Saves the current @c _configData to disk.
///
- (void)saveConfigToDisk {
[self.configData writeToFile:kConfigFilePath atomically:YES];
}
///
/// Populate @c self.configData, using the config file on disk if possible,
/// otherwise an empty mutable dictionary.
///
/// If the config file's permissions are not @c 0644, will attempt to set them
/// but will fail silently if this cannot be done.
///
- (void)reloadConfigData {
if (!self.configData) self.configData = [NSMutableDictionary dictionary];
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:self.configFilePath]) {
_configData = [NSMutableDictionary dictionary];
return;
}
// Ensure the config file permissions are 0644. Fail silently if they can't be changed.
NSDictionary *fileAttrs = [fm attributesOfItemAtPath:self.configFilePath error:nil];
if ([fileAttrs filePosixPermissions] != 0644) {
[fm setAttributes:@{ NSFilePosixPermissions: @(0644) }
ofItemAtPath:self.configFilePath
error:nil];
}
if (![fm fileExistsAtPath:self.configFilePath]) return;
NSError *error;
NSData *readData = [NSData dataWithContentsOfFile:self.configFilePath
options:NSDataReadingMappedIfSafe
error:&error];
if (error) {
fprintf(stderr, "%s\n", [[NSString stringWithFormat:@"Could not read configuration file %@: %@",
self.configFilePath, [error localizedDescription]] UTF8String]);
_configData = [NSMutableDictionary dictionary];
LOGE(@"Could not read configuration file: %@", [error localizedDescription]);
return;
}
@@ -191,16 +200,30 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
format:NULL
error:&error];
if (error) {
fprintf(stderr, "%s\n",
[[NSString stringWithFormat:@"Could not parse configuration file %@: %@",
self.configFilePath,
[error localizedDescription]] UTF8String]);
_configData = [NSMutableDictionary dictionary];
LOGE(@"Could not parse configuration file: %@", [error localizedDescription]);
return;
}
_configData = [configData mutableCopy];
// Ensure no-one is trying to change the client mode behind Santa's back.
if (self.configData[kClientModeKey] && configData[kClientModeKey] &&
![self.configData[kClientModeKey] isEqual:configData[kClientModeKey]] &&
geteuid() == 0) {
NSMutableDictionary *configDataMutable = [configData mutableCopy];
configDataMutable[kClientModeKey] = self.configData[kClientModeKey];
self.configData = configDataMutable;
[self saveConfigToDisk];
} else {
self.configData = [configData mutableCopy];
}
}
#pragma mark Private
///
/// Saves the current @c self.configData to disk.
///
- (void)saveConfigToDisk {
[self.configData writeToFile:self.configFilePath atomically:YES];
}
@end

View File

@@ -17,4 +17,4 @@
///
/// @return YES if dropping was successful or unnecessary.
///
BOOL DropRootPrivileges();
BOOL DropRootPrivileges(void);

View File

@@ -16,8 +16,9 @@
BOOL DropRootPrivileges() {
if (getuid() == 0 || geteuid() == 0 || getgid() == 0 || getegid() == 0) {
if (setgid(-2) != 0 || setgroups(0, NULL) != 0 || setegid(-2) != 0 ||
setuid(-2) != 0 || seteuid(-2) != 0) {
uid_t nobody = (uid_t)-2;
if (setgid(nobody) != 0 || setgroups(0, NULL) != 0 || setegid(nobody) != 0 ||
setuid(nobody) != 0 || seteuid(nobody) != 0) {
return false;
}

View File

@@ -83,7 +83,7 @@
unsigned char sha2[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha2);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH *2];
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha2[i]];
}
@@ -92,11 +92,11 @@
}
- (NSString *)machoType {
if ([self isDylib]) { return @"Dynamic Library"; }
if ([self isKext]) { return @"Kernel Extension"; }
if ([self isFat]) { return @"Fat Binary"; }
if ([self isMachO]) { return @"Thin Binary"; }
if ([self isScript]) { return @"Script"; }
if ([self isDylib]) return @"Dynamic Library";
if ([self isKext]) return @"Kernel Extension";
if ([self isFat]) return @"Fat Binary";
if ([self isMachO]) return @"Thin Binary";
if ([self isScript]) return @"Script";
return @"Unknown (not executable?)";
}
@@ -121,8 +121,9 @@
struct fat_arch *fat_archs = (struct fat_arch *)[archs bytes];
// For each arch, get the name of it's architecture
for (int i = 0; i < narch; ++i) {
[ret addObject:[self nameForCPUType:NSSwapBigIntToHost(fat_archs[i].cputype)]];
for (uint32_t i = 0; i < narch; ++i) {
cpu_type_t cpu = (cpu_type_t)NSSwapBigIntToHost((unsigned int)fat_archs[i].cputype);
[ret addObject:[self nameForCPUType:cpu]];
}
return ret;
} else {
@@ -135,8 +136,7 @@
- (BOOL)isDylib {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_DYLIB ||
mach_header->filetype == MH_FVMLIB) {
if (mach_header->filetype == MH_DYLIB || mach_header->filetype == MH_FVMLIB) {
return YES;
}
@@ -183,15 +183,15 @@
return NO;
}
# pragma mark Bundle Information
#pragma mark Bundle Information
///
/// Try and determine the bundle that the represented executable is contained within, if any.
///
/// Rationale: An NSBundle has a method executablePath for discovering the main binary within a
/// bundle but provides no way to get an NSBundle object when only the executablePath is known. Also,
/// a bundle can contain multiple binaries within the MacOS folder and we want any of these to count
/// as being part of the bundle.
/// bundle but provides no way to get an NSBundle object when only the executablePath is known.
/// Also a bundle can contain multiple binaries within the MacOS folder and we want any of these
/// to count as being part of the bundle.
///
/// This method relies on executable bundles being laid out as follows:
///
@@ -237,7 +237,7 @@
NSURL *url = [NSURL fileURLWithPath:self.path isDirectory:NO];
self.infoDict =
(__bridge_transfer NSDictionary*)CFBundleCopyInfoDictionaryForURL((__bridge CFURLRef) url);
(__bridge_transfer NSDictionary *)CFBundleCopyInfoDictionaryForURL((__bridge CFURLRef)url);
return self.infoDict;
}
@@ -259,7 +259,7 @@
- (NSArray *)downloadURLs {
char *path = (char *)[self.path fileSystemRepresentation];
size_t size = getxattr(path, "com.apple.metadata:kMDItemWhereFroms", NULL, 0, 0, 0);
size_t size = (size_t)getxattr(path, "com.apple.metadata:kMDItemWhereFroms", NULL, 0, 0, 0);
char *value = malloc(size);
if (!value) return nil;
@@ -282,7 +282,7 @@
return nil;
}
# pragma mark Internal Methods
#pragma mark Internal Methods
///
/// Look through the file for the first mach_header. If the file is thin, this will be the
@@ -297,8 +297,8 @@
if ([self isFatHeader:fat_header]) {
// Get the bytes for the fat_arch
NSData *archHdr = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
sizeof(struct fat_arch))];
NSData *archHdr =
[self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header), sizeof(struct fat_arch))];
if (!archHdr) return nil;
struct fat_arch *fat_arch = (struct fat_arch *)[archHdr bytes];
@@ -333,7 +333,7 @@
@try {
return [self.fileData subdataWithRange:range];
}
@catch (NSException *exception) {
@catch (NSException *e) {
return nil;
}
}

View File

@@ -0,0 +1,34 @@
/// 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.
///
/// Simple file watching class using dispatch sources. Will automatically
/// reload the watch if the file is deleted. Will continue watching for
/// events until deallocated.
///
@interface SNTFileWatcher : NSObject
///
/// Designated initializer
/// Initializes the watcher and begins watching for modifications.
///
/// @param filePath the file to watch.
/// @param handler the handler to call when changes happen.
///
/// @note Shortly after the file has been opened and monitoring has begun, the provided handler
/// will be called.
///
- (instancetype)initWithFilePath:(NSString *)filePath handler:(void (^)(void))handler;
@end

View File

@@ -0,0 +1,100 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTFileWatcher.h"
@interface SNTFileWatcher ()
@property NSString *filePath;
@property dispatch_source_t monitoringSource;
@property(strong) void (^eventHandler)(void);
@property(strong) void (^internalEventHandler)(void);
@property(strong) void (^internalCancelHandler)(void);
@end
@implementation SNTFileWatcher
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initWithFilePath:(NSString *)filePath handler:(void (^)(void))handler {
self = [super init];
if (self) {
_filePath = filePath;
_eventHandler = handler;
if (!_filePath || !_eventHandler) return nil;
[self beginWatchingFile];
}
return self;
}
- (void)dealloc {
[self stopWatchingFile];
}
- (void)beginWatchingFile {
__weak __typeof(self) weakSelf = self;
int mask = (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_RENAME);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
self.internalEventHandler = ^{
unsigned long l = dispatch_source_get_data(weakSelf.monitoringSource);
if (l & DISPATCH_VNODE_DELETE || l & DISPATCH_VNODE_RENAME) {
if (weakSelf.monitoringSource) dispatch_source_cancel(weakSelf.monitoringSource);
} else {
weakSelf.eventHandler();
}
};
self.internalCancelHandler = ^{
int fd;
if (weakSelf.monitoringSource) {
fd = (int)dispatch_source_get_handle(weakSelf.monitoringSource);
close(fd);
}
while ((fd = open([weakSelf.filePath fileSystemRepresentation], O_EVTONLY)) < 0) {
usleep(1000);
}
weakSelf.monitoringSource =
dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
dispatch_source_set_event_handler(weakSelf.monitoringSource, weakSelf.internalEventHandler);
dispatch_source_set_cancel_handler(weakSelf.monitoringSource, weakSelf.internalCancelHandler);
dispatch_resume(weakSelf.monitoringSource);
weakSelf.eventHandler();
};
dispatch_async(queue, self.internalCancelHandler);
}
- (void)stopWatchingFile {
if (!self.monitoringSource) return;
int fd = (int)dispatch_source_get_handle(self.monitoringSource);
dispatch_source_set_event_handler_f(self.monitoringSource, NULL);
dispatch_source_set_cancel_handler(self.monitoringSource, ^{ close(fd); });
dispatch_source_cancel(self.monitoringSource);
self.monitoringSource = nil;
}
@end

View File

@@ -16,11 +16,12 @@
/// Common defines between kernel <-> userspace
///
#include <sys/param.h>
#ifndef SANTA__COMMON__KERNELCOMMON_H
#define SANTA__COMMON__KERNELCOMMON_H
// Defines the lengths of paths and Vnode IDs passed around.
#define MAX_PATH_LEN 1024 // ==PATH_LEN from syslimits.h
#define MAX_VNODE_ID_STR 21 // digits in UINT64_MAX + 1 for NULL-terminator
// Defines the name of the userclient class and the driver bundle ID.
@@ -50,15 +51,20 @@ typedef enum {
ACTION_RESPOND_CHECKBW_ALLOW = 11,
ACTION_RESPOND_CHECKBW_DENY = 12,
// NOTIFY
ACTION_NOTIFY_EXEC_ALLOW_NODAEMON = 30,
ACTION_NOTIFY_EXEC_ALLOW_CACHED = 31,
ACTION_NOTIFY_EXEC_DENY_CACHED = 32,
// SHUTDOWN
ACTION_REQUEST_SHUTDOWN = 60,
ACTION_REQUEST_SHUTDOWN = 90,
// ERROR
ACTION_ERROR = 99,
} santa_action_t;
#define CHECKBW_RESPONSE_VALID(x) (x == ACTION_RESPOND_CHECKBW_ALLOW || \
x == ACTION_RESPOND_CHECKBW_DENY)
#define CHECKBW_RESPONSE_VALID(x) \
(x == ACTION_RESPOND_CHECKBW_ALLOW || x == ACTION_RESPOND_CHECKBW_DENY)
// Message struct that is sent down the IODataQueue.
typedef struct {
@@ -67,7 +73,7 @@ typedef struct {
uid_t userId;
pid_t pid;
pid_t ppid;
char path[MAX_PATH_LEN];
char path[MAXPATHLEN];
} santa_message_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -15,9 +15,9 @@
#import "SNTLogging.h"
#ifdef DEBUG
static int logLevel = LOG_LEVEL_DEBUG; // default to info
static int logLevel = LOG_LEVEL_DEBUG;
#else
static int logLevel = LOG_LEVEL_INFO;
static int logLevel = LOG_LEVEL_INFO; // default to info
#endif
void logMessage(int level, FILE *destination, NSString *format, ...) {
@@ -58,6 +58,6 @@ void logMessage(int level, FILE *destination, NSString *format, ...) {
}
fprintf(destination, "%s\n", [[NSString stringWithFormat:@"[%@] %@ %@: %@",
[dateFormatter stringFromDate:[NSDate date]], levelName, binaryName, s] UTF8String]);
[dateFormatter stringFromDate:[NSDate date]], levelName, binaryName, s] UTF8String]);
}
}

View File

@@ -22,7 +22,7 @@
customMsg:(NSString *)customMsg {
self = [super init];
if (self) {
_shasum = shasum;
_shasum = shasum;
_state = state;
_type = type;
_customMsg = customMsg;
@@ -34,11 +34,10 @@
#define ENCODE(obj, key) if (obj) [coder encodeObject:obj forKey:key]
#define DECODE(cls, key) [decoder decodeObjectOfClass:[cls class] forKey:key]
#define DECODEARRAY(cls, key) \
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
+ (BOOL)supportsSecureCoding { return YES; }
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.shasum, @"shasum");
@@ -58,4 +57,7 @@
return self;
}
#undef DECODE
#undef ENCODE
@end

View File

@@ -95,4 +95,9 @@
///
@property NSNumber *ppid;
///
/// The name of the parent process.
///
@property NSString *parentName;
@end

View File

@@ -24,7 +24,9 @@
[decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [cls class], nil] \
forKey:key]
+ (BOOL)supportsSecureCoding { return YES; }
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
ENCODE(self.idx, @"idx");
@@ -43,6 +45,7 @@
ENCODE(@(self.decision), @"decision");
ENCODE(self.pid, @"pid");
ENCODE(self.ppid, @"ppid");
ENCODE(self.parentName, @"parentName");
ENCODE(self.loggedInUsers, @"loggedInUsers");
ENCODE(self.currentSessions, @"currentSessions");
@@ -64,9 +67,10 @@
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
_decision = [DECODE(NSNumber, @"decision") intValue];
_decision = (santa_eventstate_t)[DECODE(NSNumber, @"decision") intValue];
_pid = DECODE(NSNumber, @"pid");
_ppid = DECODE(NSNumber, @"ppid");
_parentName = DECODE(NSString, @"parentName");
_loggedInUsers = DECODEARRAY(NSString, @"loggedInUsers");
_currentSessions = DECODEARRAY(NSString, @"currentSessions");
@@ -74,11 +78,11 @@
return self;
}
- (BOOL)isEqual:(SNTStoredEvent *)other {
- (BOOL)isEqual:(id)other {
if (other == self) return YES;
if (![other isKindOfClass:[SNTStoredEvent class]]) return NO;
return ([self.fileSHA256 isEqual:other.fileSHA256] &&
[self.idx isEqual:other.idx]);
SNTStoredEvent *o;
return ([self.fileSHA256 isEqual:o.fileSHA256] && [self.idx isEqual:o.idx]);
}
- (NSUInteger)hash {
@@ -91,8 +95,8 @@
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@",
self.idx, self.fileSHA256];
return
[NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-256: %@", self.idx, self.fileSHA256];
}
@end

View File

@@ -21,14 +21,11 @@
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (!platformExpert) return nil;
NSString *serial = CFBridgingRelease(
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault,
0));
NSString *serial = CFBridgingRelease(IORegistryEntryCreateCFProperty(
platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0));
IOObjectRelease(platformExpert);
return serial;
}
@@ -37,10 +34,8 @@
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
if (!platformExpert) return nil;
NSString *uuid = CFBridgingRelease(
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformUUIDKey),
kCFAllocatorDefault, 0));
NSString *uuid = CFBridgingRelease(IORegistryEntryCreateCFProperty(
platformExpert, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0));
IOObjectRelease(platformExpert);
@@ -60,14 +55,16 @@
}
+ (NSString *)longHostname {
return [[NSHost currentHost] name];
char hostname[MAXHOSTNAMELEN];
gethostname(hostname, (int)sizeof(hostname));
return @(hostname);
}
# pragma mark - Internal
#pragma mark - Internal
+ (NSDictionary *)_systemVersionDictionary {
return [NSDictionary dictionaryWithContentsOfFile:
@"/System/Library/CoreServices/SystemVersion.plist"];
return [NSDictionary
dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"];
}
@end

View File

@@ -63,7 +63,7 @@ typedef void (^SNTXPCRejectedBlock)(void);
/// @warning Do not send a message to this object if you didn't set @c remoteInterface above
/// before calling the @c resume method. Doing so will throw an exception.
///
@property(readonly) id remoteObjectProxy;
@property(readonly, nonatomic) id remoteObjectProxy;
///
/// The interface this object exports.

View File

@@ -59,8 +59,7 @@
if (self) {
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name
options:options];
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name options:options];
if (!_validatorInterface || !_currentConnection) return nil;
}
@@ -75,24 +74,22 @@
#pragma mark Connection set-up
- (void)resume {
if (_listenerObject) {
if (self.listenerObject) {
// A new listener doesn't do anything until a client connects.
self.listenerObject.delegate = self;
[self.listenerObject resume];
} else {
// A new client begins the validation process.
NSXPCConnection *connection = _currentConnection;
NSXPCConnection *connection = self.currentConnection;
connection.remoteObjectInterface = _validatorInterface;
connection.remoteObjectInterface = self.validatorInterface;
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
self.currentConnection = nil;
};
connection.interruptionHandler = ^{
[self.currentConnection invalidate];
};
connection.interruptionHandler = ^{ [self.currentConnection invalidate]; };
[connection resume];
@@ -123,6 +120,7 @@
for (int sleepLoops = 0; sleepLoops < 1000 && !verificationComplete; sleepLoops++) {
usleep(5000);
}
if (!verificationComplete) [self invalidate];
}
}
@@ -132,7 +130,7 @@
if (self.currentConnection) return NO;
connection.exportedObject = self;
connection.exportedInterface = _validatorInterface;
connection.exportedInterface = self.validatorInterface;
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];

View File

@@ -25,18 +25,18 @@
///
/// Kernel ops
///
- (void)cacheCount:(void (^)(uint64_t))reply;
- (void)cacheCount:(void (^)(int64_t))reply;
- (void)flushCache:(void (^)(BOOL))reply;
///
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule withReply:(void (^)())reply;
- (void)databaseRuleAddRules:(NSArray *)rules withReply:(void (^)())reply;
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseEventCount:(void (^)(uint64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 withReply:(void (^)(SNTStoredEvent *))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply;
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
@@ -44,7 +44,7 @@
/// Misc ops
///
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode withReply:(void (^)())reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
@end

View File

@@ -32,7 +32,7 @@
ofReply:YES];
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTRule class], nil]
forSelector:@selector(databaseRuleAddRules:withReply:)
forSelector:@selector(databaseRuleAddRules:cleanSlate:reply:)
argumentIndex:0
ofReply:NO];

View File

@@ -20,27 +20,51 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
#pragma mark Object Lifecycle
bool SantaDecisionManager::init() {
dataqueue_lock_ = IORWLockAlloc();
cached_decisions_lock_ = IORWLockAlloc();
if (!super::init()) return false;
sdm_lock_grp_ = lck_grp_alloc_init("santa-locks", lck_grp_attr_alloc_init());
cached_decisions_lock_ = lck_rw_alloc_init(sdm_lock_grp_,
lck_attr_alloc_init());
cached_decisions_ = OSDictionary::withCapacity(1000);
return kIOReturnSuccess;
dataqueue_ = IOSharedDataQueue::withCapacity((sizeof(santa_message_t) +
DATA_QUEUE_ENTRY_HEADER_SIZE)
* kMaxQueueEvents);
if (!dataqueue_) return kIOReturnNoMemory;
shared_memory_ = dataqueue_->getMemoryDescriptor();
if (!shared_memory_) return kIOReturnNoMemory;
client_pid_ = 0;
return true;
}
void SantaDecisionManager::free() {
if (shared_memory_) {
shared_memory_->release();
shared_memory_ = NULL;
}
if (dataqueue_) {
dataqueue_->release();
dataqueue_ = NULL;
}
if (cached_decisions_) {
cached_decisions_->release();
cached_decisions_ = NULL;
}
if (cached_decisions_lock_) {
IORWLockFree(cached_decisions_lock_);
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
cached_decisions_lock_ = NULL;
}
if (dataqueue_lock_ ) {
IORWLockFree(dataqueue_lock_);
dataqueue_lock_ = NULL;
if (sdm_lock_grp_) {
lck_grp_free(sdm_lock_grp_);
sdm_lock_grp_ = NULL;
}
super::free();
@@ -48,45 +72,46 @@ void SantaDecisionManager::free() {
#pragma mark Client Management
void SantaDecisionManager::ConnectClient(IOSharedDataQueue *queue, pid_t pid) {
void SantaDecisionManager::ConnectClient(mach_port_t port, pid_t pid) {
if (!pid) return;
if (!queue) return;
// Any decisions made while the daemon wasn't
// connected should be cleared
cached_decisions_->flushCollection();
ClearCache();
dataqueue_ = queue;
dataqueue_->retain();
dataqueue_->setNotificationPort(port);
owning_pid_ = pid;
owning_proc_ = proc_find(pid);
client_pid_ = pid;
client_proc_ = proc_find(pid);
failed_queue_requests_ = 0;
}
void SantaDecisionManager::DisconnectClient() {
owning_pid_ = -1;
void SantaDecisionManager::DisconnectClient(bool itDied) {
if (client_pid_ < 1) return;
client_pid_ = -1;
// Ask santad to shutdown, in case it's running.
santa_message_t message;
message.action = ACTION_REQUEST_SHUTDOWN;
message.userId = 0;
message.pid = 0;
message.ppid = 0;
message.vnode_id = 0;
PostToQueue(message);
if (!itDied) {
santa_message_t message = {.action = ACTION_REQUEST_SHUTDOWN};
PostToQueue(message);
}
dataqueue_->release();
dataqueue_ = NULL;
dataqueue_->setNotificationPort(NULL);
proc_rele(owning_proc_);
owning_proc_ = NULL;
proc_rele(client_proc_);
client_proc_ = NULL;
}
bool SantaDecisionManager::ClientConnected() {
return owning_pid_ > 0;
return client_pid_ > 0;
}
# pragma mark Listener Control
IOMemoryDescriptor *SantaDecisionManager::GetMemoryDescriptor() {
return shared_memory_;
}
#pragma mark Listener Control
kern_return_t SantaDecisionManager::StartListener() {
vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
@@ -94,7 +119,12 @@ kern_return_t SantaDecisionManager::StartListener() {
reinterpret_cast<void *>(this));
if (!vnode_listener_) return kIOReturnInternalError;
LOGD("Vnode listener started.");
fileop_listener_ = kauth_listen_scope(KAUTH_SCOPE_FILEOP,
fileop_scope_callback,
reinterpret_cast<void *>(this));
if (!fileop_listener_) return kIOReturnInternalError;
LOGD("Listeners started.");
return kIOReturnSuccess;
}
@@ -103,6 +133,9 @@ kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;
kauth_unlisten_scope(fileop_listener_);
fileop_listener_ = NULL;
// Wait for any active invocations to finish before returning
do {
IOSleep(5);
@@ -111,7 +144,7 @@ kern_return_t SantaDecisionManager::StopListener() {
// Delete any cached decisions
ClearCache();
LOGD("Vnode listener stopped.");
LOGD("Listeners stopped.");
return kIOReturnSuccess;
}
@@ -120,7 +153,7 @@ kern_return_t SantaDecisionManager::StopListener() {
void SantaDecisionManager::AddToCache(
const char *identifier, santa_action_t decision, uint64_t microsecs) {
IORWLockWrite(cached_decisions_lock_);
lck_rw_lock_exclusive(cached_decisions_lock_);
if (cached_decisions_->getCount() > kMaxCacheSize) {
// This could be made a _lot_ smarter, say only removing entries older
@@ -138,24 +171,30 @@ void SantaDecisionManager::AddToCache(
cached_decisions_->setObject(identifier, pending);
pending->release(); // it was retained when added to the dictionary
} else {
SantaMessage *pending = OSDynamicCast(
SantaMessage, cached_decisions_->getObject(identifier));
SantaMessage *pending =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
if (pending) {
pending->setAction(decision, microsecs);
}
}
IORWLockUnlock(cached_decisions_lock_);
lck_rw_unlock_exclusive(cached_decisions_lock_);
}
void SantaDecisionManager::CacheCheck(const char *identifier) {
IORWLockRead(cached_decisions_lock_);
lck_rw_lock_shared(cached_decisions_lock_);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
IORWLockUnlock(cached_decisions_lock_);
if (shouldInvalidate) {
IORWLockWrite(cached_decisions_lock_);
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
// shared_to_exclusive will return false if a previous reader upgraded
// and if that happens the lock will have been unlocked. If that happens,
// which is rare, relock exclusively.
lck_rw_lock_exclusive(cached_decisions_lock_);
}
cached_decisions_->removeObject(identifier);
IORWLockUnlock(cached_decisions_lock_);
lck_rw_unlock_exclusive(cached_decisions_lock_);
} else {
lck_rw_unlock_shared(cached_decisions_lock_);
}
}
@@ -164,23 +203,25 @@ uint64_t SantaDecisionManager::CacheCount() {
}
void SantaDecisionManager::ClearCache() {
IORWLockWrite(cached_decisions_lock_);
lck_rw_lock_exclusive(cached_decisions_lock_);
cached_decisions_->flushCollection();
IORWLockUnlock(cached_decisions_lock_);
lck_rw_unlock_exclusive(cached_decisions_lock_);
}
#pragma mark Decision Fetching
santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
santa_action_t result = ACTION_UNSET;
uint64_t decision_time = 0;
IORWLockRead(cached_decisions_lock_);
SantaMessage *cached_decision = OSDynamicCast(
SantaMessage, cached_decisions_->getObject(identifier));
lck_rw_lock_shared(cached_decisions_lock_);
SantaMessage *cached_decision =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
if (cached_decision) {
result = cached_decision->getAction();
decision_time = cached_decision->getMicrosecs();
}
IORWLockUnlock(cached_decisions_lock_);
lck_rw_unlock_shared(cached_decisions_lock_);
if (CHECKBW_RESPONSE_VALID(result)) {
uint64_t diff_time = GetCurrentUptime();
@@ -200,9 +241,9 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
}
if (decision_time < diff_time) {
IORWLockWrite(cached_decisions_lock_);
lck_rw_lock_exclusive(cached_decisions_lock_);
cached_decisions_->removeObject(identifier);
IORWLockUnlock(cached_decisions_lock_);
lck_rw_unlock_exclusive(cached_decisions_lock_);
return ACTION_UNSET;
}
}
@@ -210,16 +251,44 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
return result;
}
# pragma mark Queue Management
santa_action_t SantaDecisionManager::GetFromDaemon(
santa_message_t message, char *vnode_id_str) {
santa_action_t return_action = ACTION_UNSET;
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
IORWLockWrite(dataqueue_lock_);
bool kr = false;
if (dataqueue_) {
kr = dataqueue_->enqueue(&message, sizeof(message));
// Wait for the daemon to respond or die.
do {
// Send request to daemon...
if (!PostToQueue(message)) {
OSIncrementAtomic(&failed_queue_requests_);
if (failed_queue_requests_ > kMaxQueueFailures) {
LOGE("Failed to queue more than %d requests, killing daemon",
kMaxQueueFailures);
proc_signal(client_pid_, SIGKILL);
}
LOGE("Failed to queue request for %s.", message.path);
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
// ... and wait for it to respond. If after kRequestLoopSleepMilliseconds
// * kMaxRequestLoops it still hasn't responded, send request again.
for (int i = 0; i < kMaxRequestLoops; ++i) {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
if (CHECKBW_RESPONSE_VALID(return_action)) break;
}
} while (!CHECKBW_RESPONSE_VALID(return_action) &&
proc_exiting(client_proc_) == 0);
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back.");
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
IORWLockUnlock(dataqueue_lock_);
return kr;
return return_action;
}
santa_action_t SantaDecisionManager::FetchDecision(
@@ -236,70 +305,48 @@ santa_action_t SantaDecisionManager::FetchDecision(
// Check to see if item is in cache
return_action = GetFromCache(vnode_id_str);
// If item wasn't in cache, fetch decision from daemon.
if (!CHECKBW_RESPONSE_VALID(return_action)) {
// Add pending request to cache
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
// If item wasn in cache return it.
if CHECKBW_RESPONSE_VALID(return_action) return return_action;
// Get path
char path[MAX_PATH_LEN];
int name_len = MAX_PATH_LEN;
if (vn_getpath(vnode, path, &name_len) != 0) {
path[0] = '\0';
}
// Add pending request to cache.
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
if (!ClientConnected()) {
LOGI("Execution request without daemon running: %s", path);
AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_ALLOW,
GetCurrentUptime());
return ACTION_RESPOND_CHECKBW_ALLOW;
}
// Prepare to send message to daemon
santa_message_t message;
strncpy(message.path, path, MAX_PATH_LEN);
message.userId = kauth_cred_getuid(credential);
message.pid = proc_selfpid();
message.ppid = proc_selfppid();
message.action = ACTION_REQUEST_CHECKBW;
message.vnode_id = vnode_id;
// Wait for the daemon to respond or die.
do {
// Send request to daemon...
if (!PostToQueue(message)) {
LOGE("Failed to queue request for %s.", path);
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
// ... and wait for it to respond. If after kRequestLoopSleepMilliseconds
// * kMaxRequestLoops it still hasn't responded, send request again.
for (int i = 0; i < kMaxRequestLoops; ++i) {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
if (CHECKBW_RESPONSE_VALID(return_action)) break;
}
} while (!CHECKBW_RESPONSE_VALID(return_action) &&
proc_exiting(owning_proc_) == 0);
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back.");
CacheCheck(vnode_id_str);
return ACTION_ERROR;
}
// Get path
char path[MAXPATHLEN];
int name_len = MAXPATHLEN;
if (vn_getpath(vnode, path, &name_len) != 0) {
path[0] = '\0';
}
return return_action;
// Prepare message to send to daemon.
santa_message_t message = {};
strlcpy(message.path, path, sizeof(message.path));
message.userId = kauth_cred_getuid(credential);
message.pid = proc_selfpid();
message.ppid = proc_selfppid();
message.action = ACTION_REQUEST_CHECKBW;
message.vnode_id = vnode_id;
if (ClientConnected()) {
return GetFromDaemon(message, vnode_id_str);
} else {
LOGI("Execution request without daemon running: %s", path);
message.action = ACTION_NOTIFY_EXEC_ALLOW_NODAEMON;
PostToQueue(message);
return ACTION_RESPOND_CHECKBW_ALLOW;
}
}
# pragma mark Misc
#pragma mark Misc
uint64_t SantaDecisionManager::GetVnodeIDForVnode(const vfs_context_t context,
const vnode_t vp) {
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
bool kr = false;
kr = dataqueue_->enqueue(&message, sizeof(message));
return kr;
}
uint64_t SantaDecisionManager::GetVnodeIDForVnode(
const vfs_context_t context, const vnode_t vp) {
struct vnode_attr vap;
VATTR_INIT(&vap);
VATTR_WANTED(&vap, va_fileid);
@@ -314,7 +361,7 @@ uint64_t SantaDecisionManager::GetCurrentUptime() {
return (uint64_t)((sec * 1000000) + usec);
}
# pragma mark Invocation Tracking & PID comparison
#pragma mark Invocation Tracking & PID comparison
void SantaDecisionManager::IncrementListenerInvocations() {
OSIncrementAtomic(&listener_invocations_);
@@ -326,7 +373,35 @@ void SantaDecisionManager::DecrementListenerInvocations() {
#undef super
#pragma mark Kauth Callback
#pragma mark Kauth Callbacks
extern "C" int fileop_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
if (!(action == KAUTH_FILEOP_CLOSE && arg2 & KAUTH_FILEOP_CLOSE_MODIFIED)) {
return KAUTH_RESULT_DEFER;
}
if (idata == NULL) {
LOGE("FileOp callback established without valid decision manager.");
return KAUTH_RESULT_DEFER;
}
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
sdm->IncrementListenerInvocations();
vfs_context_t context = vfs_context_create(NULL);
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
sdm->GetVnodeIDForVnode(context, (vnode_t)arg0));
sdm->CacheCheck(vnode_id_str);
vfs_context_rele(context);
sdm->DecrementListenerInvocations();
return KAUTH_RESULT_DEFER;
}
extern "C" int vnode_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
@@ -339,8 +414,8 @@ extern "C" int vnode_scope_callback(
LOGE("Vnode callback established without valid decision manager.");
return returnResult;
}
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
SantaDecisionManager *sdm =
OSDynamicCast(SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vfs_context_t vfs_context = reinterpret_cast<vfs_context_t>(arg0);
vnode_t vnode = reinterpret_cast<vnode_t>(arg1);
@@ -351,34 +426,24 @@ extern "C" int vnode_scope_callback(
// Don't operate on ACCESS events, as they're advisory
if (action & KAUTH_VNODE_ACCESS) return returnResult;
// Filter for only writes
if (action & KAUTH_VNODE_WRITE_DATA ||
action & KAUTH_VNODE_APPEND_DATA ||
action & KAUTH_VNODE_DELETE) {
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
sdm->GetVnodeIDForVnode(vfs_context, vnode));
// If an execution request is pending, deny write
if (sdm->GetFromCache(vnode_id_str) == ACTION_REQUEST_CHECKBW) {
LOGD("Denying write due to pending execution: %s", vnode_id_str);
*(reinterpret_cast<int *>(arg3)) = EACCES;
return KAUTH_RESULT_DENY;
}
// Otherwise remove from cache
sdm->CacheCheck(vnode_id_str);
return returnResult;
}
// Filter for only EXECUTE actions
if (action & KAUTH_VNODE_EXECUTE) {
sdm->IncrementListenerInvocations();
// Fetch decision
santa_action_t returnedAction = sdm->FetchDecision(
credential, vfs_context, vnode);
santa_action_t returnedAction =
sdm->FetchDecision(credential, vfs_context, vnode);
// If file has dirty blocks, remove from cache and deny. This would usually
// be the case if a file has been written to and flushed but not yet
// closed.
if (vnode_hasdirtyblks(vnode)) {
char vnode_id_str[MAX_VNODE_ID_STR];
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
sdm->GetVnodeIDForVnode(vfs_context, vnode));
sdm->CacheCheck(vnode_id_str);
returnedAction = ACTION_RESPOND_CHECKBW_DENY;
}
switch (returnedAction) {
case ACTION_RESPOND_CHECKBW_ALLOW:

View File

@@ -15,7 +15,9 @@
#ifndef SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
#define SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
#include <IOKit/IODataQueueShared.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOSharedDataQueue.h>
#include <libkern/c++/OSDictionary.h>
#include <sys/kauth.h>
@@ -26,35 +28,6 @@
#include "SNTKernelCommon.h"
#include "SNTLogging.h"
///
/// The maximum number of milliseconds a cached deny message should be
/// considered valid.
///
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
///
/// The maximum number of milliseconds a cached allow message should be
/// considered valid.
///
const uint64_t kMaxAllowCacheTimeMilliseconds = 1000 * 60 * 60 * 24;
///
/// While waiting for a response from the daemon, this is the number of
/// milliseconds to sleep for before checking the cache for a response.
///
const int kRequestLoopSleepMilliseconds = 10;
///
/// While waiting for a response from the daemon, this is the maximum number
/// of loops to wait before sending the request again.
///
const int kMaxRequestLoops = 50;
///
/// Maximum number of entries in the in-kernel cache.
///
const int kMaxCacheSize = 10000;
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
/// and responding to the request appropriately.
@@ -68,33 +41,37 @@ class SantaDecisionManager : public OSObject {
public:
/// Used for initialization after instantiation. Required because
/// constructors cannot throw inside kernel-space.
bool init();
bool init() override;
/// Called automatically when retain count drops to 0.
void free();
void free() override;
/// Called by SantaDriverClient during connection to provide the shared
/// dataqueue memory to the client.
IOMemoryDescriptor *GetMemoryDescriptor();
/// Called by SantaDriverClient when a client connects, providing the data
/// queue used to pass messages and the pid of the client process.
void ConnectClient(IOSharedDataQueue *queue, pid_t pid);
void ConnectClient(mach_port_t port, pid_t pid);
/// Called by SantaDriverClient when a client disconnects
void DisconnectClient();
void DisconnectClient(bool itDied = false);
/// Returns whether a client is currently connected or not.
bool ClientConnected();
/// Starts the kauth listener.
/// Starts the kauth listeners.
kern_return_t StartListener();
/// Stops the kauth listener. After stopping new callback requests,
/// 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();
/// Adds a decision to the cache, with a timestamp.
void AddToCache(const char *identifier,
const santa_action_t decision,
const uint64_t microsecs);
const uint64_t microsecs = GetCurrentUptime());
/// Checks to see if a given identifier is in the cache and removes it.
void CacheCheck(const char *identifier);
@@ -109,10 +86,8 @@ class SantaDecisionManager : public OSObject {
/// entry has expired.
santa_action_t GetFromCache(const char *identifier);
/// Posts the requested message to the client data queue, if there is one.
/// Uses dataqueue_lock_ to ensure two threads don't try to write to the
/// queue at the same time.
bool PostToQueue(santa_message_t);
/// Fetches a response from the daemon.
santa_action_t GetFromDaemon(santa_message_t message, char *identifier);
/// Fetches an execution decision for a file, first using the cache and then
/// by sending a message to the daemon and waiting until a response arrives.
@@ -122,11 +97,14 @@ class SantaDecisionManager : public OSObject {
const vfs_context_t vfs_context,
const vnode_t vnode);
/// Posts the requested message to the client data queue.
bool PostToQueue(santa_message_t);
/// Fetches the vnode_id for a given vnode.
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
/// Returns the current system uptime in microseconds
uint64_t GetCurrentUptime();
static uint64_t GetCurrentUptime();
/// Increments the count of active vnode callback's pending.
void IncrementListenerInvocations();
@@ -134,19 +112,64 @@ class SantaDecisionManager : public OSObject {
/// Decrements the count of active vnode callback's pending.
void DecrementListenerInvocations();
protected:
///
/// The maximum number of milliseconds a cached deny message should be
/// considered valid.
///
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
///
/// The maximum number of milliseconds a cached allow message should be
/// considered valid.
///
const uint64_t kMaxAllowCacheTimeMilliseconds = 1000 * 60 * 60 * 24;
///
/// While waiting for a response from the daemon, this is the number of
/// milliseconds to sleep for before checking the cache for a response.
///
const int kRequestLoopSleepMilliseconds = 10;
///
/// While waiting for a response from the daemon, this is the maximum number
/// of loops to wait before sending the request again.
///
const int kMaxRequestLoops = 50;
///
/// Maximum number of entries in the in-kernel cache.
///
const int kMaxCacheSize = 10000;
///
/// Maximum number of PostToQueue failures to allow.
///
const int kMaxQueueFailures = 10;
///
/// The maximum number of messages can be kept in
/// the IODataQueue at any time.
///
const int kMaxQueueEvents = 512;
private:
lck_grp_t *sdm_lock_grp_;
lck_rw_t *cached_decisions_lock_;
OSDictionary *cached_decisions_;
IORWLock *cached_decisions_lock_;
IOSharedDataQueue *dataqueue_;
IORWLock *dataqueue_lock_;
IOMemoryDescriptor *shared_memory_;
SInt32 failed_queue_requests_;
SInt32 listener_invocations_;
pid_t owning_pid_;
proc_t owning_proc_;
pid_t client_pid_;
proc_t client_proc_;
kauth_listener_t vnode_listener_;
kauth_listener_t fileop_listener_;
};
///
@@ -163,4 +186,19 @@ extern "C" int vnode_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
///
/// 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.
///
extern "C" int fileop_scope_callback(
kauth_cred_t credential, void *idata, kauth_action_t action,
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
#endif // SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H

View File

@@ -24,8 +24,12 @@ bool SantaDriver::start(IOService *provider) {
if (!super::start(provider)) return false;
santaDecisionManager = new SantaDecisionManager;
santaDecisionManager->init();
santaDecisionManager->StartListener();
if (!santaDecisionManager->init() ||
santaDecisionManager->StartListener() != kIOReturnSuccess) {
santaDecisionManager->release();
santaDecisionManager = NULL;
return false;
}
registerService();
@@ -44,7 +48,7 @@ void SantaDriver::stop(IOService *provider) {
super::stop(provider);
}
SantaDecisionManager* SantaDriver::GetDecisionManager() {
SantaDecisionManager *SantaDriver::GetDecisionManager() {
return santaDecisionManager;
}

View File

@@ -31,17 +31,16 @@ class com_google_SantaDriver : public IOService {
public:
/// Called by the kernel when the kext is loaded
bool start(IOService *provider);
bool start(IOService *provider) override;
/// Called by the kernel when the kext is unloaded
void stop(IOService *provider);
void stop(IOService *provider) override;
/// Returns a pointer to the SantaDecisionManager created in start().
SantaDecisionManager* GetDecisionManager();
SantaDecisionManager *GetDecisionManager();
private:
SantaDecisionManager *santaDecisionManager;
};
#endif // SANTA__SANTA_DRIVER__SANTADRIVER_H

View File

@@ -36,71 +36,58 @@ bool SantaDriverClient::initWithTask(
}
bool SantaDriverClient::start(IOService *provider) {
fProvider = OSDynamicCast(com_google_SantaDriver, provider);
myProvider = OSDynamicCast(com_google_SantaDriver, provider);
if (!fProvider) return false;
if (!myProvider) return false;
if (!super::start(provider)) return false;
fSDM = fProvider->GetDecisionManager();
decisionManager = myProvider->GetDecisionManager();
if (!decisionManager) return false;
return true;
}
void SantaDriverClient::stop(IOService *provider) {
super::stop(provider);
fProvider = NULL;
myProvider = NULL;
decisionManager = NULL;
}
IOReturn SantaDriverClient::clientClose() {
terminate(kIOServiceSynchronous);
return kIOReturnSuccess;
decisionManager->DisconnectClient(true);
return terminate(kIOServiceSynchronous) ? kIOReturnSuccess : kIOReturnError;
}
bool SantaDriverClient::terminate(IOOptionBits options) {
fSDM->DisconnectClient();
decisionManager->DisconnectClient();
LOGI("Client disconnected.");
fSharedMemory->release();
fDataQueue->release();
fSharedMemory = NULL;
fDataQueue = NULL;
if (fProvider && fProvider->isOpen(this)) fProvider->close(this);
if (myProvider && myProvider->isOpen(this)) myProvider->close(this);
return super::terminate(options);
}
#pragma mark Fetching memory and data queue notifications
IOReturn SantaDriverClient::registerNotificationPort(mach_port_t port,
UInt32 type,
UInt32 ref) {
if ((!fDataQueue) || (port == MACH_PORT_NULL)) return kIOReturnError;
IOReturn SantaDriverClient::registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 ref) {
if (port == MACH_PORT_NULL) return kIOReturnError;
fDataQueue->setNotificationPort(port);
decisionManager->ConnectClient(port, proc_selfpid());
LOGI("Client connected, PID: %d.", proc_selfpid());
return kIOReturnSuccess;
}
IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
IOOptionBits *options,
IOMemoryDescriptor **memory) {
*memory = NULL;
IOReturn SantaDriverClient::clientMemoryForType(
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) {
if (type != kIODefaultMemoryType) return kIOReturnNoMemory;
*options = 0;
*memory = decisionManager->GetMemoryDescriptor();
(*memory)->retain();
if (type == kIODefaultMemoryType) {
if (!fSharedMemory) return kIOReturnNoMemory;
fSharedMemory->retain(); // client will decrement this ref
*memory = fSharedMemory;
fSDM->ConnectClient(fDataQueue, proc_selfpid());
LOGI("Client connected, PID: %d.", proc_selfpid());
return kIOReturnSuccess;
}
return kIOReturnNoMemory;
return kIOReturnSuccess;
}
#pragma mark Callable Methods
@@ -108,23 +95,11 @@ IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
IOReturn SantaDriverClient::open() {
if (isInactive()) return kIOReturnNotAttached;
if (!fProvider->open(this)) {
if (!myProvider->open(this)) {
LOGW("A second client tried to connect.");
return kIOReturnExclusiveAccess;
}
fDataQueue = IOSharedDataQueue::withCapacity((sizeof(santa_message_t) +
DATA_QUEUE_ENTRY_HEADER_SIZE)
* kMaxQueueEvents);
if (!fDataQueue) return kIOReturnNoMemory;
fSharedMemory = fDataQueue->getMemoryDescriptor();
if (!fSharedMemory) {
fDataQueue->release();
fDataQueue = NULL;
return kIOReturnVMError;
}
return kIOReturnSuccess;
}
@@ -139,9 +114,7 @@ IOReturn SantaDriverClient::static_open(
IOReturn SantaDriverClient::allow_binary(const uint64_t vnode_id) {
char vnode_id_str[21];
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
fSDM->AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_ALLOW,
fSDM->GetCurrentUptime());
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_ALLOW);
return kIOReturnSuccess;
}
@@ -158,9 +131,7 @@ IOReturn SantaDriverClient::static_allow_binary(
IOReturn SantaDriverClient::deny_binary(const uint64_t vnode_id) {
char vnode_id_str[21];
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
fSDM->AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_DENY,
fSDM->GetCurrentUptime());
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_DENY);
return kIOReturnSuccess;
}
@@ -175,7 +146,7 @@ IOReturn SantaDriverClient::static_deny_binary(
}
IOReturn SantaDriverClient::clear_cache() {
fSDM->ClearCache();
decisionManager->ClearCache();
return kIOReturnSuccess;
}
@@ -188,7 +159,7 @@ IOReturn SantaDriverClient::static_clear_cache(
}
IOReturn SantaDriverClient::cache_count(uint64_t *output) {
*output = fSDM->CacheCount();
*output = decisionManager->CacheCount();
return kIOReturnSuccess;
}

View File

@@ -16,9 +16,6 @@
#define SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H
#include <IOKit/IOUserClient.h>
#include <IOKit/IOSharedDataQueue.h>
#include <IOKit/IOLib.h>
#include <IOKit/IODataQueueShared.h>
#include <sys/kauth.h>
#include <sys/vnode.h>
#include <sys/proc.h>
@@ -28,9 +25,6 @@
#include "SantaMessage.h"
#include "SNTKernelCommon.h"
// The maximum number of messages can be kept in the IODataQueue at any time.
const int kMaxQueueEvents = 256;
///
/// This class is instantiated by IOKit when a new client process attempts to
/// connect to the driver. Starting, stopping, handling connections, allocating
@@ -39,39 +33,34 @@ const int kMaxQueueEvents = 256;
/// Documentation on how the IOUserClient parts of this code work can be found
/// here:
/// https://developer.apple.com/library/mac/samplecode/SimpleUserClient/Listings/User_Client_Info_txt.html
/// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/WritingDeviceDriver/WritingDeviceDriver.pdf
///
class com_google_SantaDriverClient : public IOUserClient {
OSDeclareDefaultStructors(com_google_SantaDriverClient);
private:
IOSharedDataQueue *fDataQueue;
IOMemoryDescriptor *fSharedMemory;
com_google_SantaDriver *fProvider;
SantaDecisionManager *fSDM;
public:
/// Called as part of IOServiceOpen in clients
bool initWithTask(task_t owningTask, void *securityID, UInt32 type);
bool initWithTask(task_t owningTask, void *securityID, UInt32 type) override;
/// Called after initWithTask as part of IOServiceOpen
bool start(IOService *provider);
bool start(IOService *provider) override;
/// Called when this class is stopping
void stop(IOService *provider);
void stop(IOService *provider) override;
/// Called when a client disconnects
IOReturn clientClose();
IOReturn clientClose() override;
/// Called when the driver is shutting down
bool terminate(IOOptionBits options);
bool terminate(IOOptionBits options) override;
/// Called in clients with IOConnectSetNotificationPort
IOReturn registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 refCon);
mach_port_t port, UInt32 type, UInt32 refCon) override;
/// Called in clients with IOConnectMapMemory
IOReturn clientMemoryForType(
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory);
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) override;
/// Called in clients with IOConnectCallScalarMethod etc. Dispatches
/// to the requested selector using the SantaDriverMethods enum in
@@ -80,7 +69,7 @@ class com_google_SantaDriverClient : public IOUserClient {
UInt32 selector,
IOExternalMethodArguments *arguments,
IOExternalMethodDispatch *dispatch,
OSObject *target, void *reference);
OSObject *target, void *reference) override;
///
/// The userpsace callable methods are below. Each method corresponds
@@ -122,6 +111,10 @@ class com_google_SantaDriverClient : public IOUserClient {
com_google_SantaDriverClient *target,
void *reference,
IOExternalMethodArguments *arguments);
private:
com_google_SantaDriver *myProvider;
SantaDecisionManager *decisionManager;
};
#endif // SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H

View File

@@ -24,8 +24,8 @@ santa_action_t SantaMessage::getAction() const {
return action_;
}
void SantaMessage::setAction(const santa_action_t action,
const uint64_t microsecs) {
void SantaMessage::setAction(
const santa_action_t action, const uint64_t microsecs) {
action_ = action;
microsecs_ = microsecs;
}

View File

@@ -26,10 +26,6 @@
class SantaMessage : public OSObject {
OSDeclareDefaultStructors(SantaMessage)
private:
santa_action_t action_;
uint64_t microsecs_;
public:
// Returns the time the action was last set.
uint64_t getMicrosecs() const;
@@ -39,6 +35,10 @@ class SantaMessage : public OSObject {
// Sets the acion and receive time.
void setAction(const santa_action_t action, const uint64_t microsecs);
private:
santa_action_t action_;
uint64_t microsecs_;
};
#endif // SANTA__SANTA_DRIVER__SANTAMESSAGE_H

View File

@@ -36,7 +36,7 @@ static NSMutableDictionary *registeredCommands;
int longestCommandName = 0;
for (NSString *cmdName in registeredCommands) {
if ([cmdName length] > longestCommandName) {
if ((int)[cmdName length] > longestCommandName) {
longestCommandName = (int)[cmdName length];
}
}
@@ -55,18 +55,17 @@ static NSMutableDictionary *registeredCommands;
+ (NSString *)helpForCommandWithName:(NSString *)commandName {
Class<SNTCommand> command = registeredCommands[commandName];
if (command) {
NSMutableString *helpText = [[NSMutableString alloc] init];
[helpText appendFormat:@"Help for '%@':\n", commandName];
[helpText appendString:[command longHelpText]];
return helpText;
NSString *longHelp = [command longHelpText];
if (longHelp) {
return [NSString stringWithFormat:@"Help for '%@':\n%@", commandName, longHelp];
} else {
return @"This command does not have any help information.";
}
}
return nil;
}
+ (SNTXPCConnection *)connectToDaemon {
// TODO(rah): Re-factor this so that successfully establishing the connection runs the command,
// instead of having to sleep until the connection is made.
SNTXPCConnection *daemonConn =
[[SNTXPCConnection alloc] initClientWithName:[SNTXPCControlInterface serviceId]
options:NSXPCConnectionPrivileged];
@@ -78,7 +77,7 @@ static NSMutableDictionary *registeredCommands;
};
daemonConn.invalidationHandler = ^{
printf("An error occurred communicating with the daemon\n");
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
};

View File

@@ -25,7 +25,7 @@
@implementation SNTCommandBinaryInfo
REGISTER_COMMAND_NAME(@"binaryinfo");
REGISTER_COMMAND_NAME(@"binaryinfo")
+ (BOOL)requiresRoot {
return NO;
@@ -36,59 +36,64 @@ REGISTER_COMMAND_NAME(@"binaryinfo");
}
+ (NSString *)shortHelpText {
return @"Prints information about the given binary.";
return @"Prints information about a binary.";
}
+ (NSString *)longHelpText {
return (@"The details provided will be the same ones Santa uses to make a decision about binaries"
@"This includes SHA-1, SHA-256, code signing information and the type of binary");
return (@"The details provided will be the same ones Santa uses to make a decision\n"
@"about binaries. This includes SHA-256, SHA-1, code signing information and\n"
@"the type of binary.");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
NSString *filePath = [arguments firstObject];
if (!filePath) {
LOGI(@"Missing file path");
printf("Missing file path\n");
exit(1);
}
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
LOGI(@"Invalid file");
printf("Invalid file\n");
exit(1);
}
LOGI(@"Info for file: %@", [fileInfo path]);
LOGI(@"-----------------------------------------------------------");
LOGI(@"%-20s: %@", "SHA-1", [fileInfo SHA1]);
LOGI(@"%-20s: %@", "SHA-256", [fileInfo SHA256]);
printf("%-19s: %s\n", "Path", [[fileInfo path] UTF8String]);
printf("%-19s: %s\n", "SHA-256", [[fileInfo SHA256] UTF8String]);
printf("%-19s: %s\n", "SHA-1", [[fileInfo SHA1] UTF8String]);
printf("%-19s: %s\n", "Bundle Name", [[fileInfo bundleName] UTF8String]);
printf("%-19s: %s\n", "Bundle Version", [[fileInfo bundleVersion] UTF8String]);
printf("%-19s: %s\n", "Bundle Version Str", [[fileInfo bundleShortVersionString] UTF8String]);
NSArray *archs = [fileInfo architectures];
if (archs) {
LOGI(@"%-20s: %@ (%@)", "Type", [fileInfo machoType], [archs componentsJoinedByString:@", "]);
printf("%-19s: %s (%s)\n", "Type",
[[fileInfo machoType] UTF8String],
[[archs componentsJoinedByString:@", "] UTF8String]);
} else {
LOGI(@"%-20s: %@", "Type", [fileInfo machoType]);
printf("%-19s: %s\n", "Type", [[fileInfo machoType] UTF8String]);
}
SNTCodesignChecker *csc = [[SNTCodesignChecker alloc] initWithBinaryPath:filePath];
LOGI(@"%-20s: %s", "Code-signed", (csc) ? "Yes" : "No");
printf("%-19s: %s\n", "Code-signed", (csc) ? "Yes" : "No");
if (csc) {
LOGI(@"Signing chain\n");
printf("Signing chain:\n");
[csc.certificates enumerateObjectsUsingBlock:^(SNTCertificate *c,
unsigned long idx,
BOOL *stop) {
idx++; // index from 1
LOGI(@" %2lu. %-20s: %@", idx, "SHA-1", c.SHA1);
LOGI(@" %-20s: %@", "SHA-256", c.SHA256);
LOGI(@" %-20s: %@", "Common Name", c.commonName);
LOGI(@" %-20s: %@", "Organization", c.orgName);
LOGI(@" %-20s: %@", "Organizational Unit", c.orgUnit);
LOGI(@" %-20s: %@", "Valid From", c.validFrom);
LOGI(@" %-20s: %@", "Valid Until", c.validUntil);
LOGI(@"");
printf(" %2lu. %-20s: %s\n", idx, "SHA-256", [c.SHA256 UTF8String]);
printf(" %-20s: %s\n", "SHA-1", [c.SHA1 UTF8String]);
printf(" %-20s: %s\n", "Common Name", [c.commonName UTF8String]);
printf(" %-20s: %s\n", "Organization", [c.orgName UTF8String]);
printf(" %-20s: %s\n", "Organizational Unit", [c.orgUnit UTF8String]);
printf(" %-20s: %s\n", "Valid From", [[c.validFrom description] UTF8String]);
printf(" %-20s: %s\n", "Valid Until", [[c.validUntil description] UTF8String]);
printf("\n");
}];
}

View File

@@ -23,7 +23,7 @@
@implementation SNTCommandFlushCache
REGISTER_COMMAND_NAME(@"flushcache");
REGISTER_COMMAND_NAME(@"flushcache")
+ (BOOL)requiresRoot {
return YES;
@@ -34,12 +34,12 @@ REGISTER_COMMAND_NAME(@"flushcache");
}
+ (NSString *)shortHelpText {
return @"Flush the kernel cache";
return @"Flush the kernel cache.";
}
+ (NSString *)longHelpText {
return @"Flushes the in-kernel cache of whitelisted binaries.\n\n"
@"Returns 0 if successful, 1 otherwise";
return (@"Flushes the in-kernel cache of whitelisted binaries.\n"
@"Returns 0 if successful, 1 otherwise");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {

View File

@@ -32,7 +32,7 @@
@implementation SNTCommandRule
REGISTER_COMMAND_NAME(@"rule");
REGISTER_COMMAND_NAME(@"rule")
+ (BOOL)requiresRoot {
return YES;
@@ -43,60 +43,59 @@ REGISTER_COMMAND_NAME(@"rule");
}
+ (NSString *)shortHelpText {
return @"Adds a rule for the given binary or hash.";
return @"Manually add/remove rules.";
}
+ (NSString *)longHelpText {
return (@"santactl rule {add|remove}\n"
@"--whitelist: add to whitelist\n"
@"--blacklist: add to blacklist\n"
@"--silent-blacklist: add to silent blacklist\n"
@"--message {message}: custom message\n"
@"--path {path}: path of binary to add\n"
@"--sha256 {sha256}: hash to add\n"
);
return (@"Usage: santactl rule {add|remove} [options]\n"
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --message {message}: custom message\n"
@" --path {path}: path of binary to add\n"
@" --sha256 {sha256}: hash to add\n");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
SNTConfigurator *config = [SNTConfigurator configurator];
// Ensure we have no privileges
if (!DropRootPrivileges()) {
LOGE(@"Failed to drop root privileges. Exiting.");
printf("Failed to drop root privileges.\n");
exit(1);
}
if ([config syncBaseURL] != nil) {
LOGE(@"SyncBaseURL is set, rules are managed centrally");
printf("SyncBaseURL is set, rules are managed centrally.\n");
exit(1);
}
NSString *action = [arguments firstObject];
// add or remove
if (!action) {
LOGI(@"Missing action");
printf("Missing action - add or remove?\n");
exit(1);
}
int state = RULESTATE_UNKNOWN;
if ([action compare:@"add" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
} else if ([action compare:@"remove" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_REMOVE;
} else {
LOGI(@"Unknown action, expected add or remove");
printf("Unknown action, expected add or remove.\n");
exit(1);
}
NSString *customMsg = @"";
NSString *SHA256 = nil;
NSString *filePath = nil;
// parse arguments
for (int i=1; i < [arguments count] ; i++ ) {
for (NSUInteger i = 1; i < [arguments count] ; i++ ) {
NSString* argument = [arguments objectAtIndex:i];
if ([argument compare:@"--whitelist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_WHITELIST;
} else if ([argument compare:@"--blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
@@ -105,62 +104,61 @@ REGISTER_COMMAND_NAME(@"rule");
state = RULESTATE_SILENT_BLACKLIST;
} else if ([argument compare:@"--message" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
LOGI(@"No message specified");
printf("No message specified.\n");
}
customMsg = [arguments objectAtIndex:i];
} else if ([argument compare:@"--path" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
LOGI(@"No path specified");
printf("No path specified.\n");
}
filePath = [arguments objectAtIndex:i];
} else if ([argument compare:@"--sha256" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if (++i > ([arguments count])) {
LOGI(@"No SHA-256 specified");
printf("No SHA-256 specified.\n");
}
SHA256 = [arguments objectAtIndex:i];
} else {
LOGI(@"Unknown argument %@", argument);
printf("Unknown argument %s.\n", [argument UTF8String]);
exit(1);
}
}
if (state == RULESTATE_UNKNOWN) {
LOGI(@"No state specified");
printf("No state specified.\n");
exit(1);
}
if (filePath) {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
LOGI(@"Not a regular file or executable bundle");
printf("Not a regular file or executable bundle.\n");
exit(1);
}
SHA256 = [fileInfo SHA256];
} else if (SHA256) {
} else {
LOGI(@"No SHA-256 or binary specified");
printf("No SHA-256 or binary specified.\n");
exit(1);
}
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = SHA256;
newRule.state = state;
newRule.type = RULETYPE_BINARY;
newRule.customMsg = customMsg;
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule withReply:^{
if (state == RULESTATE_REMOVE) {
LOGI(@"Removed rule for SHA-256: %@", [newRule shasum]);
} else {
LOGI(@"Added rule for SHA-256: %@", [newRule shasum]);
}
exit(0);
}];
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^{
if (state == RULESTATE_REMOVE) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
exit(0);
}];
}
@end

View File

@@ -14,11 +14,6 @@
#import "SNTCommandController.h"
#include <IOKit/kext/KextManager.h>
#import "SNTFileInfo.h"
#import "SNTKernelCommon.h"
#import "SNTLogging.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -27,7 +22,7 @@
@implementation SNTCommandStatus
REGISTER_COMMAND_NAME(@"status");
REGISTER_COMMAND_NAME(@"status")
+ (BOOL)requiresRoot {
return NO;
@@ -38,85 +33,56 @@ REGISTER_COMMAND_NAME(@"status");
}
+ (NSString *)shortHelpText {
return @"Get status about Santa";
return @"Show Santa status information.";
}
+ (NSString *)longHelpText {
return @"Returns status information about Santa.";
return nil;
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
// Version information
LOGI(@">>> Versions");
LOGI(@"%-30s | %@", "santa-driver version", [self kextVersion]);
LOGI(@"%-30s | %@", "santad version", [self daemonVersion]);
LOGI(@"%-30s | %@",
"santactl version",
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]);
LOGI(@"%-30s | %@", "SantaGUI version", [self guiVersion]);
LOGI(@"");
// Daemon status
__block NSString *clientMode;
[[daemonConn remoteObjectProxy] clientMode:^(santa_clientmode_t cm) {
switch (cm) {
case CLIENTMODE_MONITOR:
clientMode = @"Monitor"; break;
case CLIENTMODE_LOCKDOWN:
clientMode = @"Lockdown"; break;
default:
clientMode = [NSString stringWithFormat:@"Unknown (%d)", cm]; break;
}
}];
do { usleep(5000); } while (!clientMode);
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
// Kext status
if (daemonConn) {
__block uint64_t cacheCount = -1;
[[daemonConn remoteObjectProxy] cacheCount:^(uint64_t count) {
cacheCount = count;
}];
do { usleep(5000); } while (cacheCount == -1);
LOGI(@">>> Kernel Info");
LOGI(@"%-30s | %d", "Kernel cache count", cacheCount);
LOGI(@"");
__block int64_t cacheCount = -1;
[[daemonConn remoteObjectProxy] cacheCount:^(int64_t count) {
cacheCount = count;
}];
do { usleep(5000); } while (cacheCount == -1);
printf(">>> Kernel Info\n");
printf(" %-25s | %lld\n", "Kernel cache count", cacheCount);
// Database counts
__block uint64_t eventCount = 1, binaryRuleCount = -1, certRuleCount = -1;
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(uint64_t binary, uint64_t certificate) {
binaryRuleCount = binary;
certRuleCount = certificate;
}];
[[daemonConn remoteObjectProxy] databaseEventCount:^(uint64_t count) {
eventCount = count;
}];
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
LOGI(@">>> Database Info");
LOGI(@"%-30s | %d", "Binary Rules", binaryRuleCount);
LOGI(@"%-30s | %d", "Certificate Rules", certRuleCount);
LOGI(@"%-30s | %d", "Events Pending Upload", eventCount);
LOGI(@"");
} else {
LOGI(@">>> santad is not running, cannot provide any more information.");
}
// Database counts
__block int64_t eventCount = 1, binaryRuleCount = -1, certRuleCount = -1;
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(int64_t binary, int64_t certificate) {
binaryRuleCount = binary;
certRuleCount = certificate;
}];
[[daemonConn remoteObjectProxy] databaseEventCount:^(int64_t count) {
eventCount = count;
}];
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
printf(">>> Database Info\n");
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
exit(0);
}
+ (NSString *)kextVersion {
NSDictionary *loadedKexts = CFBridgingRelease(
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef)@[ @(USERCLIENT_ID) ],
(__bridge CFArrayRef)@[ @"CFBundleVersion" ]));
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
}
SNTFileInfo *driverInfo =
[[SNTFileInfo alloc] initWithPath:@"/Library/Extensions/santa-driver.kext"];
if (driverInfo) {
return [driverInfo.bundleVersion stringByAppendingString:@" (unloaded)"];
}
return @"not found";
}
+ (NSString *)daemonVersion {
SNTFileInfo *daemonInfo = [[SNTFileInfo alloc] initWithPath:@"/usr/libexec/santad"];
return daemonInfo.bundleVersion;
}
+ (NSString *)guiVersion {
SNTFileInfo *guiInfo =
[[SNTFileInfo alloc] initWithPath:@"/Applications/Santa.app/Contents/MacOS/Santa"];
return guiInfo.bundleVersion;
}
@end

View File

@@ -0,0 +1,21 @@
/// 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.
/// Category on NSData providing the option of getting zlib or gzip compressed data.
@interface NSData (Zlib)
- (NSData *)zlibCompressed;
- (NSData *)gzipCompressed;
@end

View File

@@ -0,0 +1,66 @@
/// 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 "NSData+Zlib.h"
#include <zlib.h>
@implementation NSData (Zlib)
- (NSData *)compressIncludingGzipHeader:(BOOL)includeHeader {
if ([self length]) {
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
stream.avail_in = (uint)[self length];
stream.next_in = (Bytef *)[self bytes];
stream.total_out = 0;
stream.avail_out = 0;
NSUInteger chunkSize = 16384;
int windowSize = 15;
if (includeHeader) {
windowSize += 16;
}
if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION,
Z_DEFLATED, windowSize, 8, Z_DEFAULT_STRATEGY) == Z_OK) {
NSMutableData *data = [NSMutableData dataWithLength:chunkSize];
while (stream.avail_out == 0) {
if (stream.total_out >= [data length]) {
data.length += chunkSize;
}
stream.next_out = (uint8_t *)[data mutableBytes] + stream.total_out;
stream.avail_out = (uInt)([data length] - stream.total_out);
deflate(&stream, Z_FINISH);
}
deflateEnd(&stream);
data.length = stream.total_out;
return data;
}
}
return nil;
}
- (NSData *)zlibCompressed {
return [self compressIncludingGzipHeader:NO];
}
- (NSData *)gzipCompressed {
return [self compressIncludingGzipHeader:YES];
}
@end

View File

@@ -58,24 +58,24 @@
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
if (challenge.previousFailureCount > 0) {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (self.serverHostname && ![self.serverHostname isEqual:protectionSpace.host]) {
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (![protectionSpace.protocol isEqual:NSURLProtectionSpaceHTTPS]) {
LOGD(@"Protection Space: %@ is not a secure protocol", protectionSpace.protocol);
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
LOGE(@"%@ is not a secure protocol", protectionSpace.protocol);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
if (!protectionSpace.receivesCredentialSecurely) {
LOGD(@"Protection Space: secure authentication or protocol cannot be established.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
LOGE(@"Secure authentication or protocol cannot be established.");
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
@@ -88,7 +88,7 @@
return;
} else {
LOGE(@"Server asked for client authentication but no usable client certificate found.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
} else if (authMethod == NSURLAuthenticationMethodServerTrust) {
@@ -97,8 +97,8 @@
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
return;
} else {
LOGE(@"Server asked for client authentication but no usable client certificate found.");
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
LOGE(@"Unable to verify server identity.");
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
return;
}
}
@@ -111,11 +111,11 @@
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler {
if (self.refusesRedirects) {
completionHandler(NULL);
} else {
completionHandler(request);
}
if (self.refusesRedirects) {
completionHandler(NULL);
} else {
completionHandler(request);
}
}
#pragma mark Private Helpers for URLSession:didReceiveChallenge:completionHandler:
@@ -136,13 +136,13 @@
///
- (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
__block OSStatus err = errSecSuccess;
__block SecIdentityRef foundIdentity;
__block SecIdentityRef foundIdentity = NULL;
if (self.clientCertFile) {
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:self.clientCertFile options:0 error:&error];
if (error) {
LOGE(@"Client Trust: Couldn't open client certificate %@: %@",
LOGD(@"Client Trust: Couldn't open client certificate %@: %@",
self.clientCertFile,
[error localizedDescription]);
return nil;
@@ -158,7 +158,7 @@
NSArray *identities = CFBridgingRelease(cfIdentities);
if (err != errSecSuccess) {
LOGE(@"Client Trust: Couldn't load client certificate %@: %d", self.clientCertFile, err);
LOGD(@"Client Trust: Couldn't load client certificate %@: %d", self.clientCertFile, err);
return nil;
}
@@ -169,7 +169,8 @@
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
(id)kSecClass : (id)kSecClassIdentity,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll }, (CFTypeRef *)&cfIdentities);
(id)kSecMatchLimit : (id)kSecMatchLimitAll
}, (CFTypeRef *)&cfIdentities);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to load client identities, SecItemCopyMatching returned: %d",
@@ -181,56 +182,63 @@
// Manually iterate through available identities to find one with an allowed issuer.
[identities enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
SecIdentityRef identityRef = (__bridge SecIdentityRef)obj;
SecIdentityRef identityRef = (__bridge SecIdentityRef)obj;
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(identityRef, &certificate);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to read certificate data: %d. Skipping identity.", (int)err);
return;
}
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
CFRelease(certificate);
// Switch identity finding method depending on config
if (self.clientCertCommonName && clientCert.commonName) {
if ([clientCert.commonName compare:self.clientCertCommonName
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(identityRef, &certificate);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to read certificate data: %d. Skipping identity.", (int)err);
return;
}
} else if (self.clientCertIssuerCn && clientCert.issuerCommonName) {
if ([clientCert.issuerCommonName compare:self.clientCertIssuerCn
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
} else {
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
if (!decoder) continue;
if ([clientCert.issuerCommonName isEqual:decoder.commonName] &&
[clientCert.issuerCountryName isEqual:decoder.countryName] &&
[clientCert.issuerOrgName isEqual:decoder.organizationName] &&
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
CFRelease(certificate);
// Switch identity finding method depending on config
if (self.clientCertCommonName && clientCert.commonName) {
if ([clientCert.commonName compare:self.clientCertCommonName
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
} else if (self.clientCertIssuerCn && clientCert.issuerCommonName) {
if ([clientCert.issuerCommonName compare:self.clientCertIssuerCn
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
} else {
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
if (!decoder) {
LOGW(@"Unable to decode allowed distinguished name.");
continue;
}
if ([clientCert.issuerCommonName isEqual:decoder.commonName] &&
[clientCert.issuerCountryName isEqual:decoder.countryName] &&
[clientCert.issuerOrgName isEqual:decoder.organizationName] &&
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
}
}
}
}];
}
if (foundIdentity) {
LOGD(@"Client Trust: Valid client identity %@.", foundIdentity);
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(foundIdentity, &certificate);
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
LOGD(@"Client Trust: Valid client identity %@.", clientCert);
NSURLCredential *cred =
[NSURLCredential credentialWithIdentity:foundIdentity
certificates:nil
@@ -278,7 +286,7 @@
// Set this array of certs as the anchors to trust.
err = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)certRefs);
if (err != errSecSuccess) {
LOGE(@"Server Trust: Could not set anchor certificates: %d", err);
LOGD(@"Server Trust: Could not set anchor certificates: %d", err);
return nil;
}
}
@@ -287,12 +295,12 @@
SecTrustResultType result = kSecTrustResultInvalid;
err = SecTrustEvaluate(serverTrust, &result);
if (err != errSecSuccess) {
LOGE(@"Server Trust: Unable to evaluate certificate chain for server: %d", err);
LOGD(@"Server Trust: Unable to evaluate certificate chain for server: %d", err);
return nil;
}
// Print details about the server's leaf certificate.
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(protectionSpace.serverTrust, 0);
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (firstCert) {
SNTCertificate *cert = [[SNTCertificate alloc] initWithSecCertificateRef:firstCert];
LOGD(@"Server Trust: Server leaf cert: %@", cert);
@@ -301,7 +309,7 @@
// Having a trust level "unspecified" by the user is the usual result, described at
// https://developer.apple.com/library/mac/qa/qa1360
if (result != kSecTrustResultProceed && result != kSecTrustResultUnspecified) {
LOGE(@"Server Trust: Server isn't trusted. SecTrustResultType: %d", result);
LOGD(@"Server Trust: Server isn't trusted. SecTrustResultType: %d", result);
return nil;
}

View File

@@ -20,23 +20,22 @@
#import "SNTCommandSyncPostflight.h"
#import "SNTCommandSyncPreflight.h"
#import "SNTCommandSyncRuleDownload.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncState.h"
#import "SNTConfigurator.h"
#import "SNTDropRootPrivs.h"
#import "SNTLogging.h"
#import "SNTSystemInfo.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@interface SNTCommandSync : NSObject<SNTCommand>
@property NSURLSession *session;
@property SNTXPCConnection *daemonConn;
@property SNTCommandSyncStatus *progress;
@property SNTCommandSyncState *syncState;
@end
@implementation SNTCommandSync
REGISTER_COMMAND_NAME(@"sync");
REGISTER_COMMAND_NAME(@"sync")
+ (BOOL)requiresRoot {
return NO;
@@ -47,11 +46,11 @@ REGISTER_COMMAND_NAME(@"sync");
}
+ (NSString *)shortHelpText {
return @"Synchronizes Santa with the server";
return @"Synchronizes Santa with the server.";
}
+ (NSString *)longHelpText {
return @"";
return nil;
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
@@ -113,24 +112,28 @@ REGISTER_COMMAND_NAME(@"sync");
s.daemonConn = daemonConn;
// Gather some data needed during some sync stages
s.progress = [[SNTCommandSyncStatus alloc] init];
s.syncState = [[SNTCommandSyncState alloc] init];
s.progress.syncBaseURL = config.syncBaseURL;
if (!s.progress.syncBaseURL) {
s.syncState.syncBaseURL = config.syncBaseURL;
if (!s.syncState.syncBaseURL) {
LOGE(@"Missing SyncBaseURL. Can't sync without it.");
exit(1);
} else if (![s.syncState.syncBaseURL.scheme isEqual:@"https"]) {
LOGW(@"SyncBaseURL is not over HTTPS!");
}
authURLSession.serverHostname = s.progress.syncBaseURL.host;
authURLSession.serverHostname = s.syncState.syncBaseURL.host;
s.progress.machineID = config.machineIDOverride;
if (!s.progress.machineID || [s.progress.machineID isEqual:@""]) {
s.progress.machineID = [SNTSystemInfo hardwareUUID];
}
if (!s.progress.machineID || [s.progress.machineID isEqual:@""]) {
s.syncState.machineID = config.machineID;
if ([s.syncState.machineID length] == 0) {
LOGE(@"Missing Machine ID. Can't sync without it.");
exit(1);
}
s.progress.machineOwner = config.machineOwner;
s.syncState.machineOwner = config.machineOwner;
if ([s.syncState.machineOwner length] == 0) {
s.syncState.machineOwner = @"";
LOGW(@"Missing Machine Owner.");
}
if (arguments.count == 2 && [[arguments firstObject] isEqual:@"singleevent"]) {
[s eventUploadSingleEvent:arguments[1]];
@@ -141,12 +144,12 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)preflight {
[SNTCommandSyncPreflight performSyncInSession:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Preflight complete");
if (self.progress.uploadLogURL) {
if (self.syncState.uploadLogURL) {
[self logUpload];
} else {
[self eventUpload];
@@ -160,7 +163,7 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)logUpload {
[SNTCommandSyncLogUpload performSyncInSession:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
@@ -175,7 +178,7 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)eventUpload {
[SNTCommandSyncEventUpload performSyncInSession:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
@@ -191,7 +194,7 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)eventUploadSingleEvent:(NSString *)sha256 {
[SNTCommandSyncEventUpload uploadSingleEventWithSHA256:sha256
session:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
@@ -206,7 +209,7 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)ruleDownload {
[SNTCommandSyncRuleDownload performSyncInSession:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
@@ -221,7 +224,7 @@ REGISTER_COMMAND_NAME(@"sync");
- (void)postflight {
[SNTCommandSyncPostflight performSyncInSession:self.session
progress:self.progress
syncState:self.syncState
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {

View File

@@ -0,0 +1,80 @@
/// 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.
extern NSString * const kURLPreflight;
extern NSString * const kURLEventUpload;
extern NSString * const kURLRuleDownload;
extern NSString * const kURLPostflight;
extern NSString * const kSerialNumber;
extern NSString * const kHostname;
extern NSString * const kSantaVer;
extern NSString * const kOSVer;
extern NSString * const kOSBuild;
extern NSString * const kPrimaryUser;
extern NSString * const kRequestCleanSync;
extern NSString * const kBatchSize;
extern NSString * const kUploadLogsURL;
extern NSString * const kClientMode;
extern NSString * const kClientModeMonitor;
extern NSString * const kClientModeLockdown;
extern NSString * const kCleanSync;
extern NSString * const kEvents;
extern NSString * const kFileSHA256;
extern NSString * const kFilePath;
extern NSString * const kFileName;
extern NSString * const kExecutingUser;
extern NSString * const kExecutionTime;
extern NSString * const kDecision;
extern NSString * const kDecisionAllowUnknown;
extern NSString * const kDecisionAllowBinary;
extern NSString * const kDecisionAllowCertificate;
extern NSString * const kDecisionAllowScope;
extern NSString * const kDecisionBlockUnknown;
extern NSString * const kDecisionBlockBinary;
extern NSString * const kDecisionBlockCertificate;
extern NSString * const kDecisionBlockScope;
extern NSString * const kDecisionUnknown;
extern NSString * const kLoggedInUsers;
extern NSString * const kCurrentSessions;
extern NSString * const kFileBundleID;
extern NSString * const kFileBundleName;
extern NSString * const kFileBundleVersion;
extern NSString * const kFileBundleShortVersionString;
extern NSString * const kPID;
extern NSString * const kPPID;
extern NSString * const kParentName;
extern NSString * const kSigningChain;
extern NSString * const kCertSHA256;
extern NSString * const kCertCN;
extern NSString * const kCertOrg;
extern NSString * const kCertOU;
extern NSString * const kCertValidFrom;
extern NSString * const kCertValidUntil;
extern NSString * const kLogUploadField;
extern NSString * const kRules;
extern NSString * const kRuleSHA256;
extern NSString * const kRulePolicy;
extern NSString * const kRulePolicyWhitelist;
extern NSString * const kRulePolicyBlacklist;
extern NSString * const kRulePolicySilentBlacklist;
extern NSString * const kRulePolicyRemove;
extern NSString * const kRuleType;
extern NSString * const kRuleTypeBinary;
extern NSString * const kRuleTypeCertificate;
extern NSString * const kRuleCustomMsg;
extern NSString * const kCursor;

View File

@@ -0,0 +1,82 @@
/// 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 "SNTCommandSyncConstants.h"
NSString * const kURLPreflight = @"preflight/";
NSString * const kURLEventUpload = @"eventupload/";
NSString * const kURLRuleDownload = @"ruledownload/";
NSString * const kURLPostflight = @"postflight/";
NSString * const kSerialNumber = @"serial_num";
NSString * const kHostname = @"hostname";
NSString * const kSantaVer = @"santa_version";
NSString * const kOSVer = @"os_version";
NSString * const kOSBuild = @"os_build";
NSString * const kPrimaryUser = @"primary_user";
NSString * const kRequestCleanSync = @"request_clean_sync";
NSString * const kBatchSize = @"batch_size";
NSString * const kUploadLogsURL = @"upload_logs_url";
NSString * const kClientMode = @"client_mode";
NSString * const kClientModeMonitor = @"MONITOR";
NSString * const kClientModeLockdown = @"LOCKDOWN";
NSString * const kCleanSync = @"clean_sync";
NSString * const kEvents = @"events";
NSString * const kFileSHA256 = @"file_sha256";
NSString * const kFilePath = @"file_path";
NSString * const kFileName = @"file_name";
NSString * const kExecutingUser = @"executing_user";
NSString * const kExecutionTime = @"execution_time";
NSString * const kDecision = @"decision";
NSString * const kDecisionAllowUnknown = @"ALLOW_UNKNOWN";
NSString * const kDecisionAllowBinary = @"ALLOW_BINARY";
NSString * const kDecisionAllowCertificate = @"ALLOW_CERTIFICATE";
NSString * const kDecisionAllowScope = @"ALLOW_SCOPE";
NSString * const kDecisionBlockUnknown = @"BLOCK_UNKNOWN";
NSString * const kDecisionBlockBinary = @"BLOCK_BINARY";
NSString * const kDecisionBlockCertificate = @"BLOCK_CERTIFICATE";
NSString * const kDecisionBlockScope = @"BLOCK_SCOPE";
NSString * const kDecisionUnknown = @"UNKNOWN";
NSString * const kLoggedInUsers = @"logged_in_users";
NSString * const kCurrentSessions = @"current_sessions";
NSString * const kFileBundleID = @"file_bundle_id";
NSString * const kFileBundleName = @"file_bundle_name";
NSString * const kFileBundleVersion = @"file_bundle_version";
NSString * const kFileBundleShortVersionString = @"file_bundle_version_string";
NSString * const kPID = @"pid";
NSString * const kPPID = @"ppid";
NSString * const kParentName = @"parent_name";
NSString * const kSigningChain = @"signing_chain";
NSString * const kCertSHA256 = @"sha256";
NSString * const kCertCN = @"cn";
NSString * const kCertOrg = @"org";
NSString * const kCertOU = @"ou";
NSString * const kCertValidFrom = @"valid_from";
NSString * const kCertValidUntil = @"valid_until";
NSString * const kLogUploadField = @"files";
NSString * const kRules = @"rules";
NSString * const kRuleSHA256 = @"sha256";
NSString * const kRulePolicy = @"policy";
NSString * const kRulePolicyWhitelist = @"WHITELIST";
NSString * const kRulePolicyBlacklist = @"BLACKLIST";
NSString * const kRulePolicySilentBlacklist = @"SILENT_BLACKLIST";
NSString * const kRulePolicyRemove = @"REMOVE";
NSString * const kRuleType = @"rule_type";
NSString * const kRuleTypeBinary = @"BINARY";
NSString * const kRuleTypeCertificate = @"CERTIFICATE";
NSString * const kRuleCustomMsg = @"custom_msg";
NSString * const kCursor = @"cursor";

View File

@@ -12,19 +12,19 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCommandSyncStatus;
@class SNTCommandSyncState;
@class SNTXPCConnection;
@interface SNTCommandSyncEventUpload : NSObject
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;
+ (void)uploadSingleEventWithSHA256:(NSString *)SHA256
session:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;

View File

@@ -17,7 +17,8 @@
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -25,11 +26,11 @@
@implementation SNTCommandSyncEventUpload
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = [NSURL URLWithString:[@"eventupload/" stringByAppendingString:progress.machineID]
relativeToURL:progress.syncBaseURL];
NSURL *url = [NSURL URLWithString:[kURLEventUpload stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
[[daemonConn remoteObjectProxy] databaseEventsPending:^(NSArray *events) {
if ([events count] == 0) {
@@ -38,7 +39,7 @@
[self uploadEventsFromArray:events
toURL:url
inSession:session
batchSize:progress.eventBatchSize
batchSize:syncState.eventBatchSize
daemonConn:daemonConn
completionHandler:handler];
}
@@ -47,12 +48,12 @@
+ (void)uploadSingleEventWithSHA256:(NSString *)SHA256
session:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = [NSURL URLWithString:[@"eventupload/" stringByAppendingString:progress.machineID]
relativeToURL:progress.syncBaseURL];
[[daemonConn remoteObjectProxy] databaseEventForSHA256:SHA256 withReply:^(SNTStoredEvent *event) {
NSURL *url = [NSURL URLWithString:[kURLEventUpload stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
[[daemonConn remoteObjectProxy] databaseEventForSHA256:SHA256 reply:^(SNTStoredEvent *event) {
if (!event) {
handler(YES);
return;
@@ -70,7 +71,7 @@
+ (void)uploadEventsFromArray:(NSArray *)events
toURL:(NSURL *)url
inSession:(NSURLSession *)session
batchSize:(int32_t)batchSize
batchSize:(NSUInteger)batchSize
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSMutableArray *uploadEvents = [[NSMutableArray alloc] init];
@@ -83,7 +84,7 @@
if (eventIds.count >= batchSize) break;
}
NSDictionary *uploadReq = @{ @"events": uploadEvents };
NSDictionary *uploadReq = @{ kEvents: uploadEvents };
NSData *requestBody;
@try {
@@ -101,8 +102,11 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
LOGI(@"Uploaded %d events", eventIds.count);
@@ -129,38 +133,54 @@
#define ADDKEY(dict, key, value) if (value) dict[key] = value
NSMutableDictionary *newEvent = [NSMutableDictionary dictionary];
ADDKEY(newEvent, @"file_sha256", event.fileSHA256);
ADDKEY(newEvent, @"file_path", [event.filePath stringByDeletingLastPathComponent]);
ADDKEY(newEvent, @"file_name", [event.filePath lastPathComponent]);
ADDKEY(newEvent, @"executing_user", event.executingUser);
ADDKEY(newEvent, @"execution_time", @([event.occurrenceDate timeIntervalSince1970]));
ADDKEY(newEvent, @"decision", @(event.decision));
ADDKEY(newEvent, @"logged_in_users", event.loggedInUsers);
ADDKEY(newEvent, @"current_sessions", event.currentSessions);
ADDKEY(newEvent, kFileSHA256, event.fileSHA256);
ADDKEY(newEvent, kFilePath, [event.filePath stringByDeletingLastPathComponent]);
ADDKEY(newEvent, kFileName, [event.filePath lastPathComponent]);
ADDKEY(newEvent, kExecutingUser, event.executingUser);
ADDKEY(newEvent, kExecutionTime, @([event.occurrenceDate timeIntervalSince1970]));
ADDKEY(newEvent, kLoggedInUsers, event.loggedInUsers);
ADDKEY(newEvent, kCurrentSessions, event.currentSessions);
ADDKEY(newEvent, @"file_bundle_id", event.fileBundleID);
ADDKEY(newEvent, @"file_bundle_name", event.fileBundleName);
ADDKEY(newEvent, @"file_bundle_version", event.fileBundleVersion);
ADDKEY(newEvent, @"file_bundle_version_string", event.fileBundleVersionString);
switch (event.decision) {
case EVENTSTATE_ALLOW_UNKNOWN: ADDKEY(newEvent, kDecision, kDecisionAllowUnknown); break;
case EVENTSTATE_ALLOW_BINARY: ADDKEY(newEvent, kDecision, kDecisionAllowBinary); break;
case EVENTSTATE_ALLOW_CERTIFICATE:
ADDKEY(newEvent, kDecision, kDecisionAllowCertificate);
break;
case EVENTSTATE_ALLOW_SCOPE: ADDKEY(newEvent, kDecision, kDecisionAllowScope); break;
case EVENTSTATE_BLOCK_UNKNOWN: ADDKEY(newEvent, kDecision, kDecisionBlockUnknown); break;
case EVENTSTATE_BLOCK_BINARY: ADDKEY(newEvent, kDecision, kDecisionBlockBinary); break;
case EVENTSTATE_BLOCK_CERTIFICATE:
ADDKEY(newEvent, kDecision, kDecisionBlockCertificate);
break;
case EVENTSTATE_BLOCK_SCOPE: ADDKEY(newEvent, kDecision, kDecisionBlockScope); break;
default: ADDKEY(newEvent, kDecision, kDecisionUnknown);
}
ADDKEY(newEvent, @"pid", event.pid);
ADDKEY(newEvent, @"ppid", event.ppid);
ADDKEY(newEvent, kFileBundleID, event.fileBundleID);
ADDKEY(newEvent, kFileBundleName, event.fileBundleName);
ADDKEY(newEvent, kFileBundleVersion, event.fileBundleVersion);
ADDKEY(newEvent, kFileBundleShortVersionString, event.fileBundleVersionString);
ADDKEY(newEvent, kPID, event.pid);
ADDKEY(newEvent, kPPID, event.ppid);
ADDKEY(newEvent, kParentName, event.parentName);
NSMutableArray *signingChain = [NSMutableArray arrayWithCapacity:event.signingChain.count];
for (int i = 0; i < event.signingChain.count; i++) {
for (NSUInteger i = 0; i < event.signingChain.count; i++) {
SNTCertificate *cert = [event.signingChain objectAtIndex:i];
NSMutableDictionary *certDict = [NSMutableDictionary dictionary];
ADDKEY(certDict, @"sha256", cert.SHA256);
ADDKEY(certDict, @"cn", cert.commonName);
ADDKEY(certDict, @"org", cert.orgName);
ADDKEY(certDict, @"ou", cert.orgUnit);
ADDKEY(certDict, @"valid_from", @([cert.validFrom timeIntervalSince1970]));
ADDKEY(certDict, @"valid_until", @([cert.validUntil timeIntervalSince1970]));
ADDKEY(certDict, kCertSHA256, cert.SHA256);
ADDKEY(certDict, kCertCN, cert.commonName);
ADDKEY(certDict, kCertOrg, cert.orgName);
ADDKEY(certDict, kCertOU, cert.orgUnit);
ADDKEY(certDict, kCertValidFrom, @([cert.validFrom timeIntervalSince1970]));
ADDKEY(certDict, kCertValidUntil, @([cert.validUntil timeIntervalSince1970]));
[signingChain addObject:certDict];
}
newEvent[@"signing_chain"] = signingChain;
newEvent[kSigningChain] = signingChain;
return newEvent;
#undef ADDKEY

View File

@@ -12,13 +12,13 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCommandSyncStatus;
@class SNTCommandSyncState;
@class SNTXPCConnection;
@interface SNTCommandSyncLogUpload : NSObject
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;

View File

@@ -14,27 +14,72 @@
#import "SNTCommandSyncLogUpload.h"
#import "NSData+Zlib.h"
#include "SNTCommonEnums.h"
#include "SNTLogging.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
@implementation SNTCommandSyncLogUpload
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = progress.uploadLogURL;
NSURL *url = syncState.uploadLogURL;
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:@"POST"];
NSString *boundary = @"santa-sync-upload-boundary";
NSString *boundary = @"----santa-sync-upload-boundary";
NSString *contentType =
[NSString stringWithFormat:@"multipart/form-data; charset=UTF-8; boundary=%@", boundary];
[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
NSArray *logsToUpload = [self logsToUpload];
// Upload the logs
[[session uploadTaskWithRequest:req
fromData:[self requestBodyWithLogs:logsToUpload andBoundary:boundary]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
LOGI(@"Uploaded %d logs", [logsToUpload count]);
handler(YES);
}
}] resume];
}
+ (NSData *)requestBodyWithLogs:(NSArray *)logsToUpload andBoundary:(NSString *)boundary {
// Prepare the body of the request, encoded as a multipart/form-data.
// Along the way, gzip the individual log files and append .gz to their filenames.
NSMutableData *reqBody = [[NSMutableData alloc] init];
for (NSString *log in logsToUpload) {
[reqBody appendData:
[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:
[[NSString stringWithFormat:@"Content-Disposition: form-data; "
@"name=\"%@\"; "
@"filename=\"%@.gz\"\r\n", kLogUploadField, [log lastPathComponent]]
dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:
[@"Content-Type: application/x-gzip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[[NSData dataWithContentsOfFile:log] gzipCompressed]];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[reqBody appendData:
[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
return reqBody;
}
+ (NSArray *)logsToUpload {
// General logs
NSMutableArray *logsToUpload = [@[ @"/var/log/santa.log",
@"/var/log/system.log" ] mutableCopy];
@@ -51,38 +96,7 @@
}
}
// Prepare the body of the request, encoded as a multipart/form-data.
// Along the way, gzip the individual log files (they'll be stored in blobstore gzipped, which is
// what we want) and append .gz to their filenames.
NSMutableData *reqBody = [[NSMutableData alloc] init];
for (NSString *log in logsToUpload) {
[reqBody appendData:
[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:
[[NSString stringWithFormat:@"Content-Disposition: multipart/form-data; "
@"name=\"files\"; "
@"filename=\"%@.gz\"\r\n", [log lastPathComponent]]
dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:
[@"Content-Type: application/x-gzip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[reqBody appendData:[NSData dataWithContentsOfFile:log]];
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[reqBody appendData:
[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
// Upload the logs
[[session uploadTaskWithRequest:req
fromData:reqBody
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
handler(NO);
} else {
LOGI(@"Uploaded %d logs", [logsToUpload count]);
handler(YES);
}
}] resume];
return logsToUpload;
}
@end

View File

@@ -12,13 +12,13 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCommandSyncStatus;
@class SNTCommandSyncState;
@class SNTXPCConnection;
@interface SNTCommandSyncPostflight : NSObject
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;

View File

@@ -16,28 +16,32 @@
#include "SNTLogging.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
@implementation SNTCommandSyncPostflight
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = [NSURL URLWithString:[@"postflight/" stringByAppendingString:progress.machineID]
relativeToURL:progress.syncBaseURL];
NSURL *url = [NSURL URLWithString:[kURLPostflight stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
[req setHTTPMethod:@"POST"];
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
handler(NO);
} else {
handler(YES);
}
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
handler(YES);
}
}] resume];
}

View File

@@ -12,13 +12,13 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCommandSyncStatus;
@class SNTCommandSyncState;
@class SNTXPCConnection;
@interface SNTCommandSyncPreflight : NSObject
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;

View File

@@ -17,7 +17,8 @@
#include "SNTKernelCommon.h"
#include "SNTLogging.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTSystemInfo.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -25,20 +26,23 @@
@implementation SNTCommandSyncPreflight
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = [NSURL URLWithString:[@"preflight/" stringByAppendingString:progress.machineID]
relativeToURL:progress.syncBaseURL];
NSURL *url = [NSURL URLWithString:[kURLPreflight stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
NSMutableDictionary *requestDict = [NSMutableDictionary dictionary];
requestDict[@"serial_no"] = [SNTSystemInfo serialNumber];
requestDict[@"hostname"] = [SNTSystemInfo shortHostname];
requestDict[@"santa_version"] =
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
requestDict[@"os_version"] = [SNTSystemInfo osVersion];
requestDict[@"os_build"] = [SNTSystemInfo osBuild];
requestDict[@"primary_user"] = progress.machineOwner;
requestDict[kSerialNumber] = [SNTSystemInfo serialNumber];
requestDict[kHostname] = [SNTSystemInfo shortHostname];
requestDict[kSantaVer] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
requestDict[kOSVer] = [SNTSystemInfo osVersion];
requestDict[kOSBuild] = [SNTSystemInfo osBuild];
requestDict[kPrimaryUser] = syncState.machineOwner;
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"]) {
requestDict[kRequestCleanSync] = @YES;
}
NSData *requestBody = [NSJSONSerialization dataWithJSONObject:requestDict
options:0
@@ -53,17 +57,25 @@
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGD(@"HTTP Response: %@",
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
progress.eventBatchSize = [r[@"batch_size"] intValue];
progress.uploadLogURL = [NSURL URLWithString:r[@"upload_logs_url"]];
syncState.eventBatchSize = [r[kBatchSize] intValue];
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
if (r[@"client_mode"]) {
[[daemonConn remoteObjectProxy] setClientMode:[r[@"client_mode"] intValue] withReply:^{}];
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_MONITOR reply:^{}];
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_LOCKDOWN reply:^{}];
}
if ([r[kCleanSync] boolValue]) {
syncState.cleanSync = YES;
LOGD(@"Clean sync requested by server");
}
handler(YES);

View File

@@ -12,13 +12,13 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
@class SNTCommandSyncStatus;
@class SNTCommandSyncState;
@class SNTXPCConnection;
@interface SNTCommandSyncRuleDownload : NSObject
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler;

View File

@@ -14,7 +14,8 @@
#import "SNTCommandSyncRuleDownload.h"
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTRule.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -24,15 +25,15 @@
@implementation SNTCommandSyncRuleDownload
+ (void)performSyncInSession:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSURL *url = [NSURL URLWithString:[@"ruledownload/" stringByAppendingString:progress.machineID]
relativeToURL:progress.syncBaseURL];
NSURL *url = [NSURL URLWithString:[kURLRuleDownload stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
[self ruleDownloadWithCursor:nil
url:url
session:session
progress:progress
syncState:syncState
daemonConn:daemonConn
completionHandler:handler];
}
@@ -40,14 +41,14 @@
+ (void)ruleDownloadWithCursor:(NSString *)cursor
url:(NSURL *)url
session:(NSURLSession *)session
progress:(SNTCommandSyncStatus *)progress
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSDictionary *requestDict = (cursor ? @{ @"cursor": cursor } : @{});
NSDictionary *requestDict = (cursor ? @{ kCursor: cursor } : @{});
if (!progress.downloadedRules) {
progress.downloadedRules = [NSMutableArray array];
if (!syncState.downloadedRules) {
syncState.downloadedRules = [NSMutableArray array];
}
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
@@ -59,8 +60,11 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %d %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
@@ -69,43 +73,69 @@
handler(NO);
}
NSArray *receivedRules = resp[@"rules"];
NSArray *receivedRules = resp[kRules];
for (NSDictionary *rule in receivedRules) {
if (![rule isKindOfClass:[NSDictionary class]]) continue;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = rule[@"shasum"];
newRule.state = [rule[@"state"] intValue];
if (newRule.state <= RULESTATE_UNKNOWN || newRule.state >= RULESTATE_MAX) continue;
newRule.type = [rule[@"type"] intValue];
if (newRule.type <= RULETYPE_UNKNOWN || newRule.type >= RULETYPE_MAX) continue;
NSString *customMsg = rule[@"custom_msg"];
if (customMsg) {
newRule.customMsg = customMsg;
}
[progress.downloadedRules addObject:newRule];
SNTRule *r = [self ruleFromDictionary:rule];
if (r) [syncState.downloadedRules addObject:r];
}
if (resp[@"cursor"]) {
[self ruleDownloadWithCursor:resp[@"cursor"]
if (resp[kCursor]) {
[self ruleDownloadWithCursor:resp[kCursor]
url:url
session:session
progress:progress
syncState:syncState
daemonConn:daemonConn
completionHandler:handler];
} else {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:progress.downloadedRules withReply:^{
LOGI(@"Added %d rule(s)", progress.downloadedRules.count);
handler(YES);
}];
if (syncState.downloadedRules.count) {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
cleanSlate:syncState.cleanSync
reply:^{
LOGI(@"Added %d rule(s)", syncState.downloadedRules.count);
handler(YES);
}];
} else {
handler(YES);
}
}
}
}] resume];
}
+ (SNTRule *)ruleFromDictionary:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) return nil;
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = dict[kRuleSHA256];
NSString *policyString = dict[kRulePolicy];
if ([policyString isEqual:kRulePolicyWhitelist]) {
newRule.state = RULESTATE_WHITELIST;
} else if ([policyString isEqual:kRulePolicyBlacklist]) {
newRule.state = RULESTATE_BLACKLIST;
} else if ([policyString isEqual:kRulePolicySilentBlacklist]) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
} else if ([policyString isEqual:kRulePolicyRemove]) {
newRule.state = RULESTATE_REMOVE;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
newRule.type = RULETYPE_BINARY;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
newRule.type = RULETYPE_CERT;
} else {
return nil;
}
NSString *customMsg = dict[kRuleCustomMsg];
if (customMsg) {
newRule.customMsg = customMsg;
}
return newRule;
}
@end

View File

@@ -14,7 +14,7 @@
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@interface SNTCommandSyncStatus : NSObject
@interface SNTCommandSyncState : NSObject
/// The base API URL
@property NSURL *syncBaseURL;
@@ -23,6 +23,10 @@
@property NSString *machineID;
@property NSString *machineOwner;
/// Clean sync flag, sent from server. If True, all existing rules
/// should be deleted before inserting any new rules.
@property BOOL cleanSync;
/// Batch size for uploading events, sent from server
@property int32_t eventBatchSize;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTCommandSyncStatus.h"
#import "SNTCommandSyncState.h"
@implementation SNTCommandSyncStatus
@implementation SNTCommandSyncState
@end

View File

@@ -134,8 +134,10 @@
data.length,
kSequenceOfSetOfOIDValueTemplate,
&a);
SecAsn1CoderRelease(coder);
if (err != errSecSuccess) return nil;
if (err != errSecSuccess) {
SecAsn1CoderRelease(coder);
return nil;
}
// The data is decoded but now it's in a number of embedded structs.
// Massage that into a nice dictionary of OID->String pairs.
@@ -145,7 +147,10 @@
OIDKeyValue *keyValue = anAttr->vals[0];
// Sanity check
if (keyValue->value.Length > data.length) return nil;
if (keyValue->value.Length > data.length) {
SecAsn1CoderRelease(coder);
return nil;
}
// Get the string value. First try creating as a UTF-8 string. If that fails,
// fallback to trying as an ASCII string. If it still doesn't work, continue on
@@ -169,6 +174,7 @@
dict[objectId] = valueString;
}
SecAsn1CoderRelease(coder);
return dict;
}

View File

@@ -0,0 +1,88 @@
/// 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 "SNTCommandController.h"
#include <IOKit/kext/KextManager.h>
#import "SNTFileInfo.h"
#import "SNTKernelCommon.h"
#import "SNTXPCConnection.h"
@interface SNTCommandVersion : NSObject<SNTCommand>
@end
@implementation SNTCommandVersion
REGISTER_COMMAND_NAME(@"version")
+ (BOOL)requiresRoot {
return NO;
}
+ (BOOL)requiresDaemonConn {
return NO;
}
+ (NSString *)shortHelpText {
return @"Show Santa component versions.";
}
+ (NSString *)longHelpText {
return nil;
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
exit(0);
}
+ (NSString *)santaKextVersion {
NSDictionary *loadedKexts = CFBridgingRelease(
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef)@[ @(USERCLIENT_ID) ],
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
);
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
}
SNTFileInfo *driverInfo =
[[SNTFileInfo alloc] initWithPath:@"/Library/Extensions/santa-driver.kext"];
if (driverInfo) {
return [driverInfo.bundleVersion stringByAppendingString:@" (unloaded)"];
}
return @"not found";
}
+ (NSString *)santadVersion {
SNTFileInfo *daemonInfo = [[SNTFileInfo alloc] initWithPath:@"/usr/libexec/santad"];
return daemonInfo.bundleVersion;
}
+ (NSString *)santaAppVersion {
SNTFileInfo *guiInfo =
[[SNTFileInfo alloc] initWithPath:@"/Applications/Santa.app/Contents/MacOS/Santa"];
return guiInfo.bundleVersion;
}
+ (NSString *)santactlVersion {
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
}
@end

View File

@@ -1,3 +1,3 @@
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif
#endif

View File

@@ -13,6 +13,7 @@
/// limitations under the License.
#include <pwd.h>
#include <sys/stat.h>
#include <sys/types.h>
#import "SNTApplication.h"
@@ -26,6 +27,7 @@
#import "SNTDriverManager.h"
#import "SNTEventTable.h"
#import "SNTExecutionController.h"
#import "SNTFileWatcher.h"
#import "SNTRuleTable.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -35,6 +37,7 @@
@property SNTDriverManager *driverManager;
@property SNTEventTable *eventTable;
@property SNTExecutionController *execController;
@property SNTFileWatcher *configFileWatcher;
@property SNTRuleTable *ruleTable;
@property SNTXPCConnection *controlConnection;
@property SNTXPCConnection *notifierConnection;
@@ -57,13 +60,13 @@
// Initialize tables
_ruleTable = [SNTDatabaseController ruleTable];
if (! _ruleTable) {
if (!_ruleTable) {
LOGE(@"Failed to initialize rule table.");
return nil;
}
_eventTable = [SNTDatabaseController eventTable];
if (! _eventTable) {
if (!_eventTable) {
LOGE(@"Failed to initialize event table.");
return nil;
}
@@ -82,19 +85,19 @@
[[SNTDaemonControlController alloc] initWithDriverManager:_driverManager];
[_controlConnection resume];
// Get client mode and begin observing for updates
SNTConfigurator *configurator = [SNTConfigurator configurator];
santa_clientmode_t clientMode = [configurator clientMode];
[configurator addObserver:self
forKeyPath:@"clientMode"
options:NSKeyValueObservingOptionNew
context:NULL];
_configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^{
[[SNTConfigurator configurator] reloadConfigData];
// Ensure config file remains root:wheel 0644
chown([kDefaultConfigFilePath fileSystemRepresentation], 0, 0);
chmod([kDefaultConfigFilePath fileSystemRepresentation], 0644);
}];
// Initialize the binary checker object
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
ruleTable:_ruleTable
eventTable:_eventTable
operatingMode:clientMode
notifierConnection:_notifierConnection];
if (!_execController) return nil;
}
@@ -102,29 +105,27 @@
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if ([keyPath isEqual:@"clientMode"]) {
self.execController.operatingMode = [change[NSKeyValueChangeNewKey] intValue];
}
}
- (void)run {
LOGI(@"Connected to driver, activating.");
dispatch_queue_t q = dispatch_queue_create("com.google.santad.driver_queue",
DISPATCH_QUEUE_CONCURRENT);
// Create a concurrent queue to put requests on, then set its priority to high.
dispatch_queue_t q =
dispatch_queue_create("com.google.santad.driver_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[self.driverManager listenWithBlock:^(santa_message_t message) {
@autoreleasepool {
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
// Sleep before exiting to give driver chance to ready itself
exit(0);
}
case ACTION_NOTIFY_EXEC_ALLOW_NODAEMON:
case ACTION_NOTIFY_EXEC_ALLOW_CACHED:
case ACTION_NOTIFY_EXEC_DENY_CACHED: {
// TODO(rah): Implement.
break;
}
case ACTION_REQUEST_CHECKBW: {
// Validate the binary aynchronously on a concurrent queue so we don't
// hold up other execution requests in the background.

View File

@@ -34,8 +34,8 @@
#pragma mark Kernel ops
- (void)cacheCount:(void (^)(uint64_t))reply; {
uint64_t count = [self.driverManager cacheCount];
- (void)cacheCount:(void (^)(int64_t))reply {
int64_t count = [self.driverManager cacheCount];
reply(count);
}
@@ -45,21 +45,21 @@
#pragma mark Database ops
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply {
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply {
SNTRuleTable *rdb = [SNTDatabaseController ruleTable];
reply([rdb binaryRuleCount], [rdb certificateRuleCount]);
}
- (void)databaseRuleAddRule:(SNTRule *)rule withReply:(void (^)())reply {
[self databaseRuleAddRules:@[ rule ] withReply:reply];
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
[self databaseRuleAddRules:@[ rule ] cleanSlate:cleanSlate reply:reply];
}
- (void)databaseRuleAddRules:(NSArray *)rules withReply:(void (^)())reply {
[[SNTDatabaseController ruleTable] addRules:rules];
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
[[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
// If any rules were added that were not whitelist, flush cache.
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", RULESTATE_WHITELIST];
if ([rules filteredArrayUsingPredicate:p].count) {
if ([rules filteredArrayUsingPredicate:p].count || cleanSlate) {
LOGI(@"Received non-whitelist rule, flushing cache");
[self.driverManager flushCache];
}
@@ -67,11 +67,11 @@
reply();
}
- (void)databaseEventCount:(void (^)(uint64_t count))reply {
- (void)databaseEventCount:(void (^)(int64_t count))reply {
reply([[SNTDatabaseController eventTable] pendingEventsCount]);
}
- (void)databaseEventForSHA256:(NSString *)sha256 withReply:(void (^)(SNTStoredEvent *))reply {
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply {
reply([[SNTDatabaseController eventTable] pendingEventForSHA256:sha256]);
}
@@ -89,7 +89,7 @@
reply([[SNTConfigurator configurator] clientMode]);
}
- (void)setClientMode:(santa_clientmode_t)mode withReply:(void (^)())reply {
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply {
[[SNTConfigurator configurator] setClientMode:mode];
reply();
}

View File

@@ -28,14 +28,12 @@ static NSString * const kEventsDatabaseName = @"events.db";
static FMDatabaseQueue *eventDatabaseQueue = nil;
static dispatch_once_t eventDatabaseToken;
dispatch_once(&eventDatabaseToken, ^{
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
#ifndef DEBUG
[eventDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#ifndef DEBUG
[eventDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
#endif
});
@@ -46,14 +44,12 @@ static NSString * const kEventsDatabaseName = @"events.db";
static FMDatabaseQueue *ruleDatabaseQueue = nil;
static dispatch_once_t ruleDatabaseToken;
dispatch_once(&ruleDatabaseToken, ^{
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
#ifndef DEBUG
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#ifndef DEBUG
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
#endif
});
return [[SNTRuleTable alloc] initWithDatabaseQueue:ruleDatabaseQueue];
@@ -65,9 +61,11 @@ static NSString * const kEventsDatabaseName = @"events.db";
+ (void)createDatabasePath {
NSFileManager *fm = [NSFileManager defaultManager];
NSDictionary *attrs = @{ NSFileOwnerAccountName: @"root",
NSFileGroupOwnerAccountName: @"wheel",
NSFilePosixPermissions: @0755 };
NSDictionary *attrs = @{
NSFileOwnerAccountName : @"root",
NSFileGroupOwnerAccountName : @"wheel",
NSFilePosixPermissions : @0755
};
if (![fm fileExistsAtPath:kDatabasePath]) {
[fm createDirectoryAtPath:kDatabasePath

View File

@@ -28,7 +28,7 @@
/// is the current version of the table. The return value is the new version of the table. If
/// updating the table failed, return a negative number. If there was no update to apply, return 0.
///
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version;
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version;
///
/// Wrappers around the respective FMDatabaseQueue methods. If the object we initialized with was

View File

@@ -38,18 +38,17 @@
return nil;
}
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
[self doesNotRecognizeSelector:_cmd];
return -1;
return 0;
}
/// Called at the end of initialization to ensure the table in the
/// database exists and uses the latest schema.
- (void)updateTableSchema {
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
int currentVersion = [db userVersion];
int newVersion = [self initializeDatabase:db fromVersion:currentVersion];
uint32_t currentVersion = [db userVersion];
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
if (newVersion < 1) return;
LOGD(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);

View File

@@ -21,6 +21,13 @@
///
@interface SNTDriverManager : NSObject
///
/// Handles locating and connecting to the driver. If driver is not loaded, will
/// sleep until it is. If driver is loaded but connection fails (like if a client is
/// already connected), will return nil.
///
- (instancetype)init;
///
/// Handles requests from the kernel using the given block.
/// @note Loops indefinitely unless there is an error trying to read data from the data queue.

View File

@@ -27,6 +27,8 @@
@implementation SNTDriverManager
static const int MAX_DELAY = 15;
#pragma mark init/dealloc
- (instancetype)init {
@@ -42,13 +44,15 @@
}
// Locate driver. Wait for it if necessary.
int delay = 1;
do {
CFRetain(classToMatch); // this ref is released by IOServiceGetMatchingService
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
if (!serviceObject) {
LOGD(@"Waiting for Santa driver to become available");
sleep(5);
sleep(delay);
if (delay < MAX_DELAY) delay *= 2;
}
} while (!serviceObject);
CFRelease(classToMatch);
@@ -79,7 +83,7 @@
IOServiceClose(_connection);
}
# pragma mark Incoming messages
#pragma mark Incoming messages
- (void)listenWithBlock:(void (^)(santa_message_t message))callback {
kern_return_t kr;
@@ -104,8 +108,9 @@
return;
}
// This will call clientMemoryForType() inside our user client class,
// which activates the Kauth listeners.
// This will call clientMemoryForType() inside our user client class.
// The Kauth listener will start intercepting at this point and sending requests
// to our queue.
kr = IOConnectMapMemory(self.connection, kIODefaultMemoryType, mach_task_self(),
&address, &size, kIOMapAnywhere);
if (kr != kIOReturnSuccess) {
@@ -117,18 +122,17 @@
self.queueMemory = (IODataQueueMemory *)address;
dataSize = sizeof(vdata);
while (IODataQueueWaitForAvailableData(self.queueMemory,
self.receivePort) == kIOReturnSuccess) {
do {
while (IODataQueueDataAvailable(self.queueMemory)) {
kr = IODataQueueDequeue(self.queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
callback(vdata);
} else {
LOGD(@"Error receiving data: %d", kr);
LOGE(@"Error dequeuing data: %d", kr);
exit(2);
}
}
}
} while (IODataQueueWaitForAvailableData(self.queueMemory, self.receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), self.receivePort);

View File

@@ -24,6 +24,7 @@
///
/// Add event to the database.
///
/// @param event the event to store.
/// @return YES if event was successfully stored.
///
@@ -31,18 +32,23 @@
///
/// Retrieves all events in the database
///
/// @return NSArray of SNTStoredEvent's
///
- (NSArray *)pendingEvents;
///
/// Retrieves number of events in database without fetching every event.
///
/// @return Number of events in database.
///
- (NSUInteger)pendingEventsCount;
///
/// Retrieve an event from the database.
/// Retrieve an event from the database with a given SHA-256. If multiple events
/// exist for the same SHA-256, just the first is returned.
///
/// @param sha256 a SHA-256 of the binary to return an event for.
/// @return a single SNTStoredEvent.
///
- (SNTStoredEvent *)pendingEventForSHA256:(NSString *)sha256;
@@ -50,12 +56,15 @@
///
/// Delete a single event from the database using its index.
///
- (void)deleteEventWithId:(NSNumber *)id;
/// @param index the event ID.
///
- (void)deleteEventWithId:(NSNumber *)index;
///
/// Delete multiple events from the database with an array of IDs.
///
/// @param indexes an array of event IDs.
///
- (void)deleteEventsWithIds:(NSArray *)ids;
- (void)deleteEventsWithIds:(NSArray *)indexes;
@end

View File

@@ -20,17 +20,15 @@
@implementation SNTEventTable
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
int newVersion = 0;
if (version < 1) {
[db executeUpdate:@"CREATE TABLE 'events' ("
"'idx' INTEGER PRIMARY KEY AUTOINCREMENT,"
"'filesha256' TEXT NOT NULL,"
"'eventdata' BLOB"
@");"];
@"'idx' INTEGER PRIMARY KEY AUTOINCREMENT,"
@"'filesha256' TEXT NOT NULL,"
@"'eventdata' BLOB);"];
[db executeUpdate:@"CREATE INDEX filesha256 ON events (filesha256);"];
newVersion = 1;
}
@@ -57,16 +55,6 @@
return success;
}
- (SNTStoredEvent *)eventFromResultSet:(FMResultSet *)rs {
NSData *eventData = [rs dataForColumn:@"eventdata"];
if (!eventData) return nil;
SNTStoredEvent *event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
return event;
}
#pragma mark Querying/Retreiving
- (NSUInteger)pendingEventsCount {
@@ -81,8 +69,8 @@
__block SNTStoredEvent *storedEvent;
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE filesha256=? LIMIT 1;",
sha256];
FMResultSet *rs =
[db executeQuery:@"SELECT * FROM events WHERE filesha256=? LIMIT 1;", sha256];
if ([rs next]) {
storedEvent = [self eventFromResultSet:rs];
@@ -101,7 +89,13 @@
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
while ([rs next]) {
[pendingEvents addObject:[self eventFromResultSet:rs]];
id obj = [self eventFromResultSet:rs];
if (obj) {
[pendingEvents addObject:obj];
} else {
NSNumber *idx = [rs objectForColumnName:@"idx"];
[db executeUpdate:@"DELETE FROM events WHERE idx=?", idx];
}
}
[rs close];
@@ -110,6 +104,16 @@
return pendingEvents;
}
- (SNTStoredEvent *)eventFromResultSet:(FMResultSet *)rs {
NSData *eventData = [rs dataForColumn:@"eventdata"];
if (!eventData) return nil;
SNTStoredEvent *event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
return event;
}
#pragma mark Deleting
- (void)deleteEventWithId:(NSNumber *)index {

View File

@@ -37,12 +37,10 @@
@property SNTRuleTable *ruleTable;
@property SNTEventTable *eventTable;
@property SNTXPCConnection *notifierConnection;
@property santa_clientmode_t operatingMode;
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
operatingMode:(santa_clientmode_t)operating_mode
notifierConnection:(SNTXPCConnection *)notifierConn;
///
@@ -54,7 +52,7 @@
/// @param userName the user who's executing the binary
/// @param pid the process id being executed
/// @param ppid the parent process id
/// @param vnoteId the id of the vnode being executed
/// @param vnodeId the id of the vnode being executed
///
- (void)validateBinaryWithPath:(NSString *)path
userName:(NSString *)userName
@@ -62,5 +60,4 @@
ppid:(NSNumber *)ppid
vnodeId:(uint64_t)vnodeId;
@end

View File

@@ -14,12 +14,14 @@
#import "SNTExecutionController.h"
#include <libproc.h>
#include <utmpx.h>
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTDriverManager.h"
#import "SNTDropRootPrivs.h"
#import "SNTEventTable.h"
@@ -37,14 +39,12 @@
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
operatingMode:(santa_clientmode_t)operatingMode
notifierConnection:(SNTXPCConnection *)notifier {
self = [super init];
if (self) {
_driverManager = driverManager;
_ruleTable = ruleTable;
_eventTable = eventTable;
_operatingMode = operatingMode;
_notifierConnection = notifier;
LOGI(@"Log format: Decision (A|D), Reason (B|C|S|?), SHA-256, Path, Cert SHA-256, Cert CN");
@@ -70,6 +70,10 @@
santa_action_t respondedAction = ACTION_UNSET;
SNTRule *rule;
// Get name of parent process. Do this before responding to be sure parent doesn't go away.
char pname[PROC_PIDPATHINFO_MAXSIZE];
proc_name([ppid intValue], pname, PROC_PIDPATHINFO_MAXSIZE);
// Step 1 - binary rule?
rule = [self.ruleTable binaryRuleForSHA256:sha256];
if (rule) {
@@ -102,7 +106,9 @@
}
// Step 5 - log to database and potentially alert user
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY || !rule) {
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY ||
!rule ||
[[SNTConfigurator configurator] logAllEvents]) {
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.fileSHA256 = sha256;
se.filePath = path;
@@ -123,6 +129,7 @@
se.decision = [self eventStateForDecision:respondedAction type:rule.type];
se.pid = pid;
se.ppid = ppid;
se.parentName = @(pname);
NSArray *loggedInUsers, *currentSessions;
[self loggedInUsers:&loggedInUsers sessions:&currentSessions];
@@ -140,8 +147,10 @@
[self initiateEventUploadForSHA256:sha256];
}
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:rule.customMsg];
if (!rule || rule.state != RULESTATE_SILENT_BLACKLIST) {
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:rule.customMsg];
}
}
}
@@ -156,7 +165,7 @@
/// Checks whether the file at @c path is in-scope for checking with Santa.
///
/// Files that are out of scope:
/// + Non Mach-O files
/// + Non Mach-O files that are not part of an installer package.
/// + Files in whitelisted directories.
///
/// @return @c YES if file is in scope, @c NO otherwise.
@@ -167,10 +176,11 @@
return NO;
}
// If file is not a Mach-O file, we're not interested.
// TODO(rah): Consider adding an option to check scripts
// If file is not a Mach-O file, we're not interested unless it's part of an install package.
// TODO(rah): Consider adding an option to check all scripts.
// TODO(rah): Consider adding an option to disable package script checks.
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
if (![binInfo isMachO]) {
if (![binInfo isMachO] && ![path hasPrefix:@"/private/tmp/PKInstallSandbox."]) {
return NO;
}
@@ -234,8 +244,8 @@
if (cert && cert.SHA256 && cert.commonName) {
// Also ensure there are no pipes in the cert's common name.
NSString *printCommonName = [cert.commonName stringByReplacingOccurrencesOfString:@"|"
withString:@"<pipe>"];
NSString *printCommonName =
[cert.commonName stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
outLog = [NSString stringWithFormat:@"%@|%@|%@|%@|%@|%@",
d, r, sha256, printPath, cert.SHA256, printCommonName];
} else {
@@ -265,15 +275,10 @@
}
- (santa_action_t)defaultDecision {
switch (self.operatingMode) {
case CLIENTMODE_MONITOR:
return ACTION_RESPOND_CHECKBW_ALLOW;
case CLIENTMODE_LOCKDOWN:
return ACTION_RESPOND_CHECKBW_DENY;
default:
// This should never happen, panic and lockdown.
LOGE(@"Client mode is unset while enforcement is in effect. Blocking.");
return ACTION_RESPOND_CHECKBW_DENY;
switch ([[SNTConfigurator configurator] clientMode]) {
case CLIENTMODE_MONITOR: return ACTION_RESPOND_CHECKBW_ALLOW;
case CLIENTMODE_LOCKDOWN: return ACTION_RESPOND_CHECKBW_DENY;
default: return ACTION_RESPOND_CHECKBW_DENY; // This can't happen.
}
}
@@ -307,12 +312,12 @@
sessionName = [NSString stringWithFormat:@"%s@%s", nxt->ut_user, nxt->ut_line];
}
if (userName && ![userName isEqual:@""]) {
loggedInUsers[userName] = @"";
if ([userName length] > 0) {
loggedInUsers[userName] = [NSNull null];
}
if (sessionName && ![sessionName isEqual:@":"]) {
loggedInHosts[sessionName] = @"";
if ([sessionName length] > 1) {
loggedInHosts[sessionName] = [NSNull null];
}
}

View File

@@ -27,17 +27,17 @@
///
/// @return Number of rules in the database
///
- (long)ruleCount;
- (NSUInteger)ruleCount;
///
/// @return Number of binary rules in the database
///
- (long)binaryRuleCount;
- (NSUInteger)binaryRuleCount;
///
/// @return Number of certificate rules in the database
///
- (long)certificateRuleCount;
- (NSUInteger)certificateRuleCount;
///
/// @return Rule for binary with given SHA-256
@@ -54,8 +54,9 @@
/// transaction will abort if any rule fails to add.
///
/// @param rules Array of SNTRule's to add.
/// @param cleanSlate If true, remove all rules before adding the new rules.
/// @return YES if all rules were added successfully.
///
- (BOOL)addRules:(NSArray *)rules;
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate;
@end

View File

@@ -16,12 +16,13 @@
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "SNTLogging.h"
#import "SNTRule.h"
@implementation SNTRuleTable
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
int newVersion = 0;
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
uint32_t newVersion = 0;
if (version < 1) {
[db executeUpdate:@"CREATE TABLE 'rules' ("
@@ -54,24 +55,24 @@
#pragma mark Entry Counts
- (long)ruleCount {
__block long count = 0;
- (NSUInteger)ruleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM rules"];
count = [db longForQuery:@"SELECT COUNT(*) FROM rules"];
}];
return count;
}
- (long)binaryRuleCount {
__block long count = 0;
- (NSUInteger)binaryRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM binrules"];
}];
return count;
}
- (long)certificateRuleCount {
__block long count = 0;
- (NSUInteger)certificateRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM certrules"];
}];
@@ -95,7 +96,7 @@
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM certrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
rule = [self ruleFromResultSet:rs];
}
[rs close];
}];
@@ -119,13 +120,21 @@
#pragma mark Adding
- (BOOL)addRules:(NSArray *)rules {
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate {
__block BOOL failed = NO;
if (!rules || rules.count < 1) {
LOGE(@"Received request to add rules with nil/empty array.");
return NO;
}
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}
for (SNTRule *rule in rules) {
if (![rule isKindOfClass:[SNTRule class]] ||
!rule.shasum || rule.shasum.length == 0 ||
if (![rule isKindOfClass:[SNTRule class]] || rule.shasum.length == 0 ||
rule.state == RULESTATE_UNKNOWN || rule.type == RULETYPE_UNKNOWN) {
*rollback = failed = YES;
return;
@@ -139,8 +148,8 @@
}
} else {
if (![db executeUpdate:@"INSERT OR REPLACE INTO rules "
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
rule.shasum, @(rule.state), @(rule.type), rule.customMsg]) {
*rollback = failed = YES;
return;

View File

@@ -193,7 +193,7 @@
/// Begin listening for events
queueMemory = (IODataQueueMemory *)address;
while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess) {
do {
while (IODataQueueDataAvailable(queueMemory)) {
dataSize = sizeof(vdata);
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
@@ -227,7 +227,7 @@
TFAILINFO("Error receiving data: %d", kr);
}
}
}
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), receivePort);

View File

@@ -226,8 +226,8 @@
(void)sut.issuerCountryName;
// Now break some of the properties
OCMStub([sutMock x509ValueForLabel:OCMOCK_ANY fromDictionary:OCMOCK_ANY]);
OCMStub([sutMock dateForX509Key:OCMOCK_ANY]);
OCMExpect([sutMock x509ValueForLabel:OCMOCK_ANY fromDictionary:OCMOCK_ANY]);
OCMExpect([sutMock dateForX509Key:OCMOCK_ANY]);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");

View File

@@ -113,4 +113,23 @@
XCTAssertEqual(self.sut.pendingEventsCount, 0);
}
- (void)testDeleteCorruptEvent {
[self.dbq inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO events (filesha256) VALUES ('deadbeef')"];
}];
NSArray *events = [self.sut pendingEvents];
for (SNTStoredEvent *event in events) {
if ([event.fileSHA256 isEqual:@"deadbeef"]) XCTFail("Received bad event");
}
[self.dbq inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE filesha256='deadbeef'"];
if ([rs next]) {
XCTFail("Bad event was not deleted.");
}
[rs close];
}];
}
@end

View File

@@ -19,6 +19,7 @@
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTDriverManager.h"
#import "SNTEventTable.h"
#import "SNTFileInfo.h"
@@ -30,6 +31,7 @@
@end
@interface SNTExecutionControllerTest : XCTestCase
@property id mockConfigurator;
@property id mockCodesignChecker;
@property id mockDriverManager;
@property id mockFileInfo;
@@ -47,15 +49,26 @@
fclose(stdout);
self.mockCodesignChecker = OCMClassMock([SNTCodesignChecker class]);
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
OCMStub([self.mockCodesignChecker initWithBinaryPath:OCMOCK_ANY])
.andReturn(self.mockCodesignChecker);
self.mockConfigurator = OCMClassMock([SNTConfigurator class]);
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
self.mockDriverManager = OCMClassMock([SNTDriverManager class]);
self.mockFileInfo = OCMClassMock([SNTFileInfo class]);
self.mockFileInfo = OCMClassMock([SNTFileInfo class]);
OCMStub([self.mockFileInfo alloc]).andReturn(self.mockFileInfo);
OCMStub([self.mockFileInfo initWithPath:OCMOCK_ANY]).andReturn(self.mockFileInfo);
self.mockRuleDatabase = OCMClassMock([SNTRuleTable class]);
self.mockEventDatabase = OCMClassMock([SNTEventTable class]);
self.sut = [[SNTExecutionController alloc] initWithDriverManager:self.mockDriverManager
ruleTable:self.mockRuleDatabase
eventTable:self.mockEventDatabase
operatingMode:CLIENTMODE_MONITOR
notifierConnection:nil];
}
@@ -73,9 +86,11 @@
id mockSut = OCMPartialMock(self.sut);
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
SNTRule *rule = [[SNTRule alloc] init];
rule.state = RULESTATE_WHITELIST;
OCMStub([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
OCMExpect([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
@@ -91,13 +106,11 @@
id mockSut = OCMPartialMock(self.sut);
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
[[[self.mockFileInfo stub] andReturn:self.mockFileInfo] alloc];
(void)[[[self.mockFileInfo stub] andReturn:self.mockFileInfo] initWithPath:[OCMArg any]];
[[[self.mockFileInfo stub] andReturn:@"a"] SHA256];
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
SNTRule *rule = [[SNTRule alloc] init];
rule.state = RULESTATE_BLACKLIST;
OCMStub([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
OCMExpect([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
@@ -114,15 +127,12 @@
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
id cert = OCMClassMock([SNTCertificate class]);
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
OCMStub([self.mockCodesignChecker initWithBinaryPath:OCMOCK_ANY])
.andReturn(self.mockCodesignChecker);
OCMStub([self.mockCodesignChecker leafCertificate]).andReturn(cert);
OCMStub([cert SHA256]).andReturn(@"a");
OCMExpect([self.mockCodesignChecker leafCertificate]).andReturn(cert);
OCMExpect([cert SHA256]).andReturn(@"a");
SNTRule *rule = [[SNTRule alloc] init];
rule.state = RULESTATE_WHITELIST;
OCMStub([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
OCMExpect([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
@@ -139,15 +149,12 @@
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
id cert = OCMClassMock([SNTCertificate class]);
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
OCMStub([self.mockCodesignChecker initWithBinaryPath:OCMOCK_ANY])
.andReturn(self.mockCodesignChecker);
OCMStub([self.mockCodesignChecker leafCertificate]).andReturn(cert);
OCMStub([cert SHA256]).andReturn(@"a");
OCMExpect([self.mockCodesignChecker leafCertificate]).andReturn(cert);
OCMExpect([cert SHA256]).andReturn(@"a");
SNTRule *rule = [[SNTRule alloc] init];
rule.state = RULESTATE_BLACKLIST;
OCMStub([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
OCMExpect([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
@@ -163,7 +170,7 @@
id mockSut = OCMPartialMock(self.sut);
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
[self.sut setOperatingMode:CLIENTMODE_MONITOR];
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_MONITOR);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
pid:@(12)
@@ -172,7 +179,7 @@
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
forVnodeID:1234]);
[self.sut setOperatingMode:CLIENTMODE_LOCKDOWN];
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
pid:@(12)
@@ -186,7 +193,7 @@
id mockSut = OCMPartialMock(self.sut);
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(NO);
[self.sut setOperatingMode:CLIENTMODE_LOCKDOWN];
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
[self.sut validateBinaryWithPath:@"/a/file"
userName:@"nobody"
pid:@(24)

View File

@@ -0,0 +1,132 @@
/// 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 <XCTest/XCTest.h>
#import "SNTFileWatcher.h"
@interface SNTFileWatcherTest : XCTestCase
@property NSFileManager *fm;
@property NSString *file;
@end
@implementation SNTFileWatcherTest
- (void)setUp {
[super setUp];
self.fm = [NSFileManager defaultManager];
self.file = @"/tmp/SNTFileWatcherTest_File";
[self createFile];
usleep(10000);
}
- (void)tearDown {
[self deleteFile];
usleep(10000);
[super tearDown];
}
- (void)createFile {
[self.fm createFileAtPath:self.file contents:nil attributes:nil];
}
- (void)deleteFile {
[self.fm removeItemAtPath:self.file error:NULL];
}
- (void)testPlainInit {
XCTAssertThrows([[SNTFileWatcher alloc] init]);
}
- (void)testInitFileExists {
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Init: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
[exp fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)testInitNewFile {
[self deleteFile];
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Init: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
[exp fulfill];
}];
[self createFile];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)testFileChanged {
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Changed: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
NSString *d = [NSString stringWithContentsOfFile:self.file
encoding:NSUTF8StringEncoding
error:nil];
if ([d isEqual:@"0x8BADF00D"]) {
[exp fulfill];
}
}];
[[@"0x8BADF00D" dataUsingEncoding:NSUTF8StringEncoding] writeToFile:self.file atomically:NO];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)testFileReplaced {
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Replaced: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
NSString *d = [NSString stringWithContentsOfFile:self.file
encoding:NSUTF8StringEncoding
error:nil];
if ([d isEqual:@"0xFACEFEED"]) {
[exp fulfill];
}
}];
[[@"0xFACEFEED" dataUsingEncoding:NSUTF8StringEncoding] writeToFile:self.file atomically:YES];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)testFileExtended {
int fd = open(self.file.fileSystemRepresentation, O_WRONLY);
write(fd, "0xDEAD", 6);
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Extended: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
int file = open(self.file.fileSystemRepresentation, O_RDONLY);
char fileData[10];
read(file, fileData, 10);
if (strncmp(fileData, "0xDEADBEEF", 10) == 0) {
[exp fulfill];
}
}];
write(fd, "BEEF", 4);
close(fd);
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
@end

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