Compare commits

...

45 Commits
0.9.6 ... 0.9.8

Author SHA1 Message Date
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
99 changed files with 2356 additions and 1627 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,11 @@
---
language: objective-c
cache: cocoapods
sudo: false
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

@@ -10,8 +10,6 @@
</array>
<key>MachServices</key>
<dict>
<key>SantaXPCNotifications</key>
<true/>
<key>SantaXPCControl</key>
<true/>
</dict>

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

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,7 +78,6 @@
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 */; };
@@ -89,8 +88,8 @@
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 +98,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 +114,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 */; };
@@ -144,6 +147,8 @@
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 */; };
@@ -306,8 +311,8 @@
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 +339,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>"; };
@@ -356,6 +363,8 @@
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>"; };
@@ -482,15 +491,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;
@@ -642,14 +646,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";
@@ -707,6 +713,8 @@
0D536EDA1B94E9230039A26D /* SNTEventLog.m */,
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */,
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */,
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */,
0D3AF83118F87CEF0087BCEE /* Resources */,
);
name = santad;
@@ -734,6 +742,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 +763,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 +809,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 +943,7 @@
0D91BCA8174E8A6500131A7D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
LastUpgradeCheck = 0700;
TargetAttributes = {
0D260DAB18B68E12002A0B55 = {
TestTargetID = 0D385DB5180DE4A900418BC6;
@@ -1256,6 +1278,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 +1299,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 +1331,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 */,
@@ -1329,7 +1353,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 +1387,7 @@
0DCD605519115D17006B445C /* SNTXPCControlInterface.m in Sources */,
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */,
0DCD604F19115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1521,6 +1547,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;
@@ -1565,6 +1592,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 +1762,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,5 +1,5 @@
<?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="8191" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
@@ -44,6 +44,7 @@
</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">
@@ -81,18 +82,18 @@
</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"/>
<rect key="frame" x="165" y="142" width="219" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="4hh-R2-86s"/>
<constraint firstAttribute="width" constant="215" id="4hh-R2-86s"/>
</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"/>
<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" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.shortenedHash" id="xgu-71-9ZT"/>
<binding destination="-2" name="value" keyPath="self.event.fileSHA256" id="9KB-0b-qLV"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
@@ -218,7 +219,7 @@
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</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">
<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">
@@ -228,6 +229,7 @@ DQ
</buttonCell>
<connections>
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="4KL-Z2-1op"/>
</connections>
</button>
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
@@ -236,7 +238,7 @@ DQ
<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">
<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">
@@ -246,6 +248,7 @@ DQ
</buttonCell>
<connections>
<action selector="openEventDetails:" target="-2" id="VhL-ql-rCV"/>
<outlet property="nextKeyView" destination="BbV-3h-mmL" id="Xkz-va-iGc"/>
</connections>
</button>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="f1p-GL-O3o">
@@ -291,6 +294,17 @@ DQ
</binding>
</connections>
</textField>
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kiB-jZ-69S">
<rect key="frame" x="-6" y="353" width="37" height="32"/>
<animations/>
<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"/>
</buttonCell>
<connections>
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="vl5-A8-O0H"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
@@ -347,7 +361,10 @@ DQ
</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="307.5"/>
</window>
</objects>
<resources>

View File

@@ -19,6 +19,7 @@
#import "SNTFileWatcher.h"
#import "SNTNotificationManager.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;
@@ -36,7 +37,7 @@
self.configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kDefaultConfigFilePath
handler:^{
[[SNTConfigurator configurator] reloadConfigData];
[[SNTConfigurator configurator] reloadConfigData];
}];
self.notificationManager = [[SNTNotificationManager alloc] init];
@@ -47,16 +48,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];
@@ -74,15 +74,18 @@
dispatch_async(dispatch_get_main_queue(), ^{
__weak __typeof(self) weakSelf = self;
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
[weakSelf attemptReconnection];
self.listener.invalidationHandler = ^{
[weakSelf attemptReconnection];
};
self.listener.invalidationHandler = self.listener.rejectedHandler;
[self.listener resume];
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
[daemonConn resume];
[[daemonConn remoteObjectProxy] setNotificationListener:listener.endpoint];
});
}

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

@@ -104,10 +104,6 @@
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
@@ -134,14 +130,20 @@
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 == EVENTSTATE_BLOCK_UNKNOWN) {
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

@@ -67,7 +67,8 @@
// 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 }
withObject:@{ @"event" : event,
@"custommsg" : message }
waitUntilDone:NO];
}

View File

