Compare commits

...

69 Commits
0.9.3 ... 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
Russell Hancox
b4b1fbb9e6 santad: Run watchdog thread loop once before sleeping 2015-10-31 14:01:44 -04:00
Russell Hancox
209eaff3c6 SNTFileInfo: Embed SHA hashing loop in an autoreleasepool to avoid temporary RAM spikes 2015-10-31 13:45:47 -04:00
Russell Hancox
c3f70703fd santactl/status: Expose peak CPU/RAM use from santad. 2015-10-29 16:20:57 -04:00
Russell Hancox
f2967e7b94 santad: Switch watchdog CPU counter from rusage to task_info, capture peak CPU/RAM use. 2015-10-29 16:20:25 -04:00
Russell Hancox
77c46b5c43 SNTFileInfo: switch from NSData to NSFileHandle.
This seems to work much better than NSData with either mapped (SIGBUS when file is deleted) or uncached (ballooning memory use) reading.
2015-10-29 16:17:12 -04:00
Russell Hancox
5fda5bc081 santactl/binaryinfo: Only print bundle lines if bundle info is present 2015-10-29 12:35:27 -04:00
Russell Hancox
33a7b38c6a SNTFileInfo: check for NULL ptrs when parsing for embedded plist 2015-10-27 18:35:11 -04:00
Russell Hancox
2a7c0bd58c SNTFileInfo: Go back to using mmap, uncached read balloons memory use 2015-10-27 18:08:16 -04:00
Russell Hancox
86e4d0db0f santactl: Use yyyy instead of YYYY in NSDateFormatter 2015-10-27 17:58:23 -04:00
Russell Hancox
1310fea64d santa-driver: Only try to use/release proc_t if proc_find found it. 2015-10-22 11:29:49 -04:00
Russell Hancox
382f5a5bb9 Merge pull request #30 from stephanemoore/patch-1
Fix application deadlock.
2015-10-22 08:39:54 -04:00
Stephane Moore
ff3303e312 Fix application deadlock.
Fix application deadlock by asynchronously dispatching to the main queue in -[SNTAppDelegate createConnection].
2015-10-21 17:45:59 -07:00
Russell Hancox
6ce0ef62e9 SantaGUI: Ensure connection is only made on main thread 2015-10-15 18:31:07 -04:00
Russell Hancox
2a03341fb6 santad: Add configuration option for turning off PAGEZERO protection. 2015-10-15 18:10:00 -04:00
Russell Hancox
77a55dde56 santad: Catch errors archiving/unarchiving SNTStoredEvent, delete events that fail 2015-10-15 18:09:46 -04:00
Russell Hancox
1a71cdff4a santad/santactl: Report back if rule adding/removing failed rather than assuming success. 2015-10-15 12:15:38 -04:00
Russell Hancox
63f65c51c3 SNTFileInfo: Use NSURL method for getting quarantine data, don't try to use <10.10 2015-10-15 12:14:53 -04:00
Russell Hancox
75de2526c1 santactl/binaryinfo: Only print quarantine fields if they're not empty 2015-10-14 23:37:16 -04:00
Russell Hancox
6fc4b7b120 santactl/binaryinfo: Increase key padding +1 2015-10-14 23:31:50 -04:00
Russell Hancox
7b8068139b santad, santactl/sync: Collect and upload quarantine data with events. 2015-10-14 23:02:20 -04:00
Russell Hancox
ced7de884f santactl/binaryinfo: Add quarantine data to output, add print method to simplify changes. 2015-10-14 20:12:04 -04:00
Russell Hancox
bc51c9f25b SNTFileInfo: Add com.apple.quarantine data accessors for downloaded files. 2015-10-14 20:11:32 -04:00
Russell Hancox
c412e8b9a7 SNTFileInfo: Fix embedded plist parsing, extract into separate method 2015-10-14 20:07:50 -04:00
Russell Hancox
4e0ff224b6 Project: Remove SNTCertificate/SNTCodesignChecker, use new CocoaPod versions 2015-10-12 17:23:42 -04:00
105 changed files with 2788 additions and 2886 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>

17
Podfile
View File

@@ -2,7 +2,20 @@ platform :osx, "10.9"
inhibit_all_warnings!
target :Santa do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
end
target :santactl do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'FMDB'
end
target :santad do
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'FMDB'
post_install do |installer|
@@ -20,6 +33,8 @@ target :santad do
end
target :LogicTests do
pod 'OCMock'
pod 'FMDB'
pod 'MOLCertificate'
pod 'MOLCodesignChecker'
pod 'OCMock'
end

View File

