Compare commits

...

96 Commits
0.9.4 ... 0.9.9

Author SHA1 Message Date
Russell Hancox
488b28bfd5 SantaGUI: Log to syslog 2016-04-11 15:15:03 -04:00
Russell Hancox
0fceb7b2e1 SantaGUI: Post notifications to main thread using dispatch_async 2016-04-11 15:14:52 -04:00
Russell Hancox
a79d1a98e7 santactl/fileinfo: Only print "Signing Chain" header if certificates array isn't empty 2016-04-08 16:21:15 -04:00
Russell Hancox
43434fd445 santactl/fileinfo: Don't crash on <512b files 2016-04-08 16:20:49 -04:00
Russell Hancox
492e523884 Project: Move enums in SNTCommonEnums to NS_ENUM, part 2 2016-04-08 15:41:26 -04:00
Russell Hancox
3d1fdb7a2b Project: Move enums in SNTCommonEnums to NS_ENUM, part 1 2016-04-08 15:17:32 -04:00
Russell Hancox
95a4bf0ec7 santad: Ensure launchd/santad rules are whitelisted on every startup
If they weren't already and the database is not new, log an error.
2016-04-08 15:16:12 -04:00
Russell Hancox
0d4f261e14 santad: Have SNTRuleTable return NSError when failing to add rules so user can see why 2016-04-08 15:07:43 -04:00
Russell Hancox
e96288b41b santad: Exclusive-lock rules.db when opening 2016-04-08 12:11:08 -04:00
Russell Hancox
deda1abcf7 SantaGUI: Detect value type for silenced notifications to prevent crashes from bad plist 2016-04-08 10:32:48 -04:00
Russell Hancox
ee79d75483 santad: Set ThrottleInterval to 1s. 2016-04-07 17:03:49 -04:00
Russell Hancox
0e9e445ddf SantaGUI: Reconnect when listener loses a client.
Also move WEAKIFY/STRONGIFY macros into their own header.
2016-04-07 17:03:09 -04:00
Russell Hancox
e64720bcd9 Project: Tell Travis to use xcode7 2016-04-07 15:33:10 -04:00
Russell Hancox
6e27590b57 SantaGUI: Add 'dismiss for a day' checkbox.
Fixes #39.
2016-04-07 14:40:33 -04:00
Russell Hancox
916c3c7a2a SNTXPCConnection: Re-add forced establishment of clients, better tests.
Previously SNTXPCConnection had two-way validation which, due to the method of
implementation, forced a client to connect to a server straight away. Once that
was removed, it meant invalidationHandlers aren't called if either end dies
before the connection is established.

This also puts back the acceptedHandler, which can be used to know when the
connection has finished being established (particularly useful on the server
side), updates the __weak stuff to use WEAKIFY/STRONGIFY macros (and now
actually switch them back to strong within each block) and make the
tests a lot better by using in-process anonymousListener's rather than
lots of mocking.
2016-04-06 23:25:55 -04:00
Russell Hancox
8a5fde8ceb LogicTests: Stop instrumenting program flow, it causes 100s of log lines during tests 2016-04-06 17:30:08 -04:00
Russell Hancox
f5bd9bde7f SantaGUI: Use ultralight system font for title of message window 2016-04-06 15:23:18 -04:00
Russell Hancox
b987f61924 SantaGUI: Fix centering constraint for publisher label 2016-04-06 15:22:21 -04:00
Russell Hancox
482b51a2f9 santactl/sync: Fix userAgent not being set 2016-04-05 14:52:48 -04:00
Russell Hancox
93f2078eda SantaGUI: Fix some constraint bugs in MessageWindow 2016-04-04 18:15:20 -04:00
Russell Hancox
158ae11e61 Tests: Remove old stuff from XPCConnectionTest 2016-04-01 17:53:37 -04:00
Russell Hancox
d282388266 santactl/sync: Release certificate after use 2016-04-01 17:31:24 -04:00
Russell Hancox
6ecdfcba38 santactl/sync: If unable to find client identity, let default handling occur. 2016-04-01 09:54:15 -04:00
Russell Hancox
88dc8a547e README: Add video of block event, as an example 2016-03-31 13:51:35 -04:00
Russell Hancox
58e24b3c11 santagui: Remove old comment, don't activateIgnoringOtherApps twice. 2016-03-31 09:27:38 -04:00
Russell Hancox
5f1b3a2284 santad: Initialize ppath to (null) before calling proc_pidpath 2016-03-30 16:12:52 -04:00
Russell Hancox
31be2584f2 Conf: Use facility instead of sender for santad/santactl logs 2016-03-29 18:00:30 -04:00
Russell Hancox
a2311e5128 santad: Attempt to load santa-driver before connecting to it 2016-03-29 14:08:29 -04:00
Russell Hancox
e94d42187b santactl/sync: Don't log successful stages
Currently a standard succesful sync will print 4 success lines, one for
each stage that was run, followed by a line that the entire sync was
successful. As each stage will also log if it did anything, these
success messages aren't useful. Instead, just log if they failed.
2016-03-25 16:44:06 -04:00
Russell Hancox
2b99cc3f62 Logging: strncmp for binaryName 2016-03-25 14:20:46 -04:00
Russell Hancox
cb7f782893 santad: Fix typo in cert protection error 2016-03-23 17:45:08 -04:00
Russell Hancox
d5a0f8a74b Logging: Remove extraneous ; 2016-03-23 16:46:38 -04:00
Russell Hancox
2ebd71df24 santactl/sync: Fix single-event upload with extra arguments 2016-03-23 16:46:21 -04:00
Russell Hancox
479203f47c santa-driver: Style and type cleanups, inlining some small functions 2016-03-22 15:38:48 -04:00
Russell Hancox
022b9209d9 LogicTests: Delete resources that aren't used anymore. 2016-03-22 15:36:56 -04:00
Russell Hancox
771c2c868f SantaGUI: Increase contrast of user-defined block window messages. 2016-03-21 18:00:02 -04:00
Russell Hancox
5285a728b1 santa-driver: Don't record fileop events from santad 2016-03-21 16:15:20 -04:00
Russell Hancox
41e6583920 SantaGUI: Improve accessiblity of message dialog
+ VoiceOver: add more useful label descriptions
+ VoiceOver: skip some fields
+ Color: increase contrast
2016-03-21 13:49:50 -04:00
Russell Hancox
cbb60b3a05 SantaGUI: Have daemon reply when setting notification listener so GUI can ensure it connected 2016-03-17 17:55:31 -04:00
Russell Hancox
cf1d1e3557 santa-driver: Better handle secondary volumes 2016-03-15 15:10:41 -04:00
Russell Hancox
8f05ee7d79 santa-driver: Rename some action types 2016-03-15 12:53:44 -04:00
Russell Hancox
641bd07c0b Project: New icon 2016-03-14 16:38:07 -04:00
Russell Hancox
7d9dc0a853 Tests: Fix kernel tests 2016-03-14 16:13:28 -04:00
Russell Hancox
e0a46be1b7 santactl/fileinfo: When resolving path, store bundle ref if possible. 2016-03-14 12:55:20 -04:00
Russell Hancox
fd82c67b56 santactl/fileinfo: Add disk image file type 2016-03-14 12:55:20 -04:00
Russell Hancox
f0a83b6f19 santactl/fileinfo: Add simultaneous hashing. 2016-03-14 12:52:25 -04:00
Russell Hancox
736b45bb46 SNTXPCConnection: Remove client validation of server
Now that santad<->SantaGUI work more like the client/server they are,
having an SNTXPCConnection 'client' validate its server is no longer necessary.
Having the validation in the 'server' only simplifies the code.
2016-03-11 17:06:43 -05:00
Russell Hancox
8eae9b7cb7 santad/SantaGUI: Refactor GUI<>santad connection logic and add queuing.
Instead of having santad create a listener for SantaGUI to connect to
and then reverse the client-server relationship, have SantaGUI create an
anonymous listener that it sends to santad using the control interface.

Also add a queue for notifications so that blocks that occur while
SantaGUI isn't running will show up once it starts.
2016-03-11 14:58:12 -05:00
Russell Hancox
0aa2d2c613 santactl/fileinfo: Print useful info when codesign validation fails 2016-03-10 18:23:21 -05:00
Russell Hancox
ad43db10f2 Tests: Attempt to fix FileWatcher tests 2016-03-10 17:17:02 -05:00
Russell Hancox
606f507422 Project: Update CocoaPods 2016-03-10 16:34:08 -05:00
Russell Hancox
36b7778883 LogicTests: Fix SNTXPCConnection test 2016-03-10 15:53:40 -05:00
Russell Hancox
7b032a6a73 Project: Travis, build in local dir instead of DerivedData 2016-03-10 15:53:27 -05:00
Russell Hancox
0e00237e44 Project: Add clang-format file, apply most of the fixes it suggested 2016-03-10 15:53:06 -05:00
Russell Hancox
e9ec9a7d7f santad: Log quarantine URL if one exists.
Fixes #34
2016-03-10 13:24:31 -05:00
Russell Hancox
6834507f3a XPC: Allow multiple XPC clients to a server 2016-03-10 12:21:49 -05:00
Russell Hancox
90e99255b1 santa-driver/santad: Split decision making and logging onto 2 data queues
This resolves an issue where the data queue can be overwhelmed by logging requests and fail to respond to decisions for an extended period of time.
2016-03-10 12:21:17 -05:00
Russell Hancox
b6487000a3 SNTFileInfo: Use NSBundle to find executable path in bundles.
Fixes #37
2016-03-10 12:19:52 -05:00
Russell Hancox
18ce2f72ed Config: Fix config reloading 2016-03-10 12:18:05 -05:00
Russell Hancox
8a2d04bf69 santactl/rule: Fix print error 2016-03-09 15:41:27 -05:00
Russell Hancox
a210ffecec Logging: Create one ASL client per-thread. 2016-03-07 17:31:31 -05:00
Russell Hancox
aff96e8144 Config: Warn if SyncBaseURL is an invalid URL 2016-03-07 12:36:00 -05:00
Russell Hancox
3d4c639bb4 santactl/sync: Fix logic when auto-detecting certificates.
Now, instead of assuming an identity can be found that the server asked
for, look for a chain of certs resulting in an identity that matches the
server's request.
2016-03-07 12:32:32 -05:00
Russell Hancox
d507e79505 santad: Fix quarantine data collection.
This previously didn't work for root (santactl fileinfo was fine)
because quarantine data is per-user.
2016-03-07 12:30:36 -05:00
Russell Hancox
d3e242ff42 Project: Update Travis settings 2016-02-05 19:37:11 -05:00
Russell Hancox
df7616403d SantaGUI: Show entire SHA-256 in fixed-width font 2016-01-14 16:51:29 -05:00
Russell Hancox
962b15517a SantaGUI: Add a transparent button to be the first responder, so tabbing the dialog works. 2015-12-28 17:24:29 -05:00
Russell Hancox
d295f2391f santactl/sync: In --debug log the full NSError for failed requests 2015-12-15 12:36:07 -05:00
Russell Hancox
c042222eea santad: Add user/group info to file changelogs also 2015-12-14 22:32:59 -05:00
Russell Hancox
63f6596bc2 santactl: Rename binaryinfo -> fileinfo. 2015-12-14 18:09:40 -05:00
Russell Hancox
d8a8aba0ea SNTFileInfo: Move machoType method to binaryinfo command, add XAR archive detection. 2015-12-14 17:25:32 -05:00
Russell Hancox
d9d9682029 santactl/sync: Let related-binary search take up to 5s 2015-12-14 16:37:19 -05:00
Russell Hancox
4a27a8ac70 Rakefile: Use Xcode to figure out where built products went, to avoid relying on particular Xcode settings. 2015-12-14 16:36:11 -05:00
Russell Hancox
32857ff304 Project: Apply latest Xcode recommendations 2015-12-14 16:35:34 -05:00
Russell Hancox
375bfd3862 santa-driver: Put locks around vnode_pid_map, use an OSObject subclass to store PID/PPID.
Put a R/W lock around vnode_pid_map_ to prevent use-after-free.
Create SantaPIDAndPPID to use instead of creating and then scanning strings.
Also rename SantaMessage -> SantaCachedDecision, as that's what it is.
2015-12-14 16:34:38 -05:00
Russell Hancox
9430c41b8a santad: Include user and group names in execution logs 2015-12-11 12:58:09 -05:00
Russell Hancox
9b342e146a santactl/sync: Include code sign info with related executables and encode to dict. 2015-12-10 17:37:22 -05:00
Russell Hancox
e5685f2959 santad: Don't try to add empty argument to array when processing execution arguments 2015-12-10 17:02:11 -05:00
Russell Hancox
4150feece2 santactl/sync: When uploading events for bundles, look for other bundled executables.
Many application bundles have related helper tools, which will individually need to be whitelisted unless they're covered by a certificate. To help make user's lives easier, when an event is triggered for a binary inside a bundle look for other executables in the same bundle and upload an event for those too (with an obvious tag) so that the server can let the user vote to whitelist all the binaries together.
2015-12-10 17:01:49 -05:00
Russell Hancox
6879ec5deb santa-driver: in DecisionManager free locks before anything else 2015-12-10 16:56:13 -05:00
Russell Hancox
28ad00ffad SantaGUI: Split block messages into unknown and banned.
This is so that a message can be configured for banned executables without having to provide a custom message for every single one.
2015-12-10 12:13:52 -05:00
Russell Hancox
bf51049fbf santa-driver: Save pid/ppid from VFS context when decision making for use when logging
Previously the execution logging from fileop didn't work when using posix_spawn as proc_selfpid/proc_selfppid still refer to the process calling posix_spawn. We can get the correct pid/ppid from the vfs_context in the vnode scope but we can't log executions from there as the arguments end up being wrong. Instead, save the vnode_id->pid/ppid mapping in the vnode scope and use that in the fileop scope for logging.
2015-12-10 12:12:38 -05:00
Russell Hancox
36189e9122 santad: Update SNTFileInfo to always get strings from bundle Info.plist data.
Also perform a one-time update of any events created before this change.
2015-12-04 13:09:56 -05:00
Russell Hancox
4c747463ac santad: Separate execution requests and logging into separate queues with appropriate priorities. 2015-12-04 12:39:26 -05:00
Russell Hancox
b4b1fbb9e6 santad: Run watchdog thread loop once before sleeping 2015-10-31 14:01:44 -04:00
Russell Hancox
209eaff3c6 SNTFileInfo: Embed SHA hashing loop in an autoreleasepool to avoid temporary RAM spikes 2015-10-31 13:45:47 -04:00
Russell Hancox
c3f70703fd santactl/status: Expose peak CPU/RAM use from santad. 2015-10-29 16:20:57 -04:00
Russell Hancox
f2967e7b94 santad: Switch watchdog CPU counter from rusage to task_info, capture peak CPU/RAM use. 2015-10-29 16:20:25 -04:00
Russell Hancox
77c46b5c43 SNTFileInfo: switch from NSData to NSFileHandle.
This seems to work much better than NSData with either mapped (SIGBUS when file is deleted) or uncached (ballooning memory use) reading.
2015-10-29 16:17:12 -04:00
Russell Hancox
5fda5bc081 santactl/binaryinfo: Only print bundle lines if bundle info is present 2015-10-29 12:35:27 -04:00
Russell Hancox
33a7b38c6a SNTFileInfo: check for NULL ptrs when parsing for embedded plist 2015-10-27 18:35:11 -04:00
Russell Hancox
2a7c0bd58c SNTFileInfo: Go back to using mmap, uncached read balloons memory use 2015-10-27 18:08:16 -04:00
Russell Hancox
86e4d0db0f santactl: Use yyyy instead of YYYY in NSDateFormatter 2015-10-27 17:58:23 -04:00
Russell Hancox
1310fea64d santa-driver: Only try to use/release proc_t if proc_find found it. 2015-10-22 11:29:49 -04:00
Russell Hancox
382f5a5bb9 Merge pull request #30 from stephanemoore/patch-1
Fix application deadlock.
2015-10-22 08:39:54 -04:00
Stephane Moore
ff3303e312 Fix application deadlock.
Fix application deadlock by asynchronously dispatching to the main queue in -[SNTAppDelegate createConnection].
2015-10-21 17:45:59 -07:00
115 changed files with 3341 additions and 2287 deletions

22
.clang-format Normal file
View File

@@ -0,0 +1,22 @@
BasedOnStyle: Google
Language: Cpp
Standard: Cpp11
# Disable ColumnLimit because it causes some very weird line breaks.
# For ObjC the limit is 100
# For Cpp the limit is 80
ColumnLimit: 0
# Allow short case statements to be on a single line
AllowShortCaseLabelsOnASingleLine: true
# Ban short loops and functions on a single line
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
# Allow spaces in NSArray/NSDictionary literals @[ and @{
SpacesInContainerLiterals: true
# For pointers, always put the * next to the variable name.
DerivePointerAlignment: false
PointerAlignment: Right

View File

@@ -1,9 +1,12 @@
---
language: objective-c
cache: cocoapods
sudo: false
osx_image: xcode7
before_install:
- gem install activesupport
- gem install cocoapods xcpretty
script:
- xcodebuild -workspace Santa.xcworkspace -scheme All build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}
- xcodebuild -workspace Santa.xcworkspace -scheme All -derivedDataPath build build test CODE_SIGN_IDENTITY='' | xcpretty -sc && exit ${PIPESTATUS[0]}

View File

@@ -2,7 +2,5 @@
> /var/log/santa.log format="[$((Time)(ISO8601Z.3))] $Message" mode=0644 rotate=seq compress file_max=25M all_max=100M uid=0 gid=0
? [= Sender kernel] [S= Message santa-driver:] claim
? [= Sender kernel] [S= Message santa-driver:] file /var/log/santa.log
? [= Sender santad] claim
? [= Sender santad] file /var/log/santa.log
? [= Sender santactl] claim
? [= Sender santactl] file /var/log/santa.log
? [= Facility com.google.santa] claim
? [= Facility com.google.santa] file /var/log/santa.log

View File

@@ -7,11 +7,10 @@
<key>ProgramArguments</key>
<array>
<string>/Library/Extensions/santa-driver.kext/Contents/MacOS/santad</string>
<string>--syslog</string>
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCNotifications</key>
<true/>
<key>SantaXPCControl</key>
<true/>
</dict>
@@ -21,5 +20,7 @@
<true />
<key>ProcessType</key>
<string>Interactive</string>
<key>ThrottleInterval</key>
<integer>1</integer>
</dict>
</plist>

View File

@@ -7,6 +7,7 @@
<key>ProgramArguments</key>
<array>
<string>/Applications/Santa.app/Contents/MacOS/Santa</string>
<string>--syslog</string>
</array>
<key>RunAtLoad</key>
<true/>

View File

@@ -2,14 +2,15 @@ platform :osx, "10.9"
inhibit_all_warnings!
target :santactl do
target :Santa do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
end
target :Santa do
target :santactl do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'FMDB'
end
target :santad do

View File

@@ -1,13 +1,11 @@
PODS:
- FMDB (2.5):
- FMDB/standard (= 2.5)
- FMDB/common (2.5)
- FMDB/standard (2.5):
- FMDB/common
- MOLCertificate (1.1)
- MOLCodesignChecker (1.2):
- MOLCertificate (~> 1.1)
- OCMock (3.2)
- FMDB (2.6):
- FMDB/standard (= 2.6)
- FMDB/standard (2.6)
- MOLCertificate (1.3)
- MOLCodesignChecker (1.4):
- MOLCertificate (~> 1.3)
- OCMock (3.2.2)
DEPENDENCIES:
- FMDB
@@ -16,9 +14,9 @@ DEPENDENCIES:
- OCMock
SPEC CHECKSUMS:
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
MOLCertificate: 57fb88b33e83008d45c75644937fca61ed9f63d3
MOLCodesignChecker: e887eeeb7cd87c9b808d1682187c8a27f8ff1100
OCMock: 28def049ef47f996b515a8eeea958be7ccab2dbb
FMDB: c1968bab3ab0aed38f66cb778ae1e7fa9a652b6e
MOLCertificate: a776221906b5a46dd1bd749d0682bef3ee68c1f5
MOLCodesignChecker: 34e60cc6beadabfb4762b6e5087e12837774f85f
OCMock: 18c9b7e67d4c2770e95bb77a9cc1ae0c91fe3835
COCOAPODS: 0.39.0

View File