@@ -23,8 +23,8 @@
typedef enum {
RULETYPE_UNKNOWN,
RULETYPE_BINARY = 1,
RULETYPE_CERT = 2,
RULETYPE_BINARY = 1,
RULETYPE_CERT = 2,
RULETYPE_MAX
} santa_ruletype_t;
@@ -32,10 +32,10 @@ typedef enum {
typedef enum {
RULESTATE_UNKNOWN,
RULESTATE_WHITELIST = 1,
RULESTATE_BLACKLIST = 2,
RULESTATE_WHITELIST = 1,
RULESTATE_BLACKLIST = 2,
RULESTATE_SILENT_BLACKLIST = 3,
RULESTATE_REMOVE = 4,
RULESTATE_REMOVE = 4,
RULESTATE_MAX
} santa_rulestate_t;
@@ -43,7 +43,7 @@ typedef enum {
typedef enum {
CLIENTMODE_UNKNOWN,
CLIENTMODE_MONITOR = 1,
CLIENTMODE_MONITOR = 1,
CLIENTMODE_LOCKDOWN = 2,
CLIENTMODE_MAX
@@ -52,15 +52,17 @@ typedef enum {
typedef enum {
EVENTSTATE_UNKNOWN,
EVENTSTATE_ALLOW_UNKNOWN = 1,
EVENTSTATE_ALLOW_BINARY = 2,
EVENTSTATE_ALLOW_UNKNOWN = 1,
EVENTSTATE_ALLOW_BINARY = 2,
EVENTSTATE_ALLOW_CERTIFICATE = 3,
EVENTSTATE_ALLOW_SCOPE = 4,
EVENTSTATE_ALLOW_SCOPE = 4,
EVENTSTATE_BLOCK_UNKNOWN = 5,
EVENTSTATE_BLOCK_BINARY = 6,
EVENTSTATE_BLOCK_UNKNOWN = 5,
EVENTSTATE_BLOCK_BINARY = 6,
EVENTSTATE_BLOCK_CERTIFICATE = 7,
EVENTSTATE_BLOCK_SCOPE = 8,
EVENTSTATE_BLOCK_SCOPE = 8,
EVENTSTATE_RELATED_BINARY = 9,
EVENTSTATE_MAX
} santa_eventstate_t;

View File

@@ -21,7 +21,7 @@
@interface SNTConfigurator : NSObject
/// Default config file path
extern NSString * const kDefaultConfigFilePath;
extern NSString *const kDefaultConfigFilePath;
#pragma mark - Daemon Settings
@@ -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;
}
@@ -192,12 +193,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 +304,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 +314,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 +345,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,7 +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.
@@ -42,6 +46,7 @@
@property NSString *path;
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property NSString *fileOwnerHomeDir;
// Cached properties
@property NSBundle *bundleRef;
@@ -57,14 +62,16 @@ 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;
}
@@ -76,6 +83,13 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
_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;
@@ -85,14 +99,20 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return [self initWithPath:path error:NULL];
}
- (NSString *)SHA1 {
#pragma mark Hashing
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256 {
const int chunkSize = 4096;
CC_SHA1_CTX c;
CC_SHA1_Init(&c);
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;
int readSize = 0;
if (offset + chunkSize > self.fileSize) {
readSize = (int)(self.fileSize - offset);
} else {
@@ -101,71 +121,62 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
NSData *chunk = [self safeSubdataWithRange:NSMakeRange(offset, readSize)];
if (!chunk) {
CC_SHA1_Final(NULL, &c);
return nil;
if (sha1) CC_SHA1_Final(NULL, &c1);
if (sha256) CC_SHA256_Final(NULL, &c256);
return;
}
CC_SHA1_Update(&c, chunk.bytes, readSize);
if (sha1) CC_SHA1_Update(&c1, chunk.bytes, readSize);
if (sha256) CC_SHA256_Update(&c256, chunk.bytes, readSize);
}
}
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(sha1, &c);
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]];
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);
return buf;
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 {
const int chunkSize = 4096;
CC_SHA256_CTX c;
CC_SHA256_Init(&c);
for (uint64_t offset = 0; offset < self.fileSize; offset += chunkSize) {
@autoreleasepool {
int readSize;
if (offset + chunkSize > self.fileSize) {
readSize = (int)(self.fileSize - offset);
} else {
readSize = chunkSize;
}
NSData *chunk = [self safeSubdataWithRange:NSMakeRange(offset, readSize)];
if (!chunk) {
CC_SHA256_Final(NULL, &c);
return nil;
}
CC_SHA256_Update(&c, chunk.bytes, readSize);
}
}
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(sha256, &c);
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 isScript]) return @"Script";
if ([self isDylib]) return @"Dynamic Library";
if ([self isKext]) return @"Kernel Extension";
if ([self isFat]) return @"Fat Binary";
if ([self isMachO]) return @"Thin Binary";
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;
@@ -191,12 +202,19 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
return (strncmp("#!", magic, 2) == 0);
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && 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 (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.
@@ -288,39 +306,44 @@ 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];
NSDate *timeStamp = [self quarantineData][@"LSQuarantineTimeStamp"];
return timeStamp;
}
#pragma mark Internal Methods
@@ -353,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);
@@ -416,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];
@@ -431,7 +454,7 @@ 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];
@@ -476,13 +499,69 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
///
/// 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;
}
@@ -511,9 +590,10 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
/// + 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.
/// returns the path to that bundles CFBundleExecutable and stores a reference to the
/// bundle in the bundle out-param.
///
- (NSString *)resolvePath:(NSString *)path {
- (NSString *)resolvePath:(NSString *)path bundle:(NSBundle **)bundle {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
@@ -528,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

@@ -15,6 +15,7 @@
#import "SNTLogging.h"
#import <asl.h>
#import <pthread.h>
#ifdef DEBUG
static LogLevel logLevel = LOG_LEVEL_DEBUG;
@@ -22,24 +23,31 @@ 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"] ||
strcmp(binaryName, "santad") == 0) {
useSyslog = YES;
pthread_key_create(&syslogKey, syslogClientDestructor);
}
});
if (logLevel < level) return;
@@ -50,20 +58,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

@@ -78,7 +78,7 @@
- (NSString *)description {
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %d, Type: %d",
self.shasum, self.state, self.type];
self.shasum, self.state, self.type];
}
@end

View File

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

View File