@@ -1,17 +1,22 @@
PODS:
- FMDB (2.5):
- FMDB/standard (= 2.5)
- FMDB/common (2.5)
- FMDB/standard (2.5):
- FMDB/common
- OCMock (3.1.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
- MOLCertificate
- MOLCodesignChecker
- OCMock
SPEC CHECKSUMS:
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
OCMock: a10ea9f0a6e921651f96f78b6faee95ebc813b92
FMDB: c1968bab3ab0aed38f66cb778ae1e7fa9a652b6e
MOLCertificate: a776221906b5a46dd1bd749d0682bef3ee68c1f5
MOLCodesignChecker: 34e60cc6beadabfb4762b6e5087e12837774f85f
OCMock: 18c9b7e67d4c2770e95bb77a9cc1ae0c91fe3835
COCOAPODS: 0.38.2
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

@@ -26,6 +26,7 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
02D7203A6CEE2A946FB08ECE /* libPods-santactl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A2EA300312100AE62218F84E /* libPods-santactl.a */; };
0D0016A3192BCD3C005E7FCD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D9A7F3E1759330500035EB5 /* Foundation.framework */; };
0D0016A6192BCD3C005E7FCD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0016A5192BCD3C005E7FCD /* main.m */; };
0D0016AE192BCD8C005E7FCD /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
@@ -36,21 +37,16 @@
0D10BE891A0AAF6700C0C944 /* SNTDropRootPrivs.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */; };
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */; };
0D10BE8C1A0AB3FD00C0C944 /* SNTDERDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7FFD4A1A017D4B00F34435 /* SNTDERDecoder.m */; };
0D1AF477187C7A2C00D3298D /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D1AF478187C7A2C00D3298D /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */; };
0D1B477119A53419008CADD3 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D1B476F19A53419008CADD3 /* AboutWindow.xib */; };
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D42D2B719D2042900955F08 /* SNTConfigurator.m */; };
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0D2CD4601A81C7B100C9C910 /* dn.plist */; };
0D31DF4718D254B3002B300D /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D9A7F3E1759330500035EB5 /* Foundation.framework */; };
0D35BDA218FD71CE00921A21 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDA118FD71CE00921A21 /* main.m */; };
0D35BDAC18FD7CFD00921A21 /* SNTCommandController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDAB18FD7CFD00921A21 /* SNTCommandController.m */; };
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D35BDB418FD84F600921A21 /* SNTCommandSync.m */; };
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0D35BDC018FDA5C800921A21 /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0D35BDC418FDA5D100921A21 /* SNTXPCConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FDC9518C93A020044685C /* SNTXPCConnection.m */; };
0D377C2A17A071B7008453DB /* SNTEventTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D377C2917A071B7008453DB /* SNTEventTable.m */; };
0D37C10F18F6029A0069BC61 /* SNTDatabaseTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D37C10E18F6029A0069BC61 /* SNTDatabaseTable.m */; };
@@ -82,8 +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 */; };
0D59C0E417710E6000748EBF /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.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 */; };
@@ -92,11 +86,10 @@
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8218C68D7E0044685C /* GIAG2.crt */; };
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8418C68E500044685C /* GIAG2.pem */; };
0D6FDC8718C6913D0044685C /* apple.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D6FDC8618C6913D0044685C /* apple.pem */; };
0D6FDC8C18C69AF90044685C /* SNTCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1AF476187C7A2C00D3298D /* SNTCertificate.m */; };
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 */; };
@@ -105,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 */; };
@@ -119,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 */; };
@@ -141,18 +138,17 @@
0DCD605C19117A90006B445C /* SNTCommandSyncPreflight.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD605B19117A90006B445C /* SNTCommandSyncPreflight.m */; };
0DCD6062191188B1006B445C /* SNTAuthenticatingURLSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */; };
0DD0D487194F5187005F27EB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D3AFBF718FB4C870087BCEE /* IOKit.framework */; };
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */; };
0DD0D48D194F6D5B005F27EB /* SNTCodesignCheckerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */; };
0DD0D48F194F78F8005F27EB /* SNTFileInfoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */; };
0DD0D491194F9947005F27EB /* SNTExecutionControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */; };
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DA73C9E1934F8100056D7C4 /* SNTLogging.m */; };
0DD65D98184D2F0A00822DA7 /* SNTCodesignChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */; };
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D8C200B180F359A00CE2BF8 /* Security.framework */; };
0DE4C8A618FF3B1700466D04 /* SNTCommandFlushCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE4C8A518FF3B1700466D04 /* SNTCommandFlushCache.m */; };
0DE50F681912716A007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */; };
0DE5B54C1C92722300C00603 /* SNTNotificationQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */; };
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */; };
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
@@ -165,6 +161,7 @@
0DF395661AB76ABC00CBC520 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DF395651AB76ABC00CBC520 /* libz.dylib */; };
4092327A1A51B66400A04527 /* SNTCommandRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 409232791A51B65D00A04527 /* SNTCommandRule.m */; };
8BFD9B39112F4D16B3D0EFFB /* libPods-LogicTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */; };
CB7CD33A4D1B4745552333F4 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7544A9B819620883181F257 /* libPods-Santa.a */; };
E86AE075D7F24FB88FB627C5 /* libPods-santad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0A84545E322F475FA0B505D5 /* libPods-santad.a */; };
/* End PBXBuildFile section */
@@ -252,8 +249,6 @@
0D10BE851A0AABD600C0C944 /* SNTDropRootPrivs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDropRootPrivs.m; sourceTree = "<group>"; };
0D10BE881A0AAC2100C0C944 /* SNTDropRootPrivs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTDropRootPrivs.h; sourceTree = "<group>"; };
0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDERDecoderTest.m; sourceTree = "<group>"; };
0D1AF475187C7A2C00D3298D /* SNTCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCertificate.h; sourceTree = "<group>"; };
0D1AF476187C7A2C00D3298D /* SNTCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCertificate.m; sourceTree = "<group>"; };
0D1B476D19A53419008CADD3 /* SNTAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAboutWindowController.h; sourceTree = "<group>"; };
0D1B476E19A53419008CADD3 /* SNTAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAboutWindowController.m; sourceTree = "<group>"; };
0D1B476F19A53419008CADD3 /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
@@ -306,8 +301,6 @@
0D536ED61B8E7A2E0039A26D /* missing_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = missing_pagezero; sourceTree = "<group>"; };
0D536ED91B94E9230039A26D /* SNTEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTEventLog.h; sourceTree = "<group>"; };
0D536EDA1B94E9230039A26D /* SNTEventLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTEventLog.m; sourceTree = "<group>"; };
0D59C0E217710E6000748EBF /* SNTCodesignChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCodesignChecker.h; sourceTree = "<group>"; };
0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCodesignChecker.m; sourceTree = "<group>"; };
0D63DD5A1906FCB400D346C4 /* SNTDatabaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTDatabaseController.h; sourceTree = "<group>"; };
0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTDatabaseController.m; sourceTree = "<group>"; };
0D668E7F18D1121700E29A8B /* SNTMessageWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTMessageWindow.h; sourceTree = "<group>"; };
@@ -318,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>"; };
@@ -346,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>"; };
@@ -363,13 +358,13 @@
0DCD605B19117A90006B445C /* SNTCommandSyncPreflight.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncPreflight.m; sourceTree = "<group>"; };
0DCD6060191188B1006B445C /* SNTAuthenticatingURLSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTAuthenticatingURLSession.h; sourceTree = "<group>"; };
0DCD6061191188B1006B445C /* SNTAuthenticatingURLSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTAuthenticatingURLSession.m; sourceTree = "<group>"; };
0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCertificateTest.m; sourceTree = "<group>"; };
0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCodesignCheckerTest.m; sourceTree = "<group>"; };
0DD0D48E194F78F8005F27EB /* SNTFileInfoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTFileInfoTest.m; sourceTree = "<group>"; };
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTExecutionControllerTest.m; sourceTree = "<group>"; };
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>"; };
@@ -384,10 +379,16 @@
0DF395651AB76ABC00CBC520 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
13A4FE400F3857C0F5831498 /* Pods-LogicTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LogicTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests.debug.xcconfig"; sourceTree = "<group>"; };
409232791A51B65D00A04527 /* SNTCommandRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SNTCommandRule.m; path = rule/SNTCommandRule.m; sourceTree = "<group>"; };
54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santactl.release.xcconfig"; path = "Pods/Target Support Files/Pods-santactl/Pods-santactl.release.xcconfig"; sourceTree = "<group>"; };
583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santactl.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santactl/Pods-santactl.debug.xcconfig"; sourceTree = "<group>"; };
627BB4EC9917DC20E89D718C /* Pods-santad.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.debug.xcconfig"; path = "Pods/Target Support Files/Pods-santad/Pods-santad.debug.xcconfig"; sourceTree = "<group>"; };
752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-LogicTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
8003CA1D3E46447BCEA56440 /* Pods-santad.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-santad.release.xcconfig"; path = "Pods/Target Support Files/Pods-santad/Pods-santad.release.xcconfig"; sourceTree = "<group>"; };
8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.release.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.release.xcconfig"; sourceTree = "<group>"; };
A2EA300312100AE62218F84E /* libPods-santactl.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-santactl.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BE74E23CF5A553E5F02462B9 /* Pods-LogicTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LogicTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests.release.xcconfig"; sourceTree = "<group>"; };
DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Santa.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Santa/Pods-Santa.debug.xcconfig"; sourceTree = "<group>"; };
E7544A9B819620883181F257 /* libPods-Santa.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Santa.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -418,6 +419,7 @@
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */,
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */,
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */,
02D7203A6CEE2A946FB08ECE /* libPods-santactl.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -429,6 +431,7 @@
0D6F12D819EC8822006B218E /* SecurityInterface.framework in Frameworks */,
0D8C200C180F359A00CE2BF8 /* Security.framework in Frameworks */,
0D385DB8180DE4A900418BC6 /* Cocoa.framework in Frameworks */,
CB7CD33A4D1B4745552333F4 /* libPods-Santa.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -458,8 +461,6 @@
isa = PBXGroup;
children = (
0D260DB018B68E12002A0B55 /* Resources */,
0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */,
0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */,
0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */,
0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */,
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */,
@@ -490,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;
@@ -641,6 +637,8 @@
0D385DB7180DE4A900418BC6 /* Cocoa.framework */,
752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */,
0A84545E322F475FA0B505D5 /* libPods-santad.a */,
E7544A9B819620883181F257 /* libPods-Santa.a */,
A2EA300312100AE62218F84E /* libPods-santactl.a */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -648,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";
@@ -665,10 +665,6 @@
0D91BCD5174E8AAB00131A7D /* common */ = {
isa = PBXGroup;
children = (
0D1AF475187C7A2C00D3298D /* SNTCertificate.h */,
0D1AF476187C7A2C00D3298D /* SNTCertificate.m */,
0D59C0E217710E6000748EBF /* SNTCodesignChecker.h */,
0D59C0E317710E6000748EBF /* SNTCodesignChecker.m */,
0D91BCE4174E8B5E00131A7D /* SNTCommonEnums.h */,
0D42D2B619D2042900955F08 /* SNTConfigurator.h */,
0D42D2B719D2042900955F08 /* SNTConfigurator.m */,
@@ -717,6 +713,8 @@
0D536EDA1B94E9230039A26D /* SNTEventLog.m */,
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */,
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
0DE5B5491C926E3300C00603 /* SNTNotificationQueue.h */,
0DE5B54A1C926E3300C00603 /* SNTNotificationQueue.m */,
0D3AF83118F87CEF0087BCEE /* Resources */,
);
name = santad;
@@ -744,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 = (
@@ -752,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 */ = {
@@ -775,6 +786,10 @@
BE74E23CF5A553E5F02462B9 /* Pods-LogicTests.release.xcconfig */,
627BB4EC9917DC20E89D718C /* Pods-santad.debug.xcconfig */,
8003CA1D3E46447BCEA56440 /* Pods-santad.release.xcconfig */,
DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */,
8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */,
583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */,
54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
@@ -794,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 */,
@@ -830,6 +846,7 @@
0D260DA918B68E12002A0B55 /* Frameworks */,
0D260DAA18B68E12002A0B55 /* Resources */,
85CE5DF0D54C438A8933A631 /* Copy Pods Resources */,
4B68B2D454C1C55D29AA76D8 /* Embed Pods Frameworks */,
);
buildRules = (
);
@@ -844,9 +861,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 0D35BDA918FD71CE00921A21 /* Build configuration list for PBXNativeTarget "santactl" */;
buildPhases = (
AAFCB010997D370F4297BF35 /* Check Pods Manifest.lock */,
0DD98E671A5DD02000A754C6 /* Update Version Info */,
0D35BD9A18FD71CE00921A21 /* Sources */,
0D35BD9B18FD71CE00921A21 /* Frameworks */,
32AA711FA23052E0F2EE59EB /* Copy Pods Resources */,
);
buildRules = (
);
@@ -861,10 +880,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 0D385DE3180DE4A900418BC6 /* Build configuration list for PBXNativeTarget "Santa" */;
buildPhases = (
4347374E751B0BF8CF6A7110 /* Check Pods Manifest.lock */,
0DD98E681A5DD03E00A754C6 /* Update Version Info */,
0D385DB2180DE4A900418BC6 /* Sources */,
0D385DB3180DE4A900418BC6 /* Frameworks */,
0D385DB4180DE4A900418BC6 /* Resources */,
309C93A0ACB98D32A337D908 /* Embed Pods Frameworks */,
2AB78B9612925DE634AE8F7C /* Copy Pods Resources */,
);
buildRules = (
);
@@ -921,7 +943,7 @@
0D91BCA8174E8A6500131A7D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
LastUpgradeCheck = 0700;
TargetAttributes = {
0D260DAB18B68E12002A0B55 = {
TestTargetID = 0D385DB5180DE4A900418BC6;
@@ -1073,6 +1095,51 @@
shellPath = /bin/sh;
shellScript = "GIT_TAG=$(git describe --abbrev=0 --tags)\nsed -i '' \"s/TO.BE.FILLED/${GIT_TAG}/\" ${DERIVED_FILE_DIR}/santa-driver_info.c";
};
2AB78B9612925DE634AE8F7C /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-resources.sh\"\n";
showEnvVarsInLog = 0;
};
309C93A0ACB98D32A337D908 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Santa/Pods-Santa-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
32AA711FA23052E0F2EE59EB /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santactl/Pods-santactl-resources.sh\"\n";
showEnvVarsInLog = 0;
};
34C9C9E8C5454BBE980DF8A9 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1103,6 +1170,36 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-santad/Pods-santad-resources.sh\"\n";
showEnvVarsInLog = 0;
};
4347374E751B0BF8CF6A7110 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
4B68B2D454C1C55D29AA76D8 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
85CE5DF0D54C438A8933A631 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1118,6 +1215,21 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-LogicTests/Pods-LogicTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
AAFCB010997D370F4297BF35 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
AE05898CB3CE4507B2F43B91 /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1165,21 +1277,18 @@
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */,
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */,
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */,
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */,
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */,
0DE5B54C1C92722300C00603 /* SNTNotificationQueue.m in Sources */,
0D3AFBE718FB32CB0087BCEE /* SNTXPCConnectionTest.m in Sources */,
0DCD605719115E54006B445C /* SNTDaemonControlController.m in Sources */,
0D41DAD41A7C28C800A890FE /* SNTEventTableTest.m in Sources */,
0D3AFBEE18FB4C6C0087BCEE /* SNTApplication.m in Sources */,
0DD0D48F194F78F8005F27EB /* SNTFileInfoTest.m in Sources */,
0DC5D86E191AED220078A5C0 /* SNTRuleTable.m in Sources */,
0D31DF4718D254B3002B300D /* SNTCodesignChecker.m in Sources */,
0DD0D492194F9BEF005F27EB /* SNTLogging.m in Sources */,
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */,
0DD0D48D194F6D5B005F27EB /* SNTCodesignCheckerTest.m in Sources */,
0DCD605919115E5A006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE50F691912B0CD007B2B0C /* SNTRule.m in Sources */,
0D6FDC8C18C69AF90044685C /* SNTCertificate.m in Sources */,
0D10BE8C1A0AB3FD00C0C944 /* SNTDERDecoder.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1189,9 +1298,8 @@
buildActionMask = 2147483647;
files = (
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */,
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.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 */,
@@ -1210,7 +1318,6 @@
0DCD6043190ACCB8006B445C /* SNTFileInfo.m in Sources */,
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */,
0D0A1EC3191998C900B8450F /* SNTCommandSyncRuleDownload.m in Sources */,
0D35BDC018FDA5C800921A21 /* SNTCertificate.m in Sources */,
0DB390991AB1E11400614002 /* SNTCommandVersion.m in Sources */,
0D42D2B519D1D98A00955F08 /* SNTSystemInfo.m in Sources */,
0D827E6719DF3C74006EC811 /* SNTCommandStatus.m in Sources */,
@@ -1224,16 +1331,15 @@
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 */,
0D1AF478187C7A2C00D3298D /* SNTCertificate.m in Sources */,
0DD65D98184D2F0A00822DA7 /* SNTCodesignChecker.m in Sources */,
0D385DC4180DE4A900418BC6 /* main.m in Sources */,
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */,
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */,
@@ -1247,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 */,
@@ -1265,7 +1372,6 @@
0DCD604B19105433006B445C /* SNTStoredEvent.m in Sources */,
0DB8ACC1185662DC00FEF9C7 /* SNTApplication.m in Sources */,
0D9A7F421759330500035EB5 /* main.m in Sources */,
0D1AF477187C7A2C00D3298D /* SNTCertificate.m in Sources */,
0DA73C9F1934F8100056D7C4 /* SNTLogging.m in Sources */,
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */,
0DCD6042190ACCB8006B445C /* SNTFileInfo.m in Sources */,
@@ -1277,11 +1383,11 @@
0D377C2A17A071B7008453DB /* SNTEventTable.m in Sources */,
0DE50F681912716A007B2B0C /* SNTRule.m in Sources */,
0D37C10F18F6029A0069BC61 /* SNTDatabaseTable.m in Sources */,
0D59C0E417710E6000748EBF /* SNTCodesignChecker.m in Sources */,
0D42D2B819D2042900955F08 /* SNTConfigurator.m in Sources */,
0DCD605519115D17006B445C /* SNTXPCControlInterface.m in Sources */,
0D536EDB1B94E9230039A26D /* SNTEventLog.m in Sources */,
0DCD604F19115A06006B445C /* SNTXPCNotifierInterface.m in Sources */,
0DE5B54B1C926E3300C00603 /* SNTNotificationQueue.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1441,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;
@@ -1485,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;
@@ -1493,6 +1601,7 @@
};
0D35BDA718FD71CE00921A21 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
@@ -1533,6 +1642,7 @@
};
0D35BDA818FD71CE00921A21 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_MODULES = YES;
@@ -1567,6 +1677,7 @@
};
0D385DE4180DE4A900418BC6 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1608,6 +1719,7 @@
};
0D385DE5180DE4A900418BC6 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1650,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];
@@ -71,17 +71,22 @@
#pragma mark Connection handling
- (void)createConnection {
__weak __typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
__weak __typeof(self) weakSelf = self;
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
options:NSXPCConnectionPrivileged];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.rejectedHandler = ^{
NSXPCListener *listener = [NSXPCListener anonymousListener];
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
self.listener.exportedObject = self.notificationManager;
self.listener.invalidationHandler = ^{
[weakSelf attemptReconnection];
};
self.listener.invalidationHandler = self.listener.rejectedHandler;
[self.listener resume];
};
[self.listener resume];
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
[daemonConn resume];
[[daemonConn remoteObjectProxy] setNotificationListener:listener.endpoint];
});
}
- (void)attemptReconnection {

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

@@ -16,7 +16,7 @@
#import <SecurityInterface/SFCertificatePanel.h>
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTConfigurator.h"
#import "SNTFileInfo.h"
#import "SNTMessageWindow.h"
@@ -67,7 +67,7 @@
- (IBAction)showCertInfo:(id)sender {
// SFCertificatePanel expects an NSArray of SecCertificateRef's
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[self.event.signingChain count]];
for (SNTCertificate *cert in self.event.signingChain) {
for (MOLCertificate *cert in self.event.signingChain) {
[certArray addObject:(id)cert.certRef];
}
@@ -104,12 +104,8 @@
}
}
- (NSString *)shortenedHash {
return [self.event.fileSHA256 substringWithRange:NSMakeRange(0, 10)];
}
- (NSString *)publisherInfo {
SNTCertificate *leafCert = [self.event.signingChain firstObject];
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
@@ -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

@@ -1,121 +0,0 @@
/// Copyright 2015 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// SNTCertificate wraps a @c SecCertificateRef to provide Objective-C accessors to
/// commonly used certificate data. Accessors cache data for repeated access.
///
@interface SNTCertificate : NSObject<NSSecureCoding>
///
/// Initialize a SNTCertificate object with a valid SecCertificateRef. Designated initializer.
///
/// @param certRef valid SecCertificateRef, which will be retained.
///
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef;
///
/// Initialize a SNTCertificate object with certificate data in DER format.
///
/// @param certData DER-encoded certificate data.
/// @return initialized SNTCertificate or nil if certData is not a DER-encoded certificate.
///
- (instancetype)initWithCertificateDataDER:(NSData *)certData;
///
/// Initialize a SNTCertificate object with certificate data in PEM format.
/// If multiple PEM certificates exist within the string, the first is used.
///
/// @param certData PEM-encoded certificate data.
/// @return initialized SNTCertifcate or nil if certData is not a PEM-encoded certificate.
///
- (instancetype)initWithCertificateDataPEM:(NSString *)certData;
///
/// Returns an array of SNTCertificate's for all of the certificates in @c pemData.
///
/// @param pemData PEM-encoded certificates.
/// @return array of SNTCertificate objects.
///
+ (NSArray *)certificatesFromPEM:(NSString *)pemData;
///
/// Access the underlying certificate ref.
///
@property(readonly, nonatomic) SecCertificateRef certRef;
///
/// SHA-1 hash of the certificate data.
///
@property(readonly, nonatomic) NSString *SHA1;
///
/// SHA-256 hash of the certificate data.
///
@property(readonly, nonatomic) NSString *SHA256;
///
/// Certificate data.
///
@property(readonly, nonatomic) NSData *certData;
///
/// Common Name e.g: "Software Signing"
///
@property(readonly, nonatomic) NSString *commonName;
///
/// Country Name e.g: "US"
///
@property(readonly, nonatomic) NSString *countryName;
///
/// Organizational Name e.g: "Apple Inc."
///
@property(readonly, nonatomic) NSString *orgName;
///
/// Organizational Unit Name e.g: "Apple Software"
///
@property(readonly, nonatomic) NSString *orgUnit;
///
/// Is this cert a CA?
///
@property(readonly, nonatomic) BOOL isCA;
///
/// The cert serial number
///
@property(readonly, nonatomic) NSString *serialNumber;
///
/// Issuer details, same fields as above.
///
@property(readonly, nonatomic) NSString *issuerCommonName;
@property(readonly, nonatomic) NSString *issuerCountryName;
@property(readonly, nonatomic) NSString *issuerOrgName;
@property(readonly, nonatomic) NSString *issuerOrgUnit;
///
/// Validity Not Before
///
@property(readonly, nonatomic) NSDate *validFrom;
///
/// Validity Not After
///
@property(readonly, nonatomic) NSDate *validUntil;
@end

View File

@@ -1,375 +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 "SNTCertificate.h"
#import <CommonCrypto/CommonDigest.h>
#import <Security/Security.h>
@interface SNTCertificate ()
/// A container for cached property values
@property NSMutableDictionary *memoizedData;
@end
@implementation SNTCertificate
static NSString *const kCertDataKey = @"certData";
#pragma mark Init/Dealloc
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef {
self = [super init];
if (self) {
_certRef = certRef;
CFRetain(_certRef);
}
return self;
}
- (instancetype)initWithCertificateDataDER:(NSData *)certData {
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
if (cert) {
// Despite the header file claiming that SecCertificateCreateWithData will return NULL if
// @c certData doesn't contain a valid DER-encoded X509 cert, this isn't always true.
// radar://problem/16124651
// To workaround, check that the certificate serial number can be retrieved. According to
// RFC5280, the serial number field is required.
NSData *ser = CFBridgingRelease(SecCertificateCopySerialNumber(cert, NULL));
if (ser) {
self = [self initWithSecCertificateRef:cert];
} else {
self = nil;
}
CFRelease(cert); // was retained in initWithSecCertificateRef
} else {
self = nil;
}
return self;
}
- (instancetype)initWithCertificateDataPEM:(NSString *)certData {
// Find the PEM and extract the base64-encoded DER data from within
NSScanner *scanner = [NSScanner scannerWithString:certData];
NSString *base64der;
// Locate and parse DER data into |base64der|
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
if (!([scanner scanString:@"-----BEGIN CERTIFICATE-----" intoString:NULL] &&
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&base64der] &&
[scanner scanString:@"-----END CERTIFICATE-----" intoString:NULL])) {
return nil;
}
// base64-decode the DER
SecTransformRef transform = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
if (!transform) return nil;
NSData *input = [base64der dataUsingEncoding:NSUTF8StringEncoding];
NSData *output = nil;
if (SecTransformSetAttribute(transform,
kSecTransformInputAttributeName,
(__bridge CFDataRef)input,
NULL)) {
output = CFBridgingRelease(SecTransformExecute(transform, NULL));
}
if (transform) CFRelease(transform);
return [self initWithCertificateDataDER:output];
}
+ (NSArray *)certificatesFromPEM:(NSString *)pemData {
NSScanner *scanner = [NSScanner scannerWithString:pemData];
NSMutableArray *certs = [[NSMutableArray alloc] init];
while (YES) {
NSString *curCert;
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&curCert];
// If there was no data, break.
if (!curCert) break;
curCert = [curCert stringByAppendingString:@"-----END CERTIFICATE-----"];
SNTCertificate *cert = [[SNTCertificate alloc] initWithCertificateDataPEM:curCert];
// If the data couldn't be turned into a valid SNTCertificate, continue.
if (!cert) continue;
[certs addObject:cert];
}
return certs;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)dealloc {
if (_certRef) CFRelease(_certRef);
}
#pragma mark Equality & description
- (BOOL)isEqual:(id)other {
if (self == other) return YES;
if (![other isKindOfClass:[SNTCertificate class]]) return NO;
SNTCertificate *o = other;
return [self.certData isEqual:o.certData];
}
- (NSUInteger)hash {
return [self.certData hash];
}
- (NSString *)description {
return
[NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@", self.orgName, self.orgUnit, self.commonName];
}
#pragma mark NSSecureCoding
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.certData forKey:kCertDataKey];
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
NSData *certData = [decoder decodeObjectOfClass:[NSData class] forKey:kCertDataKey];
if ([certData length] == 0) return nil;
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
self = [self initWithSecCertificateRef:cert];
if (cert) CFRelease(cert);
return self;
}
#pragma mark Private Accessors
///
/// For a given selector, caches the value that selector would return on subsequent invocations,
/// using the provided block to get the value on the first invocation.
/// Assumes the selector's value will never change.
///
- (id)memoizedSelector:(SEL)selector forBlock:(id (^)(void))block {
NSString *selName = NSStringFromSelector(selector);
if (!self.memoizedData) {
self.memoizedData = [NSMutableDictionary dictionary];
}
if (!self.memoizedData[selName]) {
id val = block();
if (val) {
self.memoizedData[selName] = val;
} else {
self.memoizedData[selName] = [NSNull null];
}
}
// Return the value if there is one, or nil if the value is NSNull
return self.memoizedData[selName] != [NSNull null] ? self.memoizedData[selName] : nil;
}
- (NSDictionary *)allCertificateValues {
return [self memoizedSelector:_cmd forBlock:^id{
return CFBridgingRelease(SecCertificateCopyValues(self.certRef, NULL, NULL));
}];
}
- (NSDictionary *)x509SubjectName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SubjectName];
}];
}
- (NSDictionary *)x509IssuerName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1IssuerName];
}];
}
///
/// Retrieve the value with the specified label from the X509 dictionary provided
///
/// @param desiredLabel The label you want, e.g: kSecOIDOrganizationName.
/// @param dict The dictionary to look in (Subject or Issuer)
/// @return An @c NSString, the value for the specified label.
///
- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict {
@try {
NSArray *valArray = dict[(__bridge NSString *)kSecPropertyKeyValue];
for (NSDictionary *curCertVal in valArray) {
NSString *valueLabel = curCertVal[(__bridge NSString *)kSecPropertyKeyLabel];
if ([valueLabel isEqual:desiredLabel]) {
return curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
}
}
return nil;
}
@catch (NSException *e) {
return nil;
}
}
///
/// Retrieve the specified date from the certificate's values and convert from a reference date
/// to an NSDate object.
///
/// @param key The identifier for the date: @c kSecOIDX509V1ValiditityNot{Before,After}
/// @return An @c NSDate representing the date and time the certificate is valid from or expires.
///
- (NSDate *)dateForX509Key:(NSString *)key {
NSDictionary *curCertVal = [self allCertificateValues][key];
NSNumber *value = curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
NSTimeInterval interval = [value doubleValue];
if (interval) {
return [NSDate dateWithTimeIntervalSinceReferenceDate:interval];
}
return nil;
}
#pragma mark Public Accessors
- (NSString *)SHA1 {
return [self memoizedSelector:_cmd forBlock:^id{
NSMutableData *SHA1Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH];
CC_SHA1([self.certData bytes], (CC_LONG)[self.certData length], [SHA1Buffer mutableBytes]);
const unsigned char *bytes = (const unsigned char *)[SHA1Buffer bytes];
NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[hexDigest appendFormat:@"%02x", bytes[i]];
}
return hexDigest;
}];
}
- (NSString *)SHA256 {
return [self memoizedSelector:_cmd forBlock:^id{
NSMutableData *SHA256Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH];
CC_SHA256([self.certData bytes], (CC_LONG)[self.certData length], [SHA256Buffer mutableBytes]);
const unsigned char *bytes = (const unsigned char *)[SHA256Buffer bytes];
NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[hexDigest appendFormat:@"%02x", bytes[i]];
}
return hexDigest;
}];
}
- (NSData *)certData {
return CFBridgingRelease(SecCertificateCopyData(self.certRef));
}
- (NSString *)commonName {
return [self memoizedSelector:_cmd forBlock:^id{
CFStringRef commonName = NULL;
SecCertificateCopyCommonName(self.certRef, &commonName);
return CFBridgingRelease(commonName);
}];
}
- (NSString *)countryName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSString *)orgName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSString *)orgUnit {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
fromDictionary:[self x509SubjectName]];
}];
}
- (NSDate *)validFrom {
return [self memoizedSelector:_cmd forBlock:^id{
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotBefore];
}];
}
- (NSDate *)validUntil {
return [self memoizedSelector:_cmd forBlock:^id{
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotAfter];
}];
}
- (NSString *)issuerCommonName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCommonName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerCountryName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerOrgName {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
fromDictionary:[self x509IssuerName]];
}];
}
- (NSString *)issuerOrgUnit {
return [self memoizedSelector:_cmd forBlock:^id{
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
fromDictionary:[self x509IssuerName]];
}];
}
- (BOOL)isCA {
return [[self memoizedSelector:_cmd forBlock:^id{
NSDictionary *dict = [self allCertificateValues][(__bridge NSString *)kSecOIDBasicConstraints];
return [self x509ValueForLabel:@"Certificate Authority"
fromDictionary:dict];
}] isEqual:@"Yes"];
}
- (NSString *)serialNumber {
return [self memoizedSelector:_cmd forBlock:^id{
NSDictionary *dict = [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SerialNumber];
return dict[(__bridge NSString *)kSecPropertyKeyValue];
}];
}
@end

View File

@@ -1,90 +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.
@class SNTCertificate;
///
/// SNTCodesignChecker validates a binary (either on-disk or in memory) has been signed
/// and if so allows for pulling out the certificates that were used to sign it.
///
@interface SNTCodesignChecker : NSObject
///
/// The SecStaticCodeRef that this SNTCodesignChecker is working around
///
@property(readonly) SecStaticCodeRef codeRef;
///
/// Returns a dictionary of raw signing information
///
@property(readonly) NSDictionary *signingInformation;
///
/// Returns an array of @c SNTCertificate objects representing the chain that signed this binary.
///
@property(readonly) NSArray *certificates;
///
/// Returns the leaf certificate that this binary was signed with
///
@property(readonly, nonatomic) SNTCertificate *leafCertificate;
///
/// Returns the on-disk path of this binary.
///
@property(readonly, nonatomic) NSString *binaryPath;
///
/// Designated initializer
/// Takes ownership of the codeRef reference.
///
/// @param codeRef a SecStaticCodeRef or SecCodeRef representing a binary.
/// @return an initialized SNTCodesignChecker if the binary is validly signed, nil otherwise.
///
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef;
///
/// Convenience initializer for a binary on disk.
///
/// @param binaryPath A binary file on disk
/// @return an initialized SNTCodesignChecker if file is a binary and is signed, nil otherwise.
///
- (instancetype)initWithBinaryPath:(NSString *)binaryPath;
///
/// Convenience initializer for a binary that is running, by its process ID.
///
/// @param PID Id of a running process.
/// @return an initialized SNTCodesignChecker if binary is signed, nil otherwise.
///
- (instancetype)initWithPID:(pid_t)PID;
///
/// Convenience initializer for the currently running process.
///
/// @return an initialized SNTCodesignChecker if current binary is signed, nil otherwise.
///
- (instancetype)initWithSelf;
///
/// Compares the signatures of the binaries represented by this SNTCodesignChecker and
/// @c otherChecker.
///
/// If both binaries are correctly signed and the leaf signatures are identical.
///
/// @return YES if both binaries are signed with the same leaf certificate.
///
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker;
@end

View File

@@ -1,193 +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 "SNTCodesignChecker.h"
#import <Security/Security.h>
#import "SNTCertificate.h"
/**
* kStaticSigningFlags are the flags used when validating signatures on disk.
*
* Don't validate resources but do validate nested code. Ignoring resources _dramatically_ speeds
* up validation (see below) but does mean images, plists, etc will not be checked and modifying
* these will not be considered invalid. To ensure any code inside the binary is still checked,
* we check nested code.
*
* Timings with different flags:
* Checking Xcode 5.1.1 bundle:
* kSecCSDefaultFlags: 3.895s
* kSecCSDoNotValidateResources: 0.013s
* kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.013s
*
* Checking Google Chrome 36.0.1985.143 bundle:
* kSecCSDefaultFlags: 0.529s
* kSecCSDoNotValidateResources: 0.032s
* kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.033s
*/
static const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
/**
* kSigningFlags are the flags used when validating signatures for running binaries.
*
* No special flags needed currently.
*/
static const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
@interface SNTCodesignChecker ()
/// Array of @c SNTCertificate's representing the chain of certs this executable was signed with.
@property NSMutableArray *certificates;
@end
@implementation SNTCodesignChecker
#pragma mark Init/dealloc
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef {
self = [super init];
if (self) {
// First check the signing is valid
if (CFGetTypeID(codeRef) == SecStaticCodeGetTypeID()) {
if (SecStaticCodeCheckValidity(codeRef, kStaticSigningFlags, NULL) != errSecSuccess) {
return nil;
}
} else if (CFGetTypeID(codeRef) == SecCodeGetTypeID()) {
if (SecCodeCheckValidity((SecCodeRef)codeRef, kSigningFlags, NULL) != errSecSuccess) {
return nil;
}
} else {
return nil;
}
// Get CFDictionary of signing information for binary
OSStatus status = errSecSuccess;
CFDictionaryRef signingDict = NULL;
status = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingDict);
_signingInformation = CFBridgingRelease(signingDict);
if (status != errSecSuccess) return nil;
// Get array of certificates.
NSArray *certs = _signingInformation[(id)kSecCodeInfoCertificates];
if (!certs) return nil;
// Wrap SecCertificateRef objects in SNTCertificate and put in a new NSArray
NSMutableArray *mutableCerts = [[NSMutableArray alloc] initWithCapacity:certs.count];
for (NSUInteger i = 0; i < certs.count; ++i) {
SecCertificateRef certRef = (__bridge SecCertificateRef)certs[i];
SNTCertificate *newCert = [[SNTCertificate alloc] initWithSecCertificateRef:certRef];
[mutableCerts addObject:newCert];
}
_certificates = [mutableCerts copy];
_codeRef = codeRef;
CFRetain(_codeRef);
}
return self;
}
- (instancetype)initWithBinaryPath:(NSString *)binaryPath {
SecStaticCodeRef codeRef = NULL;
// Get SecStaticCodeRef for binary
if (SecStaticCodeCreateWithPath(
(__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath isDirectory:NO],
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
}
if (codeRef) CFRelease(codeRef);
return self;
}
- (instancetype)initWithPID:(pid_t)PID {
SecCodeRef codeRef = NULL;
NSDictionary *attributes = @{ (__bridge NSString *)kSecGuestAttributePid : @(PID) };
if (SecCodeCopyGuestWithAttributes(
NULL,
(__bridge CFDictionaryRef)attributes,
kSecCSDefaultFlags,
&codeRef) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeRef];
} else {
self = nil;
}
if (codeRef) CFRelease(codeRef);
return self;
}
- (instancetype)initWithSelf {
SecCodeRef codeSelf = NULL;
if (SecCodeCopySelf(kSecCSDefaultFlags, &codeSelf) == errSecSuccess) {
self = [self initWithSecStaticCodeRef:codeSelf];
} else {
self = nil;
}
if (codeSelf) CFRelease(codeSelf);
return self;
}
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (void)dealloc {
if (_codeRef) {
CFRelease(_codeRef);
_codeRef = NULL;
}
}
#pragma mark Description
- (NSString *)description {
NSString *binarySource;
if (CFGetTypeID(self.codeRef) == SecStaticCodeGetTypeID()) {
binarySource = @"On-disk";
} else {
binarySource = @"In-memory";
}
return [NSString stringWithFormat:@"%@ binary, signed by %@, located at: %@",
binarySource, self.leafCertificate.orgName, self.binaryPath];
}
#pragma mark Public accessors
- (SNTCertificate *)leafCertificate {
return [self.certificates firstObject];
}
- (NSString *)binaryPath {
CFURLRef path;
OSStatus status = SecCodeCopyPath(self.codeRef, kSecCSDefaultFlags, &path);
NSURL *pathURL = CFBridgingRelease(path);
if (status != errSecSuccess) return nil;
return [pathURL path];
}
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker {
return [self.certificates isEqual:otherChecker.certificates];
}
@end

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
@@ -57,6 +57,13 @@ extern NSString * const kDefaultConfigFilePath;
///
@property(nonatomic) NSRegularExpression *blacklistPathRegex;
///
/// Enable __PAGEZERO protection, defaults to YES
/// If this flag is set to NO, 32-bit binaries that are missing
/// the __PAGEZERO segment will not be blocked.
///
@property(readonly, nonatomic) BOOL enablePageZeroProtection;
#pragma mark - GUI Settings
///
@@ -87,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,37 +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 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];
@@ -80,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;
}
@@ -174,6 +176,11 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
[self saveConfigToDisk];
}
- (BOOL)enablePageZeroProtection {
NSNumber *keyValue = self.configData[kEnablePageZeroProtectionKey];
return keyValue ? [keyValue boolValue] : YES;
}
- (NSURL *)moreInfoURL {
return [NSURL URLWithString:self.configData[kMoreInfoURLKey]];
}
@@ -186,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 {
@@ -287,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) {
@@ -297,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;
}
}
@@ -328,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 .
///
@@ -109,8 +121,8 @@
///
/// @return Either the Info.plist in the bundle this file is part of, or an embedded plist if there
/// is one. In the odd case that a file has both an embedded Info.plist and is part of a bundle,
/// the Info.plist from the bundle will be returned.
/// is one. In the unlikely event that a file has both an embedded Info.plist and is part of a
/// bundle, the embedded plist will be returned.
///
- (NSDictionary *)infoPlist;
@@ -135,10 +147,24 @@
- (NSString *)bundleShortVersionString;
///
/// @return any URLs this file may have been downloaded from, using the
/// @c com.apple.metadata:kMDItemWhereFroms extended attribute.
/// @return LaunchServices quarantine data - download URL as an absolute string.
///
- (NSArray *)downloadURLs;
- (NSString *)quarantineDataURL;
///
/// @return LaunchServices quarantine data - referer URL as an absolute string.
///
- (NSString *)quarantineRefererURL;
///
/// @return LaunchServices quarantine data - agent bundle ID.
///
- (NSString *)quarantineAgentBundleID;
///
/// @return LaunchServices quarantine data - timestamp.
///
- (NSDate *)quarantineTimestamp;
///
/// @return The size of the file in bytes.