@@ -1,6 +1,12 @@
Santa [![Build Status](https://travis-ci.org/google/santa.png?branch=master)](https://travis-ci.org/google/santa)
=====
<p align="center">
<a href="#santa--">
<img src="./Source/SantaGUI/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</a>
</p>
Santa is a binary whitelisting/blacklisting system for OS X. It consists of
a kernel extension that monitors for executions, a userland daemon that makes
execution decisions based on the contents of a SQLite database, a GUI agent that
@@ -62,7 +68,7 @@ hosts in whatever other ways you see fit.
Get Help
========
If you have questions or need help getting started, the
If you have questions or need help getting started, the
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is the
best place to start.
@@ -102,6 +108,15 @@ option) if it would be useful to others.
* Tests: There aren't enough of them.
Screenshots
===========
A tool like Santa doesn't really lend itself to screenshots, so here's a video instead.
<p align="center">
<img src="https://zippy.gfycat.com/MadFatalAmphiuma.gif" alt="Santa Block Video" />
</p>
Building
========
```sh

View File

@@ -25,6 +25,17 @@ def xcodebuild(opts)
end
end
def xcodebuilddir
if not $xcode_build_dir
output = `xcodebuild #{XCODEBUILD_DEFAULTS} -scheme All -showBuildSettings`
if match = output.match(/BUILD_DIR = (.*)/)
$xcode_build_dir = match.captures.first
puts "Found Xcode build dir #{$xcode_build_dir}"
end
end
$xcode_build_dir
end
task :init do
unless File.exists?(WORKSPACE) and File.exists?('Pods')
puts "Pods missing, running 'pod install'"
@@ -88,8 +99,8 @@ namespace :install do
Rake::Task['build:build'].invoke(config)
puts "Installing with configuration: #{config}"
Rake::Task['remove_existing'].invoke()
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/santa-driver.kext /Library/Extensions"
system "sudo cp -r #{OUTPUT_PATH}/Products/#{config}/Santa.app /Applications"
system "sudo cp -r #{xcodebuilddir}/#{config}/santa-driver.kext /Library/Extensions"
system "sudo cp -r #{xcodebuilddir}/#{config}/Santa.app /Applications"
end
end
@@ -107,11 +118,11 @@ task :dist do
FileUtils.mkdir_p("#{DIST_PATH}/dsym")
BINARIES.each do |x|
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/binaries")
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{DIST_PATH}/binaries")
end
DSYMS.each do |x|
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/dsym")
FileUtils.cp_r("#{xcodebuilddir}/Release/#{x}", "#{DIST_PATH}/dsym")
end
@@ -137,7 +148,7 @@ namespace :tests do
begin
puts "\033[?25l\033[12h" # hide cursor
puts "Running kernel tests"
system "cd /tmp/santa_kerneltests_tmp && sudo #{Dir.pwd}/#{OUTPUT_PATH}/Products/Debug/KernelTests"
system "cd /tmp/santa_kerneltests_tmp && sudo #{xcodebuilddir}/Debug/KernelTests"
rescue Exception
ensure
puts "\033[?25h\033[12l\n\n" # unhide cursor

View File

@@ -78,19 +78,14 @@
0D536ED81B8E7A2E0039A26D /* missing_pagezero in Resources */ = {isa = PBXBuildFile; fileRef = 0D536ED61B8E7A2E0039A26D /* missing_pagezero */; };
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D536EDA1B94E9230039A26D /* SNTEventLog.m */; };
0D536EDC1B94E9230039A26D /* SNTEventLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D536EDA1B94E9230039A26D /* SNTEventLog.m */; };
0D54E0B11976F8D3000BB59F /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
0D63DD5C1906FCB400D346C4 /* SNTDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */; };
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */; };
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D668E8018D1121700E29A8B /* SNTMessageWindow.m */; };
0D6F12D819EC8822006B218E /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DCD5F771909C659006B445C /* SecurityInterface.framework */; };
0D6F12DA19EDE51E006B218E /* tubitak.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D6F12D919EDE411006B218E /* tubitak.crt */; };
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8218C68D7E0044685C /* GIAG2.crt */; };
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8418C68E500044685C /* GIAG2.pem */; };
0D6FDC8718C6913D0044685C /* apple.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8618C6913D0044685C /* apple.pem */; };
0D6FDC9618C93A020044685C /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D6FDC9718C93A020044685C /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D7A7AF3174FCF4C00B77646 /* SantaMessage.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */; };
0D7A7AF4174FCF4C00B77646 /* SantaMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D7A7AF2174FCF4C00B77646 /* SantaMessage.h */; };
0D7A7AF3174FCF4C00B77646 /* SantaCachedDecision.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A7AF1174FCF4C00B77646 /* SantaCachedDecision.cc */; };
0D7A7AF4174FCF4C00B77646 /* SantaCachedDecision.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D7A7AF2174FCF4C00B77646 /* SantaCachedDecision.h */; };
0D7D01871774F93A005DBAB4 /* SNTDriverManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */; };
0D7FFD4B1A017D4B00F34435 /* SNTDERDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7FFD4A1A017D4B00F34435 /* SNTDERDecoder.m */; };
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B719D2042900955F08 /* SNTConfigurator.m */; };
@@ -99,6 +94,8 @@
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 */; };
0D89310E1C931979002E8D74 /* SNTXPCControlInterface.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD605419115D17006B445C /* SNTXPCControlInterface.m */; };
0D89310F1C931986002E8D74 /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
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 */; };
@@ -113,13 +110,15 @@
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 */; };
0DB98BFA1C1B9F3000B65DB1 /* SantaPIDAndPPID.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0DB98BF81C1B9F3000B65DB1 /* SantaPIDAndPPID.cc */; };
0DB98BFB1C1B9F3000B65DB1 /* SantaPIDAndPPID.h in Headers */ = {isa = PBXBuildFile; fileRef = 0DB98BF91C1B9F3000B65DB1 /* SantaPIDAndPPID.h */; };
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
0DC5D86E191AED220078A5C0 /* SNTRuleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */; };
0DC5D871192160180078A5C0 /* SNTCommandSyncLogUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC5D870192160180078A5C0 /* SNTCommandSyncLogUpload.m */; };
0DC765EA1B28D9EA00BAE651 /* santad in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D9A7F3D1759330500035EB5 /* santad */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
0DC765EB1B28D9EA00BAE651 /* santactl in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0D35BD9E18FD71CE00921A21 /* santactl */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
0DCA552718C95928002A7DAE /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD5FBE1909D64A006B445C /* SNTCommandBinaryInfo.m */; };
0DCD5FBF1909D64A006B445C /* SNTCommandFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD5FBE1909D64A006B445C /* SNTCommandFileInfo.m */; };
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
0DCD6043190ACCB8006B445C /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
0DCD6044190ACCB8006B445C /* SNTFileInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6041190ACCB8006B445C /* SNTFileInfo.m */; };
@@ -138,12 +137,15 @@
0DD0D48F194F78F8005F27EB /* SNTFileInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */; };
0DD0D491194F9947005F27EB /* SNTExecutionControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */; };
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DE2CE561CA05561002B649A /* SNTAccessibleTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE2CE551CA05561002B649A /* SNTAccessibleTextField.m */; };
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8C200B180F359A00CE2BF8 /* Security.framework */; };
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */; };
0DE50F681912716A007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */; };
0DE5B54C1C92722300C00603 /* SNTNotificationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */; };
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */; };
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
@@ -292,6 +294,7 @@
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaDecisionManager.cc; sourceTree = "<group>"; };
0D4644C4182AF81700098690 /* SantaDecisionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaDecisionManager.h; sourceTree = "<group>"; };
0D4A5006176A4602004F63BF /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
0D5058CF1CB70123008784BA /* SNTStrengthify.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTStrengthify.h; sourceTree = "<group>"; };
0D536ED51B8E7A2E0039A26D /* bad_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = bad_pagezero; sourceTree = "<group>"; };
0D536ED61B8E7A2E0039A26D /* missing_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = missing_pagezero; sourceTree = "<group>"; };
0D536ED91B94E9230039A26D /* SNTEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTEventLog.h; sourceTree = "<group>"; };
@@ -300,14 +303,10 @@
0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDatabaseController.m; sourceTree = "<group>"; };
0D668E7F18D1121700E29A8B /* SNTMessageWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTMessageWindow.h; sourceTree = "<group>"; };
0D668E8018D1121700E29A8B /* SNTMessageWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTMessageWindow.m; sourceTree = "<group>"; };
0D6F12D919EDE411006B218E /* tubitak.crt */ = {isa = PBXFileReference; lastKnownFileType = file; path = tubitak.crt; sourceTree = "<group>"; };
0D6FDC8218C68D7E0044685C /* GIAG2.crt */ = {isa = PBXFileReference; lastKnownFileType = file; path = GIAG2.crt; sourceTree = "<group>"; };
0D6FDC8418C68E500044685C /* GIAG2.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GIAG2.pem; sourceTree = "<group>"; };
0D6FDC8618C6913D0044685C /* apple.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = apple.pem; sourceTree = "<group>"; };
0D6FDC9418C93A020044685C /* SNTXPCConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTXPCConnection.h; sourceTree = "<group>"; };
0D6FDC9518C93A020044685C /* SNTXPCConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTXPCConnection.m; sourceTree = "<group>"; };
0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaMessage.cc; sourceTree = "<group>"; };
0D7A7AF2174FCF4C00B77646 /* SantaMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaMessage.h; sourceTree = "<group>"; };
0D7A7AF1174FCF4C00B77646 /* SantaCachedDecision.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaCachedDecision.cc; sourceTree = "<group>"; };
0D7A7AF2174FCF4C00B77646 /* SantaCachedDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaCachedDecision.h; sourceTree = "<group>"; };
0D7D01851774F93A005DBAB4 /* SNTDriverManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTDriverManager.h; sourceTree = "<group>"; };
0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = SNTDriverManager.m; sourceTree = "<group>"; };
0D7FFD491A017D4B00F34435 /* SNTDERDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTDERDecoder.h; sourceTree = "<group>"; };
@@ -334,12 +333,14 @@
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>"; };
0DB98BF81C1B9F3000B65DB1 /* SantaPIDAndPPID.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaPIDAndPPID.cc; sourceTree = "<group>"; };
0DB98BF91C1B9F3000B65DB1 /* SantaPIDAndPPID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaPIDAndPPID.h; sourceTree = "<group>"; };
0DC5D86C191AED220078A5C0 /* SNTRuleTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRuleTable.m; sourceTree = "<group>"; };
0DC5D86F192160180078A5C0 /* SNTCommandSyncLogUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncLogUpload.h; sourceTree = "<group>"; };
0DC5D870192160180078A5C0 /* SNTCommandSyncLogUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncLogUpload.m; sourceTree = "<group>"; };
0DC8C9E3180CC3BC00FCFB29 /* SNTXPCNotifierInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTXPCNotifierInterface.h; sourceTree = "<group>"; };
0DCD5F771909C659006B445C /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = System/Library/Frameworks/SecurityInterface.framework; sourceTree = SDKROOT; };
0DCD5FBE1909D64A006B445C /* SNTCommandBinaryInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandBinaryInfo.m; sourceTree = "<group>"; };
0DCD5FBE1909D64A006B445C /* SNTCommandFileInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFileInfo.m; sourceTree = "<group>"; };
0DCD6040190ACCB8006B445C /* SNTFileInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTFileInfo.h; sourceTree = "<group>"; };
0DCD6041190ACCB8006B445C /* SNTFileInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileInfo.m; sourceTree = "<group>"; };
0DCD604919105433006B445C /* SNTStoredEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTStoredEvent.h; sourceTree = "<group>"; };
@@ -353,9 +354,13 @@
0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAuthenticatingURLSession.m; sourceTree = "<group>"; };
0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileInfoTest.m; sourceTree = "<group>"; };
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTExecutionControllerTest.m; sourceTree = "<group>"; };
0DE2CE541CA05561002B649A /* SNTAccessibleTextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAccessibleTextField.h; sourceTree = "<group>"; };
0DE2CE551CA05561002B649A /* SNTAccessibleTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAccessibleTextField.m; sourceTree = "<group>"; };
0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandFlushCache.m; sourceTree = "<group>"; };
0DE50F6619127169007B2B0C /* SNTRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTRule.h; sourceTree = "<group>"; };
0DE50F671912716A007B2B0C /* SNTRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRule.m; sourceTree = "<group>"; };
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTNotificationQueue.h; sourceTree = "<group>"; };
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTNotificationQueue.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; };
0DE71A731B95F7F900518526 /* SNTCachedDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCachedDecision.h; sourceTree = "<group>"; };
@@ -469,12 +474,8 @@
0D536ED51B8E7A2E0039A26D /* bad_pagezero */,
0D536ED61B8E7A2E0039A26D /* missing_pagezero */,
0D2CD4601A81C7B100C9C910 /* dn.plist */,
0D6FDC8618C6913D0044685C /* apple.pem */,
0D6FDC8218C68D7E0044685C /* GIAG2.crt */,
0D6FDC8418C68E500044685C /* GIAG2.pem */,
0D260DB118B68E12002A0B55 /* Tests-Info.plist */,
0D260DB718B68E12002A0B55 /* Tests-Prefix.pch */,
0D6F12D919EDE411006B218E /* tubitak.crt */,
);
path = Resources;
sourceTree = "<group>";
@@ -482,15 +483,10 @@
0D35BDA018FD71CE00921A21 /* santactl */ = {
isa = PBXGroup;
children = (
0D35BDA118FD71CE00921A21 /* main.m */,
0D35BDAA18FD7CFD00921A21 /* SNTCommandController.h */,
0D35BDA118FD71CE00921A21 /* main.m */,
0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */,
0DCD5FBC1909D4FD006B445C /* binaryinfo */,
0DE4C8A318FF3AFA00466D04 /* flushcache */,
409232751A51914400A04527 /* rule */,
0D827E6819DF4F3F006EC811 /* status */,
0D35BDB618FD84FC00921A21 /* sync */,
0DB390971AB1E0F200614002 /* version */,
0DAF01141C1B794B00F5B6C3 /* Commands */,
0D35BDA318FD71CE00921A21 /* Resources */,
);
name = santactl;
@@ -540,6 +536,8 @@
0D385DC3180DE4A900418BC6 /* main.m */,
0D1B476D19A53419008CADD3 /* SNTAboutWindowController.h */,
0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */,
0DE2CE541CA05561002B649A /* SNTAccessibleTextField.h */,
0DE2CE551CA05561002B649A /* SNTAccessibleTextField.m */,
0D385DEA180DE51600418BC6 /* SNTAppDelegate.h */,
0D385DEB180DE51600418BC6 /* SNTAppDelegate.m */,
0D668E7F18D1121700E29A8B /* SNTMessageWindow.h */,
@@ -642,14 +640,16 @@
0D91BCB9174E8A7E00131A7D /* santa-driver */ = {
isa = PBXGroup;
children = (
0D7A7AF2174FCF4C00B77646 /* SantaCachedDecision.h */,
0D7A7AF1174FCF4C00B77646 /* SantaCachedDecision.cc */,
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
0D9A7F321759144800035EB5 /* SantaDriver.h */,
0D9A7F311759144800035EB5 /* SantaDriver.cc */,
0D9A7F361759148E00035EB5 /* SantaDriverClient.h */,
0D9A7F351759148E00035EB5 /* SantaDriverClient.cc */,
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
0D7A7AF2174FCF4C00B77646 /* SantaMessage.h */,
0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */,
0DB98BF91C1B9F3000B65DB1 /* SantaPIDAndPPID.h */,
0DB98BF81C1B9F3000B65DB1 /* SantaPIDAndPPID.cc */,
0DA36C1F199EA46600A129D6 /* Resources */,
);
name = "santa-driver";
@@ -675,6 +675,7 @@
0DE50F671912716A007B2B0C /* SNTRule.m */,
0DCD604919105433006B445C /* SNTStoredEvent.h */,
0DCD604A19105433006B445C /* SNTStoredEvent.m */,
0D5058CF1CB70123008784BA /* SNTStrengthify.h */,
0D42D2B319D1D98A00955F08 /* SNTSystemInfo.h */,
0D42D2B419D1D98A00955F08 /* SNTSystemInfo.m */,
0D6FDC9418C93A020044685C /* SNTXPCConnection.h */,
@@ -707,6 +708,8 @@
0D536EDA1B94E9230039A26D /* SNTEventLog.m */,
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */,
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */,
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */,
0D3AF83118F87CEF0087BCEE /* Resources */,
);
name = santad;
@@ -734,6 +737,19 @@
name = DataLayer;
sourceTree = "<group>";
};
0DAF01141C1B794B00F5B6C3 /* Commands */ = {
isa = PBXGroup;
children = (
0DCD5FBC1909D4FD006B445C /* fileinfo */,
0DE4C8A318FF3AFA00466D04 /* flushcache */,
409232751A51914400A04527 /* rule */,
0D827E6819DF4F3F006EC811 /* status */,
0D35BDB618FD84FC00921A21 /* sync */,
0DB390971AB1E0F200614002 /* version */,
);
name = Commands;
sourceTree = "<group>";
};
0DB390971AB1E0F200614002 /* version */ = {
isa = PBXGroup;
children = (
@@ -742,12 +758,12 @@
name = version;
sourceTree = "<group>";
};
0DCD5FBC1909D4FD006B445C /* binaryinfo */ = {
0DCD5FBC1909D4FD006B445C /* fileinfo */ = {
isa = PBXGroup;
children = (
0DCD5FBE1909D64A006B445C /* SNTCommandBinaryInfo.m */,
0DCD5FBE1909D64A006B445C /* SNTCommandFileInfo.m */,
);
path = binaryinfo;
path = fileinfo;
sourceTree = "<group>";
};
0DE4C8A318FF3AFA00466D04 /* flushcache */ = {
@@ -788,7 +804,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
0D7A7AF4174FCF4C00B77646 /* SantaMessage.h in Headers */,
0D7A7AF4174FCF4C00B77646 /* SantaCachedDecision.h in Headers */,
0DB98BFB1C1B9F3000B65DB1 /* SantaPIDAndPPID.h in Headers */,
0D4644C6182AF81700098690 /* SantaDecisionManager.h in Headers */,
0D9A7F341759144800035EB5 /* SantaDriver.h in Headers */,
0D9A7F381759148E00035EB5 /* SantaDriverClient.h in Headers */,
@@ -921,7 +938,7 @@
0D91BCA8174E8A6500131A7D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
LastUpgradeCheck = 0700;
TargetAttributes = {
0D260DAB18B68E12002A0B55 = {
TestTargetID = 0D385DB5180DE4A900418BC6;
@@ -957,12 +974,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */,
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */,
0D6F12DA19EDE51E006B218E /* tubitak.crt in Resources */,
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */,
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */,
0D6FDC8718C6913D0044685C /* apple.pem in Resources */,
0D536ED81B8E7A2E0039A26D /* missing_pagezero in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1256,6 +1269,7 @@
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */,
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */,
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */,
0DE5B54C1C92722300C00603 /* SNTNotificationQueue.m in Sources */,
0D3AFBE718FB32CB0087BCEE /* SNTXPCConnectionTest.m in Sources */,
0DCD605719115E54006B445C /* SNTDaemonControlController.m in Sources */,
0D41DAD41A7C28C800A890FE /* SNTEventTableTest.m in Sources */,
@@ -1276,7 +1290,7 @@
files = (
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */,
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */,
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */,
0DCD5FBF1909D64A006B445C /* SNTCommandFileInfo.m in Sources */,
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */,
0DCD6062191188B1006B445C /* SNTAuthenticatingURLSession.m in Sources */,
0DCD605619115D17006B445C /* SNTXPCControlInterface.m in Sources */,
@@ -1308,12 +1322,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D54E0B11976F8D3000BB59F /* SNTFileInfo.m in Sources */,
0DCA552718C95928002A7DAE /* SNTXPCConnection.m in Sources */,
0D385DF1180DE51600418BC6 /* SNTAppDelegate.m in Sources */,
0D88680A1AC48A1200B86659 /* SNTSystemInfo.m in Sources */,
0D89310F1C931986002E8D74 /* SNTRule.m in Sources */,
0DCD605119115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
0D827E6519DF392E006EC811 /* SNTConfigurator.m in Sources */,
0D89310E1C931979002E8D74 /* SNTXPCControlInterface.m in Sources */,
0D385DF2180DE51600418BC6 /* SNTMessageWindowController.m in Sources */,
0D385DF3180DE51600418BC6 /* SNTNotificationManager.m in Sources */,
0D385DC4180DE4A900418BC6 /* main.m in Sources */,
@@ -1322,6 +1337,7 @@
0DA73CA11934F8100056D7C4 /* SNTLogging.m in Sources */,
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */,
0DE2CE561CA05561002B649A /* SNTAccessibleTextField.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1329,7 +1345,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0D7A7AF3174FCF4C00B77646 /* SantaMessage.cc in Sources */,
0D7A7AF3174FCF4C00B77646 /* SantaCachedDecision.cc in Sources */,
0DB98BFA1C1B9F3000B65DB1 /* SantaPIDAndPPID.cc in Sources */,
0D9A7F331759144800035EB5 /* SantaDriver.cc in Sources */,
0D9A7F371759148E00035EB5 /* SantaDriverClient.cc in Sources */,
0D4644C5182AF81700098690 /* SantaDecisionManager.cc in Sources */,
@@ -1362,6 +1379,7 @@
0DCD605519115D17006B445C /* SNTXPCControlInterface.m in Sources */,
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */,
0DCD604F19115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1501,7 +1519,6 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/LogicTests/Resources/Tests-Prefix.pch";
@@ -1521,6 +1538,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
@@ -1551,7 +1569,6 @@
);
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_GENERATE_TEST_COVERAGE_FILES = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Tests/LogicTests/Resources/Tests-Prefix.pch";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -1565,6 +1582,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
INFOPLIST_FILE = "Tests/LogicTests/Resources/Tests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = xctest;
@@ -1734,6 +1752,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -39,15 +39,18 @@
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@@ -62,10 +65,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
@@ -38,15 +38,18 @@
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -62,10 +65,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -48,15 +48,18 @@
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@@ -71,10 +74,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
@@ -38,15 +38,18 @@
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -62,10 +65,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,21 +23,24 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@@ -52,10 +55,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
@@ -38,15 +38,18 @@
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -62,10 +65,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
@@ -38,16 +38,19 @@
ReferencedContainer = "container:Santa.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
debugAsWhichUser = "root"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -63,10 +66,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -31,25 +31,25 @@
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"idiom" : "mac",
"filename" : "santa-hat-icon-256.png",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"idiom" : "mac",
"filename" : "santa-hat-icon-256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "santa-hat-icon-512.png",
"size" : "256x256",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "santa-hat-icon-512.png",
"size" : "512x512",
"scale" : "1x"
},
{
@@ -62,4 +62,4 @@
"version" : 1,
"author" : "xcode"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="15A282b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9532" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
<development version="6300" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9532"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
@@ -14,63 +15,95 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<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">
<window title="Santa Blocked Execution" 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="497" height="381"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<rect key="contentRect" x="167" y="107" width="497" height="439"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="497" height="381"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="439"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kiB-jZ-69S">
<rect key="frame" x="-6" y="411" width="37" height="32"/>
<buttonCell key="cell" type="push" title="Hidden Button" alternateTitle="This button exists so neither of the other two buttons is pre-selected when the dialog opens." bezelStyle="rounded" alignment="center" borderStyle="border" focusRingType="none" transparent="YES" imageScaling="proportionallyDown" inset="2" id="XGa-Sl-F4t">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</buttonCell>
<connections>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="vl5-A8-O0H"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
<rect key="frame" x="207" y="311" width="83" height="40"/>
<animations/>
<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"/>
<rect key="frame" x="206" y="368" width="85" height="41"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" refusesFirstResponder="YES" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
<font key="font" metaFont="systemUltraLight" size="34"/>
<color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
<connections>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="z5y-RR-IEH"/>
</connections>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
<rect key="frame" x="22" y="264" width="454" height="17"/>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR" customClass="SNTAccessibleTextField">
<rect key="frame" x="22" y="329" width="454" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
<textFieldCell key="cell" selectable="YES" refusesFirstResponder="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.attributedCustomMessage" id="376-sj-4Q1"/>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="VC7-bE-uHc"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ">
<rect key="frame" x="165" y="192" width="294" height="17"/>
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="146" y="132" width="1" height="167"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="E7T-9h-ofr">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC" userLabel="Label: Application">
<rect key="frame" x="8" y="282" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.filePath" id="qfp-sR-Nmu"/>
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr">
<rect key="frame" x="165" y="217" width="294" height="17"/>
<textField toolTip="Application Name" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="282" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="Pav-ZA-iAu"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
<font key="font" metaFont="systemBold"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
@@ -80,31 +113,68 @@
</binding>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="165" y="142" width="294" height="17"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H" userLabel="Label: Path">
<rect key="frame" x="8" y="257" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="4hh-R2-86s"/>
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
</constraints>
<animations/>
<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"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Filename" id="KgY-X1-ESG">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YNz-ka-cBi" userLabel="Label: Path">
<rect key="frame" x="8" y="232" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="3wU-P0-gAC"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="adC-be-Beh">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField toolTip="Binary Path" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="257" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
</constraints>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Name" id="E7T-9h-ofr">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.shortenedHash" id="xgu-71-9ZT"/>
<binding destination="-2" name="value" keyPath="self.event.filePath.lastPathComponent" id="bOu-gv-1Vh"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
<rect key="frame" x="165" y="167" width="294" height="17"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5" userLabel="Label: Publisher">
<rect key="frame" x="8" y="207" 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"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField toolTip="Publisher" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w" userLabel="Publisher" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="207" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
</constraints>
<animations/>
<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"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.publisherInfo" id="CEI-Cu-7pC">
@@ -114,93 +184,18 @@
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL">
<rect key="frame" x="8" y="92" width="120" height="17"/>
<animations/>
<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"/>
<animations/>
<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="8" y="167" width="120" height="17"/>
<animations/>
<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"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H">
<rect key="frame" x="8" y="192" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="KgY-X1-ESG">
<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="KEB-eH-x2Y">
<rect key="frame" x="8" y="142" width="120" height="17"/>
<animations/>
<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="165" y="92" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<font key="font" metaFont="system"/>
<color key="textColor" 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.executingUser" id="IcM-Lt-xTT">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<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="142"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
</constraints>
<animations/>
<color key="borderColor" white="0.0" alpha="0.17999999999999999" 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"/>
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS" userLabel="Publisher Certs Button">
<rect key="frame" x="40" y="208" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
</constraints>
<animations/>
<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"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</buttonCell>
<connections>
<action selector="showCertInfo:" target="-2" id="dB0-a3-X31"/>
@@ -211,53 +206,54 @@
</binding>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL">
<rect key="frame" x="256" y="33" width="110" height="25"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y" userLabel="Label: Identifier">
<rect key="frame" x="8" y="182" 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"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="165" y="182" width="219" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
<constraint firstAttribute="width" constant="215" id="4hh-R2-86s"/>
</constraints>
<animations/>
<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>
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="SHA-256" id="X4W-9e-eIu">
<font key="font" metaFont="fixedUser" size="11"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
<connections>
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
<binding destination="-2" name="value" keyPath="self.event.fileSHA256" id="9KB-0b-qLV"/>
</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>
<animations/>
<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"/>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J" userLabel="Label: Parent">
<rect key="frame" x="8" y="157" 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"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField toolTip="Parent Process" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="f1p-GL-O3o" userLabel="Parent" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="157" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="displayPatternValue1" keyPath="self.event.parentName" id="Lce-TO-q9V">
@@ -272,82 +268,163 @@ DQ
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC">
<rect key="frame" x="8" y="217" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL" userLabel="Label: User">
<rect key="frame" x="8" y="132" 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"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
</userDefinedRuntimeAttributes>
</textFieldCell>
</textField>
<textField toolTip="Executing User" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="h6f-PY-cc0" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="132" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="IcM-Lt-xTT">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<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" 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"/>
<outlet property="nextKeyView" destination="BbV-3h-mmL" id="Xkz-va-iGc"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL" userLabel="Dismiss Button">
<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" 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>
<accessibility description="Dismiss Dialog"/>
<connections>
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="4KL-Z2-1op"/>
</connections>
</button>
<textField toolTip="Binary Path" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bDE-Tl-UHg" customClass="SNTAccessibleTextField">
<rect key="frame" x="165" y="232" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="p1W-f9-KBX"/>
</constraints>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="H1b-Ui-CYo">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.filePath" id="Sry-KY-HDb"/>
</connections>
</textField>
<button translatesAutoresizingMaskIntoConstraints="NO" id="5D8-GP-a4l">
<rect key="frame" x="91" y="80" width="315" height="29"/>
<constraints>
<constraint firstAttribute="height" constant="25" id="KvD-X6-CsO"/>
</constraints>
<buttonCell key="cell" type="check" title="Prevent future notifications for this application for a day" bezelStyle="regularSquare" imagePosition="left" alignment="center" inset="2" id="R5Y-Uc-rEP">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<binding destination="-2" name="value" keyPath="self.silenceFutureNotifications" id="tEb-2A-sht"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="0AD-PS-5V1"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" priority="500" constant="193" id="2uo-Cm-Tfp"/>
<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="pDa-fA-vnC" firstAttribute="centerY" secondItem="qgf-Jf-cJr" secondAttribute="centerY" id="AKX-pe-hEX"/>
<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="d9e-Wv-Y5H" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="FPe-Rd-G4n"/>
<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="BbV-3h-mmL" firstAttribute="top" secondItem="5D8-GP-a4l" secondAttribute="bottom" priority="900" constant="25" id="GT2-tO-2td"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pDa-fA-vnC" secondAttribute="top" id="Gd4-Nr-n5G"/>
<constraint firstItem="qgf-Jf-cJr" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="Ht4-Lg-U5N"/>
<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" priority="500" 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" priority="750" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="YNz-ka-cBi" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="KmX-kX-VCN"/>
<constraint firstItem="5D8-GP-a4l" firstAttribute="centerX" secondItem="Iwq-Lx-rLv" secondAttribute="centerX" id="LkH-F4-Ncm"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="950" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="YNz-ka-cBi" firstAttribute="centerY" secondItem="bDE-Tl-UHg" secondAttribute="centerY" id="ObQ-RA-S5V"/>
<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 firstItem="oFj-ol-xpL" firstAttribute="top" secondItem="eQb-0a-76J" secondAttribute="bottom" constant="8" id="abm-cM-PN0"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" id="asc-Ga-WHD"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="top" secondItem="bDE-Tl-UHg" secondAttribute="bottom" constant="8" id="ZoS-xV-2WA"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="aMJ-Wb-vRS"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="aOk-S0-0n2"/>
<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="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="22" 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="qgf-Jf-cJr" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="esg-lX-BAT"/>
<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="bDE-Tl-UHg" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="jdk-ak-soQ"/>
<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="pc8-G9-4pJ" firstAttribute="top" secondItem="qgf-Jf-cJr" secondAttribute="bottom" constant="8" id="lWU-tC-vWg"/>
<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 firstItem="5D8-GP-a4l" firstAttribute="top" secondItem="h6f-PY-cc0" secondAttribute="bottom" constant="25" id="lYd-VZ-lBs"/>
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="5D8-GP-a4l" secondAttribute="bottom" priority="900" constant="25" id="pCX-eX-erN"/>
<constraint firstAttribute="centerX" secondItem="7ua-5a-uSd" secondAttribute="centerX" constant="61" id="phL-j9-rPq"/>
<constraint firstItem="bDE-Tl-UHg" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="bottom" constant="8" id="pis-of-f93"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="20" id="qKi-KT-jzJ"/>
<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="eQb-0a-76J" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="u1y-6V-moc"/>
<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="pc8-G9-4pJ" firstAttribute="bottom" secondItem="C3G-wL-u7w" secondAttribute="top" constant="-8" id="zst-nc-VqA"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="YNz-ka-cBi" secondAttribute="trailing" constant="20" id="vfq-83-tKI"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="z6s-ga-iAk"/>
</constraints>
<animations/>
</view>
<point key="canvasLocation" x="112.5" y="308"/>
<connections>
<outlet property="initialFirstResponder" destination="kiB-jZ-69S" id="I96-dS-lq5"/>
</connections>
<point key="canvasLocation" x="112.5" y="302.5"/>
</window>
</objects>
<resources>

View File

@@ -0,0 +1,21 @@
/// Copyright 2016 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.
/**
An NSTextField subclass that provides an accessiblity label equal to:
(self.toolTip + self.stringValue) where available. It also sets the
accessibilityRoleDescription to "label".
*/
@interface SNTAccessibleTextField : NSTextField
@end

View File

@@ -0,0 +1,39 @@
/// Copyright 2016 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 "SNTAccessibleTextField.h"
@implementation SNTAccessibleTextField
- (BOOL)accessibilityIsIgnored {
return NO;
}
- (NSString *)accessibilityLabel {
if (self.toolTip && self.stringValue) {
return [NSString stringWithFormat:@"%@: %@", self.toolTip, self.stringValue];
} else if (self.stringValue) {
return self.stringValue;
} else if (self.toolTip) {
return self.toolTip;
} else {
return nil;
}
}
- (NSString *)accessibilityRoleDescription {
return @"label";
}
@end

View File

@@ -18,7 +18,9 @@
#import "SNTConfigurator.h"
#import "SNTFileWatcher.h"
#import "SNTNotificationManager.h"
#import "SNTStrengthify.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;
@@ -36,7 +38,7 @@
self.configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^{
[[SNTConfigurator configurator] reloadConfigData];
[[SNTConfigurator configurator] reloadConfigData];
}];
self.notificationManager = [[SNTNotificationManager alloc] init];
@@ -47,16 +49,15 @@
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
self.listener.invalidationHandler = nil;
self.listener.rejectedHandler = nil;
[self.listener invalidate];
self.listener = nil;
self.listener.invalidationHandler = nil;
[self.listener invalidate];
self.listener = nil;
}];
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
[self attemptReconnection];
[self attemptReconnection];
}];
[self createConnection];
@@ -71,25 +72,37 @@
#pragma mark Connection handling
- (void)createConnection {
dispatch_sync(dispatch_get_main_queue(), ^{
__weak __typeof(self) weakSelf = self;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
[weakSelf attemptReconnection];
};
self.listener.invalidationHandler = self.listener.rejectedHandler;
[self.listener resume];
});
WEAKIFY(self);
// Create listener for return connection from daemon.
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.acceptedHandler = ^{
dispatch_semaphore_signal(sema);
};
self.listener.invalidationHandler = ^{
STRONGIFY(self);
[self attemptReconnection];
};
[self.listener resume];
// Tell daemon to connect back to the above listener.
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
[daemonConn resume];
[[daemonConn remoteObjectProxy] setNotificationListener:listener.endpoint];
// Now wait for the connection to come in.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self attemptReconnection];
}
}
- (void)attemptReconnection {
// TODO(rah): Make this smarter.
sleep(5);
[self createConnection];
[self performSelectorInBackground:@selector(createConnection) withObject:nil];
}
#pragma mark Menu Management

View File

@@ -31,7 +31,7 @@
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.15f];
[[NSAnimationContext currentContext] setCompletionHandler:^{
[NSApp activateIgnoringOtherApps:YES];
[NSApp activateIgnoringOtherApps:YES];
}];
[[self animator] setAlphaValue:1.f];
[NSAnimationContext endGrouping];
@@ -43,9 +43,9 @@
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.15f];
[[NSAnimationContext currentContext] setCompletionHandler:^{
[weakSelf.windowController windowWillClose:sender];
[weakSelf orderOut:sender];
[weakSelf setAlphaValue:1.f];
[weakSelf.windowController windowWillClose:sender];
[weakSelf orderOut:sender];
[weakSelf setAlphaValue:1.f];
}];
[[self animator] setAlphaValue:0.f];
[NSAnimationContext endGrouping];

View File

@@ -15,7 +15,7 @@
@class SNTStoredEvent;
@protocol SNTMessageWindowControllerDelegate
- (void)windowDidClose;
- (void)windowDidCloseSilenceHash:(NSString *)hash;
@end
///
@@ -29,41 +29,9 @@
- (IBAction)closeWindow:(id)sender;
- (IBAction)showCertInfo:(id)sender;
///
/// The execution event that this window is for
///
@property SNTStoredEvent *event;
///
/// The custom message to display for this event
///
@property(copy) NSString *customMessage;
///
/// The delegate to inform when the notification is dismissed
///
@property(weak) id<SNTMessageWindowControllerDelegate> delegate;
///
/// A 'friendly' string representing the certificate information
///
@property(readonly, nonatomic) NSString *publisherInfo;
///
/// An optional message to display with this block.
///
@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;
///
/// Reference to the "Application Name" label in the XIB. Used to remove if application
/// doesn't have a CFBundleName.
///
@property IBOutlet NSTextField *applicationNameLabel;
@end

View File