@@ -12,108 +12,99 @@
/// 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 a simpler interface.
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
@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]`.
*/
- (instancetype)initServerWithListener:(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;
///
/// The interface this object exports.
///
@property(retain) NSXPCInterface *exportedInterface;
///
/// The object that responds to messages from the other end.
///
@property(retain) id exportedObject;
///
/// A block to run when the connection is invalidated.
///
@property(copy) SNTXPCInvalidationBlock invalidationHandler;
///
/// A block to run when the connection has been accepted.
///
@property(copy) SNTXPCAcceptedBlock acceptedHandler;
///
/// 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
///
@param name MachService name, must match the MachServices key in the launchd.plist
*/
- (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;
/**
Initializer a new client to a service exported by a LaunchDaemon.
///
/// 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.
///
@param name MachService name
@param privileged Use YES if the server is running as root.
*/
- (instancetype)initClientWithName:(NSString *)name privileged:(BOOL)privileged;
/**
Initialize a new client with a listener endpoint sent from another process.
@param listener An NSXPCListenerEndpoint to connect to.
*/
- (instancetype)initClientWithListener:(NSXPCListenerEndpoint *)listener;
/**
Call when the properties of the object have been set-up and you're ready for connections.
*/
- (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) NSXPCInterface *remoteInterface;
/**
A proxy to the object at the other end of the connection. (client)
*/
@property(readonly, nonatomic) id remoteObjectProxy;
/**
The interface this object exports. (server)
*/
@property(retain) NSXPCInterface *exportedInterface;
/**
The object that responds to messages from the other end. (server)
*/
@property(retain) id exportedObject;
/**
A block to run when a/the connection is invalidated/interrupted.
*/
@property(copy) void (^invalidationHandler)(void);
@end

View File

@@ -16,52 +16,49 @@
#import "MOLCodesignChecker.h"
@protocol XPCConnectionValidityRequest
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
@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;
if (!_listenerObject) return nil;
_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 +72,58 @@
- (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];
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);
}
}];
// Wait for validation to complete, at most 5s
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self invalidate];
}
self.currentConnection.remoteObjectInterface = self.remoteInterface;
self.currentConnection.interruptionHandler = self.invalidationHandler;
self.currentConnection.invalidationHandler = self.invalidationHandler;
[self.currentConnection resume];
}
}
- (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;
[self.acceptedConnections addObject:connection];
connection.invalidationHandler = ^{
[self invokeInvalidationHandler];
self.currentConnection = nil;
__weak __typeof(connection) weakConnection = connection;
connection.interruptionHandler = connection.invalidationHandler = ^{
[self.acceptedConnections removeObject:weakConnection];
if (self.invalidationHandler) self.invalidationHandler();
};
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 = self.exportedInterface;
connection.exportedObject = self.exportedObject;
[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.acceptedConnections.count) {
for (NSXPCConnection *conn in self.acceptedConnections) {
[conn invalidate];
}
[self.acceptedConnections removeAllObjects];
}
}

View File

@@ -16,6 +16,7 @@
@class SNTRule;
@class SNTStoredEvent;
@class SNTXPCConnection;
///
/// Protocol implemented by santad and utilized by santactl
@@ -32,9 +33,11 @@
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
- (void)databaseRuleAddRule:(SNTRule *)rule
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
@@ -45,8 +48,8 @@
///
/// Config ops
///
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
@@ -54,6 +57,11 @@
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
///
/// GUI Ops
///
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
@end
@interface SNTXPCControlInterface : NSObject
@@ -69,4 +77,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) {
@@ -99,22 +115,27 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
if (!itDied) {
santa_message_t *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() {
bool SantaDecisionManager::ClientConnected() const {
proc_t p = proc_find(client_pid_);
bool is_exiting = false;
if (p) {
@@ -124,8 +145,24 @@ bool SantaDecisionManager::ClientConnected() {
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
@@ -180,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) {
SantaCachedDecision *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));
SantaCachedDecision *pending = OSDynamicCast(
SantaCachedDecision, cached_decisions_->getObject(identifier));
if (pending) {
pending->setAction(decision, microsecs);
}
@@ -215,7 +252,7 @@ void SantaDecisionManager::CacheCheck(const char *identifier) {
}
}
uint64_t SantaDecisionManager::CacheCount() {
uint64_t SantaDecisionManager::CacheCount() const {
return cached_decisions_->getCount();
}
@@ -232,24 +269,24 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
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)) {
if (RESPONSE_VALID(result)) {
uint64_t 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 {
@@ -275,14 +312,14 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
// 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;
}
@@ -294,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);
@@ -314,13 +351,13 @@ santa_action_t SantaDecisionManager::FetchDecision(
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);
// 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];
@@ -331,7 +368,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
santa_message_t *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);
@@ -341,7 +378,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
#pragma mark Misc
santa_message_t* SantaDecisionManager::NewMessage() {
santa_message_t *SantaDecisionManager::NewMessage() const {
santa_message_t *message = new santa_message_t;
message->uid = kauth_getuid();
message->gid = kauth_getgid();
@@ -350,27 +387,41 @@ santa_message_t* SantaDecisionManager::NewMessage() {
return message;
}
bool SantaDecisionManager::PostToQueue(santa_message_t *message) {
bool SantaDecisionManager::PostToDecisionQueue(santa_message_t *message) {
bool kr = false;
lck_mtx_lock(dataqueue_lock_);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
lck_mtx_lock(decision_dataqueue_lock_);
kr = decision_dataqueue_->enqueue(message, sizeof(santa_message_t));
lck_mtx_unlock(decision_dataqueue_lock_);
return kr;
}
bool SantaDecisionManager::PostToLogQueue(santa_message_t *message) {
bool kr = false;
lck_mtx_lock(log_dataqueue_lock_);
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 = sizeof(santa_message_t);
dataqueue_->dequeue(0, &dataSize);
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
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(dataqueue_lock_);
lck_mtx_unlock(log_dataqueue_lock_);
return kr;
}
uint64_t SantaDecisionManager::GetVnodeIDForVnode(
const vfs_context_t ctx, const vnode_t vp) {
const vfs_context_t ctx, const vnode_t vp) const {
struct vnode_attr vap;
VATTR_INIT(&vap);
VATTR_WANTED(&vap, va_fsid);
VATTR_WANTED(&vap, va_fileid);
vnode_getattr(vp, &vap, ctx);
return vap.va_fileid;
return (((uint64_t)vap.va_fsid << 32) | vap.va_fileid);
}
uint64_t SantaDecisionManager::GetCurrentUptime() {
@@ -390,6 +441,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,
@@ -410,13 +463,24 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
// 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: {
proc_t proc = vfs_context_proc(ctx);
if (proc) {
SantaPIDAndPPID *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:
@@ -444,7 +508,20 @@ void SantaDecisionManager::FileOpCallback(
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_);
SantaPIDAndPPID *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;
}
@@ -459,27 +536,30 @@ void SantaDecisionManager::FileOpCallback(
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) {
@@ -493,7 +573,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);

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,9 +108,13 @@ 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:
///
@@ -129,15 +141,21 @@ class SantaDecisionManager : public OSObject {
const int kMaxCacheSize = 10000;
///
/// Maximum number of PostToQueue failures to allow.
/// Maximum number of PostToDecisionQueue failures to allow.
///
const int kMaxQueueFailures = 10;
const int 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;
const int kMaxDecisionQueueEvents = 512;
///
/// The maximum number of messages can be kept
/// in the logging data queue at any time.
///
const int 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,14 +210,16 @@ 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);
uint64_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp) const;
///
/// Creates a new santa_message_t with some fields pre-filled.
///
santa_message_t* NewMessage();
santa_message_t *NewMessage() const;
///
/// Returns the current system uptime in microseconds
///
static uint64_t GetCurrentUptime();
private:
@@ -200,12 +228,17 @@ 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_;
SInt32 failed_decision_queue_requests_;
SInt32 failed_log_queue_requests_;
SInt32 listener_invocations_;
@@ -243,5 +276,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,124 +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];
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) {
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) {
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
@@ -84,7 +83,7 @@ REGISTER_COMMAND_NAME(@"rule")
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) {
@@ -116,7 +115,7 @@ REGISTER_COMMAND_NAME(@"rule")
}
newRule.customMsg = arguments[i];
} else {
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:arg]];
[self printErrorUsageAndExit:[@"Unknown argument: " stringByAppendingString:arg]];
}
}
@@ -137,17 +136,17 @@ REGISTER_COMMAND_NAME(@"rule")
}
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^(BOOL success) {
if (!success) {
printf("Failed to modify rules.");
exit(1);
if (!success) {
printf("Failed to modify rules.");
exit(1);
} else {
if (newRule.state == RULESTATE_REMOVE) {
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

@@ -51,21 +51,24 @@ REGISTER_COMMAND_NAME(@"status")
__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);
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] watchdogInfo:^(uint64_t wd_cpuEvents, uint64_t wd_ramEvents,
double wd_cpuPeak, double wd_ramPeak) {
cpuEvents = wd_cpuEvents;
cpuPeak = wd_cpuPeak;
cpuPeak = wd_cpuPeak;
ramEvents = wd_ramEvents;
ramPeak = wd_ramPeak;
dispatch_group_leave(group);
@@ -77,22 +80,22 @@ REGISTER_COMMAND_NAME(@"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
@@ -110,33 +113,33 @@ REGISTER_COMMAND_NAME(@"status")
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
@"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
},
@"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]);

View File

@@ -111,13 +111,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,99 +141,50 @@
__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) {
@@ -245,7 +196,6 @@
[NSURLCredential credentialWithIdentity:foundIdentity
certificates:nil
persistence:NSURLCredentialPersistenceForSession];
CFRelease(foundIdentity);
return cred;
} else {
LOGD(@"Client Trust: No valid identity found.");
@@ -318,4 +268,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];

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];
}
@@ -162,6 +170,7 @@
ADDKEY(newEvent, kDecision, kDecisionBlockCertificate);
break;
case EVENTSTATE_BLOCK_SCOPE: ADDKEY(newEvent, kDecision, kDecisionBlockScope); break;
case EVENTSTATE_RELATED_BINARY: 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 = EVENTSTATE_RELATED_BINARY;
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

@@ -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 = 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);
}
}] 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,52 @@
[[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:^(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 {
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];
}

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,13 +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 "SNTConfigurator.h"
#import "SNTDaemonControlController.h"
@@ -28,20 +27,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
@@ -60,48 +57,45 @@
}
// 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 +103,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

@@ -25,6 +25,7 @@
@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,8 +20,11 @@
#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;
@@ -35,11 +38,9 @@ double watchdogRAMPeak = 0;
@implementation SNTDaemonControlController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager {
- (instancetype)init {
self = [super init];
if (self) {
_driverManager = driverManager;
_syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
}
@@ -52,19 +53,19 @@ double watchdogRAMPeak = 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);
@@ -131,7 +132,7 @@ double watchdogRAMPeak = 0;
[[SNTDatabaseController eventTable] deleteEventsWithIds:ids];
}
#pragma mark Misc
#pragma mark Config Ops
- (void)clientMode:(void (^)(santa_clientmode_t))reply {
reply([[SNTConfigurator configurator] clientMode]);
@@ -178,4 +179,13 @@ double watchdogRAMPeak = 0;
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
}
#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

@@ -20,9 +20,7 @@
#include "SNTLogging.h"
@interface SNTDriverManager ()
@property IODataQueueMemory *queueMemory;
@property io_connect_t connection;
@property mach_port_t receivePort;
@end
@implementation SNTDriverManager
@@ -57,7 +55,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 +63,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 +83,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 +180,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"
@@ -88,9 +90,17 @@
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];
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];
}
@@ -112,23 +122,46 @@
switch (cd.decision) {
case EVENTSTATE_ALLOW_BINARY:
d = @"ALLOW"; r = @"BINARY"; args = [self argsForPid:message.pid]; break;
d = @"ALLOW";
r = @"BINARY";
args = [self argsForPid:message.pid];
break;
case EVENTSTATE_ALLOW_CERTIFICATE:
d = @"ALLOW"; r = @"CERTIFICATE"; args = [self argsForPid:message.pid]; break;
d = @"ALLOW";
r = @"CERTIFICATE";
args = [self argsForPid:message.pid];
break;
case EVENTSTATE_ALLOW_SCOPE:
d = @"ALLOW"; r = @"SCOPE"; args = [self argsForPid:message.pid]; break;
d = @"ALLOW";
r = @"SCOPE";
args = [self argsForPid:message.pid];
break;
case EVENTSTATE_ALLOW_UNKNOWN:
d = @"ALLOW"; r = @"UNKNOWN"; args = [self argsForPid:message.pid]; break;
d = @"ALLOW";
r = @"UNKNOWN";
args = [self argsForPid:message.pid];
break;
case EVENTSTATE_BLOCK_BINARY:
d = @"DENY"; r = @"BINARY"; break;
d = @"DENY";
r = @"BINARY";
break;
case EVENTSTATE_BLOCK_CERTIFICATE:
d = @"DENY"; r = @"CERT"; break;
d = @"DENY";
r = @"CERT";
break;
case EVENTSTATE_BLOCK_SCOPE:
d = @"DENY"; r = @"SCOPE"; break;
d = @"DENY";
r = @"SCOPE";
break;
case EVENTSTATE_BLOCK_UNKNOWN:
d = @"DENY"; r = @"UNKNOWN"; break;
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 +170,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 +233,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 +244,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

@@ -19,8 +19,8 @@
@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];
@@ -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,11 +129,12 @@
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];
@@ -144,7 +143,6 @@
if (cd.decision != EVENTSTATE_ALLOW_BINARY &&
cd.decision != EVENTSTATE_ALLOW_CERTIFICATE &&
cd.decision != EVENTSTATE_ALLOW_SCOPE) {
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];
}
}
}
@@ -265,15 +261,15 @@
case EVENTSTATE_ALLOW_CERTIFICATE:
case EVENTSTATE_ALLOW_SCOPE:
case EVENTSTATE_ALLOW_UNKNOWN:
return ACTION_RESPOND_CHECKBW_ALLOW;
return ACTION_RESPOND_ALLOW;
case EVENTSTATE_BLOCK_BINARY:
case EVENTSTATE_BLOCK_CERTIFICATE:
case EVENTSTATE_BLOCK_SCOPE:
case EVENTSTATE_BLOCK_UNKNOWN:
return ACTION_RESPOND_CHECKBW_DENY;
return ACTION_RESPOND_DENY;
default:
LOGW(@"Invalid event state %d", state);
return ACTION_RESPOND_CHECKBW_DENY;
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

View File

@@ -28,7 +28,6 @@
@implementation SNTRuleTable
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Save hashes of the signing certs for launchd and santad
self.santadCertSHA = [[[[MOLCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[MOLCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
@@ -37,11 +36,11 @@
if (version < 1) {
[db executeUpdate:@"CREATE TABLE 'rules' ("
@"'shasum' TEXT NOT NULL, "
@"'state' INTEGER NOT NULL, "
@"'type' INTEGER NOT NULL, "
@"'custommsg' TEXT"
@")"];
@"'shasum' TEXT NOT NULL, "
@"'state' INTEGER NOT NULL, "
@"'type' INTEGER NOT NULL, "
@"'custommsg' TEXT"
@")"];
[db executeUpdate:@"CREATE VIEW binrules AS SELECT * FROM rules WHERE type=1"];
[db executeUpdate:@"CREATE VIEW certrules AS SELECT * FROM rules WHERE type=2"];
@@ -52,9 +51,9 @@
// This helps prevent accidentally denying critical system components while the database
// is empty. This 'initial database' will then be cleared on the first successful sync.
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
newVersion = 1;
@@ -77,7 +76,7 @@
- (NSUInteger)binaryRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM binrules"];
count = [db longForQuery:@"SELECT COUNT(*) FROM binrules"];
}];
return count;
}
@@ -85,7 +84,7 @@
- (NSUInteger)certificateRuleCount {
__block NSUInteger count = 0;
[self inDatabase:^(FMDatabase *db) {
count = [db longForQuery:@"SELECT COUNT(*) FROM certrules"];
count = [db longForQuery:@"SELECT COUNT(*) FROM certrules"];
}];
return count;
}
@@ -105,11 +104,11 @@
__block SNTRule *rule;
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM certrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
}
[rs close];
FMResultSet *rs = [db executeQuery:@"SELECT * FROM certrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
}
[rs close];
}];
return rule;
@@ -119,11 +118,11 @@
__block SNTRule *rule;
[self inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM binrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
}
[rs close];
FMResultSet *rs = [db executeQuery:@"SELECT * FROM binrules WHERE shasum=? LIMIT 1", SHA256];
if ([rs next]) {
rule = [self ruleFromResultSet:rs];
}
[rs close];
}];
return rule;
@@ -140,47 +139,47 @@
__block BOOL failed = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, RULETYPE_CERT];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", RULESTATE_WHITELIST];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count != 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad ceritifcates.");
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, RULETYPE_CERT];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", RULESTATE_WHITELIST];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count != 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad ceritifcates.");
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}
for (SNTRule *rule in rules) {
if (![rule isKindOfClass:[SNTRule class]] || rule.shasum.length == 0 ||
rule.state == RULESTATE_UNKNOWN || rule.type == RULETYPE_UNKNOWN) {
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}
for (SNTRule *rule in rules) {
if (![rule isKindOfClass:[SNTRule class]] || rule.shasum.length == 0 ||
rule.state == RULESTATE_UNKNOWN || rule.type == RULETYPE_UNKNOWN) {
if (rule.state == RULESTATE_REMOVE) {
if (![db executeUpdate:@"DELETE FROM rules WHERE shasum=? AND type=?",
rule.shasum, @(rule.type)]) {
*rollback = failed = YES;
return;
}
if (rule.state == RULESTATE_REMOVE) {
if (![db executeUpdate:@"DELETE FROM rules WHERE shasum=? AND type=?",
rule.shasum, @(rule.type)]) {
*rollback = failed = YES;
return;
}
} else {
if (![db executeUpdate:@"INSERT OR REPLACE INTO rules "
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
rule.shasum, @(rule.state), @(rule.type), rule.customMsg]) {
*rollback = failed = YES;
return;
}
} else {
if (![db executeUpdate:@"INSERT OR REPLACE INTO rules "
@"(shasum, state, type, custommsg) "
@"VALUES (?, ?, ?, ?);",
rule.shasum, @(rule.state), @(rule.type), rule.customMsg]) {
*rollback = failed = YES;
return;
}
}
}
}];
return !failed;

View File

@@ -50,7 +50,7 @@ void *watchdogThreadFunction(__unused void *idata) {
struct mach_task_basic_info taskInfo;
mach_msg_type_number_t taskInfoCount = MACH_TASK_BASIC_INFO_COUNT;
while(true) {
while (true) {
@autoreleasepool {
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
(task_info_t)&taskInfo, &taskInfoCount) == KERN_SUCCESS) {
@@ -100,7 +100,7 @@ int main(int argc, const char *argv[]) {
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
SNTApplication *s = [[SNTApplication alloc] init];
[s performSelectorInBackground:@selector(run) withObject:nil];
[s start];
// Create watchdog thread
pthread_t watchdogThread;

View File

@@ -77,8 +77,8 @@
error:nil];
CC_SHA256([fData bytes], (unsigned int)[fData length], sha256);
char buf[CC_SHA256_DIGEST_LENGTH * 2 + 1];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
snprintf(buf + (2*i), 4, "%02x", (unsigned char)sha256[i]);
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) {
snprintf(buf + (2 * i), 4, "%02x", (unsigned char)sha256[i]);
}
buf[CC_SHA256_DIGEST_LENGTH * 2] = '\0';
return @(buf);
@@ -89,9 +89,9 @@
/// Call in-kernel function: |kSantaUserClientAllowBinary| or |kSantaUserClientDenyBinary|
/// passing the |vnodeID|.
- (void)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeid {
if (action == ACTION_RESPOND_CHECKBW_ALLOW) {
if (action == ACTION_RESPOND_ALLOW) {
IOConnectCallScalarMethod(self.connection, kSantaUserClientAllowBinary, &vnodeid, 1, 0, 0);
} else if (action == ACTION_RESPOND_CHECKBW_DENY) {
} else if (action == ACTION_RESPOND_DENY) {
IOConnectCallScalarMethod(self.connection, kSantaUserClientDenyBinary, &vnodeid, 1, 0, 0);
}
}
@@ -168,7 +168,6 @@
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
unsigned int msgType = 1;
TSTART("Allocates a notification port");
if (!(receivePort = IODataQueueAllocateNotificationPort())) {
@@ -177,7 +176,7 @@
TPASS();
TSTART("Registers the notification port");
kr = IOConnectSetNotificationPort(self.connection, msgType, receivePort, 0);
kr = IOConnectSetNotificationPort(self.connection, QUEUETYPE_DECISION, receivePort, 0);
if (kr != kIOReturnSuccess) {
mach_port_destroy(mach_task_self(), receivePort);
TFAILINFO("KR: %d", kr);
@@ -186,7 +185,7 @@
TPASS();
TSTART("Maps shared memory");
kr = IOConnectMapMemory(self.connection, kIODefaultMemoryType, mach_task_self(),
kr = IOConnectMapMemory(self.connection, QUEUETYPE_DECISION, mach_task_self(),
&address, &size, kIOMapAnywhere);
if (kr != kIOReturnSuccess) {
mach_port_destroy(mach_task_self(), receivePort);
@@ -211,23 +210,23 @@
dataSize = sizeof(vdata);
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
if (kr == kIOReturnSuccess) {
if (vdata.action != ACTION_REQUEST_CHECKBW) continue;
if (vdata.action != ACTION_REQUEST_BINARY) continue;
if ([[self sha256ForPath:@(vdata.path)] isEqual:edSHA]) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_DENY forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_DENY forVnodeID:vdata.vnode_id];
} else if (strncmp("/bin/mv", vdata.path, strlen("/bin/mv")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_DENY forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_DENY forVnodeID:vdata.vnode_id];
} else if (strncmp("/bin/ls", vdata.path, strlen("/bin/ls")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
self.timesSeenLs++;
} else if (strncmp("/bin/cp", vdata.path, strlen("/bin/cp")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
self.timesSeenCp++;
} else if (strncmp("/bin/cat", vdata.path, strlen("/bin/cat")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
self.timesSeenCat++;
} else if (strncmp("/bin/ln", vdata.path, strlen("/bin/ln")) == 0) {
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
TSTART("Sends valid pid/ppid");
if (vdata.pid < 1 || vdata.ppid < 1) {
@@ -256,13 +255,13 @@
}
// Allow everything not related to our testing.
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
}
} else {
TFAILINFO("Error receiving data: %d", kr);
}
}
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
} while (IODataQueueWaitForAvailableData(queueMemory, receivePort) == kIOReturnSuccess);
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
mach_port_destroy(mach_task_self(), receivePort);
@@ -429,10 +428,10 @@
const int LIMIT = 12000;
for (int i = 0; i < LIMIT; i++) {
for (int i = 0; i < LIMIT; ++i) {
printf("\033[s"); // save cursor position
printf("%d/%i", i+1, LIMIT);
printf("%d/%i", i + 1, LIMIT);
NSString *fname = [@"testexe" stringByAppendingFormat:@".%i", i];
[[NSFileManager defaultManager] copyItemAtPath:@"/bin/hostname" toPath:fname error:NULL];

View File

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.google.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>

View File

@@ -49,13 +49,13 @@
NSString *oidStr = [SNTDERDecoder decodeOIDWithBytes:oidBytes1 length:sizeof(oidBytes1)];
XCTAssertEqualObjects(oidStr, @"1.3.6.1.4.1.311.21.20");
unsigned char oidBytes2[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0xAB, 0x0E, 0x01, 0x05, 0x2F };
unsigned char oidBytes2[] = {0x2b, 0x06, 0x01, 0x04, 0x01, 0xAB, 0x0E, 0x01, 0x05, 0x2F};
oidStr = [SNTDERDecoder decodeOIDWithBytes:oidBytes2 length:sizeof(oidBytes2)];
XCTAssertEqualObjects(oidStr, @"1.3.6.1.4.1.5518.1.5.47");
unsigned char oidBytes3[] = {0x56, 0x04, 0x0A};
oidStr = [SNTDERDecoder decodeOIDWithBytes:oidBytes3 length:sizeof(oidBytes3)];
XCTAssertEqualObjects(oidStr, @"2.6.4.10");
XCTAssertEqualObjects(oidStr, @"2.6.4.10");
}
@end

View File

@@ -88,7 +88,7 @@
- (void)testDeleteEventsWithIds {
// Add 50 events to the database
for (int i = 0; i < 50; i++) {
for (int i = 0; i < 50; ++i) {
SNTStoredEvent *newEvent = [self createTestEvent];
[self.sut addStoredEvent:newEvent];
}
@@ -115,7 +115,7 @@
- (void)testDeleteCorruptEvent {
[self.dbq inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO events (filesha256) VALUES ('deadbeef')"];
[db executeUpdate:@"INSERT INTO events (filesha256) VALUES ('deadbeef')"];
}];
NSArray *events = [self.sut pendingEvents];
@@ -124,11 +124,11 @@
}
[self.dbq inDatabase:^(FMDatabase *db) {
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE filesha256='deadbeef'"];
if ([rs next]) {
XCTFail("Bad event was not deleted.");
}
[rs close];
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE filesha256='deadbeef'"];
if ([rs next]) {
XCTFail("Bad event was not deleted.");
}
[rs close];
}];
}

View File

@@ -65,7 +65,7 @@
self.sut = [[SNTExecutionController alloc] initWithDriverManager:self.mockDriverManager
ruleTable:self.mockRuleDatabase
eventTable:self.mockEventDatabase
notifierConnection:nil
notifierQueue:nil
eventLog:nil];
}
@@ -99,7 +99,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:1234]);
}
@@ -113,7 +113,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:1234]);
}
@@ -131,7 +131,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:1234]);
}
@@ -149,7 +149,7 @@
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:1234]);
}
@@ -159,12 +159,12 @@
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_MONITOR);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:1234]);
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:1234]);
}
@@ -173,13 +173,13 @@
OCMStub([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:1234]);
}
- (void)testMissingShasum {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_ALLOW
forVnodeID:1234]);
}
@@ -188,7 +188,7 @@
OCMStub([self.mockFileInfo isMissingPageZero]).andReturn(YES);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_DENY
forVnodeID:1234]);
}

View File

@@ -74,29 +74,35 @@
}
- (void)testFileChanged {
__block BOOL fulfilled = NO;
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Changed: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
NSString *d = [NSString stringWithContentsOfFile:self.file
encoding:NSUTF8StringEncoding
error:nil];
if ([d isEqual:@"0x8BADF00D"]) {
if (!fulfilled && [d isEqual:@"0x8BADF00D"]) {
fulfilled = YES;
[exp fulfill];
}
}];
sleep(1);
[[@"0x8BADF00D" dataUsingEncoding:NSUTF8StringEncoding] writeToFile:self.file atomically:NO];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)testFileReplaced {
__block BOOL fulfilled = NO;
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Replaced: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
NSString *d = [NSString stringWithContentsOfFile:self.file
encoding:NSUTF8StringEncoding
error:nil];
if ([d isEqual:@"0xFACEFEED"]) {
if (!fulfilled && [d isEqual:@"0xFACEFEED"]) {
fulfilled = YES;
[exp fulfill];
}
}];
@@ -110,6 +116,7 @@
int fd = open(self.file.fileSystemRepresentation, O_WRONLY);
write(fd, "0xDEAD", 6);
__block BOOL fulfilled = NO;
__weak XCTestExpectation *exp = [self expectationWithDescription:@"Extended: callback called"];
__unused SNTFileWatcher *sut = [[SNTFileWatcher alloc] initWithFilePath:self.file
handler:^{
@@ -117,7 +124,8 @@
char fileData[10];
read(file, fileData, 10);
if (strncmp(fileData, "0xDEADBEEF", 10) == 0) {
if (!fulfilled && strncmp(fileData, "0xDEADBEEF", 10) == 0) {
fulfilled = YES;
[exp fulfill];
}
}];

View File

@@ -18,13 +18,14 @@
#import "MOLCodesignChecker.h"
#import "SNTXPCConnection.h"
@interface SNTXPCConnection (Testing)
@protocol XPCConnectionValidityRequest
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
- (void)invokeAcceptedHandler;
- (void)invokeRejectedHandler;
- (void)invokeInvalidationHandler;
@property NSXPCConnection *currentConnection;
@property NSXPCInterface *validatorInterface;
@end
@interface SNTXPCConnection (Testing)
//- (void)invokeAcceptedHandler;
//- (void)invokeRejectedHandler;
//- (void)invokeInvalidationHandler;
@end
@interface SNTXPCConnectionTest : XCTestCase
@@ -57,7 +58,7 @@
.andReturn(self.mockConnection);
SNTXPCConnection *sut = [[SNTXPCConnection alloc] initClientWithName:@"TestClient"
options:NSXPCConnectionPrivileged];
privileged:YES];
XCTAssertNotNil(sut);
OCMVerifyAll(self.mockConnection);
}
@@ -79,59 +80,54 @@
OCMVerify([(NSXPCListener *)self.mockListener resume]);
}
- (void)testListenerShouldAcceptNewConnection {
- (void)testServerNotValid {
OCMExpect([self.mockListener initWithMachServiceName:OCMOCK_ANY]).andReturn(self.mockListener);
SNTXPCConnection *sut = [[SNTXPCConnection alloc] initServerWithName:@"TestServer"];
XCTAssertTrue([sut listener:self.mockListener shouldAcceptNewConnection:self.mockConnection]);
OCMVerify([self.mockConnection setExportedObject:sut]);
OCMVerify([self.mockConnection setExportedInterface:OCMOCK_ANY]);
OCMVerify([self.mockConnection setInvalidationHandler:OCMOCK_ANY]);
OCMVerify([self.mockConnection setInterruptionHandler:OCMOCK_ANY]);
OCMVerify([(NSXPCConnection *)self.mockConnection resume]);
}
- (void)testIsConnectionValidFalse {
OCMExpect([self.mockListener initWithMachServiceName:OCMOCK_ANY]).andReturn(self.mockListener);
SNTXPCConnection *sut = [[SNTXPCConnection alloc] initServerWithName:@"TestServer"];
[sut setCurrentConnection:self.mockConnection];
OCMExpect([self.mockConnection processIdentifier]).andReturn(1);
id mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
OCMExpect([mockCodesignChecker alloc]).andReturn(mockCodesignChecker);
OCMStub([mockCodesignChecker alloc]).andReturn(mockCodesignChecker);
OCMExpect([mockCodesignChecker initWithPID:0]).andReturn(mockCodesignChecker);
OCMExpect([mockCodesignChecker signingInformationMatches:OCMOCK_ANY]).andReturn(NO);
[sut isConnectionValidWithBlock:^(BOOL input) {
XCTAssertFalse(input);
}];
XCTAssertFalse([sut listener:self.mockListener shouldAcceptNewConnection:self.mockConnection]);
XCTAssertNil(sut.currentConnection);
OCMVerify([self.mockConnection invalidate]);
[mockCodesignChecker stopMocking];
}
- (void)testIsConnectionValidTrue {
- (void)testServerValid {
OCMExpect([self.mockListener initWithMachServiceName:OCMOCK_ANY]).andReturn(self.mockListener);
SNTXPCConnection *sut = [[SNTXPCConnection alloc] initServerWithName:@"TestServer"];
[sut setCurrentConnection:self.mockConnection];
id mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
OCMStub([mockCodesignChecker alloc]).andReturn(mockCodesignChecker);
OCMExpect([mockCodesignChecker initWithPID:0]).andReturn(mockCodesignChecker);
OCMExpect([mockCodesignChecker signingInformationMatches:OCMOCK_ANY]).andReturn(YES);
pid_t mypid = [[NSProcessInfo processInfo] processIdentifier];
OCMExpect([self.mockConnection processIdentifier]).andReturn(mypid);
XCTAssertTrue([sut listener:self.mockListener shouldAcceptNewConnection:self.mockConnection]);
[sut isConnectionValidWithBlock:^(BOOL input) {
XCTAssertTrue(input);
}];
[mockCodesignChecker stopMocking];
}
OCMVerify([(NSXPCConnection *)self.mockConnection suspend]);
OCMVerify([self.mockConnection setRemoteObjectInterface:OCMOCK_ANY]);
OCMVerify([self.mockConnection setExportedInterface:OCMOCK_ANY]);
OCMVerify([self.mockConnection setExportedObject:OCMOCK_ANY]);
OCMVerify([(NSXPCConnection *)self.mockConnection resume]);
- (void)testServerInvalidateAllConnections {
OCMExpect([self.mockListener initWithMachServiceName:OCMOCK_ANY]).andReturn(self.mockListener);
SNTXPCConnection *sut = [[SNTXPCConnection alloc] initServerWithName:@"TestServer"];
int pid = [[NSProcessInfo processInfo] processIdentifier];
NSMutableArray *connections = [NSMutableArray array];
for (int i = 0; i < 10; ++i) {
NSXPCConnection *fakeConn = OCMClassMock([NSXPCConnection class]);
OCMStub([fakeConn processIdentifier]).andReturn(pid);
OCMExpect([fakeConn invalidate]);
[connections addObject:fakeConn];
[sut listener:self.mockListener shouldAcceptNewConnection:fakeConn];
}
[sut invalidate];
for (NSXPCConnection *fakeConn in connections) {
OCMVerifyAll((OCMockObject *)fakeConn);
}
}
@end