View File

@@ -18,8 +18,12 @@
#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.
@interface MachHeaderWithOffset : NSObject
@@ -40,38 +44,52 @@
@interface SNTFileInfo ()
@property NSString *path;
@property NSData *fileData;
// Dictionary of MachHeaderWithOffset objects where the keys are the architecture strings
@property NSDictionary *machHeaders;
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property NSString *fileOwnerHomeDir;
// Cached properties
@property NSBundle *bundleRef;
@property NSDictionary *infoDict;
@property NSDictionary *quarantineDict;
@property NSDictionary *cachedHeaders;
@end
@implementation SNTFileInfo
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
self = [super init];
if (self) {
_path = [self resolvePath:path];
NSBundle *bndl;
_path = [self resolvePath:path bundle:&bndl];
_bundleRef = bndl;
if (_path.length == 0) {
if (error) {
NSString *errStr = @"Unable to resolve empty path";
if (path) errStr = [@"Unable to resolve path: " stringByAppendingString:path];
*error = [NSError errorWithDomain:@"com.google.santa.fileinfo"
code:260
userInfo:@{ NSLocalizedDescriptionKey: errStr }];
userInfo:@{NSLocalizedDescriptionKey : errStr}];
}
return nil;
}
_fileData = [NSData dataWithContentsOfFile:_path
options:NSDataReadingUncached
error:error];
if (_fileData.length == 0) return nil;
[self parseMachHeaders];
_fileHandle = [NSFileHandle fileHandleForReadingAtPath:_path];
struct stat fileStat;
fstat(_fileHandle.fileDescriptor, &fileStat);
_fileSize = fileStat.st_size;
if (_fileSize == 0) return nil;
if (fileStat.st_uid != 0) {
struct passwd *pwd = getpwuid(fileStat.st_uid);
if (pwd) {
_fileOwnerHomeDir = @(pwd->pw_dir);
}
}
}
return self;
@@ -81,44 +99,84 @@
return [self initWithPath:path error:NULL];
}
- (NSString *)SHA1 {
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(self.fileData.bytes, (unsigned int)self.fileData.length, sha1);
#pragma mark Hashing
// Convert the binary SHA into hex
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha1[i]];
- (void)hashSHA1:(NSString **)sha1 SHA256:(NSString **)sha256 {
const int chunkSize = 4096;
CC_SHA1_CTX c1;
CC_SHA256_CTX c256;
if (sha1) CC_SHA1_Init(&c1);
if (sha256) CC_SHA256_Init(&c256);
for (uint64_t offset = 0; offset < self.fileSize; offset += chunkSize) {
@autoreleasepool {
int readSize = 0;
if (offset + chunkSize > self.fileSize) {
readSize = (int)(self.fileSize - offset);
} else {
readSize = chunkSize;
}
NSData *chunk = [self safeSubdataWithRange:NSMakeRange(offset, readSize)];
if (!chunk) {
if (sha1) CC_SHA1_Final(NULL, &c1);
if (sha256) CC_SHA256_Final(NULL, &c256);
return;
}
if (sha1) CC_SHA1_Update(&c1, chunk.bytes, readSize);
if (sha256) CC_SHA256_Update(&c256, chunk.bytes, readSize);
}
}
return buf;
if (sha1) {
unsigned char dgst[CC_SHA1_DIGEST_LENGTH];
CC_SHA1_Final(dgst, &c1);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; ++i) {
[buf appendFormat:@"%02x", (unsigned char)dgst[i]];
}
*sha1 = [buf copy];
}
if (sha256) {
unsigned char dgst[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(dgst, &c256);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i) {
[buf appendFormat:@"%02x", (unsigned char)dgst[i]];
}
*sha256 = [buf copy];
}
}
- (NSString *)SHA1 {
NSString *sha1;
[self hashSHA1:&sha1 SHA256:NULL];
return sha1;
}
- (NSString *)SHA256 {
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(self.fileData.bytes, (unsigned int)self.fileData.length, sha256);
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
[buf appendFormat:@"%02x", (unsigned char)sha256[i]];
}
return buf;
NSString *sha256;
[self hashSHA1:NULL SHA256:&sha256];
return sha256;
}
- (NSString *)machoType {
if ([self isDylib]) return @"Dynamic Library";
if ([self isKext]) return @"Kernel Extension";
if ([self isFat]) return @"Fat Binary";
if ([self isMachO]) return @"Thin Binary";
if ([self isScript]) return @"Script";
return @"Unknown (not executable?)";
}
#pragma mark File Type Info
- (NSArray *)architectures {
return [self.machHeaders allKeys];
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_EXECUTE) return YES;
return NO;
}
- (BOOL)isDylib {
struct mach_header *mach_header = [self firstMachHeader];
if (mach_header && mach_header->filetype == MH_DYLIB) return YES;
@@ -132,11 +190,11 @@
}
- (BOOL)isMachO {
return ([self.machHeaders count] > 0);
return (self.machHeaders.count > 0);
}
- (BOOL)isFat {
return ([self.machHeaders count] > 1);
return (self.machHeaders.count > 1);
}
- (BOOL)isScript {
@@ -144,17 +202,23 @@
return (strncmp("#!", magic, 2) == 0);
}
- (BOOL)isExecutable {
struct mach_header *mach_header = [self firstMachHeader];
if (!mach_header) return NO;
if (mach_header->filetype == MH_OBJECT || mach_header->filetype == MH_EXECUTE) return YES;
return NO;
- (BOOL)isXARArchive {
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 4)] bytes];
return (strncmp("xar!", magic, 4) == 0);
}
- (BOOL)isDMG {
NSUInteger last512 = self.fileSize - 512;
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(last512, 4)] bytes];
return (strncmp("koly", magic, 4) == 0);
}
#pragma mark Page Zero
- (BOOL)isMissingPageZero {
// This method only checks i386 arch because the kernel enforces this for other archs
// See bsd/kern/mach_loader.c, search for enforce_hard_pagezero.
MachHeaderWithOffset *x86Header = self.machHeaders[@"i386"];
MachHeaderWithOffset *x86Header = self.machHeaders[[self nameForCPUType:CPU_TYPE_X86]];
if (!x86Header) return NO;
struct mach_header *mh = (struct mach_header *)[x86Header.data bytes];
@@ -224,128 +288,79 @@
- (NSDictionary *)infoPlist {
if (!self.infoDict) {
NSDictionary *d = [self embeddedPlist];
if (d) {
self.infoDict = d;
return self.infoDict;
}
d = self.bundle.infoDictionary;
if (d) {
self.infoDict = d;
return self.infoDict;
}
self.infoDict = (NSDictionary *)[NSNull null];
if (self.bundle) {
NSDictionary *d = self.bundle.infoDictionary;
if (d) self.infoDict = d;
}
// Look for an embedded Info.plist if there is one.
// This could (and used to) use CFBundleCopyInfoDictionaryForURL but that uses mmap to read
// the file and so can cause SIGBUS if the file is deleted/truncated while it's working.
MachHeaderWithOffset *mhwo = [[self.machHeaders allValues] firstObject];
if (!mhwo) return self.infoDict;
struct mach_header *mh = (struct mach_header *)mhwo.data.bytes;
if (mh->filetype != MH_EXECUTE) return self.infoDict;
BOOL is64 = (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64);
uint32_t ncmds = mh->ncmds;
uint32_t nsects = 0;
uint64_t offset = mhwo.offset;
uint32_t sz_header = is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
uint32_t sz_segment = is64 ? sizeof(struct segment_command_64) : sizeof(struct segment_command);
uint32_t sz_section = is64 ? sizeof(struct section_64) : sizeof(struct section);
offset += sz_header;
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return self.infoDict;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
}
offset += lc->cmdsize;
}
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; i++) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return self.infoDict;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return self.infoDict;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (plist) self.infoDict = plist;
return self.infoDict;
}
offset += sz_section;
}
}
return self.infoDict == (NSDictionary *)[NSNull null] ? nil : self.infoDict;
}
- (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];
}
- (NSArray *)downloadURLs {
char *path = (char *)[self.path fileSystemRepresentation];
size_t size = (size_t)getxattr(path, "com.apple.metadata:kMDItemWhereFroms", NULL, 0, 0, 0);
char *value = malloc(size);
if (!value) return nil;
#pragma mark Quarantine Data
if (getxattr(path, "com.apple.metadata:kMDItemWhereFroms", value, size, 0, 0) == -1) {
free(value);
return nil;
}
NSData *data = [NSData dataWithBytes:value length:size];
free(value);
if (data) {
NSArray *urls = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:NULL
error:NULL];
return urls;
}
return nil;
- (NSString *)quarantineDataURL {
NSURL *dataURL = [self quarantineData][@"LSQuarantineDataURL"];
if (dataURL == (NSURL *)[NSNull null]) dataURL = nil;
return [dataURL absoluteString];
}
- (NSUInteger)fileSize {
return self.fileData.length;
- (NSString *)quarantineRefererURL {
NSURL *originURL = [self quarantineData][@"LSQuarantineOriginURL"];
if (originURL == (NSURL *)[NSNull null]) originURL = nil;
return [originURL absoluteString];
}
- (NSString *)quarantineAgentBundleID {
NSString *agentBundle = [self quarantineData][@"LSQuarantineAgentBundleIdentifier"];
if (agentBundle == (NSString *)[NSNull null]) agentBundle = nil;
return agentBundle;
}
- (NSDate *)quarantineTimestamp {
NSDate *timeStamp = [self quarantineData][@"LSQuarantineTimeStamp"];
return timeStamp;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {
if (self.machHeaders) return;
- (NSDictionary *)machHeaders {
if (self.cachedHeaders) return self.cachedHeaders;
// Sanity check file length
if (self.fileData.length < sizeof(struct mach_header)) {
self.machHeaders = [NSDictionary dictionary];
return;
if (self.fileSize < sizeof(struct mach_header)) {
self.cachedHeaders = [NSDictionary dictionary];
return self.cachedHeaders;
}
NSMutableDictionary *machHeaders = [NSMutableDictionary dictionary];
NSData *machHeader = [self parseSingleMachHeader:self.fileData];
NSData *machHeader = [self parseSingleMachHeader:[self safeSubdataWithRange:NSMakeRange(0,
4096)]];
if (machHeader) {
struct mach_header *mh = (struct mach_header *)[machHeader bytes];
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader offset:0];
@@ -361,7 +376,7 @@
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);
@@ -379,7 +394,8 @@
}
}
self.machHeaders = [machHeaders copy];
self.cachedHeaders = [machHeaders copy];
return self.cachedHeaders;
}
- (NSData *)parseSingleMachHeader:(NSData *)inputData {
@@ -400,26 +416,156 @@
}
///
/// Return one of the mach_header's in this file.
/// Locate an embedded plist in the file
///
- (struct mach_header *)firstMachHeader {
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
- (NSDictionary *)embeddedPlist {
// Look for an embedded Info.plist if there is one.
// This could (and used to) use CFBundleCopyInfoDictionaryForURL but that uses mmap to read
// the file and so can cause SIGBUS if the file is deleted/truncated while it's working.
MachHeaderWithOffset *mhwo = [[self.machHeaders allValues] firstObject];
if (!mhwo) return nil;
struct mach_header *mh = (struct mach_header *)mhwo.data.bytes;
if (mh->filetype != MH_EXECUTE) return self.infoDict;
BOOL is64 = (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64);
uint32_t ncmds = mh->ncmds;
uint32_t nsects = 0;
uint64_t offset = mhwo.offset;
uint32_t sz_header = is64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header);
uint32_t sz_segment = is64 ? sizeof(struct segment_command_64) : sizeof(struct segment_command);
uint32_t sz_section = is64 ? sizeof(struct section_64) : sizeof(struct section);
offset += sz_header;
// Loop through the load commands looking for the segment named __TEXT
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];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
nsects = lc->nsects;
offset += sz_segment;
break;
}
}
offset += lc->cmdsize;
}
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; ++i) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (sect && strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return nil;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (plist) return plist;
}
offset += sz_section;
}
return nil;
}
///
/// Wrap @c subdataWithRange: in a @@try/@@catch, returning nil on exception.
/// Useful for when the range is beyond the end of the file.
/// Return the first mach_header in this file.
///
- (struct mach_header *)firstMachHeader {
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
}
///
/// Extract a range of the file as an NSData, handling any exceptions.
/// Returns nil if the requested range is outside of the range of the file.
///
- (NSData *)safeSubdataWithRange:(NSRange)range {
@try {
return [self.fileData subdataWithRange:range];
if ((range.location + range.length) > self.fileSize) return nil;
[self.fileHandle seekToFileOffset:range.location];
NSData *d = [self.fileHandle readDataOfLength:range.length];
if (d.length != range.length) return nil;
return d;
}
@catch (NSException *e) {
return nil;
}
}
///
/// 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 && self.fileOwnerHomeDir) {
self.quarantineDict = (NSDictionary *)[NSNull null];
NSURL *url = [NSURL fileURLWithPath:self.path];
NSDictionary *d = [url resourceValuesForKeys:@[ NSURLQuarantinePropertiesKey ] error: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;
}
///
/// Return a human-readable string for a cpu_type_t.
///
@@ -439,7 +585,15 @@
return nil;
}
- (NSString *)resolvePath:(NSString *)path {
///
/// Resolves a given path:
/// + Follows symlinks
/// + Converts relative paths to absolute
/// + If path is a directory, checks to see if that directory is a bundle and if so
/// returns the path to that bundles CFBundleExecutable and stores a reference to the
/// bundle in the bundle out-param.
///
- (NSString *)resolvePath:(NSString *)path bundle:(NSBundle **)bundle {
// Convert to absolute, standardized path
path = [path stringByResolvingSymlinksInPath];
if (![path isAbsolutePath]) {
@@ -454,14 +608,9 @@
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

@@ -55,7 +55,7 @@
@property NSString *fileBundleVersionString;
///
/// If the executed file was signed, this is an NSArray of SNTCertificate's
/// If the executed file was signed, this is an NSArray of MOLCertificate's
/// representing the signing chain.
///
@property NSArray *signingChain;
@@ -100,4 +100,12 @@
///
@property NSString *parentName;
///
/// Quarantine data about the executed file, if any.
///
@property NSString *quarantineDataURL;
@property NSString *quarantineRefererURL;
@property NSDate *quarantineTimestamp;
@property NSString *quarantineAgentBundleID;
@end

View File

@@ -14,7 +14,7 @@
#import "SNTStoredEvent.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
@implementation SNTStoredEvent
@@ -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;
@@ -49,6 +49,11 @@
ENCODE(self.loggedInUsers, @"loggedInUsers");
ENCODE(self.currentSessions, @"currentSessions");
ENCODE(self.quarantineDataURL, @"quarantineDataURL");
ENCODE(self.quarantineRefererURL, @"quarantineRefererURL");
ENCODE(self.quarantineTimestamp, @"quarantineTimestamp");
ENCODE(self.quarantineAgentBundleID, @"quarantineAgentBundleID");
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
@@ -63,7 +68,7 @@
_fileBundleVersion = DECODE(NSString, @"fileBundleVersion");
_fileBundleVersionString = DECODE(NSString, @"fileBundleVersionString");
_signingChain = DECODEARRAY(SNTCertificate, @"signingChain");
_signingChain = DECODEARRAY(MOLCertificate, @"signingChain");
_executingUser = DECODE(NSString, @"executingUser");
_occurrenceDate = DECODE(NSDate, @"occurrenceDate");
@@ -74,6 +79,11 @@
_loggedInUsers = DECODEARRAY(NSString, @"loggedInUsers");
_currentSessions = DECODEARRAY(NSString, @"currentSessions");
_quarantineDataURL = DECODE(NSString, @"quarantineDataURL");
_quarantineRefererURL = DECODE(NSString, @"quarantineRefererURL");
_quarantineTimestamp = DECODE(NSDate, @"quarantineTimestamp");
_quarantineAgentBundleID = DECODE(NSString, @"quarantineAgentBundleID");
}
return self;
}

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

@@ -14,54 +14,51 @@
#import "SNTXPCConnection.h"
#import "SNTCodesignChecker.h"
@protocol XPCConnectionValidityRequest
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
@end
#import "MOLCodesignChecker.h"
@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;
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
SNTCodesignChecker *otherCS = [[SNTCodesignChecker 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;
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
SNTCodesignChecker *otherCS = [[SNTCodesignChecker 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,8 +33,12 @@
/// Database ops
///
- (void)databaseRuleCounts:(void (^)(int64_t binary, int64_t certificate))reply;
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply;
- (void)databaseRuleAddRule:(SNTRule *)rule
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseRuleAddRules:(NSArray *)rules
cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply;
- (void)databaseEventCount:(void (^)(int64_t count))reply;
- (void)databaseEventForSHA256:(NSString *)sha256 reply:(void (^)(SNTStoredEvent *))reply;
@@ -43,9 +48,8 @@
///
/// Config ops
///
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply;
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply;
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
@@ -53,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
@@ -68,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,30 +115,54 @@ 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 = proc_exiting(p);
proc_rele(p);
bool is_exiting = false;
if (p) {
is_exiting = proc_exiting(p);
proc_rele(p);
}
return (client_pid_ > 0 && !is_exiting);
}
IOMemoryDescriptor *SantaDecisionManager::GetMemoryDescriptor() {
return dataqueue_->getMemoryDescriptor();
void SantaDecisionManager::SetDecisionPort(mach_port_t port) {
lck_mtx_lock(decision_dataqueue_lock_);
decision_dataqueue_->setNotificationPort(port);
lck_mtx_unlock(decision_dataqueue_lock_);
}
void SantaDecisionManager::SetLogPort(mach_port_t port) {
lck_mtx_lock(log_dataqueue_lock_);
log_dataqueue_->setNotificationPort(port);
lck_mtx_unlock(log_dataqueue_lock_);
}
IOMemoryDescriptor *SantaDecisionManager::GetDecisionMemoryDescriptor() const {
return decision_dataqueue_->getMemoryDescriptor();
}
IOMemoryDescriptor *SantaDecisionManager::GetLogMemoryDescriptor() const {
return log_dataqueue_->getMemoryDescriptor();
}
#pragma mark Listener Control
@@ -177,17 +217,17 @@ void SantaDecisionManager::AddToCache(
ClearCache();
}
if (decision == ACTION_REQUEST_CHECKBW) {
SantaMessage *pending = new SantaMessage();
pending->setAction(ACTION_REQUEST_CHECKBW, 0);
if (decision == ACTION_REQUEST_BINARY) {
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);
}
@@ -212,7 +252,7 @@ void SantaDecisionManager::CacheCheck(const char *identifier) {
}
}
uint64_t SantaDecisionManager::CacheCount() {
uint64_t SantaDecisionManager::CacheCount() const {
return cached_decisions_->getCount();
}
@@ -229,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 {
@@ -272,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;
}
@@ -291,11 +331,11 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
do {
IOSleep(kRequestLoopSleepMilliseconds);
return_action = GetFromCache(vnode_id_str);
} while (return_action == ACTION_REQUEST_CHECKBW && ClientConnected());
} while (!CHECKBW_RESPONSE_VALID(return_action) && ClientConnected());
} while (return_action == ACTION_REQUEST_BINARY && ClientConnected());
} while (!RESPONSE_VALID(return_action) && ClientConnected());
// If response is still not valid, the daemon exited
if (!CHECKBW_RESPONSE_VALID(return_action)) {
if (!RESPONSE_VALID(return_action)) {
LOGE("Daemon process did not respond correctly. Allowing executions "
"until it comes back. Executable path: %s", message->path);
CacheCheck(vnode_id_str);
@@ -311,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];
@@ -328,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);
@@ -338,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();
@@ -347,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() {
@@ -387,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,
@@ -407,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:
@@ -441,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;
}
@@ -456,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) {
@@ -490,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,107 +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 "SNTCertificate.h"
#import "SNTCodesignChecker.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);
}
printf("%-19s: %s\n", "Path", [[fileInfo path] UTF8String]);
printf("%-19s: %s\n", "SHA-256", [[fileInfo SHA256] UTF8String]);
printf("%-19s: %s\n", "SHA-1", [[fileInfo SHA1] UTF8String]);
printf("%-19s: %s\n", "Bundle Name", [[fileInfo bundleName] UTF8String]);
printf("%-19s: %s\n", "Bundle Version", [[fileInfo bundleVersion] UTF8String]);
printf("%-19s: %s\n", "Bundle Version Str", [[fileInfo bundleShortVersionString] UTF8String]);
NSArray *archs = [fileInfo architectures];
if (archs) {
printf("%-19s: %s (%s)\n", "Type",
[[fileInfo machoType] UTF8String],
[[archs componentsJoinedByString:@", "] UTF8String]);
} else {
printf("%-19s: %s\n", "Type", [[fileInfo machoType] UTF8String]);
}
if ([fileInfo isMissingPageZero]) {
printf("%-19s: %s\n", "Page Zero", "__PAGEZERO segment missing/bad!");
}
SNTCodesignChecker *csc = [[SNTCodesignChecker alloc] initWithBinaryPath:filePath];
printf("%-19s: %s\n", "Code-signed", (csc) ? "Yes" : "No");
if (csc) {
printf("Signing chain:\n");
[csc.certificates enumerateObjectsUsingBlock:^(SNTCertificate *c,
unsigned long idx,
BOOL *stop) {
idx++; // index from 1
printf(" %2lu. %-20s: %s\n", idx, "SHA-256", [c.SHA256 UTF8String]);
printf(" %-20s: %s\n", "SHA-1", [c.SHA1 UTF8String]);
printf(" %-20s: %s\n", "Common Name", [c.commonName UTF8String]);
printf(" %-20s: %s\n", "Organization", [c.orgName UTF8String]);
printf(" %-20s: %s\n", "Organizational Unit", [c.orgUnit UTF8String]);
printf(" %-20s: %s\n", "Valid From", [[c.validFrom description] UTF8String]);
printf(" %-20s: %s\n", "Valid Until", [[c.validUntil description] UTF8String]);
printf("\n");
}];
}
exit(0);
}
@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