@@ -22,13 +22,38 @@
#import "SNTMessageWindow.h"
#import "SNTStoredEvent.h"
@interface SNTMessageWindowController ()
/// The execution event that this window is for
@property SNTStoredEvent *event;
/// The custom message to display for this event
@property(copy) NSString *customMessage;
/// A 'friendly' string representing the certificate information
@property(readonly, nonatomic) NSString *publisherInfo;
/// An optional message to display with this block.
@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;
/// Reference to the "Application Name" label in the XIB. Used to remove if application
/// doesn't have a CFBundleName.
@property IBOutlet NSTextField *applicationNameLabel;
/// Linked to checkbox in UI to prevent future notifications for this binary.
@property BOOL silenceFutureNotifications;
@end
@implementation SNTMessageWindowController
- (instancetype)initWithEvent:(SNTStoredEvent *)event andMessage:(NSString *)message {
self = [super initWithWindowNibName:@"MessageWindow"];
if (self) {
_event = event;
_customMessage = (message != (NSString *)[NSNull null] ? message : nil);
_customMessage = message;
}
return self;
}
@@ -61,7 +86,13 @@
}
- (void)windowWillClose:(NSNotification *)notification {
if (self.delegate) [self.delegate windowDidClose];
if (!self.delegate) return;
if (self.silenceFutureNotifications) {
[self.delegate windowDidCloseSilenceHash:self.event.fileSHA256];
} else {
[self.delegate windowDidCloseSilenceHash:nil];
}
}
- (IBAction)showCertInfo:(id)sender {
@@ -104,10 +135,6 @@
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
@@ -127,21 +154,27 @@
@"body {"
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
@" font-size: 13px;"
@" color: #AAA;"
@" color: #666;"
@" text-align: center;"
@"}"
@"</style></head><body>";
NSString *htmlFooter = @"</body></html>";
NSString *message;
if ([self.customMessage length] > 0) {
if (self.customMessage.length) {
message = self.customMessage;
} else {
message = [[SNTConfigurator configurator] defaultBlockMessage];
} else if (self.event.decision == SNTEventStateBlockUnknown) {
message = [[SNTConfigurator configurator] unknownBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because its trustworthiness cannot be determined.";
}
} else {
message = [[SNTConfigurator configurator] bannedBlockMessage];
if (!message) {
message = @"The following application has been blocked from executing<br />"
@"because it has been deemed malicious.";
}
}
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];

View File

@@ -14,22 +14,21 @@
#import "SNTNotificationManager.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
@interface SNTNotificationManager ()
///
/// The currently displayed notification
///
@property SNTMessageWindowController *currentWindowController;
///
/// The queue of pending notifications
///
@property(readonly) NSMutableArray *pendingNotifications;
@end
@implementation SNTNotificationManager
static NSString * const silencedNotificationsKey = @"SilencedNotifications";
- (instancetype)init {
self = [super init];
if (self) {
@@ -38,7 +37,9 @@
return self;
}
- (void)windowDidClose {
- (void)windowDidCloseSilenceHash:(NSString *)hash {
if (hash) [self updateSilenceDate:[NSDate date] forHash:hash];
[self.pendingNotifications removeObject:self.currentWindowController];
self.currentWindowController = nil;
@@ -50,7 +51,18 @@
}
}
#pragma mark SNTNotifierXPC protocol methods
- (void)updateSilenceDate:(NSDate *)date forHash:(NSString *)hash {
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *d = [[ud objectForKey:silencedNotificationsKey] mutableCopy];
if (date) {
d[hash] = date;
} else {
[d removeObjectForKey:hash];
}
[ud setObject:d forKey:silencedNotificationsKey];
}
#pragma mark SNTNotifierXPC protocol method
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
// See if this binary is already in the list of pending notifications.
@@ -58,39 +70,42 @@
[NSPredicate predicateWithFormat:@"event.fileSHA256==%@", event.fileSHA256];
if ([[self.pendingNotifications filteredArrayUsingPredicate:predicate] count]) return;
// See if this binary is silenced.
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSDate *silenceDate = [ud objectForKey:silencedNotificationsKey][event.fileSHA256];
if ([silenceDate isKindOfClass:[NSDate class]]) {
NSDate *oneDayAgo = [NSDate dateWithTimeIntervalSinceNow:-86400];
if ([silenceDate compare:[NSDate date]] == NSOrderedDescending) {
LOGI(@"Notification silence: date is in the future, ignoring");
[self updateSilenceDate:nil forHash:event.fileSHA256];
} else if ([silenceDate compare:oneDayAgo] == NSOrderedAscending) {
LOGI(@"Notification silence: date is more than one day ago, ignoring");
[self updateSilenceDate:nil forHash:event.fileSHA256];
} else {
LOGI(@"Notification silence: dropping notification for %@", event.fileSHA256);
return;
}
}
if (!event) {
NSLog(@"Error: Missing event object in message received from daemon!");
LOGI(@"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:)
withObject:@{ @"event": event, @"custommsg": message }
waitUntilDone:NO];
}
dispatch_async(dispatch_get_main_queue(), ^{
SNTMessageWindowController *pendingMsg =
[[SNTMessageWindowController alloc] initWithEvent:event andMessage:message];
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
- (void)postBlockNotificationMainThread:(NSDictionary *)dict {
SNTStoredEvent *event = dict[@"event"];
NSString *msg = dict[@"custommsg"];
// Create message window
SNTMessageWindowController *pendingMsg = [[SNTMessageWindowController alloc] initWithEvent:event
andMessage:msg];
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
// If a notification isn't currently being displayed, display the incoming one.
if (!self.currentWindowController) {
self.currentWindowController = pendingMsg;
[NSApp activateIgnoringOtherApps:YES];
// It's quite likely that we're currently on a background thread, and GUI code should always be
// on main thread. Open the window on the main thread so any code it runs is also.
[pendingMsg showWindow:nil];
}
// If a notification isn't currently being displayed, display the incoming one.
if (!self.currentWindowController) {
self.currentWindowController = pendingMsg;
[pendingMsg showWindow:nil];
}
});
}
@end

View File

@@ -12,61 +12,58 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#ifndef SANTA__COMMON__COMMONENUMS_H
#define SANTA__COMMON__COMMONENUMS_H
///
/// These enums are used in various places throughout the Santa client code.
/// The integer values are also stored in the database and so shouldn't be changed.
///
typedef enum {
RULETYPE_UNKNOWN,
typedef NS_ENUM(NSInteger, SNTRuleType) {
SNTRuleTypeUnknown,
RULETYPE_BINARY = 1,
RULETYPE_CERT = 2,
SNTRuleTypeBinary = 1,
SNTRuleTypeCertificate = 2,
};
RULETYPE_MAX
} santa_ruletype_t;
typedef NS_ENUM(NSInteger, SNTRuleState) {
SNTRuleStateUnknown,
typedef enum {
RULESTATE_UNKNOWN,
SNTRuleStateWhitelist = 1,
SNTRuleStateBlacklist = 2,
SNTRuleStateSilentBlacklist = 3,
SNTRuleStateRemove = 4,
};
RULESTATE_WHITELIST = 1,
RULESTATE_BLACKLIST = 2,
RULESTATE_SILENT_BLACKLIST = 3,
RULESTATE_REMOVE = 4,
typedef NS_ENUM(NSInteger, SNTClientMode) {
SNTClientModeUnknown,
RULESTATE_MAX
} santa_rulestate_t;
SNTClientModeMonitor = 1,
SNTClientModeLockdown = 2,
};
typedef enum {
CLIENTMODE_UNKNOWN,
typedef NS_ENUM(NSInteger, SNTEventState) {
SNTEventStateUnknown,
CLIENTMODE_MONITOR = 1,
CLIENTMODE_LOCKDOWN = 2,
SNTEventStateAllowUnknown = 1,
SNTEventStateAllowBinary = 2,
SNTEventStateAllowCertificate = 3,
SNTEventStateAllowScope = 4,
CLIENTMODE_MAX
} santa_clientmode_t;
SNTEventStateBlockUnknown = 5,
SNTEventStateBlockBinary = 6,
SNTEventStateBlockCertificate = 7,
SNTEventStateBlockScope = 8,
typedef enum {
EVENTSTATE_UNKNOWN,
SNTEventStateRelatedBinary = 9,
};
EVENTSTATE_ALLOW_UNKNOWN = 1,
EVENTSTATE_ALLOW_BINARY = 2,
EVENTSTATE_ALLOW_CERTIFICATE = 3,
EVENTSTATE_ALLOW_SCOPE = 4,
EVENTSTATE_BLOCK_UNKNOWN = 5,
EVENTSTATE_BLOCK_BINARY = 6,
EVENTSTATE_BLOCK_CERTIFICATE = 7,
EVENTSTATE_BLOCK_SCOPE = 8,
EVENTSTATE_MAX
} santa_eventstate_t;
typedef NS_ENUM(NSInteger, SNTRuleTableError) {
SNTRuleTableErrorEmptyRuleArray,
SNTRuleTableErrorInsertOrReplaceFailed,
SNTRuleTableErrorInvalidRule,
SNTRuleTableErrorMissingRequiredRule,
SNTRuleTableErrorRemoveFailed
};
static const char *kKextPath = "/Library/Extensions/santa-driver.kext";
static const char *kSantaDPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santad";
static const char *kSantaCtlPath = "/Library/Extensions/santa-driver.kext/Contents/MacOS/santactl";
#endif // SANTA__COMMON__COMMONENUMS_H

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
///
/// Singleton that provides an interface for managing configuration values on disk
@@ -21,14 +21,14 @@
@interface SNTConfigurator : NSObject
/// Default config file path
extern NSString * const kDefaultConfigFilePath;
extern NSString *const kDefaultConfigFilePath;
#pragma mark - Daemon Settings
///
/// The operating mode.
///
@property(nonatomic) santa_clientmode_t clientMode;
@property(nonatomic) SNTClientMode clientMode;
///
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
@@ -94,10 +94,17 @@ extern NSString * const kDefaultConfigFilePath;
@property(readonly, nonatomic) NSString *eventDetailText;
///
/// For any rule that doesn't have a custom message, this setting overrides the message
/// text that is display. If unset, a reasonable default is provided.
/// In lockdown mode this is the message shown to the user when an unknown binary
/// is blocked. If this message is not configured, a reasonable default is provided.
///
@property(readonly, nonatomic) NSString *defaultBlockMessage;
@property(readonly, nonatomic) NSString *unknownBlockMessage;
///
/// This is the message shown to the user when a binary is blocked because of a rule,
/// if that rule doesn't provide a custom message. If this is not configured, a reasonable
/// default is provided.
///
@property(readonly, nonatomic) NSString *bannedBlockMessage;
#pragma mark - Sync Settings

View File

@@ -33,38 +33,39 @@
@implementation SNTConfigurator
/// The hard-coded path to the config file
NSString * const kDefaultConfigFilePath = @"/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 kFileChangesRegexKey = @"FileChangesRegex";
static NSString * const kWhitelistRegexKey = @"WhitelistRegex";
static NSString * const kBlacklistRegexKey = @"BlacklistRegex";
static NSString * const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
static NSString * const kMoreInfoURLKey = @"MoreInfoURL";
static NSString * const kEventDetailURLKey = @"EventDetailURL";
static NSString * const kEventDetailTextKey = @"EventDetailText";
static NSString * const kDefaultBlockMessage = @"DefaultBlockMessage";
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
static NSString *const kEventDetailURLKey = @"EventDetailURL";
static NSString *const kEventDetailTextKey = @"EventDetailText";
static NSString *const kUnknownBlockMessage = @"UnknownBlockMessage";
static NSString *const kBannedBlockMessage = @"BannedBlockMessage";
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
static NSString * const kSyncLastSuccess = @"SyncLastSuccess";
static NSString * const kSyncCleanRequired = @"SyncCleanRequired";
static NSString * const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString * const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
static NSString * const kClientAuthCertificateIssuerKey = @"ClientAuthCertificateIssuerCN";
static NSString * const kServerAuthRootsDataKey = @"ServerAuthRootsData";
static NSString * const kServerAuthRootsFileKey = @"ServerAuthRootsFile";
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
static NSString *const kSyncLastSuccess = @"SyncLastSuccess";
static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString *const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
static NSString *const kClientAuthCertificateIssuerKey = @"ClientAuthCertificateIssuerCN";
static NSString *const kServerAuthRootsDataKey = @"ServerAuthRootsData";
static NSString *const kServerAuthRootsFileKey = @"ServerAuthRootsFile";
static NSString * const kMachineOwnerKey = @"MachineOwner";
static NSString * const kMachineIDKey = @"MachineID";
static NSString *const kMachineOwnerKey = @"MachineOwner";
static NSString *const kMachineIDKey = @"MachineID";
static NSString * const kMachineOwnerPlistFileKey = @"MachineOwnerPlist";
static NSString * const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
static NSString *const kMachineOwnerPlistFileKey = @"MachineOwnerPlist";
static NSString *const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
static NSString * const kMachineIDPlistFileKey = @"MachineIDPlist";
static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
- (instancetype)initWithFilePath:(NSString *)filePath {
self = [super init];
@@ -81,7 +82,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
static SNTConfigurator *sharedConfigurator = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedConfigurator = [[SNTConfigurator alloc] initWithFilePath:kDefaultConfigFilePath];
sharedConfigurator = [[SNTConfigurator alloc] initWithFilePath:kDefaultConfigFilePath];
});
return sharedConfigurator;
}
@@ -95,20 +96,23 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
#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;
- (SNTClientMode)clientMode {
NSInteger cm = [self.configData[kClientModeKey] longValue];
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown) {
return (SNTClientMode)cm;
} else {
self.configData[kClientModeKey] = @(CLIENTMODE_MONITOR);
return CLIENTMODE_MONITOR;
LOGE(@"Client mode was set to bad value: %ld. Resetting to MONITOR.", cm);
self.configData[kClientModeKey] = @(SNTClientModeMonitor);
return SNTClientModeMonitor;
}
}
- (void)setClientMode:(santa_clientmode_t)newMode {
if (newMode > CLIENTMODE_UNKNOWN && newMode < CLIENTMODE_MAX) {
- (void)setClientMode:(SNTClientMode)newMode {
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
self.configData[kClientModeKey] = @(newMode);
[self saveConfigToDisk];
} else {
LOGW(@"Ignoring request to change client mode to %ld", newMode);
}
}
@@ -192,12 +196,22 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self.configData[kEventDetailTextKey];
}
- (NSString *)defaultBlockMessage {
return self.configData[kDefaultBlockMessage];
- (NSString *)unknownBlockMessage {
return self.configData[kUnknownBlockMessage];
}
- (NSString *)bannedBlockMessage {
return self.configData[kBannedBlockMessage];
}
- (NSURL *)syncBaseURL {
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
NSString *urlStr = self.configData[kSyncBaseURLKey];
if (urlStr) {
NSURL *url = [NSURL URLWithString:urlStr];
if (!url) LOGW(@"SyncBaseURL is not a valid URL!");
return url;
}
return nil;
}
- (NSString *)syncClientAuthCertificateFile {
@@ -293,9 +307,9 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return;
}
NSDictionary *configData =
NSMutableDictionary *configData =
[NSPropertyListSerialization propertyListWithData:readData
options:NSPropertyListImmutable
options:NSPropertyListMutableContainers
format:NULL
error:&error];
if (error) {
@@ -303,28 +317,28 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return;
}
if (!self.configData) {
self.configData = [configData mutableCopy];
} else if (self.syncBaseURL) {
if (self.syncBaseURL) {
// Ensure no-one is trying to change protected keys behind our back.
NSMutableDictionary *configDataMutable = [configData mutableCopy];
BOOL changed = NO;
for (NSString *key in self.protectedKeys) {
if (geteuid() == 0 &&
((self.configData[key] && !configData[key]) ||
(!self.configData[key] && configData[key]) ||
(self.configData[key] && ![self.configData[key] isEqual:configData[key]]))) {
if (self.configData[key]) {
configDataMutable[key] = self.configData[key];
} else {
[configDataMutable removeObjectForKey:key];
if (geteuid() == 0) {
for (NSString *key in self.protectedKeys) {
if (((self.configData[key] && !configData[key]) ||
(!self.configData[key] && configData[key]) ||
(self.configData[key] && ![self.configData[key] isEqual:configData[key]]))) {
if (self.configData[key]) {
configData[key] = self.configData[key];
} else {
[configData removeObjectForKey:key];
}
changed = YES;
LOGI(@"Ignoring changed configuration key: %@", key);
}
changed = YES;
LOGI(@"Ignoring changed configuration key: %@", key);
}
}
self.configData = configDataMutable;
self.configData = configData;
if (changed) [self saveConfigToDisk];
} else {
self.configData = configData;
}
}
@@ -334,6 +348,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
/// Saves the current @c self.configData to disk.
///
- (void)saveConfigToDisk {
if (geteuid() != 0) return;
[self.configData writeToFile:self.configFilePath atomically:YES];
}

View File

@@ -41,6 +41,14 @@
///
- (NSString *)path;
///
/// Hash this file with SHA-1 and SHA-256 simultaneously.
///
/// @param sha1 If not NULL, will be filled with the SHA-1 of the file.
/// @param sha256 If not NULL, will be filled with the SHA-256 of the file.
///
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256;
///
/// @return SHA-1 hash of this binary.
///
@@ -51,12 +59,6 @@
///
- (NSString *)SHA256;
///
/// @return The type of Mach-O file, one of:
/// Dynamic Library, Kernel Extension, Fat Binary or Thin Binary.
///
- (NSString *)machoType;
///
/// @return The architectures included in this binary (e.g. x86_64, ppc).
///
@@ -92,6 +94,16 @@
///
- (BOOL)isScript;
///
/// @return YES if this file is an XAR archive.
///
- (BOOL)isXARArchive;
///
/// @return YES if this file is a disk image.
///
- (BOOL)isDMG;
///
/// @return YES if this file has a bad/missing __PAGEZERO .
///

View File

@@ -18,6 +18,11 @@
#include <mach-o/loader.h>
#include <mach-o/swap.h>
#include <pwd.h>
#include <sys/stat.h>
#include <sys/xattr.h>
#import <FMDB/FMDB.h>
// Simple class to hold the data of a mach_header and the offset within the file
// in which that header was found.
@@ -39,15 +44,15 @@
@interface SNTFileInfo ()
@property NSString *path;
@property NSData *fileData;
// Dictionary of MachHeaderWithOffset objects where the keys are the architecture strings
@property NSDictionary *machHeaders;
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property NSString *fileOwnerHomeDir;
// Cached properties
@property NSBundle *bundleRef;
@property NSDictionary *infoDict;
@property NSDictionary *quarantineDict;
@property NSDictionary *cachedHeaders;
@end
@implementation SNTFileInfo
@@ -57,23 +62,34 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
self = [super init];
if (self) {
_path = [self resolvePath:path];
NSBundle *bndl;
_path = [self resolvePath:path bundle:&bndl];
_bundleRef = bndl;
if (_path.length == 0) {
if (error) {
NSString *errStr = @"Unable to resolve empty path";
if (path) errStr = [@"Unable to resolve path: " stringByAppendingString:path];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:260
userInfo:@{ NSLocalizedDescriptionKey: errStr }];
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
_fileData = [NSData dataWithContentsOfFile:_path
options:NSDataReadingUncached
error:error];
if (_fileData.length == 0) return nil;
[self parseMachHeaders];
_fileHandle = [NSFileHandle fileHandleForReadingAtPath:_path];
struct stat fileStat;
fstat(_fileHandle.fileDescriptor, &fileStat);
_fileSize = fileStat.st_size;
if (_fileSize == 0) return nil;
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (pwd) {
_fileOwnerHomeDir = @(pwd->pw_dir);
}
}
}
return self;
@@ -83,44 +99,84 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return [self initWithPath:path error:NULL];
}
- (NSString *)SHA1 {
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(self.fileData.bytes, (unsigned int)self.fileData.length, sha1);
#pragma mark Hashing
// Convert the binary SHA into hex
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha1[i]];
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256 {
const int chunkSize = 4096;
CC_SHA1_CTX c1;
CC_SHA256_CTX c256;
if (sha1) CC_SHA1_Init(&c1);
if (sha256) CC_SHA256_Init(&c256);
for (uint64_t offset = 0; offset < self.fileSize; offset += chunkSize) {
@autoreleasepool {
int readSize = 0;
if (offset + chunkSize > self.fileSize) {
readSize = (int)(self.fileSize - offset);
} else {
readSize = chunkSize;
}
NSData *chunk = [self safeSubdataWithRange:NSMakeRange(offset, readSize)];
if (!chunk) {
if (sha1) CC_SHA1_Final(NULL, &c1);
if (sha256) CC_SHA256_Final(NULL, &c256);
return;
}
if (sha1) CC_SHA1_Update(&c1, chunk.bytes, readSize);
if (sha256) CC_SHA256_Update(&c256, chunk.bytes, readSize);
}
}
return buf;
if (sha1) {
unsigned char dgst[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(dgst, &c1);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i) {
[buf appendFormat:@"%02x", (unsigned char)dgst[i]];
}
*sha1 = [buf copy];
}
if (sha256) {
unsigned char dgst[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(dgst, &c256);
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)dgst[i]];
}
*sha256 = [buf copy];
}
}
- (NSString *)SHA1 {
NSString *sha1;
[self hashSHA1:&sha1 SHA256:NULL];
return sha1;
}
- (NSString *)SHA256 {
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha256);
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)sha256[i]];
}
return buf;
NSString *sha256;
[self hashSHA1:NULL SHA256:&sha256];
return sha256;
}
- (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";
return @"Unknown (not executable?)";
}
#pragma mark File Type Info
- (NSArray *)architectures {
return [self.machHeaders allKeys];
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_EXECUTE) return YES;
return NO;
}
- (BOOL)isDylib {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_DYLIB) return YES;
@@ -134,11 +190,11 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
- (BOOL)isMachO {
return ([self.machHeaders count] > 0);
return (self.machHeaders.count > 0);
}
- (BOOL)isFat {
return ([self.machHeaders count] > 1);
return (self.machHeaders.count > 1);
}
- (BOOL)isScript {
@@ -146,17 +202,23 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return (strncmp("#!", magic, 2) == 0);
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_OBJECT || mach_header->filetype == MH_EXECUTE) return YES;
return NO;
- (BOOL)isXARArchive {
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 4)] bytes];
return (strncmp("xar!", magic, 4) == 0);
}
- (BOOL)isDMG {
NSUInteger last512 = self.fileSize - 512;
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(last512, 4)] bytes];
return (magic && strncmp("koly", magic, 4) == 0);
}
#pragma mark Page Zero
- (BOOL)isMissingPageZero {
// This method only checks i386 arch because the kernel enforces this for other archs
// See bsd/kern/mach_loader.c, search for enforce_hard_pagezero.
MachHeaderWithOffset *x86Header = self.machHeaders[@"i386"];
MachHeaderWithOffset *x86Header = self.machHeaders[[self nameForCPUType:CPU_TYPE_X86]];
if (!x86Header) return NO;
struct mach_header *mh = (struct mach_header *)[x86Header.data bytes];
@@ -244,57 +306,61 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
- (NSString *)bundleIdentifier {
return [self.infoPlist objectForKey:@"CFBundleIdentifier"];
return [[self.infoPlist objectForKey:@"CFBundleIdentifier"] description];
}
- (NSString *)bundleName {
return [self.infoPlist objectForKey:@"CFBundleName"];
return [[self.infoPlist objectForKey:@"CFBundleName"] description];
}
- (NSString *)bundleVersion {
return [self.infoPlist objectForKey:@"CFBundleVersion"];
return [[self.infoPlist objectForKey:@"CFBundleVersion"] description];
}
- (NSString *)bundleShortVersionString {
return [self.infoPlist objectForKey:@"CFBundleShortVersionString"];
return [[self.infoPlist objectForKey:@"CFBundleShortVersionString"] description];
}
#pragma mark Quarantine Data
- (NSString *)quarantineDataURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineDataURLKey];
return [url absoluteString];
NSURL *dataURL = [self quarantineData][@"LSQuarantineDataURL"];
if (dataURL == (NSURL *)[NSNull null]) dataURL = nil;
return [dataURL absoluteString];
}
- (NSString *)quarantineRefererURL {
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineOriginURLKey];
return [url absoluteString];
NSURL *originURL = [self quarantineData][@"LSQuarantineOriginURL"];
if (originURL == (NSURL *)[NSNull null]) originURL = nil;
return [originURL absoluteString];
}
- (NSString *)quarantineAgentBundleID {
return [self quarantineData][(__bridge NSString *)kLSQuarantineAgentBundleIdentifierKey];
NSString *agentBundle = [self quarantineData][@"LSQuarantineAgentBundleIdentifier"];
if (agentBundle == (NSString *)[NSNull null]) agentBundle = nil;
return agentBundle;
}
- (NSDate *)quarantineTimestamp {
return [self quarantineData][(__bridge NSString *)kLSQuarantineTimeStampKey];
}
- (NSUInteger)fileSize {
return self.fileData.length;
NSDate *timeStamp = [self quarantineData][@"LSQuarantineTimeStamp"];
return timeStamp;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {
if (self.machHeaders) return;
- (NSDictionary *)machHeaders {
if (self.cachedHeaders) return self.cachedHeaders;
// Sanity check file length
if (self.fileData.length < sizeof(struct mach_header)) {
self.machHeaders = [NSDictionary dictionary];
return;
if (self.fileSize < sizeof(struct mach_header)) {
self.cachedHeaders = [NSDictionary dictionary];
return self.cachedHeaders;
}
NSMutableDictionary *machHeaders = [NSMutableDictionary dictionary];
NSData *machHeader = [self parseSingleMachHeader:self.fileData];
NSData *machHeader = [self parseSingleMachHeader:[self safeSubdataWithRange:NSMakeRange(0,
4096)]];
if (machHeader) {
struct mach_header *mh = (struct mach_header *)[machHeader bytes];
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader offset:0];
@@ -310,7 +376,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
NSMutableData *fatArchs = [[self safeSubdataWithRange:range] mutableCopy];
if (fatArchs) {
struct fat_arch *fat_arch = (struct fat_arch *)[fatArchs mutableBytes];
for (int i = 0; i < nfat_arch; i++) {
for (int i = 0; i < nfat_arch; ++i) {
int offset = OSSwapBigToHostInt32(fat_arch[i].offset);
int size = OSSwapBigToHostInt32(fat_arch[i].size);
int cputype = OSSwapBigToHostInt(fat_arch[i].cputype);
@@ -328,7 +394,8 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
}
self.machHeaders = [machHeaders copy];
self.cachedHeaders = [machHeaders copy];
return self.cachedHeaders;
}
- (NSData *)parseSingleMachHeader:(NSData *)inputData {
@@ -372,7 +439,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
offset += sz_header;
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
for (uint32_t i = 0; i < ncmds; ++i) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
@@ -387,11 +454,11 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; i++) {
for (uint32_t i = 0; i < nsects; ++i) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
if (sect && strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return nil;
NSDictionary *plist;
@@ -407,20 +474,23 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
///
/// Return one of the mach_header's in this file.
/// Return the first mach_header in this file.
///
- (struct mach_header *)firstMachHeader {
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
}
///
/// Wrap @c subdataWithRange: in a @@try/@@catch, returning nil on exception.
/// Useful for when the range is beyond the end of the file.
/// Extract a range of the file as an NSData, handling any exceptions.
/// Returns nil if the requested range is outside of the range of the file.
///
- (NSData *)safeSubdataWithRange:(NSRange)range {
@try {
return [self.fileData subdataWithRange:range];
if ((range.location + range.length) > self.fileSize) return nil;
[self.fileHandle seekToFileOffset:range.location];
NSData *d = [self.fileHandle readDataOfLength:range.length];
if (d.length != range.length) return nil;
return d;
}
@catch (NSException *e) {
return nil;
@@ -428,14 +498,70 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
}
///
/// Retrieve quarantine data for a file
/// Retrieve quarantine data for a file and caches the dictionary
/// This method attempts to handle fetching the quarantine data even if the running user
/// is not the one who downloaded the file.
///
- (NSDictionary *)quarantineData {
if (!self.quarantineDict && NSURLQuarantinePropertiesKey != NULL) {
if (!self.quarantineDict && self.fileOwnerHomeDir) {
self.quarantineDict = (NSDictionary *)[NSNull null];
NSURL *url = [NSURL fileURLWithPath:self.path];
NSDictionary *d = [url resourceValuesForKeys:@[ NSURLQuarantinePropertiesKey ] error:NULL];
self.quarantineDict = d[NSURLQuarantinePropertiesKey];
if (!self.quarantineDict) self.quarantineDict = (NSDictionary *)[NSNull null];
if (d[NSURLQuarantinePropertiesKey]) {
d = d[NSURLQuarantinePropertiesKey];
if (d[@"LSQuarantineIsOwnedByCurrentUser"]) {
self.quarantineDict = d;
} else if (d[@"LSQuarantineEventIdentifier"]) {
NSMutableDictionary *quarantineDict = [d mutableCopy];
// If self.path is on a quarantine disk image, LSQuarantineDiskImageURL will point to the
// disk image and self.fileOwnerHomeDir will be incorrect (probably root).
NSString *fileOwnerHomeDir = self.fileOwnerHomeDir;
if (d[@"LSQuarantineDiskImageURL"]) {
struct stat fileStat;
stat([d[@"LSQuarantineDiskImageURL"] fileSystemRepresentation], &fileStat);
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (pwd) {
fileOwnerHomeDir = @(pwd->pw_dir);
}
}
}
NSURL *dbPath = [NSURL fileURLWithPathComponents:@[
fileOwnerHomeDir,
@"Library",
@"Preferences",
@"com.apple.LaunchServices.QuarantineEventsV2"
]];
FMDatabase *db = [FMDatabase databaseWithPath:[dbPath absoluteString]];
db.logsErrors = NO;
if ([db open]) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM LSQuarantineEvent "
@"WHERE LSQuarantineEventIdentifier=?",
d[@"LSQuarantineEventIdentifier"]];
if ([rs next]) {
NSString *agentBundleID = [rs stringForColumn:@"LSQuarantineAgentBundleIdentifier"];
NSString *dataURLString = [rs stringForColumn:@"LSQuarantineDataURLString"];
NSString *originURLString = [rs stringForColumn:@"LSQuarantineOriginURLString"];
double timeStamp = [rs doubleForColumn:@"LSQuarantineTimeStamp"];
quarantineDict[@"LSQuarantineAgentBundleIdentifier"] = agentBundleID;
quarantineDict[@"LSQuarantineDataURL"] = [NSURL URLWithString:dataURLString];
quarantineDict[@"LSQuarantineOriginURL"] = [NSURL URLWithString:originURLString];
quarantineDict[@"LSQuarantineTimestamp"] =
[NSDate dateWithTimeIntervalSinceReferenceDate:timeStamp];
self.quarantineDict = quarantineDict;
}
[rs close];
[db close];
}
}
}
}
return (self.quarantineDict == (NSDictionary *)[NSNull null]) ? nil : self.quarantineDict;
}
@@ -459,7 +585,15 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return nil;
}
- (NSString *)resolvePath:(NSString *)path {
///
/// Resolves a given path:
/// + Follows symlinks
/// + Converts relative paths to absolute
/// + If path is a directory, checks to see if that directory is a bundle and if so
/// returns the path to that bundles CFBundleExecutable and stores a reference to the
/// bundle in the bundle out-param.
///
- (NSString *)resolvePath:(NSString *)path bundle:(NSBundle **)bundle {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
@@ -474,14 +608,9 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&directory]) {
return nil;
} else if (directory) {
NSString *infoPath = [path stringByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:infoPath];
if (d && d[@"CFBundleExecutable"]) {
path = [path stringByAppendingPathComponent:@"Contents/MacOS"];
return [path stringByAppendingPathComponent:d[@"CFBundleExecutable"]];
} else {
return nil;
}
NSBundle *bndl = [NSBundle bundleWithPath:path];
if (bundle) *bundle = bndl;
return [bndl executablePath];
} else {
return path;
}

View File

@@ -54,34 +54,34 @@
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();
}
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;
int fd;
if (weakSelf.monitoringSource) {
fd = (int)dispatch_source_get_handle(weakSelf.monitoringSource);
close(fd);
}
if (weakSelf.monitoringSource) {
fd = (int)dispatch_source_get_handle(weakSelf.monitoringSource);
close(fd);
}
const char *filePathCString = [weakSelf.filePath fileSystemRepresentation];
while ((fd = open(filePathCString, O_EVTONLY)) < 0) {
usleep(1000);
}
const char *filePathCString = [weakSelf.filePath fileSystemRepresentation];
while ((fd = open(filePathCString, 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.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();
weakSelf.eventHandler();
};
dispatch_async(queue, self.internalCancelHandler);
@@ -92,7 +92,9 @@
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_set_cancel_handler(self.monitoringSource, ^{
close(fd);
});
dispatch_source_cancel(self.monitoringSource);
self.monitoringSource = nil;

View File

@@ -41,33 +41,38 @@ enum SantaDriverMethods {
kSantaUserClientNMethods,
};
typedef enum {
QUEUETYPE_DECISION,
QUEUETYPE_LOG
} santa_queuetype_t;
// Enum defining actions that can be passed down the IODataQueue and in
// response methods.
typedef enum {
ACTION_UNSET = 0,
// CHECKBW
ACTION_REQUEST_CHECKBW = 10,
ACTION_RESPOND_CHECKBW_ALLOW = 11,
ACTION_RESPOND_CHECKBW_DENY = 12,
// REQUESTS
ACTION_REQUEST_SHUTDOWN = 10,
ACTION_REQUEST_BINARY = 11,
// RESPONSES
ACTION_RESPOND_ALLOW = 20,
ACTION_RESPOND_DENY = 21,
// NOTIFY
ACTION_NOTIFY_EXEC = 20,
ACTION_NOTIFY_WRITE = 21,
ACTION_NOTIFY_RENAME = 22,
ACTION_NOTIFY_LINK = 23,
ACTION_NOTIFY_EXCHANGE = 24,
ACTION_NOTIFY_DELETE = 25,
// SHUTDOWN
ACTION_REQUEST_SHUTDOWN = 90,
ACTION_NOTIFY_EXEC = 30,
ACTION_NOTIFY_WRITE = 31,
ACTION_NOTIFY_RENAME = 32,
ACTION_NOTIFY_LINK = 33,
ACTION_NOTIFY_EXCHANGE = 34,
ACTION_NOTIFY_DELETE = 35,
// ERROR
ACTION_ERROR = 99,
} santa_action_t;
#define CHECKBW_RESPONSE_VALID(x) \
(x == ACTION_RESPOND_CHECKBW_ALLOW || x == ACTION_RESPOND_CHECKBW_DENY)
#define RESPONSE_VALID(x) \
(x == ACTION_RESPOND_ALLOW || x == ACTION_RESPOND_DENY)
// Message struct that is sent down the IODataQueue.
typedef struct {

View File

@@ -22,7 +22,7 @@
#ifdef KERNEL
#ifdef DEBUG
#define LOGD(...) IOLog("D santa-driver: " __VA_ARGS__); IOLog("\n");
#define LOGD(...) IOLog("D santa-driver: " __VA_ARGS__); IOLog("\n")
#else // DEBUG
#define LOGD(...)
#endif // DEBUG

View File

@@ -15,6 +15,7 @@
#import "SNTLogging.h"
#import <asl.h>
#import <pthread.h>
#ifdef DEBUG
static LogLevel logLevel = LOG_LEVEL_DEBUG;
@@ -22,24 +23,29 @@ static LogLevel logLevel = LOG_LEVEL_DEBUG;
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
#endif
void syslogClientDestructor(void *arg) {
asl_close((aslclient)arg);
}
void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
static BOOL useSyslog = NO;
static const char *binaryName;
static dispatch_once_t pred;
static pthread_key_t syslogKey = 0;
dispatch_once(&pred, ^{
binaryName = [[[NSProcessInfo processInfo] processName] UTF8String];
binaryName = [[[NSProcessInfo processInfo] processName] UTF8String];
// If debug logging is enabled, the process must be restarted.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
logLevel = LOG_LEVEL_DEBUG;
}
// If debug logging is enabled, the process must be restarted.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--debug"]) {
logLevel = LOG_LEVEL_DEBUG;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
strcmp(binaryName, "santad") == 0) {
useSyslog = YES;
}
// If requested, redirect output to syslog.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"]) {
useSyslog = YES;
pthread_key_create(&syslogKey, syslogClientDestructor);
}
});
if (logLevel < level) return;
@@ -50,20 +56,35 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
va_end(args);
if (useSyslog) {
aslclient client = asl_open(NULL, "com.google.santa", 0);
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
aslclient client = (aslclient)pthread_getspecific(syslogKey);
if (client == NULL) {
client = asl_open(NULL, "com.google.santa", 0);
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
pthread_setspecific(syslogKey, client);
}
char *levelName;
int syslogLevel = ASL_LEVEL_DEBUG;
switch (level) {
case LOG_LEVEL_ERROR: levelName = "E"; syslogLevel = ASL_LEVEL_ERR; break;
case LOG_LEVEL_WARN: levelName = "W"; syslogLevel = ASL_LEVEL_WARNING; break;
case LOG_LEVEL_INFO: levelName = "I"; syslogLevel = ASL_LEVEL_INFO; break;
case LOG_LEVEL_DEBUG: levelName = "D"; syslogLevel = ASL_LEVEL_DEBUG; break;
case LOG_LEVEL_ERROR:
levelName = "E";
syslogLevel = ASL_LEVEL_ERR;
break;
case LOG_LEVEL_WARN:
levelName = "W";
syslogLevel = ASL_LEVEL_WARNING;
break;
case LOG_LEVEL_INFO:
levelName = "I";
syslogLevel = ASL_LEVEL_INFO;
break;
case LOG_LEVEL_DEBUG:
levelName = "D";
syslogLevel = ASL_LEVEL_DEBUG;
break;
}
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName, [s UTF8String]);
asl_close(client);
} else {
fprintf(destination, "%s\n", [s UTF8String]);
}

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
///
/// Represents a Rule.
@@ -27,12 +27,12 @@
///
/// The state of this rule
///
@property santa_rulestate_t state;
@property SNTRuleState state;
///
/// The type of object this rule is for (binary, certificate)
///
@property santa_ruletype_t type;
@property SNTRuleType type;
///
/// A custom message that will be displayed if this rule blocks a binary from executing
@@ -43,8 +43,8 @@
/// Designated initializer.
///
- (instancetype)initWithShasum:(NSString *)shasum
state:(santa_rulestate_t)state
type:(santa_ruletype_t)type
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg;
@end

View File

@@ -17,8 +17,8 @@
@implementation SNTRule
- (instancetype)initWithShasum:(NSString *)shasum
state:(santa_rulestate_t)state
type:(santa_ruletype_t)type
state:(SNTRuleState)state
type:(SNTRuleType)type
customMsg:(NSString *)customMsg {
self = [super init];
if (self) {
@@ -77,8 +77,8 @@
}
- (NSString *)description {
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %d, Type: %d",
self.shasum, self.state, self.type];
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %ld, Type: %ld",
self.shasum, self.state, self.type];
}
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
///
/// Represents an event stored in the database.
@@ -73,7 +73,7 @@
///
/// The decision santad returned.
///
@property santa_eventstate_t decision;
@property SNTEventState decision;
///
/// NSArray of logged in users when the decision was made.
@@ -108,5 +108,4 @@
@property NSDate *quarantineTimestamp;
@property NSString *quarantineAgentBundleID;
@end

View File

@@ -22,7 +22,7 @@
#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]
forKey:key]
+ (BOOL)supportsSecureCoding {
return YES;
@@ -52,8 +52,8 @@
ENCODE(self.quarantineDataURL, @"quarantineDataURL");
ENCODE(self.quarantineRefererURL, @"quarantineRefererURL");
ENCODE(self.quarantineTimestamp, @"quarantineTiemstamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
ENCODE(self.quarantineTimestamp, @"quarantineTimestamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
@@ -72,7 +72,7 @@
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
_decision = (santa_eventstate_t)[DECODE(NSNumber, @"decision") intValue];
_decision = (SNTEventState)[DECODE(NSNumber, @"decision") intValue];
_pid = DECODE(NSNumber, @"pid");
_ppid = DECODE(NSNumber, @"ppid");
_parentName = DECODE(NSString, @"parentName");

View File

@@ -0,0 +1,22 @@
/// Copyright 2016 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.
#define STRONGIFY(var) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
__strong __typeof(var) var = (Weak_##var); \
_Pragma("clang diagnostic pop")
#define WEAKIFY(var) \
__weak __typeof(var) Weak_##var = (var);

View File

@@ -12,108 +12,115 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// A validating XPC connection/listener which uses codesigning to validate that both ends of the
/// connection were signed by the same certificate chain.
///
/// Example server started by @c launchd where the @c launchd job has a @c MachServices key:
///
/// @code
/// SNTXPCConnection *conn = [[SNTXPCConnection alloc] initServerWithName:@"MyServer"];
/// conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
/// conn.exportedObject = myObject;
/// conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyClientProtocol)];
/// [conn resume];
/// @endcode
///
/// Example client, connecting to above server:
///
/// @code
/// SNTXPCConnection *conn = [[SNTXPCConnection alloc] initClientWithName:"MyServer"
/// withOptions:0];
/// conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyClientProtocol)];
/// conn.exportedObject = myObject;
/// conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
/// conn.invalidationHandler = ^{ NSLog(@"Connection invalidated") };
/// [conn resume];
/// @endcode
///
/// Either side can then send a message to the other with:
///
/// @code
/// [conn.remoteObjectProxy selectorInRemoteInterface];
/// @endcode
///
/// @note messages are always delivered on a background thread!
///
/**
A wrapper around NSXPCListener and NSXPCConnection to provide client multiplexing, signature
validation of connecting clients and forced connection establishment.
Example server started by @c launchd where the @c launchd job has a @c MachServices key:
@code
SNTXPCConnection *conn = [[SNTXPCConnection alloc] initServerWithName:@"MyServer"];
conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
conn.exportedObject = myObject;
[conn resume];
@endcode
Example client, connecting to above server:
@code
SNTXPCConnection *conn = [[SNTXPCConnection alloc] initClientWithName:"MyServer"
withOptions:0];
conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
conn.invalidationHandler = ^{ NSLog(@"Connection invalidated") };
[conn resume];
@endcode
The client can send a message to the server with:
@code
[conn.remoteObjectProxy selectorInRemoteInterface];
@endcode
One advantage of the way that SNTXPCConnection works over using NSXPCConnection directly is that
from the client-side once the resume method has finished, the connection is either valid or the
invalidation handler will be called. Ordinarily, the connection doesn't actually get made until
the first message is sent across it.
@note messages are always delivered on a background thread!
*/
@interface SNTXPCConnection : NSObject<NSXPCListenerDelegate>
typedef void (^SNTXPCInvalidationBlock)(void);
typedef void (^SNTXPCAcceptedBlock)(void);
typedef void (^SNTXPCRejectedBlock)(void);
/**
Initialize a new server with a given listener, provided by `[NSXPCListener anonymousListener]`.
*/
- (nullable instancetype)initServerWithListener:(nonnull NSXPCListener *)listener;
///
/// The interface the remote object should conform to.
///
@property(retain) NSXPCInterface *remoteInterface;
/**
Initializer for the 'server' side of the connection, started by launchd.
///
/// A proxy to the object at the other end of the connection.
///
/// @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, nonatomic) id remoteObjectProxy;
@param name MachService name, must match the MachServices key in the launchd.plist
*/
- (nullable instancetype)initServerWithName:(nonnull NSString *)name;
///
/// The interface this object exports.
///
@property(retain) NSXPCInterface *exportedInterface;
/**
Initializer a new client to a service exported by a LaunchDaemon.
///
/// The object that responds to messages from the other end.
///
@property(retain) id exportedObject;
@param name MachService name
@param privileged Use YES if the server is running as root.
*/
- (nullable instancetype)initClientWithName:(nonnull NSString *)name privileged:(BOOL)privileged;
///
/// A block to run when the connection is invalidated.
///
@property(copy) SNTXPCInvalidationBlock invalidationHandler;
/**
Initialize a new client with a listener endpoint sent from another process.
///
/// A block to run when the connection has been accepted.
///
@property(copy) SNTXPCAcceptedBlock acceptedHandler;
@param listener An NSXPCListenerEndpoint to connect to.
*/
- (nullable instancetype)initClientWithListener:(nonnull NSXPCListenerEndpoint *)listener;
///
/// A block to run when the connection has been rejected.
///
@property(copy) SNTXPCRejectedBlock rejectedHandler;
///
/// Initializer for the 'server' side of the connection, the binary that was started by launchd.
///
/// @param name MachService name
///
- (instancetype)initServerWithName:(NSString *)name;
///
/// Initializer for the 'client' side of the connection.
///
/// @param name MachService name
/// @param options Use NSXPCConnectionPrivileged if the server is running as root, otherwise use 0.
///
- (instancetype)initClientWithName:(NSString *)name options:(NSXPCConnectionOptions)options;
///
/// Call when the properties of the object have been set-up and you're ready for connections.
/// Blocks the executing thread for up to 5s while waiting for the verification to complete.
///
/**
Call when the properties of the object have been set-up and you're ready for connections.
For clients, this call can take up to 2s to complete for connection to finish establishing though
in basically all cases it will actually complete in a few milliseconds.
*/
- (void)resume;
///
/// Invalidate the connection. This must be done before the connection can be released.
///
/**
Invalidate the connection(s). This must be done before the object can be released.
*/
- (void)invalidate;
/**
The interface the remote object should conform to. (client)
*/
@property(retain, nullable) NSXPCInterface *remoteInterface;
/**
A proxy to the object at the other end of the connection. (client)
@note If the connection to the server failed, this will be nil, so you can safely send messages
and rely on the invalidationHandler for handling the failure.
*/
@property(readonly, nonatomic, nullable) id remoteObjectProxy;
/**
The interface this object exports. (server)
*/
@property(retain, nullable) NSXPCInterface *exportedInterface;
/**
The object that responds to messages from the other end. (server)
*/
@property(retain, nullable) id exportedObject;
/**
A block to run when a/the connection is accepted and fully established.
*/
@property(copy, nullable) void (^acceptedHandler)(void);
/**
A block to run when a/the connection is invalidated/interrupted/rejected.
*/
@property(copy, nullable) void (^invalidationHandler)(void);
@end

View File

@@ -16,52 +16,76 @@
#import "MOLCodesignChecker.h"
@protocol XPCConnectionValidityRequest
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
#import "SNTStrengthify.h"
/**
Protocol used during connection establishment, @see SNTXPCConnectionInterface
*/
@protocol SNTXPCConnectionProtocol
- (void)connectWithReply:(void (^)())reply;
@end
/**
Recipient object used during connection establishment. Each incoming connection
has one of these objects created which accept the message in the protocol
and call the block provided during creation before replying.
This allows the server to reset the connection's exporteed interface and
object to the correct values after the client has sent the establishment message.
*/
@interface SNTXPCConnectionInterface : NSObject<SNTXPCConnectionProtocol>
@property(strong) void (^block)(void);
@end
@implementation SNTXPCConnectionInterface
- (void)connectWithReply:(void (^)())reply {
if (self.block) self.block();
reply();
}
@end
@interface SNTXPCConnection ()
///
/// The XPC listener (used on server-side only).
///
/// The XPC listener (server only).
@property NSXPCListener *listenerObject;
/// Array of accepted connections (server only).
@property NSMutableArray *acceptedConnections;
///
/// The current connection object.
///
/// The current connection object (client only).
@property NSXPCConnection *currentConnection;
///
/// The remote interface to use while the connection hasn't been validated.
///
@property NSXPCInterface *validatorInterface;
@end
@implementation SNTXPCConnection
#pragma mark Initializers
- (instancetype)initServerWithName:(NSString *)name {
- (instancetype)initServerWithListener:(NSXPCListener *)listener {
self = [super init];
if (self) {
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
_listenerObject = [[NSXPCListener alloc] initWithMachServiceName:name];
if (!_validatorInterface || !_listenerObject) return nil;
_listenerObject = listener;
_acceptedConnections = [NSMutableArray array];
}
return self;
}
- (instancetype)initClientWithName:(NSString *)name options:(NSXPCConnectionOptions)options {
- (instancetype)initServerWithName:(NSString *)name {
return [self initServerWithListener:[[NSXPCListener alloc] initWithMachServiceName:name]];
}
- (instancetype)initClientWithListener:(NSXPCListenerEndpoint *)listener {
self = [super init];
if (self) {
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name options:options];
_currentConnection = [[NSXPCConnection alloc] initWithListenerEndpoint:listener];
if (!_currentConnection) return nil;
}
return self;
}
if (!_validatorInterface || !_currentConnection) return nil;
- (instancetype)initClientWithName:(NSString *)name privileged:(BOOL)privileged {
self = [super init];
if (self) {
NSXPCConnectionOptions options = (privileged ? NSXPCConnectionPrivileged : 0);
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name options:options];
if (!_currentConnection) return nil;
}
return self;
}
@@ -75,144 +99,100 @@
- (void)resume {
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 = self.currentConnection;
connection.remoteObjectInterface = self.validatorInterface;
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
self.currentConnection = nil;
};
connection.interruptionHandler = ^{ [self.currentConnection invalidate]; };
[connection resume];
WEAKIFY(self);
// Set-up the connection with the remote interface set to the validation interface,
// send a message to the listener to finish establishing the connection
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[connection remoteObjectProxy] isConnectionValidWithBlock:^void(BOOL response) {
pid_t pid = self.currentConnection.processIdentifier;
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if (response && [otherCS signingInformationMatches:selfCS]) {
[self.currentConnection suspend];
self.currentConnection.remoteObjectInterface = self.remoteInterface;
self.currentConnection.exportedInterface = self.exportedInterface;
self.currentConnection.exportedObject = self.exportedObject;
[self invokeAcceptedHandler];
[self.currentConnection resume];
dispatch_semaphore_signal(sema);
} else {
[self invokeRejectedHandler];
[self.currentConnection invalidate];
self.currentConnection = nil;
dispatch_semaphore_signal(sema);
}
self.currentConnection.remoteObjectInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
self.currentConnection.interruptionHandler = self.invalidationHandler;
self.currentConnection.invalidationHandler = self.invalidationHandler;
[self.currentConnection resume];
[[self.currentConnection remoteObjectProxy] connectWithReply:^{
STRONGIFY(self);
// The connection is now established
[self.currentConnection suspend];
self.currentConnection.remoteObjectInterface = self.remoteInterface;
[self.currentConnection resume];
dispatch_semaphore_signal(sema);
if (self.acceptedHandler) self.acceptedHandler();
}];
// Wait for validation to complete, at most 5s
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self invalidate];
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
// Connection was not established in a reasonable time, invalidate.
self.currentConnection.remoteObjectInterface = nil; // ensure clients don't try to use it.
[self.currentConnection invalidate];
}
}
}
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection {
// Reject connection if a connection already exists. As the invalidation/interruption handlers
// both cause the currentConnection to be nil'd out, this should be OK.
if (self.currentConnection) return NO;
pid_t pid = connection.processIdentifier;
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if (![otherCS signingInformationMatches:[[MOLCodesignChecker alloc] initWithSelf]]) {
return NO;
}
connection.exportedObject = self;
connection.exportedInterface = self.validatorInterface;
// The client passed the code signature check, now we need to resume the listener and
// return YES so that the client can send the connectWithReply message. Once the client does
// we reset the connection's exportedInterface and exportedObject.
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
self.currentConnection = nil;
SNTXPCConnectionInterface *ci = [[SNTXPCConnectionInterface alloc] init];
WEAKIFY(self);
WEAKIFY(connection);
ci.block = ^{
STRONGIFY(self)
STRONGIFY(connection);
[connection suspend];
[self.acceptedConnections addObject:connection];
WEAKIFY(connection);
connection.invalidationHandler = connection.interruptionHandler = ^{
STRONGIFY(connection);
[self.acceptedConnections removeObject:connection];
if (self.invalidationHandler) self.invalidationHandler();
};
connection.exportedInterface = self.exportedInterface;
connection.exportedObject = self.exportedObject;
[connection resume];
// The connection is now established.
if (self.acceptedHandler) self.acceptedHandler();
};
connection.interruptionHandler = ^{
// Invalidate the connection, causing the handler above to run
[self.currentConnection invalidate];
};
// At this point the client is connected and can send messages but the only message it can send
// is isConnectionValidWithBlock: and we won't send anything to it until it has.
self.currentConnection = connection;
connection.exportedInterface =
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
connection.exportedObject = ci;
[connection resume];
return YES;
}
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block {
pid_t pid = self.currentConnection.processIdentifier;
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
if ([otherCS signingInformationMatches:selfCS]) {
[self.currentConnection suspend];
self.currentConnection.remoteObjectInterface = self.remoteInterface;
self.currentConnection.exportedInterface = self.exportedInterface;
self.currentConnection.exportedObject = self.exportedObject;
[self.currentConnection resume];
[self invokeAcceptedHandler];
// Let remote end know that we accepted. In acception this must come last otherwise
// the remote end might start sending messages before the interface is fully set-up.
block(YES);
} else {
// Let remote end know that we rejected. In rejection this must come first otherwise
// the connection is invalidated before the client ever realizes.
block(NO);
[self invokeRejectedHandler];
[self.currentConnection invalidate];
self.currentConnection = nil;
}
}
- (id)remoteObjectProxy {
if (self.currentConnection && self.currentConnection.remoteObjectInterface) {
if (self.currentConnection.remoteObjectInterface) {
return [self.currentConnection remoteObjectProxyWithErrorHandler:^(NSError *error) {
[self.currentConnection invalidate];
[self.currentConnection invalidate];
}];
}
return nil;
}
- (void)invokeAcceptedHandler {
if (self.acceptedHandler) {
self.acceptedHandler();
}
}
- (void)invokeRejectedHandler {
if (self.rejectedHandler) {
self.rejectedHandler();
}
}
- (void)invokeInvalidationHandler {
if (self.invalidationHandler) {
self.invalidationHandler();
}
}
#pragma mark Connection tear-down
- (void)invalidate {
if (self.currentConnection) {
[self.currentConnection invalidate];
self.currentConnection = nil;
} else if (self.listenerObject) {
for (NSXPCConnection *conn in self.acceptedConnections) {
[conn invalidate];
}
[self.acceptedConnections removeAllObjects];
[self.listenerObject invalidate];
}
}

View File

@@ -12,10 +12,11 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
@class SNTRule;
@class SNTStoredEvent;
@class SNTXPCConnection;
///
/// Protocol implemented by santad and utilized by santactl
@@ -32,10 +33,9 @@
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(NSError *error))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply;
@@ -45,16 +45,20 @@
///
/// Config ops
///
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply;
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)clientMode:(void (^)(SNTClientMode))reply;
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
///
/// GUI Ops
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
@end
@interface SNTXPCControlInterface : NSObject
@@ -70,4 +74,10 @@
///
+ (NSXPCInterface *)controlInterface;
///
/// Retrieve a pre-configured SNTXPCConnection for communicating with santad.
/// Connections just needs any handlers set and then can be resumed and used.
///
+ (SNTXPCConnection *)configuredConnection;
@end

View File

@@ -16,6 +16,7 @@
#import "SNTRule.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
@implementation SNTXPCControlInterface
@@ -39,4 +40,11 @@
return r;
}
+ (SNTXPCConnection *)configuredConnection {
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithName:[self serviceId]
privileged:YES];
c.remoteInterface = [self controlInterface];
return c;
}
@end

View File

@@ -12,19 +12,15 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// Protocol implemented by SantaNotifier and utilized by santad
@class SNTStoredEvent;
/// Protocol implemented by SantaGUI and utilized by santad
@protocol SNTNotifierXPC
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message;
@end
@interface SNTXPCNotifierInterface : NSObject
///
/// @return the MachService ID for this service.
///
+ (NSString *)serviceId;
///
/// @return an initialized NSXPCInterface for the SNTNotifierXPC protocol.
/// Ensures any methods that accept custom classes as arguments are set-up before returning

View File

@@ -16,13 +16,8 @@
@implementation SNTXPCNotifierInterface
+ (NSString *)serviceId {
return @"SantaXPCNotifications";
}
+ (NSXPCInterface *)notifierInterface {
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
return r;
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
}
@end

View File

@@ -12,19 +12,19 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SantaMessage.h"
#include "SantaCachedDecision.h"
OSDefineMetaClassAndStructors(SantaMessage, OSObject);
OSDefineMetaClassAndStructors(SantaCachedDecision, OSObject);
uint64_t SantaMessage::getMicrosecs() const {
uint64_t SantaCachedDecision::getMicrosecs() const {
return microsecs_;
}
santa_action_t SantaMessage::getAction() const {
santa_action_t SantaCachedDecision::getAction() const {
return action_;
}
void SantaMessage::setAction(
void SantaCachedDecision::setAction(
const santa_action_t action, const uint64_t microsecs) {
action_ = action;
microsecs_ = microsecs;

View File

@@ -12,19 +12,19 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#ifndef SANTA__SANTA_DRIVER__SANTAMESSAGE_H
#define SANTA__SANTA_DRIVER__SANTAMESSAGE_H
#ifndef SANTA__SANTA_DRIVER__SANTACACHEDDECISION_H
#define SANTA__SANTA_DRIVER__SANTACACHEDDECISION_H
#include <libkern/c++/OSObject.h>
#include "SNTKernelCommon.h"
///
/// An OSObject wrapper around a @c santa_action_t and a time.
/// An OSObject subclass to store a @c santa_action_t and a timestamp.
/// Only OSObject subclasses can be inserted into an OSDictionary.
///
class SantaMessage : public OSObject {
OSDeclareDefaultStructors(SantaMessage)
class SantaCachedDecision : public OSObject {
OSDeclareDefaultStructors(SantaCachedDecision)
public:
// Returns the time the action was last set.
@@ -41,4 +41,4 @@ class SantaMessage : public OSObject {
uint64_t microsecs_;
};
#endif // SANTA__SANTA_DRIVER__SANTAMESSAGE_H
#endif // SANTA__SANTA_DRIVER__SANTACACHEDDECISIONWRAPPER_H

View File

@@ -27,14 +27,21 @@ bool SantaDecisionManager::init() {
sdm_lock_attr_ = lck_attr_alloc_init();
dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
decision_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
log_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
cached_decisions_lock_ = lck_rw_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
vnode_pid_map_lock_ = lck_rw_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
cached_decisions_ = OSDictionary::withCapacity(1000);
vnode_pid_map_ = OSDictionary::withCapacity(1000);
dataqueue_ = IOSharedDataQueue::withEntries(kMaxQueueEvents,
sizeof(santa_message_t));
if (!dataqueue_) return kIOReturnNoMemory;
decision_dataqueue_ = IOSharedDataQueue::withEntries(kMaxDecisionQueueEvents,
sizeof(santa_message_t));
if (!decision_dataqueue_) return kIOReturnNoMemory;
log_dataqueue_ = IOSharedDataQueue::withEntries(kMaxLogQueueEvents,
sizeof(santa_message_t));
if (!log_dataqueue_) return kIOReturnNoMemory;
client_pid_ = 0;
@@ -42,17 +49,24 @@ bool SantaDecisionManager::init() {
}
void SantaDecisionManager::free() {
OSSafeReleaseNULL(dataqueue_);
OSSafeReleaseNULL(cached_decisions_);
if (cached_decisions_lock_) {
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
cached_decisions_lock_ = nullptr;
}
if (dataqueue_lock_) {
lck_mtx_free(dataqueue_lock_, sdm_lock_grp_);
dataqueue_lock_ = nullptr;
if (vnode_pid_map_lock_) {
lck_rw_free(vnode_pid_map_lock_, sdm_lock_grp_);
vnode_pid_map_lock_ = nullptr;
}
if (decision_dataqueue_lock_) {
lck_mtx_free(decision_dataqueue_lock_, sdm_lock_grp_);
decision_dataqueue_lock_ = nullptr;
}
if (log_dataqueue_lock_) {
lck_mtx_free(log_dataqueue_lock_, sdm_lock_grp_);
log_dataqueue_lock_ = nullptr;
}
if (sdm_lock_attr_) {
@@ -70,25 +84,27 @@ void SantaDecisionManager::free() {
sdm_lock_grp_attr_ = nullptr;
}
OSSafeReleaseNULL(decision_dataqueue_);
OSSafeReleaseNULL(log_dataqueue_);
OSSafeReleaseNULL(cached_decisions_);
OSSafeReleaseNULL(vnode_pid_map_);
super::free();
}
#pragma mark Client Management
void SantaDecisionManager::ConnectClient(mach_port_t port, pid_t pid) {
void SantaDecisionManager::ConnectClient(pid_t pid) {
if (!pid) return;
// Any decisions made while the daemon wasn't
// connected should be cleared
ClearCache();
lck_mtx_lock(dataqueue_lock_);
dataqueue_->setNotificationPort(port);
lck_mtx_unlock(dataqueue_lock_);
client_pid_ = pid;
failed_queue_requests_ = 0;
failed_decision_queue_requests_ = 0;
failed_log_queue_requests_ = 0;
}
void SantaDecisionManager::DisconnectClient(bool itDied) {
@@ -97,32 +113,56 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
// Ask santad to shutdown, in case it's running.
if (!itDied) {
santa_message_t *message = new santa_message_t;
auto message = new santa_message_t;
message->action = ACTION_REQUEST_SHUTDOWN;
PostToQueue(message);
PostToDecisionQueue(message);
delete message;
dataqueue_->setNotificationPort(nullptr);
decision_dataqueue_->setNotificationPort(nullptr);
} else {
// If the client died, reset the data queue so when it reconnects
// If the client died, reset the data queues so when it reconnects
// it doesn't get swamped straight away.
lck_mtx_lock(dataqueue_lock_);
dataqueue_->release();
dataqueue_ = IOSharedDataQueue::withEntries(kMaxQueueEvents,
sizeof(santa_message_t));
lck_mtx_unlock(dataqueue_lock_);
}
lck_mtx_lock(decision_dataqueue_lock_);
decision_dataqueue_->release();
decision_dataqueue_ = IOSharedDataQueue::withEntries(
kMaxDecisionQueueEvents, sizeof(santa_message_t));
lck_mtx_unlock(decision_dataqueue_lock_);
lck_mtx_lock(log_dataqueue_lock_);
log_dataqueue_->release();
log_dataqueue_ = IOSharedDataQueue::withEntries(
kMaxLogQueueEvents, sizeof(santa_message_t));
lck_mtx_unlock(log_dataqueue_lock_);
}
}
bool SantaDecisionManager::ClientConnected() {
proc_t p = proc_find(client_pid_);
bool is_exiting = proc_exiting(p);
proc_rele(p);
bool SantaDecisionManager::ClientConnected() const {
auto p = proc_find(client_pid_);
auto is_exiting = false;
if (p) {
is_exiting = proc_exiting(p);
proc_rele(p);
}
return (client_pid_ > 0 && !is_exiting);
}
IOMemoryDescriptor *SantaDecisionManager::GetMemoryDescriptor() {
return dataqueue_->getMemoryDescriptor();
void SantaDecisionManager::SetDecisionPort(mach_port_t port) {
lck_mtx_lock(decision_dataqueue_lock_);
decision_dataqueue_->setNotificationPort(port);
lck_mtx_unlock(decision_dataqueue_lock_);
}
void SantaDecisionManager::SetLogPort(mach_port_t port) {
lck_mtx_lock(log_dataqueue_lock_);
log_dataqueue_->setNotificationPort(port);
lck_mtx_unlock(log_dataqueue_lock_);
}
IOMemoryDescriptor *SantaDecisionManager::GetDecisionMemoryDescriptor() const {
return decision_dataqueue_->getMemoryDescriptor();
}
IOMemoryDescriptor *SantaDecisionManager::GetLogMemoryDescriptor() const {
return log_dataqueue_->getMemoryDescriptor();
}
#pragma mark Listener Control
@@ -177,17 +217,17 @@ void SantaDecisionManager::AddToCache(
ClearCache();
}
if (decision == ACTION_REQUEST_CHECKBW) {
SantaMessage *pending = new SantaMessage();
pending->setAction(ACTION_REQUEST_CHECKBW, 0);
if (decision == ACTION_REQUEST_BINARY) {
auto pending = new SantaCachedDecision();
pending->setAction(ACTION_REQUEST_BINARY, 0);
lck_rw_lock_exclusive(cached_decisions_lock_);
cached_decisions_->setObject(identifier, pending);
lck_rw_unlock_exclusive(cached_decisions_lock_);
pending->release(); // it was retained when added to the dictionary
} else {
lck_rw_lock_exclusive(cached_decisions_lock_);
SantaMessage *pending =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
auto pending = OSDynamicCast(
SantaCachedDecision, cached_decisions_->getObject(identifier));
if (pending) {
pending->setAction(decision, microsecs);
}
@@ -197,7 +237,7 @@ void SantaDecisionManager::AddToCache(
void SantaDecisionManager::CacheCheck(const char *identifier) {
lck_rw_lock_shared(cached_decisions_lock_);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
auto shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
if (shouldInvalidate) {
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
// shared_to_exclusive will return false if a previous reader upgraded
@@ -212,7 +252,7 @@ void SantaDecisionManager::CacheCheck(const char *identifier) {
}
}
uint64_t SantaDecisionManager::CacheCount() {
uint64_t SantaDecisionManager::CacheCount() const {
return cached_decisions_->getCount();
}
@@ -225,28 +265,28 @@ void SantaDecisionManager::ClearCache() {
#pragma mark Decision Fetching
santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
santa_action_t result = ACTION_UNSET;
auto result = ACTION_UNSET;
uint64_t decision_time = 0;
lck_rw_lock_shared(cached_decisions_lock_);
SantaMessage *cached_decision =
OSDynamicCast(SantaMessage, cached_decisions_->getObject(identifier));
SantaCachedDecision *cached_decision = OSDynamicCast(
SantaCachedDecision, cached_decisions_->getObject(identifier));
if (cached_decision) {
result = cached_decision->getAction();
decision_time = cached_decision->getMicrosecs();
}
lck_rw_unlock_shared(cached_decisions_lock_);
if (CHECKBW_RESPONSE_VALID(result)) {
uint64_t diff_time = GetCurrentUptime();
if (RESPONSE_VALID(result)) {
auto diff_time = GetCurrentUptime();
if (result == ACTION_RESPOND_CHECKBW_ALLOW) {
if (result == ACTION_RESPOND_ALLOW) {
if ((kMaxAllowCacheTimeMilliseconds * 1000) > diff_time) {
diff_time = 0;
} else {
diff_time -= (kMaxAllowCacheTimeMilliseconds * 1000);
}
} else if (result == ACTION_RESPOND_CHECKBW_DENY) {
} else if (result == ACTION_RESPOND_DENY) {
if ((kMaxDenyCacheTimeMilliseconds * 1000) > diff_time) {
diff_time = 0;
} else {
@@ -267,19 +307,19 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
santa_action_t SantaDecisionManager::GetFromDaemon(
santa_message_t *message, const char *vnode_id_str) {
santa_action_t return_action = ACTION_UNSET;
auto return_action = ACTION_UNSET;
// Wait for the daemon to respond or die.
do {
// Add pending request to cache.
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
AddToCache(vnode_id_str, ACTION_REQUEST_BINARY, 0);
// Send request to daemon...
if (!PostToQueue(message)) {
OSIncrementAtomic(&failed_queue_requests_);
if (failed_queue_requests_ > kMaxQueueFailures) {
if (!PostToDecisionQueue(message)) {
OSIncrementAtomic(&failed_decision_queue_requests_);
if (failed_decision_queue_requests_ > kMaxDecisionQueueFailures) {
LOGE("Failed to queue more than %d requests, killing daemon",
kMaxQueueFailures);
kMaxDecisionQueueFailures);
proc_signal(client_pid_, SIGKILL);
client_pid_ = 0;
}
@@ -291,11 +331,11 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
do {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
} while (return_action == ACTION_REQUEST_CHECKBW && ClientConnected());
} while (!CHECKBW_RESPONSE_VALID(return_action) && ClientConnected());
} while (return_action == ACTION_REQUEST_BINARY && ClientConnected());
} while (!RESPONSE_VALID(return_action) && ClientConnected());
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
if (!RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back. Executable path: %s", message->path);
CacheCheck(vnode_id_str);
@@ -310,14 +350,13 @@ santa_action_t SantaDecisionManager::FetchDecision(
const vnode_t vp,
const uint64_t vnode_id,
const char *vnode_id_str) {
santa_action_t return_action = ACTION_UNSET;
if (!ClientConnected()) return ACTION_RESPOND_CHECKBW_ALLOW;
if (!ClientConnected()) return ACTION_RESPOND_ALLOW;
// Check to see if item is in cache
return_action = GetFromCache(vnode_id_str);
auto return_action = GetFromCache(vnode_id_str);
// If item was in cache return it.
if CHECKBW_RESPONSE_VALID(return_action) return return_action;
if (RESPONSE_VALID(return_action)) return return_action;
// Get path
char path[MAXPATHLEN];
@@ -326,55 +365,41 @@ santa_action_t SantaDecisionManager::FetchDecision(
path[0] = '\0';
}
santa_message_t *message = NewMessage();
auto message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
message->action = ACTION_REQUEST_CHECKBW;
message->action = ACTION_REQUEST_BINARY;
message->vnode_id = vnode_id;
proc_name(message->ppid, message->pname, sizeof(message->pname));
santa_action_t ret = GetFromDaemon(message, vnode_id_str);
return_action = GetFromDaemon(message, vnode_id_str);
delete message;
return ret;
return return_action;
}
#pragma mark Misc
santa_message_t* SantaDecisionManager::NewMessage() {
santa_message_t *message = new santa_message_t;
message->uid = kauth_getuid();
message->gid = kauth_getgid();
message->pid = proc_selfpid();
message->ppid = proc_selfppid();
return message;
}
bool SantaDecisionManager::PostToQueue(santa_message_t *message) {
bool kr = false;
lck_mtx_lock(dataqueue_lock_);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
if (!kr) {
// If enqueue failed, pop an item off the queue and try again.
uint32_t dataSize = sizeof(santa_message_t);
dataqueue_->dequeue(0, &dataSize);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
}
lck_mtx_unlock(dataqueue_lock_);
bool SantaDecisionManager::PostToDecisionQueue(santa_message_t *message) {
lck_mtx_lock(decision_dataqueue_lock_);
auto kr = decision_dataqueue_->enqueue(message, sizeof(santa_message_t));
lck_mtx_unlock(decision_dataqueue_lock_);
return kr;
}
uint64_t SantaDecisionManager::GetVnodeIDForVnode(
const vfs_context_t ctx, const vnode_t vp) {
struct vnode_attr vap;
VATTR_INIT(&vap);
VATTR_WANTED(&vap, va_fileid);
vnode_getattr(vp, &vap, ctx);
return vap.va_fileid;
}
uint64_t SantaDecisionManager::GetCurrentUptime() {
clock_sec_t sec;
clock_usec_t usec;
clock_get_system_microtime(&sec, &usec);
return (uint64_t)((sec * 1000000) + usec);
bool SantaDecisionManager::PostToLogQueue(santa_message_t *message) {
lck_mtx_lock(log_dataqueue_lock_);
auto kr = log_dataqueue_->enqueue(message, sizeof(santa_message_t));
if (!kr) {
if (OSCompareAndSwap(0, 1, &failed_log_queue_requests_)) {
LOGW("Dropping log queue messages");
}
// If enqueue failed, pop an item off the queue and try again.
uint32_t dataSize = 0;
log_dataqueue_->dequeue(0, &dataSize);
kr = log_dataqueue_->enqueue(message, sizeof(santa_message_t));
} else {
OSCompareAndSwap(1, 0, &failed_log_queue_requests_);
}
lck_mtx_unlock(log_dataqueue_lock_);
return kr;
}
#pragma mark Invocation Tracking & PID comparison
@@ -387,6 +412,8 @@ void SantaDecisionManager::DecrementListenerInvocations() {
OSDecrementAtomic(&listener_invocations_);
}
#pragma mark Callbacks
int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
const vfs_context_t ctx,
const vnode_t vp,
@@ -395,25 +422,36 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
// Get ID for the vnode and convert it to a string.
uint64_t vnode_id = GetVnodeIDForVnode(ctx, vp);
auto vnode_id = GetVnodeIDForVnode(ctx, vp);
char vnode_str[MAX_VNODE_ID_STR];
snprintf(vnode_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
// Fetch decision
santa_action_t returnedAction = FetchDecision(cred, vp, vnode_id, vnode_str);
auto returnedAction = FetchDecision(cred, vp, vnode_id, vnode_str);
// 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(vp)) {
CacheCheck(vnode_str);
returnedAction = ACTION_RESPOND_CHECKBW_DENY;
returnedAction = ACTION_RESPOND_DENY;
}
switch (returnedAction) {
case ACTION_RESPOND_CHECKBW_ALLOW:
case ACTION_RESPOND_ALLOW: {
auto proc = vfs_context_proc(ctx);
if (proc) {
auto pidWrapper = new SantaPIDAndPPID;
pidWrapper->pid = proc_pid(proc);
pidWrapper->ppid = proc_ppid(proc);
lck_rw_lock_exclusive(vnode_pid_map_lock_);
vnode_pid_map_->setObject(vnode_str, pidWrapper);
lck_rw_unlock_exclusive(vnode_pid_map_lock_);
pidWrapper->release();
}
return KAUTH_RESULT_ALLOW;
case ACTION_RESPOND_CHECKBW_DENY:
}
case ACTION_RESPOND_DENY:
*errno = EPERM;
return KAUTH_RESULT_DENY;
default:
@@ -428,8 +466,8 @@ void SantaDecisionManager::FileOpCallback(
const kauth_action_t action, const vnode_t vp,
const char *path, const char *new_path) {
if (vp) {
vfs_context_t context = vfs_context_create(nullptr);
uint64_t vnode_id = GetVnodeIDForVnode(context, vp);
auto context = vfs_context_create(nullptr);
auto vnode_id = GetVnodeIDForVnode(context, vp);
vfs_context_rele(context);
if (action == KAUTH_FILEOP_CLOSE) {
@@ -437,50 +475,68 @@ void SantaDecisionManager::FileOpCallback(
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
CacheCheck(vnode_id_str);
} else if (action == KAUTH_FILEOP_EXEC) {
santa_message_t *message = NewMessage();
auto message = NewMessage();
message->vnode_id = vnode_id;
message->action = ACTION_NOTIFY_EXEC;
strlcpy(message->path, path, sizeof(message->path));
PostToQueue(message);
char vnode_str[MAX_VNODE_ID_STR];
snprintf(vnode_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
lck_rw_lock_shared(vnode_pid_map_lock_);
auto pidWrapper = OSDynamicCast(
SantaPIDAndPPID, vnode_pid_map_->getObject(vnode_str));
if (pidWrapper) {
message->pid = pidWrapper->pid;
message->ppid = pidWrapper->ppid;
}
lck_rw_unlock_shared(vnode_pid_map_lock_);
PostToLogQueue(message);
delete message;
return;
}
}
// Filter out modifications to locations that are definitely not useful.
if (ClientConnected() && !strprefix(path, "/.") && !strprefix(path, "/dev")) {
santa_message_t *message = NewMessage();
// Filter out modifications to locations that are definitely
// not useful or made by santad.
if (proc_selfpid() != client_pid_ &&
!strprefix(path, "/.") && !strprefix(path, "/dev")) {
auto message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
proc_name(message->pid, message->pname, sizeof(message->pname));
switch (action) {
case KAUTH_FILEOP_CLOSE:
message->action = ACTION_NOTIFY_WRITE; break;
message->action = ACTION_NOTIFY_WRITE;
break;
case KAUTH_FILEOP_RENAME:
message->action = ACTION_NOTIFY_RENAME; break;
message->action = ACTION_NOTIFY_RENAME;
break;
case KAUTH_FILEOP_LINK:
message->action = ACTION_NOTIFY_LINK; break;
message->action = ACTION_NOTIFY_LINK;
break;
case KAUTH_FILEOP_EXCHANGE:
message->action = ACTION_NOTIFY_EXCHANGE; break;
message->action = ACTION_NOTIFY_EXCHANGE;
break;
case KAUTH_FILEOP_DELETE:
message->action = ACTION_NOTIFY_DELETE; break;
message->action = ACTION_NOTIFY_DELETE;
break;
default: delete message; return;
}
PostToQueue(message);
PostToLogQueue(message);
delete message;
}
}
#undef super
#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) {
SantaDecisionManager *sdm = OSDynamicCast(
auto sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vnode_t vp = nullptr;
@@ -490,7 +546,7 @@ extern "C" int fileop_scope_callback(
switch (action) {
case KAUTH_FILEOP_CLOSE:
if (!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED)) return KAUTH_RESULT_DEFER;
// Intentional fall-through
// Intentional fall-through
case KAUTH_FILEOP_DELETE:
case KAUTH_FILEOP_EXEC:
vp = reinterpret_cast<vnode_t>(arg0);
@@ -523,8 +579,8 @@ extern "C" int vnode_scope_callback(
return KAUTH_RESULT_DEFER;
}
SantaDecisionManager *sdm =
OSDynamicCast(SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
auto sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
sdm->IncrementListenerInvocations();
int result = sdm->VnodeCallback(credential,

View File

@@ -24,9 +24,10 @@
#include <sys/proc.h>
#include <sys/vnode.h>
#include "SantaMessage.h"
#include "SNTKernelCommon.h"
#include "SNTLogging.h"
#include "SantaCachedDecision.h"
#include "SantaPIDAndPPID.h"
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
@@ -48,17 +49,24 @@ class SantaDecisionManager : public OSObject {
/// Called by SantaDriverClient during connection to provide the shared
/// dataqueue memory to the client.
IOMemoryDescriptor *GetMemoryDescriptor();
IOMemoryDescriptor *GetDecisionMemoryDescriptor() const;
IOMemoryDescriptor *GetLogMemoryDescriptor() const;
/// Called by SantaDriverClient when a client connects, providing the data
/// queue used to pass messages and the pid of the client process.
void ConnectClient(mach_port_t port, pid_t pid);
/// Called by SantaDriverClient when a client connects to the decision queue,
/// providing the pid of the client process.
void ConnectClient(pid_t pid);
/// Called by SantaDriverClient when a client disconnects
void DisconnectClient(bool itDied = false);
/// Returns whether a client is currently connected or not.
bool ClientConnected();
bool ClientConnected() const;
/// Sets the Mach port for notifying the decision queue.
void SetDecisionPort(mach_port_t port);
/// Sets the Mach port for notifying the log queue.
void SetLogPort(mach_port_t port);
/// Starts the kauth listeners.
kern_return_t StartListener();
@@ -77,15 +85,15 @@ class SantaDecisionManager : public OSObject {
void CacheCheck(const char *identifier);
/// Returns the number of entries in the cache.
uint64_t CacheCount();
uint64_t CacheCount() const;
/// Clears the cache.
void ClearCache();
/// Increments the count of active vnode callback's pending.
/// Increments the count of active callbacks pending.
void IncrementListenerInvocations();
/// Decrements the count of active vnode callback's pending.
/// Decrements the count of active callbacks pending.
void DecrementListenerInvocations();
///
@@ -100,44 +108,54 @@ class SantaDecisionManager : public OSObject {
const vnode_t vp, int *errno);
///
/// FileOp Callback
/// @param vp The Vnode for this request.
/// @param action The performed action
/// @param vp The Vnode for this request. May be nullptr.
/// @param path The path being operated on.
/// @param new_path The target path for moves and links.
///
void FileOpCallback(kauth_action_t action, const vnode_t vp, const char *path, const char *new_path);
void FileOpCallback(kauth_action_t action, const vnode_t vp,
const char *path, const char *new_path);
protected:
///
/// The maximum number of milliseconds a cached deny message should be
/// considered valid.
///
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
static 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;
static 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;
static const uint32_t kRequestLoopSleepMilliseconds = 10;
///
/// Maximum number of entries in the in-kernel cache.
///
const int kMaxCacheSize = 10000;
static const uint32_t kMaxCacheSize = 10000;
///
/// Maximum number of PostToQueue failures to allow.
/// Maximum number of PostToDecisionQueue failures to allow.
///
const int kMaxQueueFailures = 10;
static const uint32_t kMaxDecisionQueueFailures = 10;
///
/// The maximum number of messages can be kept in
/// the IODataQueue at any time.
/// the decision data queue at any time.
///
const int kMaxQueueEvents = 512;
static const uint32_t kMaxDecisionQueueEvents = 512;
///
/// The maximum number of messages can be kept
/// in the logging data queue at any time.
///
static const uint32_t kMaxLogQueueEvents = 1024;
/// Fetches a response from the cache, first checking to see if the
/// entry has expired.
@@ -170,12 +188,20 @@ class SantaDecisionManager : public OSObject {
const char *vnode_id_str);
///
/// Posts the requested message to the client data queue.
/// Posts the requested message to the decision data queue.
///
/// @param message The message to send
/// @return bool true if sending was successful.
///
bool PostToQueue(santa_message_t *message);
bool PostToDecisionQueue(santa_message_t *message);
///
/// Posts the requested message to the logging data queue.
///
/// @param message The message to send
/// @return bool true if sending was successful.
///
bool PostToLogQueue(santa_message_t *message);
///
/// Fetches the vnode_id for a given vnode.
@@ -184,15 +210,37 @@ class SantaDecisionManager : public OSObject {
/// @param vp The Vnode to get the ID for
/// @return uint64_t The Vnode ID as a 64-bit unsigned int.
///
uint64_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp);
static inline uint64_t GetVnodeIDForVnode(const vfs_context_t ctx,
const vnode_t vp) {
struct vnode_attr vap;
VATTR_INIT(&vap);
VATTR_WANTED(&vap, va_fsid);
VATTR_WANTED(&vap, va_fileid);
vnode_getattr(vp, &vap, ctx);
return (((uint64_t)vap.va_fsid << 32) | vap.va_fileid);
}
///
/// Creates a new santa_message_t with some fields pre-filled.
///
santa_message_t* NewMessage();
static inline santa_message_t *NewMessage() {
auto message = new santa_message_t;
message->uid = kauth_getuid();
message->gid = kauth_getgid();
message->pid = proc_selfpid();
message->ppid = proc_selfppid();
return message;
}
///
/// Returns the current system uptime in microseconds
static uint64_t GetCurrentUptime();
///
static inline uint64_t GetCurrentUptime() {
clock_sec_t sec;
clock_usec_t usec;
clock_get_system_microtime(&sec, &usec);
return (uint64_t)((sec * 1000000) + usec);
}
private:
lck_grp_t *sdm_lock_grp_;
@@ -200,14 +248,19 @@ class SantaDecisionManager : public OSObject {
lck_attr_t *sdm_lock_attr_;
lck_rw_t *cached_decisions_lock_;
lck_mtx_t *dataqueue_lock_;
lck_mtx_t *decision_dataqueue_lock_;
lck_mtx_t *log_dataqueue_lock_;
lck_rw_t *vnode_pid_map_lock_;
OSDictionary *cached_decisions_;
OSDictionary *vnode_pid_map_;
IOSharedDataQueue *dataqueue_;
SInt32 failed_queue_requests_;
IOSharedDataQueue *decision_dataqueue_;
IOSharedDataQueue *log_dataqueue_;
int32_t failed_decision_queue_requests_;
int32_t failed_log_queue_requests_;
SInt32 listener_invocations_;
int32_t listener_invocations_;
pid_t client_pid_;
@@ -243,5 +296,4 @@ 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

@@ -48,7 +48,7 @@ void SantaDriver::stop(IOService *provider) {
super::stop(provider);
}
SantaDecisionManager *SantaDriver::GetDecisionManager() {
SantaDecisionManager *SantaDriver::GetDecisionManager() const {
return santaDecisionManager;
}

View File

@@ -18,8 +18,8 @@
#include <IOKit/IOService.h>
#include <libkern/OSKextLib.h>
#include "SantaDecisionManager.h"
#include "SNTLogging.h"
#include "SantaDecisionManager.h"
///
/// The driver class, which provides the start/stop functions and holds
@@ -37,7 +37,7 @@ class com_google_SantaDriver : public IOService {
void stop(IOService *provider) override;
/// Returns a pointer to the SantaDecisionManager created in start().
SantaDecisionManager *GetDecisionManager();
SantaDecisionManager *GetDecisionManager() const;
private:
SantaDecisionManager *santaDecisionManager;

View File

@@ -25,7 +25,7 @@ OSDefineMetaClassAndStructors(com_google_SantaDriverClient, IOUserClient);
bool SantaDriverClient::initWithTask(
task_t owningTask, void *securityID, UInt32 type) {
if (clientHasPrivilege(
owningTask, kIOClientPrivilegeAdministrator) != KERN_SUCCESS) {
owningTask, kIOClientPrivilegeAdministrator) != KERN_SUCCESS) {
LOGW("Unprivileged client attempted to connect.");
return false;
}
@@ -73,18 +73,36 @@ IOReturn SantaDriverClient::registerNotificationPort(
mach_port_t port, UInt32 type, UInt32 ref) {
if (port == MACH_PORT_NULL) return kIOReturnError;
decisionManager->ConnectClient(port, proc_selfpid());
LOGI("Client connected, PID: %d.", proc_selfpid());
switch (type) {
case QUEUETYPE_DECISION:
decisionManager->SetDecisionPort(port);
break;
case QUEUETYPE_LOG:
decisionManager->SetLogPort(port);
break;
default:
return kIOReturnBadArgument;
}
return kIOReturnSuccess;
}
IOReturn SantaDriverClient::clientMemoryForType(
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory) {
if (type != kIODefaultMemoryType) return kIOReturnNoMemory;
switch (type) {
case QUEUETYPE_DECISION:
*options = 0;
*memory = decisionManager->GetDecisionMemoryDescriptor();
decisionManager->ConnectClient(proc_selfpid());
break;
case QUEUETYPE_LOG:
*options = 0;
*memory = decisionManager->GetLogMemoryDescriptor();
break;
default:
return kIOReturnBadArgument;
}
*options = 0;
*memory = decisionManager->GetMemoryDescriptor();
(*memory)->retain();
return kIOReturnSuccess;
@@ -114,7 +132,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);
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_ALLOW);
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_ALLOW);
return kIOReturnSuccess;
}
@@ -133,7 +151,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);
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_CHECKBW_DENY);
decisionManager->AddToCache(vnode_id_str, ACTION_RESPOND_DENY);
return kIOReturnSuccess;
}

View File

@@ -17,13 +17,12 @@
#include <IOKit/IOUserClient.h>
#include <sys/kauth.h>
#include <sys/vnode.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include "SNTKernelCommon.h"
#include "SantaDecisionManager.h"
#include "SantaDriver.h"
#include "SantaMessage.h"
#include "SNTKernelCommon.h"
///
/// This class is instantiated by IOKit when a new client process attempts to
@@ -77,7 +76,7 @@ class com_google_SantaDriverClient : public IOUserClient {
/// which just calls the method on the provided target.
///
/// Called during client connection
/// Called during client connection.
IOReturn open();
static IOReturn static_open(
com_google_SantaDriverClient *target,

View File

@@ -0,0 +1,17 @@
/// 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.
#include "SantaPIDAndPPID.h"
OSDefineMetaClassAndStructors(SantaPIDAndPPID, OSObject);

View File

@@ -0,0 +1,32 @@
/// 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.
#ifndef SANTA__SANTA_DRIVER__SANTAPIDANDPPID_H
#define SANTA__SANTA_DRIVER__SANTAPIDANDPPID_H
#include <libkern/c++/OSObject.h>
///
/// An OSObject wrapper around a PID and PPID.
/// Only OSObject subclasses can be inserted into an OSDictionary.
///
class SantaPIDAndPPID : public OSObject {
OSDeclareDefaultStructors(SantaPIDAndPPID)
public:
pid_t pid;
pid_t ppid;
};
#endif // SANTA__SANTA_DRIVER__SANTAPIDANDPPID_H

View File

@@ -17,7 +17,7 @@
///
/// Protocol that each command must adhere to.
///
@protocol SNTCommand <NSObject>
@protocol SNTCommand<NSObject>
///
/// @return YES if command requires root.

View File

@@ -66,19 +66,11 @@ static NSMutableDictionary *registeredCommands;
}
+ (SNTXPCConnection *)connectToDaemon {
SNTXPCConnection *daemonConn =
[[SNTXPCConnection alloc] initClientWithName:[SNTXPCControlInterface serviceId]
options:NSXPCConnectionPrivileged];
daemonConn.remoteInterface = [SNTXPCControlInterface controlInterface];
daemonConn.rejectedHandler = ^{
printf("The daemon rejected the connection\n");
exit(1);
};
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
daemonConn.invalidationHandler = ^{
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
printf("An error occurred communicating with the daemon, is it running?\n");
exit(1);
};
[daemonConn resume];

View File

@@ -1,121 +0,0 @@
/// 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 "SNTLogging.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTFileInfo.h"
@interface SNTCommandBinaryInfo : NSObject<SNTCommand>
@end
@implementation SNTCommandBinaryInfo
REGISTER_COMMAND_NAME(@"binaryinfo")
+ (BOOL)requiresRoot {
return NO;
}
+ (BOOL)requiresDaemonConn {
return NO;
}
+ (NSString *)shortHelpText {
return @"Prints information about a binary.";
}
+ (NSString *)longHelpText {
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) {
printf("Missing file path\n");
exit(1);
}
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
printf("Invalid or empty file\n");
exit(1);
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
[self printKey:@"Path" value:fileInfo.path];
[self printKey:@"SHA-256" value:fileInfo.SHA256];
[self printKey:@"SHA-1" value:fileInfo.SHA1];
[self printKey:@"Bundle Name" value:fileInfo.bundleName];
[self printKey:@"Bundle Version" value:fileInfo.bundleVersion];
[self printKey:@"Bundle Version Str" value:fileInfo.bundleShortVersionString];
if (fileInfo.quarantineDataURL) {
[self printKey:@"Download Referer URL" value:fileInfo.quarantineRefererURL];
[self printKey:@"Download URL" value:fileInfo.quarantineDataURL];
[self printKey:@"Download Timestamp"
value:[dateFormatter stringFromDate:fileInfo.quarantineTimestamp]];
[self printKey:@"Download Agent" value:fileInfo.quarantineAgentBundleID];
}
NSArray *archs = [fileInfo architectures];
if (archs) {
NSString *s = [NSString stringWithFormat:@"%@ (%@)",
fileInfo.machoType, [archs componentsJoinedByString:@", "]];
[self printKey:@"Type" value:s];
} else {
[self printKey:@"Type" value:fileInfo.machoType];
}
if ([fileInfo isMissingPageZero]) {
[self printKey:@"Page Zero" value:@"__PAGEZERO segment missing/bad!"];
}
MOLCodesignChecker *csc = [[MOLCodesignChecker alloc] initWithBinaryPath:filePath];
[self printKey:@"Code-signed" value:(csc) ? @"Yes" : @"No"];
if (csc) {
printf("Signing chain:\n");
[csc.certificates enumerateObjectsUsingBlock:^(MOLCertificate *c,
unsigned long idx,
BOOL *stop) {
printf(" %2lu. %-20s: %s\n", idx + 1, "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",
[[dateFormatter stringFromDate:c.validFrom] UTF8String]);
printf(" %-20s: %s\n", "Valid Until",
[[dateFormatter stringFromDate:c.validUntil] UTF8String]);
printf("\n");
}];
}
exit(0);
}
+ (void)printKey:(NSString *)key value:(NSString *)value {
printf("%-21s: %s\n", [key UTF8String], [value UTF8String]);
}
@end

View File

@@ -0,0 +1,180 @@
/// 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 "SNTLogging.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTFileInfo.h"
@interface SNTCommandFileInfo : NSObject<SNTCommand>
@end
@implementation SNTCommandFileInfo
REGISTER_COMMAND_NAME(@"fileinfo")
+ (BOOL)requiresRoot {
return NO;
}
+ (BOOL)requiresDaemonConn {
return NO;
}
+ (NSString *)shortHelpText {
return @"Prints information about a file.";
}
+ (NSString *)longHelpText {
return (@"The details provided will be the same ones Santa uses to make a decision\n"
@"about executables. This includes SHA-256, SHA-1, code signing information and\n"
@"the type of file.");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
NSString *filePath = [arguments firstObject];
if (!filePath) {
printf("Missing file path\n");
exit(1);
}
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
printf("Invalid or empty file\n");
exit(1);
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss Z";
if (isatty(STDOUT_FILENO)) printf("Hashing...");
NSString *sha1, *sha256;
[fileInfo hashSHA1:&sha1 SHA256:&sha256];
if (isatty(STDOUT_FILENO)) printf("\r");
[self printKey:@"Path" value:fileInfo.path];
[self printKey:@"SHA-256" value:sha256];
[self printKey:@"SHA-1" value:sha1];
if (fileInfo.bundlePath) {
[self printKey:@"Bundle Name" value:fileInfo.bundleName];
[self printKey:@"Bundle Version" value:fileInfo.bundleVersion];
[self printKey:@"Bundle Version Str" value:fileInfo.bundleShortVersionString];
}
if (fileInfo.quarantineDataURL) {
[self printKey:@"Download Referer URL" value:fileInfo.quarantineRefererURL];
[self printKey:@"Download URL" value:fileInfo.quarantineDataURL];
[self printKey:@"Download Timestamp"
value:[dateFormatter stringFromDate:fileInfo.quarantineTimestamp]];
[self printKey:@"Download Agent" value:fileInfo.quarantineAgentBundleID];
}
NSArray *archs = [fileInfo architectures];
if (archs.count == 0) {
[self printKey:@"Type" value:[self humanReadableFileType:fileInfo]];
exit(0);
}
NSString *s = [NSString stringWithFormat:@"%@ (%@)",
[self humanReadableFileType:fileInfo],
[archs componentsJoinedByString:@", "]];
[self printKey:@"Type" value:s];
if ([fileInfo isMissingPageZero]) {
[self printKey:@"Page Zero" value:@"__PAGEZERO segment missing/bad!"];
}
NSError *error;
MOLCodesignChecker *csc = [[MOLCodesignChecker alloc] initWithBinaryPath:filePath error:&error];
if (!error) {
[self printKey:@"Code-signed" value:@"Yes"];
} else {
switch (error.code) {
case errSecCSUnsigned:
[self printKey:@"Code-signed" value:@"No"];
break;
case errSecCSSignatureFailed:
case errSecCSStaticCodeChanged:
case errSecCSSignatureNotVerifiable:
case errSecCSSignatureUnsupported:
[self printKey:@"Code-signed" value:@"Yes, but code/signatured changed/unverifiable"];
break;
case errSecCSResourceDirectoryFailed:
case errSecCSResourceNotSupported:
case errSecCSResourceRulesInvalid:
case errSecCSResourcesInvalid:
case errSecCSResourcesNotFound:
case errSecCSResourcesNotSealed:
[self printKey:@"Code-signed" value:@"Yes, but resources invalid"];
break;
case errSecCSReqFailed:
case errSecCSReqInvalid:
case errSecCSReqUnsupported:
[self printKey:@"Code-signed" value:@"Yes, but failed requirement validation"];
break;
case errSecCSInfoPlistFailed:
[self printKey:@"Code-signed" value:@"Yes, but can't validate as Info.plist is missing"];
break;
default: {
NSString *val = [NSString stringWithFormat:@"Yes, but failed to validate (%ld)",
error.code];
[self printKey:@"Code-signed" value:val];
break;
}
}
}
if (csc.certificates.count) {
printf("Signing chain:\n");
[csc.certificates enumerateObjectsUsingBlock:^(MOLCertificate *c,
unsigned long idx,
BOOL *stop) {
printf(" %2lu. %-20s: %s\n", idx + 1, "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",
[[dateFormatter stringFromDate:c.validFrom] UTF8String]);
printf(" %-20s: %s\n", "Valid Until",
[[dateFormatter stringFromDate:c.validUntil] UTF8String]);
printf("\n");
}];
}
exit(0);
}
+ (void)printKey:(NSString *)key value:(NSString *)value {
if (!key || !value) return;
printf("%-21s: %s\n", [key UTF8String], [value UTF8String]);
}
+ (NSString *)humanReadableFileType:(SNTFileInfo *)fi {
if ([fi isScript]) return @"Script";
if ([fi isXARArchive]) return @"XAR Archive";
if ([fi isDylib]) return @"Dynamic Library";
if ([fi isKext]) return @"Kernel Extension";
if ([fi isFat]) return @"Fat Binary";
if ([fi isMachO]) return @"Thin Binary";
if ([fi isDMG]) return @"Disk Image";
return @"Unknown";
}
@end

View File

@@ -44,13 +44,13 @@ REGISTER_COMMAND_NAME(@"flushcache")
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
[[daemonConn remoteObjectProxy] flushCache:^(BOOL success) {
if (success) {
LOGI(@"Cache flush requested");
exit(0);
} else {
LOGE(@"Cache flush failed");
exit(1);
}
if (success) {
LOGI(@"Cache flush requested");
exit(0);
} else {
LOGE(@"Cache flush failed");
exit(1);
}
}];
}

View File

@@ -47,7 +47,7 @@ int main(int argc, const char *argv[]) {
[commandName isEqualToString:@"usage"] ||
[commandName isEqualToString:@"commands"]) {
print_usage();
return 1;;
return 1;
}
[arguments removeObjectAtIndex:0];

View File

@@ -25,7 +25,6 @@
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@interface SNTCommandRule : NSObject<SNTCommand>
@property SNTXPCConnection *daemonConn;
@end
@@ -78,25 +77,25 @@ REGISTER_COMMAND_NAME(@"rule")
}
SNTRule *newRule = [[SNTRule alloc] init];
newRule.state = RULESTATE_UNKNOWN;
newRule.type = RULETYPE_BINARY;
newRule.state = SNTRuleStateUnknown;
newRule.type = SNTRuleTypeBinary;
NSString *path;
// Parse arguments
for (NSUInteger i = 0; i < arguments.count ; i++ ) {
for (NSUInteger i = 0; i < arguments.count; ++i) {
NSString *arg = arguments[i];
if ([arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
newRule.state = RULESTATE_WHITELIST;
newRule.state = SNTRuleStateWhitelist;
} else if ([arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_BLACKLIST;
newRule.state = SNTRuleStateBlacklist;
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
newRule.state = SNTRuleStateSilentBlacklist;
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
newRule.state = RULESTATE_REMOVE;
newRule.state = SNTRuleStateRemove;
} else if ([arg caseInsensitiveCompare:@"--certificate"] == NSOrderedSame) {
newRule.type = RULETYPE_CERT;
newRule.type = SNTRuleTypeCertificate;
} else if ([arg caseInsensitiveCompare:@"--path"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"--path requires an argument"];
@@ -116,38 +115,41 @@ REGISTER_COMMAND_NAME(@"rule")
}
newRule.customMsg = arguments[i];
} else {
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:arg]];
[self printErrorUsageAndExit:[@"Unknown argument: " stringByAppendingString:arg]];
}
}
if (path) {
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:path];
if (newRule.type == RULETYPE_BINARY) {
if (newRule.type == SNTRuleTypeBinary) {
newRule.shasum = fi.SHA256;
} else if (newRule.type == RULETYPE_CERT) {
} else if (newRule.type == SNTRuleTypeCertificate) {
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.path];
newRule.shasum = cs.leafCertificate.SHA256;
}
}
if (newRule.state == RULESTATE_UNKNOWN) {
if (newRule.state == SNTRuleStateUnknown) {
[self printErrorUsageAndExit:@"No state specified"];
} else if (!newRule.shasum) {
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
}
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^(BOOL success) {
if (!success) {
printf("Failed to modify rules.");
exit(1);
[[daemonConn remoteObjectProxy] databaseRuleAddRules:@[newRule]
cleanSlate:NO
reply:^(NSError *error) {
if (error) {
printf("Failed to modify rules: %s", [error.localizedDescription UTF8String]);
LOGD(@"Failure reason: %@", error.localizedFailureReason);
exit(1);
} else {
if (newRule.state == SNTRuleStateRemove) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
if (newRule.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);
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
exit(0);
}
}];
}

View File

@@ -48,56 +48,60 @@ REGISTER_COMMAND_NAME(@"status")
// Daemon status
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
__block double cpuPeak, ramPeak;
dispatch_group_enter(group);
[[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;
}
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogCPUEvents:^(uint64_t events) {
cpuEvents = events;
[[daemonConn remoteObjectProxy] clientMode:^(SNTClientMode cm) {
switch (cm) {
case SNTClientModeMonitor:
clientMode = @"Monitor";
break;
case SNTClientModeLockdown:
clientMode = @"Lockdown";
break;
default:
clientMode = [NSString stringWithFormat:@"Unknown (%ld)", cm];
break;
}
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogRAMEvents:^(uint64_t events) {
ramEvents = events;
[[daemonConn remoteObjectProxy] watchdogInfo:^(uint64_t wd_cpuEvents, uint64_t wd_ramEvents,
double wd_cpuPeak, double wd_ramPeak) {
cpuEvents = wd_cpuEvents;
cpuPeak = wd_cpuPeak;
ramEvents = wd_ramEvents;
ramPeak = wd_ramPeak;
dispatch_group_leave(group);
}];
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
// Kext status
__block int64_t cacheCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] cacheCount:^(int64_t count) {
cacheCount = count;
dispatch_group_leave(group);
cacheCount = count;
dispatch_group_leave(group);
}];
// Database counts
__block int64_t eventCount = -1, binaryRuleCount = -1, certRuleCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(int64_t binary, int64_t certificate) {
binaryRuleCount = binary;
certRuleCount = certificate;
dispatch_group_leave(group);
binaryRuleCount = binary;
certRuleCount = certificate;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseEventCount:^(int64_t count) {
eventCount = count;
dispatch_group_leave(group);
eventCount = count;
dispatch_group_leave(group);
}];
// Sync status
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss Z";
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] syncLastSuccess];
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
@@ -109,37 +113,39 @@ REGISTER_COMMAND_NAME(@"status")
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
@"daemon": @{
@"mode": clientMode,
@"file_logging": @(fileLogging),
@"watchdog_cpu_events": @(cpuEvents),
@"watchdog_ram_events": @(ramEvents),
},
@"kernel": @{
@"cache_count": @(cacheCount),
},
@"database": @{
@"binary_rules": @(binaryRuleCount),
@"certificate_rules": @(certRuleCount),
@"events_pending_upload": @(eventCount),
},
@"sync": @{
@"server": syncURLStr,
@"clean_required": @(syncCleanReqd),
@"last_successful": lastSyncSuccessStr
},
@"daemon" : @{
@"mode" : clientMode,
@"file_logging" : @(fileLogging),
@"watchdog_cpu_events" : @(cpuEvents),
@"watchdog_ram_events" : @(ramEvents),
@"watchdog_cpu_peak" : @(cpuPeak),
@"watchdog_ram_peak" : @(ramPeak),
},
@"kernel" : @{
@"cache_count" : @(cacheCount),
},
@"database" : @{
@"binary_rules" : @(binaryRuleCount),
@"certificate_rules" : @(certRuleCount),
@"events_pending_upload" : @(eventCount),
},
@"sync" : @{
@"server" : syncURLStr,
@"clean_required" : @(syncCleanReqd),
@"last_successful" : lastSyncSuccessStr
},
};
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *statsStr = [[NSString alloc] initWithData:statsData encoding:NSUTF8StringEncoding];
printf("%s\n", [statsStr UTF8String]);
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-22s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-22s | %lld\n", "Watchdog CPU Events", cpuEvents);
printf(" %-22s | %lld\n", "Watchdog RAM Events", ramEvents);
printf(" %-22s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
printf(" %-22s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
printf(">>> Kernel Info\n");
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
printf(">>> Database Info\n");

View File

@@ -21,7 +21,7 @@
///
/// The underlying session. Pass this session to NSURLRequest methods.
///
@property(readonly) NSURLSession *session;
@property(readonly, nonatomic) NSURLSession *session;
///
/// If set, this is the user-agent to send with requests, otherwise remains the default

View File

@@ -18,14 +18,17 @@
#import "SNTDERDecoder.h"
#import "SNTLogging.h"
@interface SNTAuthenticatingURLSession ()
@property(readwrite) NSURLSession *session;
@property NSURLSessionConfiguration *sessionConfig;
@end
@implementation SNTAuthenticatingURLSession
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (self) {
_session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:nil];
_sessionConfig = configuration;
}
return self;
}
@@ -37,16 +40,30 @@
return [self initWithSessionConfiguration:config];
}
#pragma mark Session Fetching
- (NSURLSession *)session {
if (!_session) {
_session = [NSURLSession sessionWithConfiguration:self.sessionConfig
delegate:self
delegateQueue:nil];
}
return _session;
}
#pragma mark User Agent property
- (NSString *)userAgent {
return _session.configuration.HTTPAdditionalHeaders[@"User-Agent"];
return self.sessionConfig.HTTPAdditionalHeaders[@"User-Agent"];
}
- (void)setUserAgent:(NSString *)userAgent {
NSMutableDictionary *addlHeaders = [_session.configuration.HTTPAdditionalHeaders mutableCopy];
NSMutableDictionary *addlHeaders = [self.sessionConfig.HTTPAdditionalHeaders mutableCopy];
if (!addlHeaders) addlHeaders = [NSMutableDictionary dictionary];
addlHeaders[@"User-Agent"] = userAgent;
_session.configuration.HTTPAdditionalHeaders = addlHeaders;
self.sessionConfig.HTTPAdditionalHeaders = [addlHeaders copy];
_session = nil;
}
#pragma mark NSURLSessionDelegate methods
@@ -87,8 +104,8 @@
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
return;
} else {
LOGE(@"Server asked for client authentication but no usable client certificate found.");
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
LOGW(@"Server asked for client authentication but no usable client certificate found.");
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
return;
}
} else if (authMethod == NSURLAuthenticationMethodServerTrust) {
@@ -111,13 +128,13 @@
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler {
if (self.refusesRedirects) {
LOGD(@"Rejected redirection to: %@", request.URL);
[task cancel]; // without this, the connection hangs until timeout!?!
completionHandler(NULL);
} else {
completionHandler(request);
}
if (self.refusesRedirects) {
LOGD(@"Rejected redirection to: %@", request.URL);
[task cancel]; // without this, the connection hangs until timeout!?!
completionHandler(NULL);
} else {
completionHandler(request);
}
}
#pragma mark Private Helpers for URLSession:didReceiveChallenge:completionHandler:
@@ -141,111 +158,62 @@
__block SecIdentityRef foundIdentity = NULL;
if (self.clientCertFile) {
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:self.clientCertFile options:0 error:&error];
if (error) {
LOGD(@"Client Trust: Couldn't open client certificate %@: %@",
self.clientCertFile,
[error localizedDescription]);
return nil;
}
NSDictionary *options = (self.clientCertPassword ?
@{(__bridge id)kSecImportExportPassphrase: self.clientCertPassword} :
@{});
CFArrayRef cfIdentities;
err = SecPKCS12Import(
(__bridge CFDataRef)data, (__bridge CFDictionaryRef)options, &cfIdentities);
NSArray *identities = CFBridgingRelease(cfIdentities);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Couldn't load client certificate %@: %d", self.clientCertFile, err);
return nil;
}
foundIdentity = (__bridge SecIdentityRef)identities[0][(__bridge id)kSecImportItemIdentity];
CFRetain(foundIdentity);
foundIdentity = [self identityFromFile:self.clientCertFile password:self.clientCertPassword];
} else {
CFArrayRef cfIdentities;
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
(id)kSecClass : (id)kSecClassIdentity,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll
}, (CFTypeRef *)&cfIdentities);
CFArrayRef cfResults = NULL;
SecItemCopyMatching((__bridge CFDictionaryRef) @{
(id)kSecClass : (id)kSecClassCertificate,
(id)kSecReturnRef : @YES,
(id)kSecMatchLimit : (id)kSecMatchLimitAll
}, (CFTypeRef *)&cfResults);
NSArray *results = CFBridgingRelease(cfResults);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to load client identities, SecItemCopyMatching returned: %d",
(int)err);
return nil;
NSMutableArray *allCerts = [[MOLCertificate certificatesFromArray:results] mutableCopy];
if (self.clientCertCommonName) {
foundIdentity = [self identityByFilteringArray:allCerts
commonName:self.clientCertCommonName
issuerCommonName:nil
issuerCountryName:nil
issuerOrgName:nil
issuerOrgUnit:nil];
} else if (self.clientCertIssuerCn) {
foundIdentity = [self identityByFilteringArray:allCerts
commonName:nil
issuerCommonName:self.clientCertIssuerCn
issuerCountryName:nil
issuerOrgName:nil
issuerOrgUnit:nil];
} else {
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
if (!decoder) {
LOGW(@"Unable to decode allowed distinguished name.");
continue;
}
foundIdentity = [self identityByFilteringArray:allCerts
commonName:nil
issuerCommonName:decoder.commonName
issuerCountryName:decoder.countryName
issuerOrgName:decoder.organizationName
issuerOrgUnit:decoder.organizationalUnit];
if (foundIdentity) break;
}
}
NSArray *identities = CFBridgingRelease(cfIdentities);
// 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;
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(identityRef, &certificate);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Failed to read certificate data: %d. Skipping identity.", (int)err);
return;
}
MOLCertificate *clientCert = [[MOLCertificate 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) {
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(foundIdentity, &certificate);
MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate];
if (certificate) CFRelease(certificate);
LOGD(@"Client Trust: Valid client identity %@.", clientCert);
NSURLCredential *cred =
[NSURLCredential credentialWithIdentity:foundIdentity
certificates:nil
persistence:NSURLCredentialPersistenceForSession];
CFRelease(foundIdentity);
return cred;
} else {
LOGD(@"Client Trust: No valid identity found.");
@@ -318,4 +286,91 @@
return [NSURLCredential credentialForTrust:serverTrust];
}
/**
Given an array of MOLCertificate objects and some properties, filter the array
repeatedly until an identity is found that fulfills the signing chain.
*/
- (SecIdentityRef)identityByFilteringArray:(NSArray *)array
commonName:(NSString *)commonName
issuerCommonName:(NSString *)issuerCommonName
issuerCountryName:(NSString *)issuerCountryName
issuerOrgName:(NSString *)issuerOrgName
issuerOrgUnit:(NSString *)issuerOrgUnit {
NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:4];
if (commonName) {
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF.commonName == %@",
commonName]];
}
if (issuerCommonName) {
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF.issuerCommonName == %@",
issuerCommonName]];
}
if (issuerCountryName) {
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF.issuerCountryName == %@",
issuerCountryName]];
}
if (issuerOrgName) {
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF.issuerOrgName == %@",
issuerOrgName]];
}
if (issuerOrgUnit) {
[predicates addObject:[NSPredicate predicateWithFormat:@"SELF.issuerOrgUnit == %@",
issuerOrgUnit]];
}
NSCompoundPredicate *andPreds = [NSCompoundPredicate andPredicateWithSubpredicates:predicates];
NSArray *filteredCerts = [array filteredArrayUsingPredicate:andPreds];
if (!filteredCerts.count) return NULL;
for (MOLCertificate *cert in filteredCerts) {
SecIdentityRef identityRef = NULL;
OSStatus status = SecIdentityCreateWithCertificate(NULL, cert.certRef, &identityRef);
if (status == errSecSuccess) {
return identityRef;
} else {
// Avoid infinite recursion from self-signed certs
if ((cert.commonName && [cert.commonName isEqual:cert.issuerCommonName]) &&
(cert.countryName && [cert.countryName isEqual:cert.issuerCountryName]) &&
(cert.orgName && [cert.orgName isEqual:cert.issuerOrgName]) &&
(cert.orgUnit && [cert.orgUnit isEqual:cert.issuerOrgUnit])) {
continue;
}
return [self identityByFilteringArray:array
commonName:nil
issuerCommonName:cert.commonName
issuerCountryName:cert.countryName
issuerOrgName:cert.orgName
issuerOrgUnit:cert.orgUnit];
}
}
return NULL;
}
- (SecIdentityRef)identityFromFile:(NSString *)file password:(NSString *)password {
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:file options:0 error:&error];
if (error) {
LOGD(@"Client Trust: Couldn't open client certificate %@: %@",
self.clientCertFile,
[error localizedDescription]);
return nil;
}
NSDictionary *options = (password ? @{(__bridge id)kSecImportExportPassphrase : password} : @{});
CFArrayRef cfIdentities;
OSStatus err = SecPKCS12Import(
(__bridge CFDataRef)data, (__bridge CFDictionaryRef)options, &cfIdentities);
NSArray *identities = CFBridgingRelease(cfIdentities);
if (err != errSecSuccess) {
LOGD(@"Client Trust: Couldn't load client certificate %@: %d", self.clientCertFile, err);
return nil;
}
return (__bridge SecIdentityRef)identities[0][(__bridge id)kSecImportItemIdentity];
}
@end

View File

@@ -75,7 +75,7 @@ REGISTER_COMMAND_NAME(@"sync")
authURLSession.userAgent = @"santactl-sync/";
NSString *santactlVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
if (santactlVersion) {
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
}
authURLSession.refusesRedirects = YES;
@@ -89,10 +89,10 @@ REGISTER_COMMAND_NAME(@"sync")
authURLSession.serverRootsPemData = rootsData;
if (!rootsData) {
LOGE(@"Couldn't open server root certificate file %@ with error: %@.",
[config syncServerAuthRootsFile],
[error localizedDescription]);
exit(1);
LOGE(@"Couldn't open server root certificate file %@ with error: %@.",
[config syncServerAuthRootsFile],
[error localizedDescription]);
exit(1);
}
} else if ([config syncServerAuthRootsData]) {
authURLSession.serverRootsPemData = [config syncServerAuthRootsData];
@@ -135,8 +135,15 @@ REGISTER_COMMAND_NAME(@"sync")
LOGW(@"Missing Machine Owner.");
}
if (arguments.count == 2 && [[arguments firstObject] isEqual:@"singleevent"]) {
[s eventUploadSingleEvent:arguments[1]];
if ([arguments containsObject:@"singleevent"]) {
NSUInteger idx = [arguments indexOfObject:@"singleevent"];
idx++;
NSString *obj = arguments[idx];
if (obj.length != 64) {
LOGI(@"singleevent passed without SHA-256 as next argument");
exit(1);
}
[s eventUploadSingleEvent:obj];
} else {
[s preflight];
}
@@ -148,7 +155,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Preflight complete");
if (self.syncState.uploadLogURL) {
[self logUpload];
} else {
@@ -167,7 +173,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Log upload complete");
} else {
LOGE(@"Log upload failed, continuing anyway");
}
@@ -182,7 +187,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Event upload complete");
[self ruleDownload];
} else {
LOGE(@"Event upload failed, aborting run");
@@ -198,7 +202,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Event upload complete");
exit(0);
} else {
LOGW(@"Event upload failed");
@@ -213,7 +216,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Rule download complete");
[self postflight];
} else {
LOGE(@"Rule download failed, aborting run");
@@ -228,7 +230,6 @@ REGISTER_COMMAND_NAME(@"sync")
daemonConn:self.daemonConn
completionHandler:^(BOOL success) {
if (success) {
LOGI(@"Postflight complete");
LOGI(@"Sync completed successfully");
exit(0);
} else {

View File

@@ -12,77 +12,78 @@
/// 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 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 kWhitelistRegex;
extern NSString * const kBlacklistRegex;
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 kWhitelistRegex;
extern NSString *const kBlacklistRegex;
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 kQuarantineDataURL;
extern NSString * const kQuarantineRefererURL;
extern NSString * const kQuarantineTimestamp;
extern NSString * const kQuarantineAgentBundleID;
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 kDecisionRelatedBinary;
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 kQuarantineDataURL;
extern NSString *const kQuarantineRefererURL;
extern NSString *const kQuarantineTimestamp;
extern NSString *const kQuarantineAgentBundleID;
extern NSString * const kLogUploadField;
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;
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;
extern NSString * const kBackoffInterval;
extern NSString *const kBackoffInterval;

View File

@@ -14,77 +14,78 @@
#import "SNTCommandSyncConstants.h"
NSString * const kURLPreflight = @"preflight/";
NSString * const kURLEventUpload = @"eventupload/";
NSString * const kURLRuleDownload = @"ruledownload/";
NSString * const kURLPostflight = @"postflight/";
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 kWhitelistRegex = @"whitelist_regex";
NSString * const kBlacklistRegex = @"blacklist_regex";
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 kWhitelistRegex = @"whitelist_regex";
NSString *const kBlacklistRegex = @"blacklist_regex";
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 kQuarantineDataURL = @"quarantine_data_url";
NSString * const kQuarantineRefererURL = @"quarantine_referer_url";
NSString * const kQuarantineTimestamp = @"quarantine_timestamp";
NSString * const kQuarantineAgentBundleID = @"quarantine_agent_bundle_id";
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 kDecisionRelatedBinary = @"RELATED_BINARY";
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 kQuarantineDataURL = @"quarantine_data_url";
NSString *const kQuarantineRefererURL = @"quarantine_referer_url";
NSString *const kQuarantineTimestamp = @"quarantine_timestamp";
NSString *const kQuarantineAgentBundleID = @"quarantine_agent_bundle_id";
NSString * const kLogUploadField = @"files";
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";
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";
NSString * const kBackoffInterval = @"backoff";
NSString *const kBackoffInterval = @"backoff";

View File

@@ -16,10 +16,12 @@
#include "SNTLogging.h"
#import "NSData+Zlib.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "NSData+Zlib.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTFileInfo.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -34,16 +36,16 @@
relativeToURL:syncState.syncBaseURL];
[[daemonConn remoteObjectProxy] databaseEventsPending:^(NSArray *events) {
if ([events count] == 0) {
handler(YES);
} else {
[self uploadEventsFromArray:events
toURL:url
inSession:session
batchSize:syncState.eventBatchSize
daemonConn:daemonConn
completionHandler:handler];
}
if ([events count] == 0) {
handler(YES);
} else {
[self uploadEventsFromArray:events
toURL:url
inSession:session
batchSize:syncState.eventBatchSize
daemonConn:daemonConn
completionHandler:handler];
}
}];
}
@@ -55,17 +57,17 @@
NSURL *url = [NSURL URLWithString:[kURLEventUpload stringByAppendingString:syncState.machineID]
relativeToURL:syncState.syncBaseURL];
[[daemonConn remoteObjectProxy] databaseEventForSHA256:SHA256 reply:^(SNTStoredEvent *event) {
if (!event) {
handler(YES);
return;
}
if (!event) {
handler(YES);
return;
}
[self uploadEventsFromArray:@[ event ]
toURL:url
inSession:session
batchSize:1
daemonConn:daemonConn
completionHandler:handler];
[self uploadEventsFromArray:@[ event ]
toURL:url
inSession:session
batchSize:1
daemonConn:daemonConn
completionHandler:handler];
}];
}
@@ -82,10 +84,15 @@
[uploadEvents addObject:[self dictionaryForEvent:event]];
[eventIds addObject:event.idx];
if (event.fileBundleID) {
NSArray *relatedBinaries = [self findRelatedBinaries:event];
[uploadEvents addObjectsFromArray:relatedBinaries];
}
if (eventIds.count >= batchSize) break;
}
NSDictionary *uploadReq = @{ kEvents: uploadEvents };
NSDictionary *uploadReq = @{kEvents : uploadEvents};
NSData *requestBody;
@try {
@@ -110,30 +117,31 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
LOGD(@"%@", error);
handler(NO);
} else {
LOGI(@"Uploaded %lu events", eventIds.count);
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:eventIds];
NSArray *nextEvents = [events subarrayWithRange:NSMakeRange(eventIds.count,
events.count - eventIds.count)];
if (nextEvents.count == 0) {
handler(YES);
} else {
LOGI(@"Uploaded %lu events", eventIds.count);
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:eventIds];
NSArray *nextEvents = [events subarrayWithRange:NSMakeRange(eventIds.count,
events.count - eventIds.count)];
if (nextEvents.count == 0) {
handler(YES);
} else {
[self uploadEventsFromArray:nextEvents
toURL:url
inSession:session
batchSize:batchSize
daemonConn:daemonConn
completionHandler:handler];
}
[self uploadEventsFromArray:nextEvents
toURL:url
inSession:session
batchSize:batchSize
daemonConn:daemonConn
completionHandler:handler];
}
}
}] resume];
}
@@ -150,18 +158,19 @@
ADDKEY(newEvent, kCurrentSessions, event.currentSessions);
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:
case SNTEventStateAllowUnknown: ADDKEY(newEvent, kDecision, kDecisionAllowUnknown); break;
case SNTEventStateAllowBinary: ADDKEY(newEvent, kDecision, kDecisionAllowBinary); break;
case SNTEventStateAllowCertificate:
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:
case SNTEventStateAllowScope: ADDKEY(newEvent, kDecision, kDecisionAllowScope); break;
case SNTEventStateBlockUnknown: ADDKEY(newEvent, kDecision, kDecisionBlockUnknown); break;
case SNTEventStateBlockBinary: ADDKEY(newEvent, kDecision, kDecisionBlockBinary); break;
case SNTEventStateBlockCertificate:
ADDKEY(newEvent, kDecision, kDecisionBlockCertificate);
break;
case EVENTSTATE_BLOCK_SCOPE: ADDKEY(newEvent, kDecision, kDecisionBlockScope); break;
case SNTEventStateBlockScope: ADDKEY(newEvent, kDecision, kDecisionBlockScope); break;
case SNTEventStateRelatedBinary: ADDKEY(newEvent, kDecision, kDecisionRelatedBinary); break;
default: ADDKEY(newEvent, kDecision, kDecisionUnknown);
}
@@ -180,7 +189,7 @@
ADDKEY(newEvent, kQuarantineAgentBundleID, event.quarantineAgentBundleID);
NSMutableArray *signingChain = [NSMutableArray arrayWithCapacity:event.signingChain.count];
for (NSUInteger i = 0; i < event.signingChain.count; i++) {
for (NSUInteger i = 0; i < event.signingChain.count; ++i) {
MOLCertificate *cert = [event.signingChain objectAtIndex:i];
NSMutableDictionary *certDict = [NSMutableDictionary dictionary];
@@ -199,4 +208,68 @@
#undef ADDKEY
}
+ (NSArray *)findRelatedBinaries:(SNTStoredEvent *)event {
// Prevent processing the same bundle twice.
static NSMutableDictionary *previouslyProcessedBundles;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
previouslyProcessedBundles = [NSMutableDictionary dictionary];
});
if (previouslyProcessedBundles[event.fileBundleID]) return nil;
previouslyProcessedBundles[event.fileBundleID] = @YES;
NSMutableArray *relatedEvents = [NSMutableArray array];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
__block BOOL shouldCancel = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
SNTFileInfo *originalFile = [[SNTFileInfo alloc] initWithPath:event.filePath];
NSString *bundlePath = originalFile.bundlePath;
originalFile = nil; // release originalFile early.
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:bundlePath];
NSString *file;
while (file = [dirEnum nextObject]) {
@autoreleasepool {
if (shouldCancel) break;
if ([dirEnum fileAttributes][NSFileType] != NSFileTypeRegular) continue;
file = [bundlePath stringByAppendingPathComponent:file];
// Don't record the binary that triggered this event as a related binary.
if ([file isEqual:event.filePath]) continue;
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:file];
if (fi.isExecutable) {
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.filePath = fi.path;
se.fileSHA256 = fi.SHA256;
se.decision = SNTEventStateRelatedBinary;
se.fileBundleID = event.fileBundleID;
se.fileBundleName = event.fileBundleName;
se.fileBundleVersion = event.fileBundleVersion;
se.fileBundleVersionString = event.fileBundleVersionString;
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:se.filePath];
se.signingChain = cs.certificates;
[relatedEvents addObject:[self dictionaryForEvent:se]];
}
}
}
dispatch_semaphore_signal(sema);
});
// Give the search up to 5s per event to run.
// This might need tweaking if it seems to slow down syncing or misses too much to be useful.
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
shouldCancel = YES;
LOGD(@"Timed out while searching for related events. Bundle ID: %@", event.fileBundleID);
}
return relatedEvents;
}
@end