@@ -16,8 +16,8 @@
#include "SNTLogging.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTDropRootPrivs.h"
#import "SNTFileInfo.h"
@@ -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]];
}
}
@@ -125,7 +124,7 @@ REGISTER_COMMAND_NAME(@"rule")
if (newRule.type == RULETYPE_BINARY) {
newRule.shasum = fi.SHA256;
} else if (newRule.type == RULETYPE_CERT) {
SNTCodesignChecker *cs = [[SNTCodesignChecker alloc] initWithBinaryPath:fi.path];
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.path];
newRule.shasum = cs.leafCertificate.SHA256;
}
}
@@ -136,13 +135,18 @@ REGISTER_COMMAND_NAME(@"rule")
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
}
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^{
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^(BOOL success) {
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 {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
}
exit(0);
}
}];
}

View File

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

View File

@@ -14,7 +14,7 @@
#import "SNTAuthenticatingURLSession.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTDERDecoder.h"
#import "SNTLogging.h"
@@ -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,111 +141,61 @@
__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;
}
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
CFRelease(certificate);
// Switch identity finding method depending on config
if (self.clientCertCommonName && clientCert.commonName) {
if ([clientCert.commonName compare:self.clientCertCommonName
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
} else if (self.clientCertIssuerCn && clientCert.issuerCommonName) {
if ([clientCert.issuerCommonName compare:self.clientCertIssuerCn
options:NSCaseInsensitiveSearch] == NSOrderedSame) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
} else {
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
if (!decoder) {
LOGW(@"Unable to decode allowed distinguished name.");
continue;
}
if ([clientCert.issuerCommonName isEqual:decoder.commonName] &&
[clientCert.issuerCountryName isEqual:decoder.countryName] &&
[clientCert.issuerOrgName isEqual:decoder.organizationName] &&
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
foundIdentity = identityRef;
CFRetain(foundIdentity);
*stop = YES;
return; // return from enumeration block
}
}
}
}];
}
if (foundIdentity) {
SecCertificateRef certificate = NULL;
err = SecIdentityCopyCertificate(foundIdentity, &certificate);
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate];
LOGD(@"Client Trust: Valid client identity %@.", clientCert);
NSURLCredential *cred =
[NSURLCredential credentialWithIdentity:foundIdentity
certificates:nil
persistence:NSURLCredentialPersistenceForSession];
CFRelease(foundIdentity);
return cred;
} else {
LOGD(@"Client Trust: No valid identity found.");
@@ -277,11 +227,11 @@
if (self.serverRootsPemData) {
NSString *pemStrings = [[NSString alloc] initWithData:self.serverRootsPemData
encoding:NSASCIIStringEncoding];
NSArray *certs = [SNTCertificate certificatesFromPEM:pemStrings];
NSArray *certs = [MOLCertificate certificatesFromPEM:pemStrings];
// Make a new array of the SecCertificateRef's from the SNTCertificate's.
// Make a new array of the SecCertificateRef's from the MOLCertificate's.
NSMutableArray *certRefs = [[NSMutableArray alloc] initWithCapacity:certs.count];
for (SNTCertificate *cert in certs) {
for (MOLCertificate *cert in certs) {
[certRefs addObject:(id)cert.certRef];
}
@@ -304,7 +254,7 @@
// Print details about the server's leaf certificate.
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(serverTrust, 0);
if (firstCert) {
SNTCertificate *cert = [[SNTCertificate alloc] initWithSecCertificateRef:firstCert];
MOLCertificate *cert = [[MOLCertificate alloc] initWithSecCertificateRef:firstCert];
LOGD(@"Server Trust: Server leaf cert: %@", cert);
}
@@ -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,73 +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 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,73 +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 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 "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "NSData+Zlib.h"
#import "SNTCertificate.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);
}
@@ -174,9 +183,14 @@
ADDKEY(newEvent, kPPID, event.ppid);
ADDKEY(newEvent, kParentName, event.parentName);
ADDKEY(newEvent, kQuarantineDataURL, event.quarantineDataURL);
ADDKEY(newEvent, kQuarantineRefererURL, event.quarantineRefererURL);
ADDKEY(newEvent, kQuarantineTimestamp, @([event.quarantineTimestamp timeIntervalSince1970]));
ADDKEY(newEvent, kQuarantineAgentBundleID, event.quarantineAgentBundleID);
NSMutableArray *signingChain = [NSMutableArray arrayWithCapacity:event.signingChain.count];
for (NSUInteger i = 0; i < event.signingChain.count; i++) {
SNTCertificate *cert = [event.signingChain objectAtIndex:i];
for (NSUInteger i = 0; i < event.signingChain.count; ++i) {
MOLCertificate *cert = [event.signingChain objectAtIndex:i];
NSMutableDictionary *certDict = [NSMutableDictionary dictionary];
ADDKEY(certDict, kCertSHA256, cert.SHA256);
@@ -194,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,45 +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);
}
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:^{
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
handler(YES);
}];
} 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,12 +20,17 @@
#import "SNTDropRootPrivs.h"
#import "SNTEventTable.h"
#import "SNTLogging.h"
#import "SNTNotificationQueue.h"
#import "SNTRule.h"
#import "SNTRuleTable.h"
#import "SNTXPCConnection.h"
#import "SNTXPCNotifierInterface.h"
// Globals used by the santad watchdog thread
uint64_t watchdogCPUEvents = 0;
uint64_t watchdogRAMEvents = 0;
double watchdogCPUPeak = 0;
double watchdogRAMPeak = 0;
@interface SNTDaemonControlController ()
@property dispatch_source_t syncTimer;
@@ -33,11 +38,9 @@ uint64_t watchdogRAMEvents = 0;
@implementation SNTDaemonControlController
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager {
- (instancetype)init {
self = [super init];
if (self) {
_driverManager = driverManager;
_syncTimer = [self createSyncTimer];
[self rescheduleSyncSecondsFromNow:30];
}
@@ -50,19 +53,19 @@ uint64_t watchdogRAMEvents = 0;
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
dispatch_source_set_event_handler(syncTimerQ, ^{
[self rescheduleSyncSecondsFromNow:600];
[self rescheduleSyncSecondsFromNow:600];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (![[SNTConfigurator configurator] syncBaseURL]) return;
[[SNTConfigurator configurator] setSyncBackOff:NO];
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
if (fork() == 0) {
// Ensure we have no privileges
if (!DropRootPrivileges()) {
_exit(EPERM);
}
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
}
});
dispatch_resume(syncTimerQ);
@@ -94,12 +97,14 @@ uint64_t watchdogRAMEvents = 0;
reply([rdb binaryRuleCount], [rdb certificateRuleCount]);
}
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
- (void)databaseRuleAddRule:(SNTRule *)rule cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
[self databaseRuleAddRules:@[ rule ] cleanSlate:cleanSlate reply:reply];
}
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate reply:(void (^)())reply {
[[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
- (void)databaseRuleAddRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate
reply:(void (^)(BOOL success))reply {
BOOL success = [[SNTDatabaseController ruleTable] addRules:rules cleanSlate:cleanSlate];
// If any rules were added that were not whitelist, flush cache.
NSPredicate *p = [NSPredicate predicateWithFormat:@"SELF.state != %d", RULESTATE_WHITELIST];
@@ -108,7 +113,7 @@ uint64_t watchdogRAMEvents = 0;
[self.driverManager flushCache];
}
reply();
reply(success);
}
- (void)databaseEventCount:(void (^)(int64_t count))reply {
@@ -127,7 +132,7 @@ uint64_t watchdogRAMEvents = 0;
[[SNTDatabaseController eventTable] deleteEventsWithIds:ids];
}
#pragma mark Misc
#pragma mark Config Ops
- (void)clientMode:(void (^)(santa_clientmode_t))reply {
reply([[SNTConfigurator configurator] clientMode]);
@@ -170,12 +175,17 @@ uint64_t watchdogRAMEvents = 0;
reply();
}
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply {
reply(watchdogCPUEvents);
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply {
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
}
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply {
reply(watchdogRAMEvents);
#pragma mark GUI Ops
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener {
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
c.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
[c resume];
self.notQueue.notifierConnection = c;
}
@end

View File

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

View File

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

View File

@@ -32,7 +32,13 @@
/// Handles requests from the kernel using the given block.
/// @note Loops indefinitely unless there is an error trying to read data from the data queue.
///
- (void)listenWithBlock:(void (^)(santa_message_t message))callback;
- (void)listenForDecisionRequests:(void (^)(santa_message_t message))callback;
///
/// Handles requests from the kernel using the given block.
/// @note Loops indefinitely unless there is an error trying to read data from the data queue.
///
- (void)listenForLogRequests:(void (^)(santa_message_t message))callback;
///
/// Sends a response to a query back to the kernel.

View File

@@ -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 "MOLCertificate.h"
#import "SNTCachedDecision.h"
#import "SNTCertificate.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

@@ -14,7 +14,7 @@
#import "SNTEventTable.h"
#import "SNTCertificate.h"
#import "MOLCertificate.h"
#import "SNTLogging.h"
#import "SNTStoredEvent.h"
@@ -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;
}
@@ -43,12 +77,17 @@
!event.occurrenceDate ||
!event.decision) return NO;
NSData *eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
NSData *eventData;
@try {
eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
} @catch (NSException *exception) {
return NO;
}
__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;
@@ -59,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;
}
@@ -68,14 +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 ([rs next]) {
storedEvent = [self eventFromResultSet:rs];
if (!storedEvent) {
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
}
}
[rs close];
[rs close];
}];
return storedEvent;
@@ -85,19 +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 {
NSNumber *idx = [rs objectForColumnName:@"idx"];
[db executeUpdate:@"DELETE FROM events WHERE idx=?", 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;
@@ -107,8 +148,13 @@
NSData *eventData = [rs dataForColumn:@"eventdata"];
if (!eventData) return nil;
SNTStoredEvent *event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
SNTStoredEvent *event;
@try {
event = [NSKeyedUnarchiver unarchiveObjectWithData:eventData];
event.idx = @([rs intForColumn:@"idx"]);
} @catch (NSException *exception) {
}
return event;
}
@@ -117,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];
}];
}
@@ -126,7 +172,7 @@
[self deleteEventWithId:index];
}
[self inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"VACUUM"];
[db executeUpdate:@"VACUUM"];
}];
}