View File

@@ -16,11 +16,11 @@
#import "NSData+Zlib.h"
#include "SNTCommonEnums.h"
#include "SNTLogging.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTCommonEnums.h"
@implementation SNTCommandSyncLogUpload
@@ -48,6 +48,7 @@
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
LOGD(@"%@", error);
handler(NO);
} else {
LOGI(@"Uploaded %lu logs", [logsToUpload count]);
@@ -89,7 +90,7 @@
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:diagsDir];
NSString *file;
while (file = [dirEnum nextObject]) {
if ([[file pathExtension] isEqualToString: @"panic"] ||
if ([[file pathExtension] isEqualToString:@"panic"] ||
[file hasPrefix:@"santad"] ||
[file hasPrefix:@"santactl"]) {
[logsToUpload addObject:[diagsDir stringByAppendingString:file]];

View File

@@ -40,6 +40,7 @@
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
LOGD(@"%@", error);
handler(NO);
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

View File

@@ -62,43 +62,44 @@
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
}
[req setHTTPBody:requestBody];
[req setHTTPBody:requestBody];
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
handler(NO);
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
LOGD(@"%@", error);
handler(NO);
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
syncState.eventBatchSize = [r[kBatchSize] intValue];
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
syncState.eventBatchSize = [r[kBatchSize] intValue];
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
syncState.newClientMode = CLIENTMODE_MONITOR;
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
syncState.newClientMode = CLIENTMODE_LOCKDOWN;
}
if ([r[kWhitelistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setWhitelistPathRegex:r[kWhitelistRegex] reply:^{}];
}
if ([r[kBlacklistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setBlacklistPathRegex:r[kBlacklistRegex] reply:^{}];
}
if ([r[kCleanSync] boolValue]) {
syncState.cleanSync = YES;
}
handler(YES);
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
syncState.newClientMode = SNTClientModeMonitor;
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
syncState.newClientMode = SNTClientModeLockdown;
}
if ([r[kWhitelistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setWhitelistPathRegex:r[kWhitelistRegex] reply:^{}];
}
if ([r[kBlacklistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setBlacklistPathRegex:r[kBlacklistRegex] reply:^{}];
}
if ([r[kCleanSync] boolValue]) {
syncState.cleanSync = YES;
}
handler(YES);
}
}] resume];
}