View File

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

View File

@@ -20,9 +20,9 @@
#include "SNTLogging.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTCachedDecision.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.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,20 +42,19 @@
- (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)[[SNTCodesignChecker alloc] initWithSelf];
(void)[[MOLCodesignChecker alloc] initWithSelf];
}
return self;
}
@@ -117,13 +115,13 @@
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;
}
// Get codesigning info about the file.
SNTCodesignChecker *csInfo = [[SNTCodesignChecker alloc] initWithBinaryPath:binInfo.path];
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path];
// Actually make the decision.
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
@@ -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);
}
@@ -178,10 +175,15 @@
se.currentSessions = currentSessions;
se.loggedInUsers = loggedInUsers;
se.quarantineDataURL = binInfo.quarantineDataURL;
se.quarantineRefererURL = binInfo.quarantineRefererURL;
se.quarantineTimestamp = binInfo.quarantineTimestamp;
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
@@ -189,8 +191,7 @@
[self initiateEventUploadForEvent:se];
if (!cd.silentBlock) {
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
withCustomMessage:cd.customMsg];
[self.notifierQueue addEvent:se customMessage:cd.customMsg];
}
}
}
@@ -228,7 +229,9 @@
return @"Blacklist Regex";
}
if (fi.isMissingPageZero) return @"Missing __PAGEZERO";
if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) {
return @"Missing __PAGEZERO";
}
return nil;
}
@@ -258,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;
}
}
@@ -278,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

@@ -14,8 +14,8 @@
#import "SNTRuleTable.h"
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "MOLCertificate.h"
#import "MOLCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTLogging.h"
#import "SNTRule.h"
@@ -28,20 +28,19 @@
@implementation SNTRuleTable
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Save hashes of the signing certs for launchd and santad
self.santadCertSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
self.santadCertSHA = [[[[MOLCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[MOLCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
uint32_t newVersion = 0;
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

@@ -21,10 +21,12 @@
extern uint64_t watchdogCPUEvents;
extern uint64_t watchdogRAMEvents;
extern double watchdogCPUPeak;
extern double watchdogRAMPeak;
/// Converts a timeval struct to double, converting the microseconds value to seconds.
static inline double timeval_to_double(struct timeval tv) {
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
static inline double timeval_to_double(time_value_t tv) {
return (double)tv.seconds + (double)tv.microseconds / 1000000.0;
}
/// The watchdog thread function, used to monitor santad CPU/RAM usage and print a warning
@@ -45,36 +47,39 @@ void *watchdogThreadFunction(__unused void *idata) {
double prevTotalTime = 0.0;
double prevRamUseMB = 0.0;
struct rusage usage;
struct mach_task_basic_info taskInfo;
mach_msg_type_number_t taskInfoCount = MACH_TASK_BASIC_INFO_COUNT;
while(true) {
while (true) {
@autoreleasepool {
sleep(timeInterval);
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
(task_info_t)&taskInfo, &taskInfoCount) == KERN_SUCCESS) {
// CPU
double totalTime = (timeval_to_double(taskInfo.user_time) +
timeval_to_double(taskInfo.system_time));
double percentage = (((totalTime - prevTotalTime) / (double)timeInterval) * 100.0);
prevTotalTime = totalTime;
// CPU
getrusage(RUSAGE_SELF, &usage);
double totalTime = timeval_to_double(usage.ru_utime) + timeval_to_double(usage.ru_stime);
double percentage = (((totalTime - prevTotalTime) / (double)timeInterval) * 100.0);
prevTotalTime = totalTime;
if (percentage > cpuWarnThreshold) {
LOGW(@"Watchdog: potentially high CPU use, ~%.2f%% over last %d seconds.",
percentage, timeInterval);
watchdogCPUEvents++;
}
if (percentage > cpuWarnThreshold) {
LOGW(@"Watchdog: potentially high CPU use, ~%.2f%% over last %d seconds.",
percentage, timeInterval);
watchdogCPUEvents++;
}
if (percentage > watchdogCPUPeak) watchdogCPUPeak = percentage;
// RAM
if (KERN_SUCCESS == task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
(task_info_t)&taskInfo, &taskInfoCount)) {
double ramUseMB = (double) taskInfo.resident_size / 1024 / 1024;
// RAM
double ramUseMB = (double)taskInfo.resident_size / 1024 / 1024;
if (ramUseMB > memWarnThreshold && ramUseMB > prevRamUseMB) {
LOGW(@"Watchdog: potentially high RAM use, RSS is %.2fMB.", ramUseMB);
watchdogRAMEvents++;
}
prevRamUseMB = ramUseMB;
if (ramUseMB > watchdogRAMPeak) watchdogRAMPeak = ramUseMB;
}
sleep(timeInterval);
}
}
return NULL;
@@ -95,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

@@ -1,243 +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 <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "SNTCertificate.h"
@interface SNTCertificate (Testing)
- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict;
- (NSDate *)dateForX509Key:(NSString *)key;
@end
@interface SNTCertificateTest : XCTestCase
@property NSString *testDataPEM1;
@property NSString *testDataPEM2;
@property NSData *testDataDER1;
@property NSData *testDataDER2;
@property NSString *testDataPrivateKey;
@end
@implementation SNTCertificateTest
- (void)setUp {
[super setUp];
NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"pem"];
self.testDataPEM1 = [NSString stringWithContentsOfFile:file
encoding:NSUTF8StringEncoding
error:nil];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"apple" ofType:@"pem"];
self.testDataPEM2 = [NSString stringWithContentsOfFile:file
encoding:NSUTF8StringEncoding
error:nil];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"crt"];
self.testDataDER1 = [NSData dataWithContentsOfFile:file];
file = [[NSBundle bundleForClass:[self class]] pathForResource:@"tubitak" ofType:@"crt"];
self.testDataDER2 = [NSData dataWithContentsOfFile:file];
self.testDataPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----"
@"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/"
@"RwohMrwxQpsoKwyzEngX58+PrGZ0XZrcVcHn666521IxswHZPaacBlWZ7k9XkB2Y"
@"m8mxULMBG9iIv/k5tRJN3MuJdtbQc8qLBsyFFsytL8hSRvBQNyP7N/OqnQIDAQAB"
@"AoGATpLUNNMonoH2Y/aVKGVY4ZNTLWOkkc4hQF7yNdVguRvE14UYV3Em0zs+TpOV"
@"/na5h4qh3WNkaupAy1eQYnK3fqmGLZw5e8cBCgUkIi8P//zMrKlgJKwfzQHSdJSP"
@"pkCvj2kliFwNzbA026jcwGEYV+uRCNazO5ldtOcP5EDb+qkCQQD/Ihc2mjtf7oq1"
@"VZSzo0xch3NtzTZMyFCRWqMpXHQO1fZTAe96EbI85zsTRmOVuqKnGxBvvtHJr2QY"
@"UoZ72+f7AkEA5Z9qte46t1F1ME3ZzWd6Ob1obCmuAa75eTPAgQKc+1bSVeFMGLTz"
@"n2M9wZ+mIpWvJp8QRdmOi0zpEArHqa68RwJBAO1YoY/CW4obOB8JxpR3TgqmV9PG"
@"HMXBdHJEh5Vq1O0YT1dZbZd57v6JfoOn7+zS+43Jt7i9JB0kdVHLNCD1qxECQQC3"
@"wXGGEhVO6pMbitGHvQ1k85yDIn+rvTjLs4yUMWErCfnc3CUniHeFz8d2EarD9oFq"
@"KNS+8TFPbMb+HYJW2gy1AkAHGBUKmZNPGiKJEUjc5jN1uN+B9OMLDX+3rMUO9Q2x"
@"jsn0m7Mobx+pPqbIAvsklMtA4Qdrt5a9pnwEgTWoJPYA"
@"-----END RSA PRIVATE KEY-----";
}
- (void)tearDown {
[super tearDown];
}
- (void)testInitWithDER {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataDER:self.testDataDER1];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
XCTAssertEqualObjects(sut.orgUnit, nil);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.countryName, @"US");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.issuerOrgName, @"GeoTrust Inc.");
XCTAssertEqualObjects(sut.issuerOrgUnit, nil);
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24");
XCTAssertEqualObjects(sut.SHA256,
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-04-04 15:15:55 +0000"]);
sut = [[SNTCertificate alloc] initWithCertificateDataDER:self.testDataDER2];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName,
@"TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3");
XCTAssertEqualObjects(sut.orgUnit,
@"Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE");
XCTAssertEqualObjects(sut.orgName,
@"Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK");
XCTAssertEqualObjects(sut.countryName, @"TR");
}
- (void)testInitWithValidPEM {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
XCTAssertEqualObjects(sut.orgUnit, nil);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24");
XCTAssertEqualObjects(sut.SHA256,
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-04-04 15:15:55 +0000"]);
XCTAssertTrue(sut.isCA);
XCTAssertEqualObjects(sut.serialNumber, @"146025");
sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM2];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"www.apple.com");
XCTAssertEqualObjects(sut.orgUnit, @"ISG for Akamai");
XCTAssertEqualObjects(sut.orgName, @"Apple Inc.");
XCTAssertEqualObjects(sut.issuerCommonName, @"VeriSign Class 3 Extended Validation SSL SGC CA");
XCTAssertEqualObjects(sut.issuerOrgName, @"VeriSign, Inc.");
XCTAssertEqualObjects(sut.issuerOrgUnit, @"VeriSign Trust Network");
XCTAssertEqualObjects(sut.SHA1, @"96df534f6f4306ca474d9078fc346b20f856f0d4");
XCTAssertEqualObjects(sut.SHA256,
@"129d39ff4384197dc2bcbe1a83a69b3405b7df33254b1b1ee29a23847a23555a");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-11-14 00:00:00 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-11-14 23:59:59 +0000"]);
XCTAssertFalse(sut.isCA);
XCTAssertEqualObjects(sut.serialNumber, @"5E FA 67 0E 99 E4 AB 88 E0 F2 0B 33 86 7B 78 4D");
}
- (void)testInitWithValidPEMAfterKey {
NSString *pemWithKey = [self.testDataPrivateKey stringByAppendingString:self.testDataPEM1];
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:pemWithKey];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
}
- (void)testInitWithEmptyPEM {
NSString *badPEM = @"-----BEGIN CERTIFICATE----------END CERTIFICATE-----";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithTruncatedPEM {
NSString *badPEM = @"-----BEGIN CERTIFICATE-----"
@"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithInvalidPEM {
NSString *badPEM = @"This is not a valid PEM";
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
badPEM = @"-----BEGIN CERTIFICATE-----Hello Thar-----END CERTIFICATE-----";
sut = [[SNTCertificate alloc] initWithCertificateDataPEM:badPEM];
XCTAssertNil(sut);
}
- (void)testInitWithMultipleCertsInPEM {
NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2];
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:multiPEM];
XCTAssertNotNil(sut);
XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2");
}
- (void)testArrayOfCerts {
NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2];
NSArray *certs = [SNTCertificate certificatesFromPEM:multiPEM];
XCTAssertNotNil(certs);
XCTAssertEqual(certs.count, 2);
XCTAssertEqualObjects([certs[0] commonName], @"Google Internet Authority G2");
XCTAssertEqualObjects([certs[1] commonName], @"www.apple.com");
}
- (void)testPlainInit {
XCTAssertThrows([[SNTCertificate alloc] init]);
}
- (void)testEquals {
SNTCertificate *sut1 = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
SNTCertificate *sut2 = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertEqualObjects(sut1, sut2);
}
- (void)testDescription {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
XCTAssertEqualObjects([sut description], @"/O=Google Inc/OU=(null)/CN=Google Internet Authority G2");
}
- (void)testSecureCoding {
XCTAssertTrue([SNTCertificate supportsSecureCoding]);
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
NSMutableData *encodedObject = [[NSMutableData alloc] init];
NSKeyedArchiver *archive = [[NSKeyedArchiver alloc] initForWritingWithMutableData:encodedObject];
[archive encodeObject:sut forKey:@"exampleCert"];
[archive finishEncoding];
NSKeyedUnarchiver *unarchive = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedObject];
SNTCertificate *newCert = [unarchive decodeObjectForKey:@"exampleCert"];
XCTAssertNotNil(newCert);
XCTAssertEqualObjects(newCert, sut);
XCTAssertEqualObjects(newCert.SHA1, sut.SHA1);
}
- (void)testCachingAccessors {
SNTCertificate *sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1];
id sutMock = OCMPartialMock(sut);
// Access each of the properties to get them cached
(void)sut.orgName;
(void)sut.issuerCommonName;
(void)sut.validFrom;
(void)sut.countryName;
(void)sut.issuerCountryName;
// Now break some of the properties
OCMExpect([sutMock x509ValueForLabel:OCMOCK_ANY fromDictionary:OCMOCK_ANY]);
OCMExpect([sutMock dateForX509Key:OCMOCK_ANY]);
XCTAssertEqualObjects(sut.orgName, @"Google Inc");
XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.countryName, @"US");
XCTAssertEqualObjects(sut.issuerCountryName, @"US");
}
@end