View File

@@ -44,8 +44,7 @@
syncState:(SNTCommandSyncState *)syncState
daemonConn:(SNTXPCConnection *)daemonConn
completionHandler:(void (^)(BOOL success))handler {
NSDictionary *requestDict = (cursor ? @{ kCursor: cursor } : @{});
NSDictionary *requestDict = (cursor ? @{kCursor : cursor} : @{});
if (!syncState.downloadedRules) {
syncState.downloadedRules = [NSMutableArray array];
@@ -60,51 +59,53 @@
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
long statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
LOGE(@"HTTP Response: %ld %@",
statusCode,
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
LOGD(@"%@", error);
handler(NO);
} else {
NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (!resp) {
LOGE(@"Failed to decode server's response");
handler(NO);
return;
}
NSArray *receivedRules = resp[kRules];
for (NSDictionary *rule in receivedRules) {
SNTRule *r = [self ruleFromDictionary:rule];
if (r) [syncState.downloadedRules addObject:r];
}
if (resp[kCursor]) {
[self ruleDownloadWithCursor:resp[kCursor]
url:url
session:session
syncState:syncState
daemonConn:daemonConn
completionHandler:handler];
} else {
NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (!resp) {
LOGE(@"Failed to decode server's response");
handler(NO);
return;
}
NSArray *receivedRules = resp[kRules];
for (NSDictionary *rule in receivedRules) {
SNTRule *r = [self ruleFromDictionary:rule];
if (r) [syncState.downloadedRules addObject:r];
}
if (resp[kCursor]) {
[self ruleDownloadWithCursor:resp[kCursor]
url:url
session:session
syncState:syncState
daemonConn:daemonConn
completionHandler:handler];
if (syncState.downloadedRules.count) {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
cleanSlate:syncState.cleanSync
reply:^(NSError *error) {
if (!error) {
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
handler(YES);
} else {
LOGE(@"Failed to add rule(s) to database: %@", error.localizedDescription);
LOGD(@"Failure reason: %@", error.localizedFailureReason);
handler(NO);
}
}];
} else {
if (syncState.downloadedRules.count) {
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
cleanSlate:syncState.cleanSync
reply:^(BOOL success) {
if (success) {
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
handler(YES);
} else {
LOGE(@"Failed to add rules to database");
handler(NO);
}
}];
} else {
handler(YES);
}
handler(YES);
}
}
}
}] resume];
}
@@ -117,22 +118,22 @@
NSString *policyString = dict[kRulePolicy];
if ([policyString isEqual:kRulePolicyWhitelist]) {
newRule.state = RULESTATE_WHITELIST;
newRule.state = SNTRuleStateWhitelist;
} else if ([policyString isEqual:kRulePolicyBlacklist]) {
newRule.state = RULESTATE_BLACKLIST;
newRule.state = SNTRuleStateBlacklist;
} else if ([policyString isEqual:kRulePolicySilentBlacklist]) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
newRule.state = SNTRuleStateSilentBlacklist;
} else if ([policyString isEqual:kRulePolicyRemove]) {
newRule.state = RULESTATE_REMOVE;
newRule.state = SNTRuleStateRemove;
} else {
return nil;
}
NSString *ruleTypeString = dict[kRuleType];
if ([ruleTypeString isEqual:kRuleTypeBinary]) {
newRule.type = RULETYPE_BINARY;
newRule.type = SNTRuleTypeBinary;
} else if ([ruleTypeString isEqual:kRuleTypeCertificate]) {
newRule.type = RULETYPE_CERT;
newRule.type = SNTRuleTypeCertificate;
} else {
return nil;
}

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@@ -30,7 +30,7 @@
@property BOOL cleanSync;
/// New client mode sent from server
@property santa_clientmode_t newClientMode;
@property SNTClientMode newClientMode;
/// Batch size for uploading events, sent from server
@property int32_t eventBatchSize;

View File

@@ -43,13 +43,13 @@
- (NSString *)description {
return [NSString stringWithFormat:@"/C=%@/O=%@/OU=%@/CN=%@",
self.countryName,
self.organizationName,
self.organizationalUnit,
self.commonName];
self.countryName,
self.organizationName,
self.organizationalUnit,
self.commonName];
}
# pragma mark Accessors
#pragma mark Accessors
- (NSString *)commonName {
return self.decodedObjects[(__bridge id)kSecOIDCommonName];
@@ -98,29 +98,26 @@
} OIDKeyValue;
static const SecAsn1Template kOIDValueTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OIDKeyValue) },
{ SEC_ASN1_OBJECT_ID, offsetof(OIDKeyValue, oid), NULL, 0 },
{ SEC_ASN1_ANY_CONTENTS, offsetof(OIDKeyValue, value), NULL, 0 },
{ 0, 0, NULL, 0 }
};
{SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OIDKeyValue)},
{SEC_ASN1_OBJECT_ID, offsetof(OIDKeyValue, oid), NULL, 0},
{SEC_ASN1_ANY_CONTENTS, offsetof(OIDKeyValue, value), NULL, 0},
{0, 0, NULL, 0}};
typedef struct {
OIDKeyValue **vals;
} OIDKeyValueList;
static const SecAsn1Template kSetOfOIDValueTemplate[] = {
{ SEC_ASN1_SET_OF, 0, kOIDValueTemplate, sizeof(OIDKeyValueList) },
{ 0, 0, NULL, 0 }
};
{SEC_ASN1_SET_OF, 0, kOIDValueTemplate, sizeof(OIDKeyValueList)},
{0, 0, NULL, 0}};
typedef struct {
OIDKeyValueList **lists;
} OIDKeyValueListSeq;
static const SecAsn1Template kSequenceOfSetOfOIDValueTemplate[] = {
{ SEC_ASN1_SEQUENCE_OF, 0, kSetOfOIDValueTemplate, sizeof(OIDKeyValueListSeq) },
{ 0, 0, NULL, 0 }
};
{SEC_ASN1_SEQUENCE_OF, 0, kSetOfOIDValueTemplate, sizeof(OIDKeyValueListSeq)},
{0, 0, NULL, 0}};
OSStatus err = errSecSuccess;
SecAsn1CoderRef coder;
@@ -143,7 +140,7 @@
// Massage that into a nice dictionary of OID->String pairs.
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
OIDKeyValueList *anAttr;
for (NSUInteger i = 0; (anAttr = a.lists[i]); i++) {
for (NSUInteger i = 0; (anAttr = a.lists[i]); ++i) {
OIDKeyValue *keyValue = anAttr->vals[0];
// Sanity check
@@ -178,7 +175,6 @@
return dict;
}
/**
* Decodes an ASN.1 Object Identifier into a string separated by periods.
* See http://msdn.microsoft.com/en-us/library/bb540809(v=vs.85).aspx for
@@ -200,11 +196,11 @@
if (byte & 0x80) {
inVariableLengthByte = YES;
NSUInteger a = (NSUInteger) (byte & ~0x80);
NSUInteger a = (NSUInteger)(byte & ~0x80);
variableLength = variableLength << 7;
variableLength += a;
} else if (inVariableLengthByte) {
NSUInteger a = (NSUInteger) (byte & ~0x80);
NSUInteger a = (NSUInteger)(byte & ~0x80);
variableLength = variableLength << 7;
variableLength += a;
inVariableLengthByte = NO;

View File

@@ -48,17 +48,17 @@ REGISTER_COMMAND_NAME(@"version")
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
if ([arguments containsObject:@"--json"]) {
NSDictionary *versions = @{
@"santa-driver": [self santaKextVersion],
@"santad": [self santadVersion],
@"santactl": [self santactlVersion],
@"SantaGUI": [self santaAppVersion],
@"santa-driver" : [self santaKextVersion],
@"santad" : [self santadVersion],
@"santactl" : [self santactlVersion],
@"SantaGUI" : [self santaAppVersion],
};
NSData *versionsData = [NSJSONSerialization dataWithJSONObject:versions
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *versionsStr = [[NSString alloc] initWithData:versionsData
encoding:NSUTF8StringEncoding];
printf("%s\n", [versionsStr UTF8String]);
printf("%s\n", [versionsStr UTF8String]);
} else {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
@@ -70,9 +70,8 @@ REGISTER_COMMAND_NAME(@"version")
+ (NSString *)santaKextVersion {
NSDictionary *loadedKexts = CFBridgingRelease(
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef)@[ @(USERCLIENT_ID) ],
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
);
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef) @[ @(USERCLIENT_ID) ],
(__bridge CFArrayRef) @[ @"CFBundleVersion" ]));
if (loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];

View File

@@ -20,6 +20,6 @@
///
/// Begins fielding requests from the driver
///
- (void)run;
- (void)start;
@end

View File

@@ -12,14 +12,12 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTApplication.h"
#include <sys/stat.h>
#include <sys/types.h>
#import "SNTApplication.h"
#include "SNTCommonEnums.h"
#include "SNTLogging.h"
#import "SNTCommonEnums.h"
#import "SNTConfigurator.h"
#import "SNTDaemonControlController.h"
#import "SNTDatabaseController.h"
@@ -28,20 +26,18 @@
#import "SNTEventTable.h"
#import "SNTExecutionController.h"
#import "SNTFileWatcher.h"
#import "SNTLogging.h"
#import "SNTNotificationQueue.h"
#import "SNTRuleTable.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
#import "SNTXPCNotifierInterface.h"
@interface SNTApplication ()
@property SNTDriverManager *driverManager;
@property SNTEventLog *eventLog;
@property SNTEventTable *eventTable;
@property SNTExecutionController *execController;
@property SNTFileWatcher *configFileWatcher;
@property SNTRuleTable *ruleTable;
@property SNTXPCConnection *controlConnection;
@property SNTXPCConnection *notifierConnection;
@end
@implementation SNTApplication
@@ -54,54 +50,49 @@
if (!_driverManager) {
LOGE(@"Failed to connect to driver, exiting.");
// TODO(rah): Consider trying to load the extension from within santad.
return nil;
}
// Initialize tables
_ruleTable = [SNTDatabaseController ruleTable];
if (!_ruleTable) {
SNTRuleTable *ruleTable = [SNTDatabaseController ruleTable];
if (!ruleTable) {
LOGE(@"Failed to initialize rule table.");
return nil;
}
_eventTable = [SNTDatabaseController eventTable];
if (!_eventTable) {
SNTEventTable *eventTable = [SNTDatabaseController eventTable];
if (!eventTable) {
LOGE(@"Failed to initialize event table.");
return nil;
}
// Establish XPC listener for GUI agent connections
_notifierConnection =
[[SNTXPCConnection alloc] initServerWithName:[SNTXPCNotifierInterface serviceId]];
_notifierConnection.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
[_notifierConnection resume];
SNTNotificationQueue *notQueue = [[SNTNotificationQueue alloc] init];
// Establish XPC listener for santactl connections
SNTDaemonControlController *dc = [[SNTDaemonControlController alloc] init];
dc.driverManager = _driverManager;
dc.notQueue = notQueue;
_controlConnection =
[[SNTXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceId]];
_controlConnection.exportedInterface = [SNTXPCControlInterface controlInterface];
_controlConnection.exportedObject =
[[SNTDaemonControlController alloc] initWithDriverManager:_driverManager];
_controlConnection.exportedObject = dc;
[_controlConnection resume];
_configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^{
[[SNTConfigurator configurator] reloadConfigData];
_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);
// Ensure config file remains root:wheel 0644
chown([kDefaultConfigFilePath fileSystemRepresentation], 0, 0);
chmod([kDefaultConfigFilePath fileSystemRepresentation], 0644);
}];
_eventLog = [[SNTEventLog alloc] init];
// Initialize the binary checker object
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
ruleTable:_ruleTable
eventTable:_eventTable
notifierConnection:_notifierConnection
ruleTable:ruleTable
eventTable:eventTable
notifierQueue:notQueue
eventLog:_eventLog];
if (!_execController) return nil;
}
@@ -109,53 +100,74 @@
return self;
}
- (void)run {
- (void)start {
LOGI(@"Connected to driver, activating.");
// 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 performSelectorInBackground:@selector(beginListeningForDecisionRequests) withObject:nil];
[self performSelectorInBackground:@selector(beginListeningForLogRequests) withObject:nil];
}
[self.driverManager listenWithBlock:^(santa_message_t message) {
@autoreleasepool {
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
exit(0);
}
case ACTION_NOTIFY_DELETE:
case ACTION_NOTIFY_EXCHANGE:
case ACTION_NOTIFY_LINK:
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
dispatch_async(q, ^{
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self.eventLog logFileModification:message];
}
});
break;
}
case ACTION_NOTIFY_EXEC: {
dispatch_async(q, ^{
[self.eventLog logAllowedExecution:message];
});
break;
}
case ACTION_REQUEST_CHECKBW: {
dispatch_async(q, ^{
[self.execController validateBinaryWithMessage:message];
});
break;
}
default: {
LOGE(@"Received request without a valid action: %d", message.action);
exit(1);
}
- (void)beginListeningForDecisionRequests {
dispatch_queue_t exec_queue = dispatch_queue_create(
"com.google.santad.execution_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(exec_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
[self.driverManager listenForDecisionRequests:^(santa_message_t message) {
@autoreleasepool {
switch (message.action) {
case ACTION_REQUEST_SHUTDOWN: {
LOGI(@"Driver requested a shutdown");
exit(0);
}
case ACTION_REQUEST_BINARY: {
dispatch_async(exec_queue, ^{
[self.execController validateBinaryWithMessage:message];
});
break;
}
default: {
LOGE(@"Received decision request without a valid action: %d", message.action);
exit(1);
}
}
}
}];
}
- (void)beginListeningForLogRequests {
dispatch_queue_t log_queue = dispatch_queue_create(
"com.google.santad.log_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_set_target_queue(
log_queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
[self.driverManager listenForLogRequests:^(santa_message_t message) {
@autoreleasepool {
switch (message.action) {
case ACTION_NOTIFY_DELETE:
case ACTION_NOTIFY_EXCHANGE:
case ACTION_NOTIFY_LINK:
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
dispatch_async(log_queue, ^{
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self.eventLog logFileModification:message];
}
});
break;
}
case ACTION_NOTIFY_EXEC: {
dispatch_async(log_queue, ^{
[self.eventLog logAllowedExecution:message];
});
break;
}
default:
LOGE(@"Received log request without a valid action: %d", message.action);
break;
}
}
}];
}

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
///
/// Store information about executions from decision making for later logging.
@@ -20,11 +20,12 @@
@interface SNTCachedDecision : NSObject
@property uint64_t vnodeId;
@property santa_eventstate_t decision;
@property SNTEventState decision;
@property NSString *decisionExtra;
@property NSString *sha256;
@property NSString *certSHA256;
@property NSString *certCommonName;
@property NSString *quarantineURL;
@property NSString *customMsg;
@property BOOL silentBlock;

View File

@@ -15,6 +15,7 @@
#import "SNTXPCControlInterface.h"
@class SNTDriverManager;
@class SNTNotificationQueue;
///
/// SNTDaemonControlController handles all of the RPCs from santactl
@@ -22,7 +23,6 @@
@interface SNTDaemonControlController : NSObject<SNTDaemonControlXPC>
@property SNTDriverManager *driverManager;
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager;
@property SNTNotificationQueue *notQueue;
@end

View File

@@ -20,12 +20,17 @@
#import "SNTDropRootPrivs.h"
#import "SNTEventTable.h"
#import "SNTLogging.h"
#import "SNTNotificationQueue.h"
#import "SNTRule.h"
#import "SNTRuleTable.h"
#import "SNTXPCConnection.h"
#import "SNTXPCNotifierInterface.h"
// Globals used by the santad watchdog thread
uint64_t watchdogCPUEvents = 0;
uint64_t watchdogRAMEvents = 0;
double watchdogCPUPeak = 0;
double watchdogRAMPeak = 0;
@interface SNTDaemonControlController ()
@property dispatch_source_t syncTimer;
@@ -33,11 +38,9 @@ uint64_t watchdogRAMEvents = 0;
@implementation SNTDaemonControlController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager {
- (instancetype)init {
self = [super init];
if (self) {
_driverManager = driverManager;
_syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
}
@@ -50,19 +53,19 @@ uint64_t watchdogRAMEvents = 0;
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_set_event_handler(syncTimerQ, ^{
[self rescheduleSyncSecondsFromNow:600];
[self rescheduleSyncSecondsFromNow:600];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
}
});
dispatch_resume(syncTimerQ);
@@ -94,23 +97,20 @@ uint64_t watchdogRAMEvents = 0;
reply([rdb binaryRuleCount], [rdb certificateRuleCount]);
}
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
[self databaseRuleAddRules:@[ rule ] cleanSlate:cleanSlate reply:reply];
}
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
BOOL success = [[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(NSError *error))reply {
NSError *error;
[[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate error:&error];
// If any rules were added that were not whitelist, flush cache.
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", RULESTATE_WHITELIST];
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", SNTRuleStateWhitelist];
if ([rules filteredArrayUsingPredicate:p].count || cleanSlate) {
LOGI(@"Received non-whitelist rule, flushing cache");
[self.driverManager flushCache];
}
reply(success);
reply(error);
}
- (void)databaseEventCount:(void (^)(int64_t count))reply {
@@ -129,13 +129,13 @@ uint64_t watchdogRAMEvents = 0;
[[SNTDatabaseController eventTable] deleteEventsWithIds:ids];
}
#pragma mark Misc
#pragma mark Config Ops
- (void)clientMode:(void (^)(santa_clientmode_t))reply {
- (void)clientMode:(void (^)(SNTClientMode))reply {
reply([[SNTConfigurator configurator] clientMode]);
}
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply {
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply {
[[SNTConfigurator configurator] setClientMode:mode];
reply();
}
@@ -172,12 +172,17 @@ uint64_t watchdogRAMEvents = 0;
reply();
}
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply {
reply(watchdogCPUEvents);
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply {
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
}
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply {
reply(watchdogRAMEvents);
#pragma mark GUI Ops
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener {
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
c.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
[c resume];
self.notQueue.notifierConnection = c;
}
@end

View File

@@ -14,8 +14,8 @@
#import "SNTDatabaseController.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#import "SNTEventTable.h"
#import "SNTLogging.h"
@@ -23,22 +23,24 @@
@implementation SNTDatabaseController
static NSString * const kDatabasePath = @"/var/db/santa";
static NSString * const kRulesDatabaseName = @"rules.db";
static NSString * const kEventsDatabaseName = @"events.db";
static NSString *const kDatabasePath = @"/var/db/santa";
static NSString *const kRulesDatabaseName = @"rules.db";
static NSString *const kEventsDatabaseName = @"events.db";
+ (SNTEventTable *)eventTable {
static FMDatabaseQueue *eventDatabaseQueue = nil;
static dispatch_once_t eventDatabaseToken;
dispatch_once(&eventDatabaseToken, ^{
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
#ifndef DEBUG
[eventDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
[eventDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#endif
});
@@ -49,14 +51,16 @@ 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];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
[self createDatabasePath];
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
chown([fullPath UTF8String], 0, 0);
chmod([fullPath UTF8String], 0600);
#ifndef DEBUG
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) {
db.logsErrors = NO;
}];
#endif
});
return [[SNTRuleTable alloc] initWithDatabaseQueue:ruleDatabaseQueue];

View File

@@ -56,13 +56,13 @@
/// database exists and uses the latest schema.
- (void)updateTableSchema {
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
uint32_t currentVersion = [db userVersion];
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
if (newVersion < 1) return;
uint32_t currentVersion = [db userVersion];
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
if (newVersion < 1) return;
LOGI(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
LOGI(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
[db setUserVersion:newVersion];
[db setUserVersion:newVersion];
}];
}

View File

@@ -32,7 +32,13 @@
/// 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.
///
- (void)listenWithBlock:(void (^)(santa_message_t message))callback;
- (void)listenForDecisionRequests:(void (^)(santa_message_t message))callback;
///
/// 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.
///
- (void)listenForLogRequests:(void (^)(santa_message_t message))callback;
///
/// Sends a response to a query back to the kernel.

View File

@@ -14,15 +14,13 @@
#import "SNTDriverManager.h"
#include <IOKit/IODataQueueClient.h>
#include <mach/mach.h>
#import <IOKit/IODataQueueClient.h>
#import <IOKit/Kext/KextManager.h>
#include "SNTLogging.h"
#import "SNTLogging.h"
@interface SNTDriverManager ()
@property IODataQueueMemory *queueMemory;
@property io_connect_t connection;
@property mach_port_t receivePort;
@end
@implementation SNTDriverManager
@@ -43,6 +41,9 @@ static const int MAX_DELAY = 15;
return nil;
}
// Attempt to load driver. It may already be running, so ignore any return value.
KextManagerLoadKextWithIdentifier(CFSTR(USERCLIENT_ID), NULL);
// Locate driver. Wait for it if necessary.
int delay = 1;
do {
@@ -57,7 +58,7 @@ static const int MAX_DELAY = 15;
} while (!serviceObject);
CFRelease(classToMatch);
// This calls @c initWithTask, @c attach and @c start in @c SantaDriverClient
// This calls `initWithTask`, `attach` and `start` in `SantaDriverClient`
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &_connection);
IOObjectRelease(serviceObject);
if (kr != kIOReturnSuccess) {
@@ -65,7 +66,7 @@ static const int MAX_DELAY = 15;
return nil;
}
// Call @c open in @c SantaDriverClient
// Call `open` in `SantaDriverClient`
kr = IOConnectCallMethod(_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
if (kr == kIOReturnExclusiveAccess) {
@@ -85,70 +86,78 @@ static const int MAX_DELAY = 15;
#pragma mark Incoming messages
- (void)listenWithBlock:(void (^)(santa_message_t message))callback {
- (void)listenForDecisionRequests:(void (^)(santa_message_t))callback {
[self listenForRequestsOfType:QUEUETYPE_DECISION withCallback:callback];
}
- (void)listenForLogRequests:(void (^)(santa_message_t))callback {
[self listenForRequestsOfType:QUEUETYPE_LOG withCallback:callback];
}
- (void)listenForRequestsOfType:(santa_queuetype_t)type
withCallback:(void (^)(santa_message_t))callback {
kern_return_t kr;
mach_port_t receivePort = 0;
IODataQueueMemory *queueMemory = NULL;
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
unsigned int msgType = 1;
// Allocate a mach port to receive notifactions from the IODataQueue
if (!(self.receivePort = IODataQueueAllocateNotificationPort())) {
if (!(receivePort = IODataQueueAllocateNotificationPort())) {
LOGD(@"Failed to allocate notification port");
return;
}
// This will call registerNotificationPort() inside our user client class
kr = IOConnectSetNotificationPort(self.connection, msgType, self.receivePort, 0);
kr = IOConnectSetNotificationPort(self.connection, type, receivePort, 0);
if (kr != kIOReturnSuccess) {
LOGD(@"Failed to register notification port: %d", kr);
mach_port_destroy(mach_task_self(), self.receivePort);
LOGD(@"Failed to register notification port for type %d: %d", type, kr);
mach_port_destroy(mach_task_self(), receivePort);
return;
}
// 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(),
kr = IOConnectMapMemory(self.connection, type, mach_task_self(),
&address, &size, kIOMapAnywhere);
if (kr != kIOReturnSuccess) {
LOGD(@"Failed to map memory: %d", kr);
mach_port_destroy(mach_task_self(), self.receivePort);
LOGD(@"Failed to map memory for type %d: %d", type, kr);
mach_port_destroy(mach_task_self(), receivePort);
return;
}
self.queueMemory = (IODataQueueMemory *)address;
queueMemory = (IODataQueueMemory *)address;
do {
while (IODataQueueDataAvailable(self.queueMemory)) {
while (IODataQueueDataAvailable(queueMemory)) {
santa_message_t vdata;
uint32_t dataSize = sizeof(vdata);
kr = IODataQueueDequeue(self.queueMemory, &vdata, &dataSize);
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
callback(vdata);
} else {
LOGE(@"Error dequeuing data: %d", kr);
LOGE(@"Error dequeuing data for type %d: %d", type, kr);
exit(2);
}
}
} while (IODataQueueWaitForAvailableData(self.queueMemory, self.receivePort) == kIOReturnSuccess);
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), self.receivePort);
IOConnectUnmapMemory(self.connection, type, mach_task_self(), address);
mach_port_destroy(mach_task_self(), receivePort);
}
#pragma mark Outgoing messages
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeId {
switch (action) {
case ACTION_RESPOND_CHECKBW_ALLOW:
case ACTION_RESPOND_ALLOW:
return IOConnectCallScalarMethod(self.connection,
kSantaUserClientAllowBinary,
&vnodeId,
1,
0,
0);
case ACTION_RESPOND_CHECKBW_DENY:
case ACTION_RESPOND_DENY:
return IOConnectCallScalarMethod(self.connection,
kSantaUserClientDenyBinary,
&vnodeId,
@@ -174,8 +183,8 @@ static const int MAX_DELAY = 15;
}
- (BOOL)flushCache {
return IOConnectCallScalarMethod(
self.connection, kSantaUserClientClearCache, 0, 0, 0, 0) == KERN_SUCCESS;
return IOConnectCallScalarMethod(self.connection,
kSantaUserClientClearCache, 0, 0, 0, 0) == KERN_SUCCESS;
}
@end

View File

@@ -12,7 +12,6 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "SNTKernelCommon.h"
@class SNTCachedDecision;

View File

@@ -14,11 +14,13 @@
#import "SNTEventLog.h"
#include <grp.h>
#include <libproc.h>
#include <pwd.h>
#include <sys/sysctl.h>
#import "SNTCachedDecision.h"
#import "MOLCertificate.h"
#import "SNTCachedDecision.h"
#import "SNTCommonEnums.h"
#import "SNTFileInfo.h"
#import "SNTKernelCommon.h"
@@ -84,13 +86,19 @@
if (newpath) {
outStr = [outStr stringByAppendingFormat:@"|newpath=%@", [self sanitizeString:newpath]];
}
char ppath[PATH_MAX];
if (proc_pidpath(message.pid, ppath, PATH_MAX) < 1) {
strncpy(ppath, "(null)", 6);
}
outStr =
[outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|gid=%d",
message.pid, message.ppid, message.pname, ppath, message.uid, message.gid];
char ppath[PATH_MAX] = "(null)";
proc_pidpath(message.pid, ppath, PATH_MAX);
NSString *user, *group;
struct passwd *pw = getpwuid(message.uid);
if (pw) user = @(pw->pw_name);
struct group *gr = getgrgid(message.gid);
if (gr) group = @(gr->gr_name);
outStr = [outStr stringByAppendingFormat:(@"|pid=%d|ppid=%d|process=%s|processpath=%s|"
@"uid=%d|user=%@|gid=%d|group=%@"),
message.pid, message.ppid, message.pname, ppath,
message.uid, user, message.gid, group];
if (sha256) {
outStr = [outStr stringByAppendingFormat:@"|sha256=%@", sha256];
}
@@ -111,24 +119,47 @@
NSString *d, *r, *args, *outLog;
switch (cd.decision) {
case EVENTSTATE_ALLOW_BINARY:
d = @"ALLOW"; r = @"BINARY"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_CERTIFICATE:
d = @"ALLOW"; r = @"CERTIFICATE"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_SCOPE:
d = @"ALLOW"; r = @"SCOPE"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_ALLOW_UNKNOWN:
d = @"ALLOW"; r = @"UNKNOWN"; args = [self argsForPid:message.pid]; break;
case EVENTSTATE_BLOCK_BINARY:
d = @"DENY"; r = @"BINARY"; break;
case EVENTSTATE_BLOCK_CERTIFICATE:
d = @"DENY"; r = @"CERT"; break;
case EVENTSTATE_BLOCK_SCOPE:
d = @"DENY"; r = @"SCOPE"; break;
case EVENTSTATE_BLOCK_UNKNOWN:
d = @"DENY"; r = @"UNKNOWN"; break;
case SNTEventStateAllowBinary:
d = @"ALLOW";
r = @"BINARY";
args = [self argsForPid:message.pid];
break;
case SNTEventStateAllowCertificate:
d = @"ALLOW";
r = @"CERTIFICATE";
args = [self argsForPid:message.pid];
break;
case SNTEventStateAllowScope:
d = @"ALLOW";
r = @"SCOPE";
args = [self argsForPid:message.pid];
break;
case SNTEventStateAllowUnknown:
d = @"ALLOW";
r = @"UNKNOWN";
args = [self argsForPid:message.pid];
break;
case SNTEventStateBlockBinary:
d = @"DENY";
r = @"BINARY";
break;
case SNTEventStateBlockCertificate:
d = @"DENY";
r = @"CERT";
break;
case SNTEventStateBlockScope:
d = @"DENY";
r = @"SCOPE";
break;
case SNTEventStateBlockUnknown:
d = @"DENY";
r = @"UNKNOWN";
break;
default:
d = @"ALLOW"; r = @"NOTRUNNING"; args = [self argsForPid:message.pid]; break;
d = @"ALLOW";
r = @"NOTRUNNING";
args = [self argsForPid:message.pid];
break;
}
outLog = [NSString stringWithFormat:@"action=EXEC|decision=%@|reason=%@", d, r];
@@ -137,16 +168,29 @@
outLog = [outLog stringByAppendingFormat:@"|explain=%@", cd.decisionExtra];
}
outLog = [outLog stringByAppendingFormat:@"|sha256=%@|path=%@|args=%@",
cd.sha256, [self sanitizeString:@(message.path)], [self sanitizeString:args]];
outLog = [outLog stringByAppendingFormat:@"|sha256=%@|path=%@|args=%@", cd.sha256,
[self sanitizeString:@(message.path)],
[self sanitizeString:args]];
if (cd.certSHA256) {
outLog = [outLog stringByAppendingFormat:@"|cert_sha256=%@|cert_cn=%@",
cd.certSHA256, [self sanitizeString:cd.certCommonName]];
outLog = [outLog stringByAppendingFormat:@"|cert_sha256=%@|cert_cn=%@", cd.certSHA256,
[self sanitizeString:cd.certCommonName]];
}
outLog = [outLog stringByAppendingFormat:@"|pid=%d|ppid=%d|uid=%d|gid=%d",
message.pid, message.ppid, message.uid, message.gid];
if (cd.quarantineURL) {
outLog = [outLog stringByAppendingFormat:@"|quarantine_url=%@",
[self sanitizeString:cd.quarantineURL]];
}
NSString *user, *group;
struct passwd *pw = getpwuid(message.uid);
if (pw) user = @(pw->pw_name);
struct group *gr = getgrgid(message.gid);
if (gr) group = @(gr->gr_name);
outLog = [outLog stringByAppendingFormat:@"|pid=%d|ppid=%d|uid=%d|user=%@|gid=%d|group=%@",
message.pid, message.ppid, message.uid, user,
message.gid, group];
LOGI(@"%@", outLog);
}
@@ -187,11 +231,10 @@
memcpy(&argc, argsdatabytes, sizeof(argc));
// Get pointer to beginning of string space
char *cp;
cp = (char *) argsdatabytes + sizeof(argc);
char *cp = (char *)argsdatabytes + sizeof(argc);
// Skip over exec_path
for (; cp < &argsdatabytes[size]; cp++) {
for (; cp < &argsdatabytes[size]; ++cp) {
if (*cp == '\0') {
cp++;
break;
@@ -199,13 +242,15 @@
}
// Skip trailing NULL bytes
for (; cp < &argsdatabytes[size]; cp++) if (*cp != '\0') break;
for (; cp < &argsdatabytes[size]; ++cp) {
if (*cp != '\0') break;
}
// Loop over the argv array, stripping newlines in each arg and putting in a new array.
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argc];
for (int i = 0; i < argc; i++) {
for (int i = 0; i < argc; ++i) {
NSString *arg = @(cp);
[args addObject:arg];
if (arg) [args addObject:arg];
// Move the pointer past this string and the terminator at the end.
cp += strlen(cp) + 1;

View File

@@ -32,6 +32,40 @@
newVersion = 1;
}
if (version < 2) {
// Clean-up: Find events where the bundle details might not be strings and update them.
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
while ([rs next]) {
SNTStoredEvent *se = [self eventFromResultSet:rs];
if (!se) continue;
Class NSStringClass = [NSString class];
if ([se.fileBundleID class] != NSStringClass) {
se.fileBundleID = [se.fileBundleID description];
}
if ([se.fileBundleName class] != NSStringClass) {
se.fileBundleName = [se.fileBundleName description];
}
if ([se.fileBundleVersion class] != NSStringClass) {
se.fileBundleVersion = [se.fileBundleVersion description];
}
if ([se.fileBundleVersionString class] != NSStringClass) {
se.fileBundleVersionString = [se.fileBundleVersionString description];
}
NSData *eventData;
NSNumber *idx = [rs objectForColumnName:@"idx"];
@try {
eventData = [NSKeyedArchiver archivedDataWithRootObject:se];
[db executeUpdate:@"UPDATE events SET eventdata=? WHERE idx=?", eventData, idx];
} @catch (NSException *exception) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", idx];
}
}
[rs close];
newVersion = 2;
}
return newVersion;
}
@@ -52,8 +86,8 @@
__block BOOL success = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
success = [db executeUpdate:@"INSERT INTO 'events' (filesha256, eventdata) VALUES (?, ?)",
event.fileSHA256, eventData];
success = [db executeUpdate:@"INSERT INTO 'events' (filesha256, eventdata) VALUES (?, ?)",
event.fileSHA256, eventData];
}];
return success;
@@ -64,7 +98,7 @@
- (NSUInteger)pendingEventsCount {
__block NSUInteger eventsPending = 0;
[self inDatabase:^(FMDatabase *db) {
eventsPending = [db intForQuery:@"SELECT COUNT(*) FROM events"];
eventsPending = [db intForQuery:@"SELECT COUNT(*) FROM events"];
}];
return eventsPending;
}
@@ -73,17 +107,17 @@
__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];
if (!storedEvent) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
if ([rs next]) {
storedEvent = [self eventFromResultSet:rs];
if (!storedEvent) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
[rs close];
}];
return storedEvent;
@@ -93,18 +127,18 @@
NSMutableArray *pendingEvents = [[NSMutableArray alloc] init];
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
while ([rs next]) {
id obj = [self eventFromResultSet:rs];
if (obj) {
[pendingEvents addObject:obj];
} else {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
while ([rs next]) {
id obj = [self eventFromResultSet:rs];
if (obj) {
[pendingEvents addObject:obj];
} else {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
[rs close];
}];
return pendingEvents;
@@ -119,7 +153,8 @@
@try {
event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
} @catch (NSException *exception) {}
} @catch (NSException *exception) {
}
return event;
}
@@ -128,7 +163,7 @@
- (void)deleteEventWithId:(NSNumber *)index {
[self inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", index];
[db executeUpdate:@"DELETE FROM events WHERE idx=?", index];
}];
}
@@ -137,7 +172,7 @@
[self deleteEventWithId:index];
}
[self inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"VACUUM"];
[db executeUpdate:@"VACUUM"];
}];
}

View File

@@ -12,15 +12,15 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
#import "SNTCommonEnums.h"
#include "SNTKernelCommon.h"
@class MOLCodesignChecker;
@class SNTDriverManager;
@class SNTEventLog;
@class SNTEventTable;
@class SNTNotificationQueue;
@class SNTRuleTable;
@class SNTXPCConnection;
///
/// SNTExecutionController is responsible for everything that happens when a request to execute
@@ -37,13 +37,13 @@
@property SNTDriverManager *driverManager;
@property SNTEventLog *eventLog;
@property SNTEventTable *eventTable;
@property SNTNotificationQueue *notifierQueue;
@property SNTRuleTable *ruleTable;
@property SNTXPCConnection *notifierConnection;
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierConnection:(SNTXPCConnection *)notifierConn
notifierQueue:(SNTNotificationQueue *)notifierQueue
eventLog:(SNTEventLog *)eventLog;
///

View File

@@ -20,9 +20,9 @@
#include "SNTLogging.h"
#import "SNTCachedDecision.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTCachedDecision.h"
#import "SNTCommonEnums.h"
#import "SNTConfigurator.h"
#import "SNTDriverManager.h"
@@ -30,11 +30,10 @@
#import "SNTEventLog.h"
#import "SNTEventTable.h"
#import "SNTFileInfo.h"
#import "SNTNotificationQueue.h"
#import "SNTRule.h"
#import "SNTRuleTable.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCNotifierInterface.h"
@implementation SNTExecutionController
@@ -43,17 +42,16 @@
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
ruleTable:(SNTRuleTable *)ruleTable
eventTable:(SNTEventTable *)eventTable
notifierConnection:(SNTXPCConnection *)notifier
notifierQueue:(SNTNotificationQueue *)notifierQueue
eventLog:(SNTEventLog *)eventLog {
self = [super init];
if (self) {
_driverManager = driverManager;
_ruleTable = ruleTable;
_eventTable = eventTable;
_notifierConnection = notifier;
_notifierQueue = notifierQueue;
_eventLog = eventLog;
// Workaround for xpcproxy/libsecurity bug on Yosemite
// This establishes the XPC connection between libsecurity and syspolicyd.
// Not doing this causes a deadlock as establishing this link goes through xpcproxy.
(void)[[MOLCodesignChecker alloc] initWithSelf];
@@ -63,17 +61,17 @@
#pragma mark Binary Validation
- (santa_eventstate_t)makeDecision:(SNTCachedDecision *)cd binaryInfo:(SNTFileInfo *)fi {
- (SNTEventState)makeDecision:(SNTCachedDecision *)cd binaryInfo:(SNTFileInfo *)fi {
SNTRule *rule = [self.ruleTable binaryRuleForSHA256:cd.sha256];
if (rule) {
switch (rule.state) {
case RULESTATE_WHITELIST:
return EVENTSTATE_ALLOW_BINARY;
case RULESTATE_SILENT_BLACKLIST:
case SNTRuleStateWhitelist:
return SNTEventStateAllowBinary;
case SNTRuleStateSilentBlacklist:
cd.silentBlock = YES;
case RULESTATE_BLACKLIST:
case SNTRuleStateBlacklist:
cd.customMsg = rule.customMsg;
return EVENTSTATE_BLOCK_BINARY;
return SNTEventStateBlockBinary;
default: break;
}
}
@@ -81,13 +79,13 @@
rule = [self.ruleTable certificateRuleForSHA256:cd.certSHA256];
if (rule) {
switch (rule.state) {
case RULESTATE_WHITELIST:
return EVENTSTATE_ALLOW_CERTIFICATE;
case RULESTATE_SILENT_BLACKLIST:
case SNTRuleStateWhitelist:
return SNTEventStateAllowCertificate;
case SNTRuleStateSilentBlacklist:
cd.silentBlock = YES;
case RULESTATE_BLACKLIST:
case SNTRuleStateBlacklist:
cd.customMsg = rule.customMsg;
return EVENTSTATE_BLOCK_CERTIFICATE;
return SNTEventStateBlockCertificate;
default: break;
}
}
@@ -95,19 +93,19 @@
NSString *msg = [self fileIsScopeBlacklisted:fi];
if (msg) {
cd.decisionExtra = msg;
return EVENTSTATE_BLOCK_SCOPE;
return SNTEventStateBlockScope;
}
msg = [self fileIsScopeWhitelisted:fi];
if (msg) {
cd.decisionExtra = msg;
return EVENTSTATE_ALLOW_SCOPE;
return SNTEventStateAllowScope;
}
switch ([[SNTConfigurator configurator] clientMode]) {
case CLIENTMODE_MONITOR: return EVENTSTATE_ALLOW_UNKNOWN;
case CLIENTMODE_LOCKDOWN: return EVENTSTATE_BLOCK_UNKNOWN;
default: return EVENTSTATE_BLOCK_UNKNOWN;
case SNTClientModeMonitor: return SNTEventStateAllowUnknown;
case SNTClientModeLockdown: return SNTEventStateBlockUnknown;
default: return SNTEventStateBlockUnknown;
}
}
@@ -117,7 +115,7 @@
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@(message.path) error:&fileInfoError];
if (!binInfo) {
LOGW(@"Failed to read file %@: %@", binInfo.path, fileInfoError.localizedDescription);
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
[self.driverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:message.vnode_id];
return;
}
@@ -131,20 +129,20 @@
cd.certCommonName = csInfo.leafCertificate.commonName;
cd.certSHA256 = csInfo.leafCertificate.SHA256;
cd.vnodeId = message.vnode_id;
cd.quarantineURL = binInfo.quarantineDataURL;
cd.decision = [self makeDecision:cd binaryInfo:binInfo];
// Save decision details for logging the execution later.
santa_action_t action = [self actionForEventState:cd.decision];
if (action == ACTION_RESPOND_CHECKBW_ALLOW) [self.eventLog saveDecisionDetails:cd];
if (action == ACTION_RESPOND_ALLOW) [self.eventLog saveDecisionDetails:cd];
// Send the decision to the kernel.
[self.driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
// Log to database if necessary.
if (cd.decision != EVENTSTATE_ALLOW_BINARY &&
cd.decision != EVENTSTATE_ALLOW_CERTIFICATE &&
cd.decision != EVENTSTATE_ALLOW_SCOPE) {
if (cd.decision != SNTEventStateAllowBinary &&
cd.decision != SNTEventStateAllowCertificate &&
cd.decision != SNTEventStateAllowScope) {
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
se.occurrenceDate = [[NSDate alloc] init];
se.fileSHA256 = cd.sha256;
@@ -168,7 +166,6 @@
}
struct passwd *user = getpwuid(message.uid);
endpwent();
if (user) {
se.executingUser = @(user->pw_name);
}
@@ -181,12 +178,12 @@
se.quarantineDataURL = binInfo.quarantineDataURL;
se.quarantineRefererURL = binInfo.quarantineRefererURL;
se.quarantineTimestamp = binInfo.quarantineTimestamp;
se.quarantineAgentBundleID = binInfo.quarantineAgentBundleID;
se.quarantineAgentBundleID = binInfo.quarantineAgentBundleID;
[self.eventTable addStoredEvent:se];
// If binary was blocked, do the needful
if (action != ACTION_RESPOND_CHECKBW_ALLOW) {
if (action != ACTION_RESPOND_ALLOW) {
[self.eventLog logDeniedExecution:cd withMessage:message];
// So the server has something to show the user straight away, initiate an event
@@ -194,8 +191,7 @@
[self initiateEventUploadForEvent:se];
if (!cd.silentBlock) {
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:cd.customMsg];
[self.notifierQueue addEvent:se customMessage:cd.customMsg];
}
}
}
@@ -259,21 +255,21 @@
}
}
- (santa_action_t)actionForEventState:(santa_eventstate_t)state {
- (santa_action_t)actionForEventState:(SNTEventState)state {
switch (state) {
case EVENTSTATE_ALLOW_BINARY:
case EVENTSTATE_ALLOW_CERTIFICATE:
case EVENTSTATE_ALLOW_SCOPE:
case EVENTSTATE_ALLOW_UNKNOWN:
return ACTION_RESPOND_CHECKBW_ALLOW;
case EVENTSTATE_BLOCK_BINARY:
case EVENTSTATE_BLOCK_CERTIFICATE:
case EVENTSTATE_BLOCK_SCOPE:
case EVENTSTATE_BLOCK_UNKNOWN:
return ACTION_RESPOND_CHECKBW_DENY;
case SNTEventStateAllowBinary:
case SNTEventStateAllowCertificate:
case SNTEventStateAllowScope:
case SNTEventStateAllowUnknown:
return ACTION_RESPOND_ALLOW;
case SNTEventStateBlockBinary:
case SNTEventStateBlockCertificate:
case SNTEventStateBlockScope:
case SNTEventStateBlockUnknown:
return ACTION_RESPOND_DENY;
default:
LOGW(@"Invalid event state %d", state);
return ACTION_RESPOND_CHECKBW_DENY;
LOGW(@"Invalid event state %ld", state);
return ACTION_RESPOND_DENY;
}
}
@@ -285,7 +281,7 @@
while ((nxt = getutxent())) {
if (nxt->ut_type != USER_PROCESS) continue;
NSString *userName = [NSString stringWithUTF8String:nxt->ut_user];
NSString *userName = @(nxt->ut_user);
NSString *sessionName;
if (strnlen(nxt->ut_host, 1) > 0) {

View File

@@ -0,0 +1,24 @@
/// Copyright 2016 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.
@class SNTStoredEvent;
@class SNTXPCConnection;
@interface SNTNotificationQueue : NSObject
@property(nonatomic) SNTXPCConnection *notifierConnection;
- (void)addEvent:(SNTStoredEvent *)event customMessage:(NSString *)message;
@end

View File

@@ -0,0 +1,77 @@
/// Copyright 2016 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 "SNTNotificationQueue.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
#import "SNTXPCConnection.h"
#import "SNTXPCNotifierInterface.h"
static const int kMaximumNotifications = 10;
@interface SNTNotificationQueue ()
@property NSMutableArray *pendingNotifications;
@end
@implementation SNTNotificationQueue
- (instancetype)init {
self = [super init];
if (self) {
_pendingNotifications = [NSMutableArray array];
}
return self;
}
- (void)addEvent:(SNTStoredEvent *)event customMessage:(NSString *)message {
if (!event) return;
if (self.pendingNotifications.count > kMaximumNotifications) {
LOGI(@"Pending GUI notification count is over %d, dropping.", kMaximumNotifications);
return;
}
NSDictionary *d;
if (message) {
d = @{@"event" : event,
@"message" : message};
} else {
d = @{@"event" : event};
}
@synchronized(self.pendingNotifications) {
[self.pendingNotifications addObject:d];
}
[self flushQueue];
}
- (void)flushQueue {
id rop = [self.notifierConnection remoteObjectProxy];
if (!rop) return;
@synchronized(self.pendingNotifications) {
NSMutableArray *postedNotifications = [NSMutableArray array];
for (NSDictionary *d in self.pendingNotifications) {
[rop postBlockNotification:d[@"event"] withCustomMessage:d[@"message"]];
[postedNotifications addObject:d];
}
[self.pendingNotifications removeObjectsInArray:postedNotifications];
}
}
- (void)setNotifierConnection:(SNTXPCConnection *)notifierConnection {
_notifierConnection = notifierConnection;
[self flushQueue];
}
@end

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