View File

@@ -1,103 +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 <XCTest/XCTest.h>
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
/**
Tests for @c SNTCodesignChecker
Most of these tests rely on some facts about @c launchd:
* launchd is in /sbin
* launchd is PID 1
* launchd is signed
* launchd's leaf cert has a CN of "Software Signing"
* launchd's leaf cert has an OU of "Apple Software"
* launchd's leaf cert has an ON of "Apple Inc."
These facts are pretty stable, so shouldn't be a problem.
**/
@interface SNTCodesignCheckerTest : XCTestCase
@end
@implementation SNTCodesignCheckerTest
- (void)testInitWithBinaryPath {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithBinaryPath:@"/sbin/launchd"];
XCTAssertNotNil(sut);
}
- (void)testInitWithInvalidBinaryPath {
SNTCodesignChecker *sut =
[[SNTCodesignChecker alloc] initWithBinaryPath:@"/tmp/this/file/doesnt/exist"];
XCTAssertNil(sut);
}
- (void)testInitWithPID {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut);
}
- (void)testInitWithInvalidPID {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:999999999];
XCTAssertNil(sut);
}
- (void)testInitWithSelf {
// n.b: 'self' in this case is xctest, which should always be signed.
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithSelf];
XCTAssertNotNil(sut);
}
- (void)testPlainInit {
XCTAssertThrows([[SNTCodesignChecker alloc] init]);
}
- (void)testDescription {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertEqualObjects([sut description],
@"In-memory binary, signed by Apple Inc., located at: /sbin/launchd");
}
- (void)testLeafCertificate {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut.leafCertificate);
}
- (void)testBinaryPath {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertEqualObjects(sut.binaryPath, @"/sbin/launchd");
}
- (void)testSigningInformationMatches {
SNTCodesignChecker *sut1 = [[SNTCodesignChecker alloc] initWithBinaryPath:@"/sbin/launchd"];
SNTCodesignChecker *sut2 = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertTrue([sut1 signingInformationMatches:sut2]);
}
- (void)testCodeRef {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithSelf];
XCTAssertNotNil((id)sut.codeRef);
}
- (void)testSigningInformation {
SNTCodesignChecker *sut = [[SNTCodesignChecker alloc] initWithPID:1];
XCTAssertNotNil(sut.signingInformation);
XCTAssertEqualObjects(sut.signingInformation[@"source"], @"embedded");
}
@end

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