mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ce0ef62e9 | ||
|
|
2a03341fb6 | ||
|
|
77a55dde56 | ||
|
|
1a71cdff4a | ||
|
|
63f65c51c3 | ||
|
|
75de2526c1 | ||
|
|
6fc4b7b120 | ||
|
|
7b8068139b | ||
|
|
ced7de884f | ||
|
|
bc51c9f25b | ||
|
|
c412e8b9a7 | ||
|
|
4e0ff224b6 | ||
|
|
61c817c9cb | ||
|
|
2ed384f677 | ||
|
|
7a851cb080 | ||
|
|
13aa889633 | ||
|
|
5c3fba5f41 | ||
|
|
145d9216bf | ||
|
|
84f46de940 | ||
|
|
cb9a5b6fbe | ||
|
|
d9718faba4 | ||
|
|
5472ff41f0 | ||
|
|
4f94c3b310 | ||
|
|
420f1efa50 | ||
|
|
5d2ce17817 | ||
|
|
053cb823a1 | ||
|
|
18a7992372 | ||
|
|
9e935f5bfb | ||
|
|
9f49e24dc5 | ||
|
|
dbf60f16bc | ||
|
|
0f3a228788 | ||
|
|
d905f5b095 | ||
|
|
1c310486c7 | ||
|
|
4b01c6da91 | ||
|
|
5782378616 | ||
|
|
64c97ebfba | ||
|
|
5fd4d56b00 | ||
|
|
e658b5167e | ||
|
|
cea698d720 | ||
|
|
c07f41c312 | ||
|
|
a837aa0334 | ||
|
|
0050724e22 | ||
|
|
adac4ac75c | ||
|
|
718f37024a | ||
|
|
fcb3008539 | ||
|
|
8faf3eec53 | ||
|
|
2bc3df3255 | ||
|
|
5b0e550c85 | ||
|
|
e52211abf2 | ||
|
|
9b6f231b34 | ||
|
|
b71223705f | ||
|
|
863fbe69bb | ||
|
|
2d46279961 | ||
|
|
0d0207d77f | ||
|
|
00bbade34f | ||
|
|
682f741ddc | ||
|
|
3d2744c9e3 | ||
|
|
cc286dcf16 | ||
|
|
27c6e2a7bd | ||
|
|
72c7a67ad5 | ||
|
|
8fe5e4e238 | ||
|
|
02f23d0c62 | ||
|
|
ff6f4d4152 | ||
|
|
2242f46792 | ||
|
|
642b5609b2 | ||
|
|
98878f3e7c | ||
|
|
3eb28deccf | ||
|
|
761a852156 | ||
|
|
f4ddb11c1f | ||
|
|
75158c11ea | ||
|
|
fe96706b0c | ||
|
|
b87482e824 | ||
|
|
a9ba99dc79 | ||
|
|
8884e92a1a | ||
|
|
6385514257 | ||
|
|
d3ad47022b | ||
|
|
138d4b507d | ||
|
|
3c0b195bcf | ||
|
|
d941a71bb5 | ||
|
|
08697d9daf | ||
|
|
8959871988 | ||
|
|
bb43a04992 | ||
|
|
5f93dc7991 | ||
|
|
9be8eb223c | ||
|
|
e8b6c47e0f | ||
|
|
697d442afb | ||
|
|
5dbd261b5a | ||
|
|
9bc94ca658 | ||
|
|
4404b5f849 | ||
|
|
6a4b73b8a9 | ||
|
|
b6146224b3 | ||
|
|
e3593c1b0c | ||
|
|
90a2f10da6 | ||
|
|
60bab1c004 |
@@ -1,5 +1,6 @@
|
||||
---
|
||||
language: objective-c
|
||||
cache: cocoapods
|
||||
|
||||
before_install:
|
||||
- gem install cocoapods xcpretty
|
||||
|
||||
@@ -18,7 +18,7 @@ sleep 1
|
||||
sleep 1
|
||||
|
||||
# Create hopefully useful symlink for santactl
|
||||
/bin/ln -s /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
|
||||
/bin/ln -sf /Library/Extensions/santa-driver.kext/Contents/MacOS/santactl /usr/local/bin
|
||||
|
||||
user=$(/usr/bin/stat -f '%u' /dev/console)
|
||||
[[ -z "$user" ]] && exit 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copy this file to /etc/asl to log all messages from santa-driver to the log file
|
||||
> /var/log/santa.log format="[$((Time)(utc.3))] $Message" mode=0644 rotate=seq compress file_max=10M all_max=100M uid=0 gid=0
|
||||
? [S= Message santa-driver:] claim
|
||||
? [S= Message santa-driver:] file /var/log/santa.log
|
||||
> /var/log/santa.log format="[$((Time)(ISO8601Z.3))] $Message" mode=0644 rotate=seq compress file_max=25M all_max=100M uid=0 gid=0
|
||||
? [= Sender kernel] [S= Message santa-driver:] claim
|
||||
? [= Sender kernel] [S= Message santa-driver:] file /var/log/santa.log
|
||||
? [= Sender santad] claim
|
||||
? [= Sender santad] file /var/log/santa.log
|
||||
? [= Sender santactl] claim
|
||||
|
||||
16
Podfile
16
Podfile
@@ -2,7 +2,19 @@ platform :osx, "10.9"
|
||||
|
||||
inhibit_all_warnings!
|
||||
|
||||
target :santactl do
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
end
|
||||
|
||||
target :Santa do
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
end
|
||||
|
||||
target :santad do
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'FMDB'
|
||||
|
||||
post_install do |installer|
|
||||
@@ -20,6 +32,8 @@ target :santad do
|
||||
end
|
||||
|
||||
target :LogicTests do
|
||||
pod 'OCMock'
|
||||
pod 'FMDB'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'OCMock'
|
||||
end
|
||||
|
||||
13
Podfile.lock
13
Podfile.lock
@@ -4,14 +4,21 @@ PODS:
|
||||
- FMDB/common (2.5)
|
||||
- FMDB/standard (2.5):
|
||||
- FMDB/common
|
||||
- OCMock (3.1.2)
|
||||
- MOLCertificate (1.1)
|
||||
- MOLCodesignChecker (1.2):
|
||||
- MOLCertificate (~> 1.1)
|
||||
- OCMock (3.2)
|
||||
|
||||
DEPENDENCIES:
|
||||
- FMDB
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- OCMock
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FMDB: 96e8f1bcc1329e269330f99770ad4285d9003e52
|
||||
OCMock: a10ea9f0a6e921651f96f78b6faee95ebc813b92
|
||||
MOLCertificate: 57fb88b33e83008d45c75644937fca61ed9f63d3
|
||||
MOLCodesignChecker: e887eeeb7cd87c9b808d1682187c8a27f8ff1100
|
||||
OCMock: 28def049ef47f996b515a8eeea958be7ccab2dbb
|
||||
|
||||
COCOAPODS: 0.38.0
|
||||
COCOAPODS: 0.39.0
|
||||
|
||||
35
README.md
35
README.md
@@ -1,7 +1,7 @@
|
||||
Santa [](https://travis-ci.org/google/santa)
|
||||
=====
|
||||
|
||||
Santa is a binary whitelisting/blacklisting system for Mac OS X. It consists of
|
||||
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
|
||||
notifies the user in case of a block decision and a command-line utility for
|
||||
@@ -48,9 +48,23 @@ continue to work across OS versions.
|
||||
|
||||
Intentions and Expectations
|
||||
===========================
|
||||
No single system or process will stop *all* attacks, or provide 100% security. Santa is written with the intention of helping protect users from themselves. People often download malware and trust it, giving the malware credentials, or allowing unknown software to exfiltrate more data about your system. As a centrally managed component, Santa can help stop the spread of malware among a larger fleet of machines. Additionally, Santa can aid in analyzing what is running in your fleet.
|
||||
No single system or process will stop *all* attacks, or provide 100% security.
|
||||
Santa is written with the intention of helping protect users from themselves.
|
||||
People often download malware and trust it, giving the malware credentials, or
|
||||
allowing unknown software to exfiltrate more data about your system. As a
|
||||
centrally managed component, Santa can help stop the spread of malware among a
|
||||
larger fleet of machines. Additionally, Santa can aid in analyzing what is
|
||||
running in your fleet.
|
||||
|
||||
Santa is part of a defense-in-depth strategy, and you should continue to protect hosts in whatever other ways you see fit.
|
||||
Santa is part of a defense-in-depth strategy, and you should continue to protect
|
||||
hosts in whatever other ways you see fit.
|
||||
|
||||
Get Help
|
||||
========
|
||||
|
||||
If you have questions or need help getting started, the
|
||||
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is the
|
||||
best place to start.
|
||||
|
||||
Known Issues
|
||||
============
|
||||
@@ -58,7 +72,7 @@ Santa is not yet a 1.0 and we have some known issues to be aware of:
|
||||
|
||||
* Santa only blocks execution (execve and variants), it doesn't protect against
|
||||
dynamic libraries loaded with dlopen, libraries on disk that have been replaced or
|
||||
libraries loaded using DYLD_INSERT_LIBRARIES. We are working on also protecting
|
||||
libraries loaded using `DYLD_INSERT_LIBRARIES`. We are working on also protecting
|
||||
against these avenues of attack.
|
||||
|
||||
* Kext communication security: the kext will only accept a connection from a
|
||||
@@ -107,11 +121,10 @@ and for security-reasons parts of Santa will not operate properly if not signed.
|
||||
|
||||
Kext Signing
|
||||
============
|
||||
10.9 requires a special Developer ID certificate to sign kernel extensions and
|
||||
if the kext is not signed with one of these special certificates a warning will
|
||||
be shown when loading the kext for the first time. In 10.10 this is a hard error
|
||||
and the kext will not load at all unless the machine is booted with a debug
|
||||
boot-arg.
|
||||
Kernel extensions on OS X 10.9 and later must be signed using an Apple-provided
|
||||
Developer ID certificate with a kernel extension flag. Without it, the only way
|
||||
to load an extension is to enable kext-dev-mode or disable SIP, depending on the
|
||||
OS version.
|
||||
|
||||
There are two possible solutions for this, for distribution purposes:
|
||||
|
||||
@@ -126,10 +139,6 @@ and distribute a new version of the pre-signed kext.
|
||||
Apple will only grant this for broad distribution within an organization, they
|
||||
won't issue them just for testing purposes.
|
||||
|
||||
If you just want to locally test changes to the kext code, you should enable
|
||||
kext-dev mode, instructions for which can be found on the Apple developer site.
|
||||
|
||||
|
||||
Contributing
|
||||
============
|
||||
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)
|
||||
|
||||
39
Rakefile
39
Rakefile
@@ -1,21 +1,24 @@
|
||||
require 'timeout'
|
||||
|
||||
WORKSPACE = 'Santa.xcworkspace'
|
||||
DEFAULT_SCHEME = 'All'
|
||||
OUTPUT_PATH = 'Build'
|
||||
DIST_PATH = 'Dist'
|
||||
BINARIES = ['Santa.app', 'santa-driver.kext']
|
||||
DSYMS = ['Santa.app.dSYM', 'santa-driver.kext.dSYM', 'santad.dSYM', 'santactl.dSYM']
|
||||
XCPRETTY_DEFAULTS = '-sc'
|
||||
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
|
||||
$DISABLE_XCPRETTY = false
|
||||
|
||||
task :default do
|
||||
system("rake -sT")
|
||||
end
|
||||
|
||||
def xcodebuild(opts)
|
||||
if system "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts} | " \
|
||||
"xcpretty #{XCPRETTY_DEFAULTS} && " \
|
||||
"exit ${PIPESTATUS[0]}"
|
||||
command = "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts}"
|
||||
if not $DISABLE_XCPRETTY
|
||||
command << " | xcpretty #{XCPRETTY_DEFAULTS} && exit ${PIPESTATUS[0]}"
|
||||
end
|
||||
|
||||
if system command
|
||||
puts "\e[32mPass\e[0m"
|
||||
else
|
||||
raise "\e[31mFail\e[0m"
|
||||
@@ -29,6 +32,7 @@ task :init do
|
||||
end
|
||||
unless system 'xcpretty -v >/dev/null 2>&1'
|
||||
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
|
||||
$DISABLE_XCPRETTY = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,7 +44,6 @@ end
|
||||
desc "Clean"
|
||||
task :clean => :init do
|
||||
puts "Cleaning"
|
||||
xcodebuild("-scheme All clean")
|
||||
FileUtils.rm_rf(OUTPUT_PATH)
|
||||
FileUtils.rm_rf(DIST_PATH)
|
||||
end
|
||||
@@ -94,6 +97,7 @@ end
|
||||
task :dist do
|
||||
desc "Create distribution folder"
|
||||
|
||||
Rake::Task['clean'].invoke()
|
||||
Rake::Task['build:build'].invoke("Release")
|
||||
|
||||
FileUtils.rm_rf(DIST_PATH)
|
||||
@@ -104,9 +108,13 @@ task :dist do
|
||||
|
||||
BINARIES.each do |x|
|
||||
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/binaries")
|
||||
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}.dSYM", "#{DIST_PATH}/dsym")
|
||||
end
|
||||
|
||||
DSYMS.each do |x|
|
||||
FileUtils.cp_r("#{OUTPUT_PATH}/Products/Release/#{x}", "#{DIST_PATH}/dsym")
|
||||
end
|
||||
|
||||
|
||||
Dir.glob("Conf/*") {|x| File.directory?(x) or FileUtils.cp(x, "#{DIST_PATH}/conf")}
|
||||
|
||||
puts "Distribution folder created"
|
||||
@@ -125,16 +133,17 @@ namespace :tests do
|
||||
Rake::Task['unload'].invoke()
|
||||
Rake::Task['install:debug'].invoke()
|
||||
Rake::Task['load_kext'].invoke
|
||||
timeout = 30
|
||||
puts "Running kernel tests with a #{timeout} second timeout"
|
||||
FileUtils.mkdir_p("/tmp/santa_kerneltests_tmp")
|
||||
begin
|
||||
Timeout::timeout(timeout) {
|
||||
system "sudo #{OUTPUT_PATH}/Products/Debug/KernelTests"
|
||||
}
|
||||
rescue Timeout::Error
|
||||
puts "ERROR: tests ran for longer than #{timeout} seconds and were killed."
|
||||
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"
|
||||
rescue Exception
|
||||
ensure
|
||||
puts "\033[?25h\033[12l\n\n" # unhide cursor
|
||||
FileUtils.rm_rf("/tmp/santa_kerneltests_tmp")
|
||||
Rake::Task['unload_kext'].execute
|
||||
end
|
||||
Rake::Task['unload_kext'].execute
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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 */; };
|
||||
@@ -78,8 +74,11 @@
|
||||
0D4644C5182AF81700098690 /* SantaDecisionManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0D4644C3182AF81700098690 /* SantaDecisionManager.cc */; };
|
||||
0D4644C6182AF81700098690 /* SantaDecisionManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D4644C4182AF81700098690 /* SantaDecisionManager.h */; };
|
||||
0D4A5007176A4602004F63BF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D4A5006176A4602004F63BF /* Security.framework */; };
|
||||
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */ = {isa = PBXBuildFile; fileRef = 0D536ED51B8E7A2E0039A26D /* bad_pagezero */; };
|
||||
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 */; };
|
||||
@@ -88,7 +87,6 @@
|
||||
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 */; };
|
||||
@@ -137,12 +135,9 @@
|
||||
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 */; };
|
||||
@@ -150,6 +145,8 @@
|
||||
0DE50F6C19130358007B2B0C /* SNTStoredEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DCD604A19105433006B445C /* SNTStoredEvent.m */; };
|
||||
0DE50F6E191304E0007B2B0C /* SNTRule.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE50F671912716A007B2B0C /* SNTRule.m */; };
|
||||
0DE6788D1784A8C2007A9E52 /* SNTExecutionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */; };
|
||||
0DE71A751B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
|
||||
0DE71A761B95F7F900518526 /* SNTCachedDecision.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DE71A741B95F7F900518526 /* SNTCachedDecision.m */; };
|
||||
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */; };
|
||||
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
@@ -159,6 +156,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 */
|
||||
|
||||
@@ -246,8 +244,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>"; };
|
||||
@@ -296,8 +292,10 @@
|
||||
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SantaDecisionManager.cc; sourceTree = "<group>"; };
|
||||
0D4644C4182AF81700098690 /* SantaDecisionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SantaDecisionManager.h; sourceTree = "<group>"; };
|
||||
0D4A5006176A4602004F63BF /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
|
||||
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>"; };
|
||||
0D536ED51B8E7A2E0039A26D /* bad_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = bad_pagezero; sourceTree = "<group>"; };
|
||||
0D536ED61B8E7A2E0039A26D /* missing_pagezero */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = missing_pagezero; sourceTree = "<group>"; };
|
||||
0D536ED91B94E9230039A26D /* SNTEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTEventLog.h; sourceTree = "<group>"; };
|
||||
0D536EDA1B94E9230039A26D /* SNTEventLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTEventLog.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>"; };
|
||||
@@ -353,8 +351,6 @@
|
||||
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>"; };
|
||||
@@ -362,6 +358,8 @@
|
||||
0DE50F671912716A007B2B0C /* SNTRule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTRule.m; sourceTree = "<group>"; };
|
||||
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTExecutionController.h; sourceTree = "<group>"; };
|
||||
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SNTExecutionController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
0DE71A731B95F7F900518526 /* SNTCachedDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTCachedDecision.h; sourceTree = "<group>"; };
|
||||
0DE71A741B95F7F900518526 /* SNTCachedDecision.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCachedDecision.m; sourceTree = "<group>"; };
|
||||
0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SNTCommandSyncConstants.m; sourceTree = "<group>"; };
|
||||
0DEFB7C11ACB28BC00B92AAE /* SNTCommandSyncConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTCommandSyncConstants.h; sourceTree = "<group>"; };
|
||||
0DEFB7C21ACDD80100B92AAE /* SNTFileWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SNTFileWatcher.h; sourceTree = "<group>"; };
|
||||
@@ -372,10 +370,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 */
|
||||
@@ -406,6 +410,7 @@
|
||||
0DE4C8A118FEF28200466D04 /* Security.framework in Frameworks */,
|
||||
0D35BDBD18FDA23600921A21 /* IOKit.framework in Frameworks */,
|
||||
0D35BD9F18FD71CE00921A21 /* Foundation.framework in Frameworks */,
|
||||
02D7203A6CEE2A946FB08ECE /* libPods-santactl.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -417,6 +422,7 @@
|
||||
0D6F12D819EC8822006B218E /* SecurityInterface.framework in Frameworks */,
|
||||
0D8C200C180F359A00CE2BF8 /* Security.framework in Frameworks */,
|
||||
0D385DB8180DE4A900418BC6 /* Cocoa.framework in Frameworks */,
|
||||
CB7CD33A4D1B4745552333F4 /* libPods-Santa.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -446,8 +452,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D260DB018B68E12002A0B55 /* Resources */,
|
||||
0DD0D48A194F6193005F27EB /* SNTCertificateTest.m */,
|
||||
0DD0D48C194F6D5B005F27EB /* SNTCodesignCheckerTest.m */,
|
||||
0D10BE8A1A0AB23300C0C944 /* SNTDERDecoderTest.m */,
|
||||
0D41DAD31A7C28C800A890FE /* SNTEventTableTest.m */,
|
||||
0DD0D490194F9947005F27EB /* SNTExecutionControllerTest.m */,
|
||||
@@ -462,6 +466,8 @@
|
||||
0D260DB018B68E12002A0B55 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D536ED51B8E7A2E0039A26D /* bad_pagezero */,
|
||||
0D536ED61B8E7A2E0039A26D /* missing_pagezero */,
|
||||
0D2CD4601A81C7B100C9C910 /* dn.plist */,
|
||||
0D6FDC8618C6913D0044685C /* apple.pem */,
|
||||
0D6FDC8218C68D7E0044685C /* GIAG2.crt */,
|
||||
@@ -627,6 +633,8 @@
|
||||
0D385DB7180DE4A900418BC6 /* Cocoa.framework */,
|
||||
752301D17AA44BDE8B6D0541 /* libPods-LogicTests.a */,
|
||||
0A84545E322F475FA0B505D5 /* libPods-santad.a */,
|
||||
E7544A9B819620883181F257 /* libPods-Santa.a */,
|
||||
A2EA300312100AE62218F84E /* libPods-santactl.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@@ -651,10 +659,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 */,
|
||||
@@ -688,18 +692,22 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0DA73CA519363C9F0056D7C4 /* DataLayer */,
|
||||
0D3AF83118F87CEF0087BCEE /* Resources */,
|
||||
0D9A7F411759330500035EB5 /* main.m */,
|
||||
0DB8ACBF185662DC00FEF9C7 /* SNTApplication.h */,
|
||||
0DB8ACC0185662DC00FEF9C7 /* SNTApplication.m */,
|
||||
0DE71A731B95F7F900518526 /* SNTCachedDecision.h */,
|
||||
0DE71A741B95F7F900518526 /* SNTCachedDecision.m */,
|
||||
0D8E18CB19107B56000F89B8 /* SNTDaemonControlController.h */,
|
||||
0D8E18CC19107B56000F89B8 /* SNTDaemonControlController.m */,
|
||||
0D63DD5A1906FCB400D346C4 /* SNTDatabaseController.h */,
|
||||
0D63DD5B1906FCB400D346C4 /* SNTDatabaseController.m */,
|
||||
0D7D01851774F93A005DBAB4 /* SNTDriverManager.h */,
|
||||
0D7D01861774F93A005DBAB4 /* SNTDriverManager.m */,
|
||||
0D536ED91B94E9230039A26D /* SNTEventLog.h */,
|
||||
0D536EDA1B94E9230039A26D /* SNTEventLog.m */,
|
||||
0DE6788B1784A8C2007A9E52 /* SNTExecutionController.h */,
|
||||
0DE6788C1784A8C2007A9E52 /* SNTExecutionController.m */,
|
||||
0D9A7F411759330500035EB5 /* main.m */,
|
||||
0D3AF83118F87CEF0087BCEE /* Resources */,
|
||||
);
|
||||
name = santad;
|
||||
path = Source/santad;
|
||||
@@ -757,6 +765,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>";
|
||||
@@ -812,6 +824,7 @@
|
||||
0D260DA918B68E12002A0B55 /* Frameworks */,
|
||||
0D260DAA18B68E12002A0B55 /* Resources */,
|
||||
85CE5DF0D54C438A8933A631 /* Copy Pods Resources */,
|
||||
4B68B2D454C1C55D29AA76D8 /* Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -826,9 +839,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 = (
|
||||
);
|
||||
@@ -843,10 +858,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 = (
|
||||
);
|
||||
@@ -942,8 +960,10 @@
|
||||
0D6FDC8518C68E500044685C /* GIAG2.pem in Resources */,
|
||||
0D6FDC8318C68D7E0044685C /* GIAG2.crt in Resources */,
|
||||
0D6F12DA19EDE51E006B218E /* tubitak.crt in Resources */,
|
||||
0D536ED71B8E7A2E0039A26D /* bad_pagezero in Resources */,
|
||||
0D2CD4611A81C7B100C9C910 /* dn.plist in Resources */,
|
||||
0D6FDC8718C6913D0044685C /* apple.pem in Resources */,
|
||||
0D536ED81B8E7A2E0039A26D /* missing_pagezero in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1053,6 +1073,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;
|
||||
@@ -1083,6 +1148,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;
|
||||
@@ -1098,6 +1193,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;
|
||||
@@ -1129,6 +1239,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0D88680C1AC48A1400B86659 /* SNTSystemInfo.m in Sources */,
|
||||
0D536EDC1B94E9230039A26D /* SNTEventLog.m in Sources */,
|
||||
0D63DD5E1906FCB400D346C4 /* SNTDatabaseController.m in Sources */,
|
||||
0D3AFBF018FB4C6C0087BCEE /* SNTDriverManager.m in Sources */,
|
||||
0DCD6044190ACCB8006B445C /* SNTFileInfo.m in Sources */,
|
||||
@@ -1144,7 +1255,6 @@
|
||||
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */,
|
||||
0D10BE8B1A0AB23300C0C944 /* SNTDERDecoderTest.m in Sources */,
|
||||
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */,
|
||||
0DD0D48B194F6193005F27EB /* SNTCertificateTest.m in Sources */,
|
||||
0D28D53819D9F5910015C5EB /* SNTConfigurator.m in Sources */,
|
||||
0D3AFBE718FB32CB0087BCEE /* SNTXPCConnectionTest.m in Sources */,
|
||||
0DCD605719115E54006B445C /* SNTDaemonControlController.m in Sources */,
|
||||
@@ -1152,12 +1262,10 @@
|
||||
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 */,
|
||||
0DD0D48D194F6D5B005F27EB /* SNTCodesignCheckerTest.m in Sources */,
|
||||
0DE71A761B95F7F900518526 /* SNTCachedDecision.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;
|
||||
@@ -1167,7 +1275,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0DA73CA21934F88D0056D7C4 /* SNTLogging.m in Sources */,
|
||||
0D35BDC218FDA5D100921A21 /* SNTCodesignChecker.m in Sources */,
|
||||
0D35BDB518FD84F600921A21 /* SNTCommandSync.m in Sources */,
|
||||
0DCD5FBF1909D64A006B445C /* SNTCommandBinaryInfo.m in Sources */,
|
||||
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */,
|
||||
@@ -1188,7 +1295,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 */,
|
||||
@@ -1210,8 +1316,6 @@
|
||||
0D827E6519DF392E006EC811 /* SNTConfigurator.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 */,
|
||||
@@ -1243,8 +1347,8 @@
|
||||
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 */,
|
||||
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
|
||||
0DC5D86D191AED220078A5C0 /* SNTRuleTable.m in Sources */,
|
||||
@@ -1254,9 +1358,9 @@
|
||||
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 */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1469,6 +1573,7 @@
|
||||
};
|
||||
0D35BDA718FD71CE00921A21 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 583E60E97931BA83D5273304 /* Pods-santactl.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -1509,6 +1614,7 @@
|
||||
};
|
||||
0D35BDA818FD71CE00921A21 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 54DD0E77B8BE005AFB7E944A /* Pods-santactl.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -1543,6 +1649,7 @@
|
||||
};
|
||||
0D385DE4180DE4A900418BC6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = DE1F4E0329B023976A1BAC19 /* Pods-Santa.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@@ -1584,6 +1691,7 @@
|
||||
};
|
||||
0D385DE5180DE4A900418BC6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8D738300867507BD7985972F /* Pods-Santa.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
@@ -1623,7 +1731,10 @@
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PROVISIONING_PROFILE = "";
|
||||
@@ -1643,8 +1754,11 @@
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
|
||||
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
|
||||
CLANG_WARN_CXX0X_EXTENSIONS = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_C_LANGUAGE_STANDARD = c99;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
PROVISIONING_PROFILE = "";
|
||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14E46" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
|
||||
@@ -37,7 +37,7 @@
|
||||
<rect key="frame" x="18" y="65" width="444" height="60"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="title">Santa is an application whitelisting system for Mac OS X.
|
||||
<string key="title">Santa is an application whitelisting system for OS X.
|
||||
|
||||
There are no user-configurable settings.</string>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="15A282b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
|
||||
<connections>
|
||||
<outlet property="applicationNameLabel" destination="qgf-Jf-cJr" id="1JX-X8-03v"/>
|
||||
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
|
||||
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
|
||||
</connections>
|
||||
@@ -15,14 +16,15 @@
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
|
||||
<windowStyleMask key="styleMask" utility="YES"/>
|
||||
<rect key="contentRect" x="167" y="107" width="497" height="356"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1600"/>
|
||||
<rect key="contentRect" x="167" y="107" width="497" height="381"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<view key="contentView" id="Iwq-Lx-rLv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="497" height="356"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="497" height="381"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
|
||||
<rect key="frame" x="207" y="286" width="83" height="40"/>
|
||||
<rect key="frame" x="207" y="311" width="83" height="40"/>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
|
||||
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
|
||||
<color key="textColor" red="0.18696189413265307" green="0.18696189413265307" blue="0.18696189413265307" alpha="1" colorSpace="calibratedRGB"/>
|
||||
@@ -30,11 +32,12 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
|
||||
<rect key="frame" x="22" y="239" width="454" height="17"/>
|
||||
<rect key="frame" x="22" y="264" width="454" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
|
||||
<animations/>
|
||||
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -48,6 +51,7 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="E7T-9h-ofr">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
|
||||
@@ -57,11 +61,31 @@
|
||||
<binding destination="-2" name="value" keyPath="self.event.filePath" id="qfp-sR-Nmu"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr">
|
||||
<rect key="frame" x="165" y="217" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="Pav-ZA-iAu"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
|
||||
<rect key="frame" x="165" y="142" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" 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"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
|
||||
@@ -76,6 +100,7 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
|
||||
@@ -91,6 +116,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL">
|
||||
<rect key="frame" x="8" y="92" width="120" height="17"/>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="User" id="1ut-uT-hQD">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -99,6 +125,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J">
|
||||
<rect key="frame" x="8" y="117" width="120" height="17"/>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Parent" id="gze-4A-1w5">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -107,6 +134,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5">
|
||||
<rect key="frame" x="8" y="167" width="120" height="17"/>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Publisher" id="yL9-yD-JXX">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -118,6 +146,7 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="KgY-X1-ESG">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -126,6 +155,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y">
|
||||
<rect key="frame" x="8" y="142" width="120" height="17"/>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Identifier" id="eKN-Ic-5zy">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -137,7 +167,8 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -151,10 +182,11 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
|
||||
<rect key="frame" x="146" y="92" width="1" height="117"/>
|
||||
<rect key="frame" x="146" y="92" width="1" height="142"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<font key="titleFont" metaFont="system"/>
|
||||
@@ -165,6 +197,7 @@
|
||||
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
|
||||
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSInfo" imagePosition="overlaps" alignment="center" refusesFirstResponder="YES" imageScaling="proportionallyDown" inset="2" id="R72-Qy-Xbb">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -184,6 +217,7 @@
|
||||
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
|
||||
<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">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -201,6 +235,7 @@ DQ
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<buttonCell key="cell" type="roundTextured" title="Open Event..." bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X1b-TF-1TL">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -218,15 +253,40 @@ DQ
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.parentName" id="arL-Mc-4xj">
|
||||
<binding destination="-2" name="displayPatternValue1" keyPath="self.event.parentName" id="Lce-TO-q9V">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
<string key="NSDisplayPattern">%{value1}@ (%{value2}@)</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="-2" name="displayPatternValue2" keyPath="self.event.ppid" previousBinding="Lce-TO-q9V" id="ofI-kH-F2d">
|
||||
<dictionary key="options">
|
||||
<string key="NSDisplayPattern">%{value1}@ (%{value2}@)</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC">
|
||||
<rect key="frame" x="8" y="217" width="120" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSIsNil</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
@@ -238,6 +298,7 @@ DQ
|
||||
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="f1p-GL-O3o" secondAttribute="bottom" constant="8" id="496-VQ-Fx5"/>
|
||||
<constraint firstItem="eQb-0a-76J" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="-116" id="6Q5-Oo-1cI"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="7K6-bY-Rn6"/>
|
||||
<constraint firstItem="pDa-fA-vnC" firstAttribute="centerY" secondItem="qgf-Jf-cJr" secondAttribute="centerY" id="AKX-pe-hEX"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="ALv-0v-szi"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="E6D-7P-17g"/>
|
||||
<constraint firstItem="cJf-k6-OxS" firstAttribute="centerY" secondItem="C3G-wL-u7w" secondAttribute="centerY" id="FdL-ZZ-Vbe"/>
|
||||
@@ -245,10 +306,12 @@ DQ
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="G0I-O2-S91"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-45" id="GD2-Ka-deo"/>
|
||||
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pDa-fA-vnC" secondAttribute="top" id="Gd4-Nr-n5G"/>
|
||||
<constraint firstItem="qgf-Jf-cJr" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="Ht4-Lg-U5N"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="IwX-ja-ZIs"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" id="JY4-N1-j8e"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" priority="500" id="JY4-N1-j8e"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="leading" priority="999" id="MVr-jY-GDj"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="Nsl-zf-poH"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="750" constant="30" id="Nsl-zf-poH"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="SCl-Ky-VmT"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="centerY" secondItem="pc8-G9-4pJ" secondAttribute="centerY" id="SLv-F7-w5k"/>
|
||||
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="Scq-zQ-Sao"/>
|
||||
@@ -256,17 +319,20 @@ DQ
|
||||
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="V0a-Py-iEc"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="leading" priority="999" id="Z6G-l9-G4a"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="top" secondItem="eQb-0a-76J" secondAttribute="bottom" constant="8" id="abm-cM-PN0"/>
|
||||
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" id="asc-Ga-WHD"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="eQb-0a-76J" secondAttribute="trailing" constant="20" id="b0B-3w-grH"/>
|
||||
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" priority="999" id="b5A-M7-ZsD"/>
|
||||
<constraint firstItem="KEB-eH-x2Y" firstAttribute="centerY" secondItem="PXc-xv-A28" secondAttribute="centerY" id="cHe-pZ-0Oq"/>
|
||||
<constraint firstItem="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="30" id="dYg-zP-wh2"/>
|
||||
<constraint firstItem="h6f-PY-cc0" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="eSz-lz-Fdh"/>
|
||||
<constraint firstItem="qgf-Jf-cJr" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="esg-lX-BAT"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="fGd-YS-phP"/>
|
||||
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" id="h3d-Kc-q88"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="7ua-5a-uSd" secondAttribute="trailing" constant="12" id="ioO-NJ-Jqo"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="lvJ-Rk-UT5" secondAttribute="centerY" id="jfs-YI-7Ae"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="jlD-Lo-abc"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="trailing" constant="20" id="kOG-Cj-hFG"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="qgf-Jf-cJr" secondAttribute="bottom" constant="8" id="lWU-tC-vWg"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="trailing" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" id="lse-kg-lA2"/>
|
||||
<constraint firstItem="eQb-0a-76J" firstAttribute="top" secondItem="KEB-eH-x2Y" secondAttribute="bottom" constant="8" id="m2z-1O-ifB"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="pdq-a6-Y73"/>
|
||||
@@ -279,6 +345,7 @@ DQ
|
||||
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="35" id="ukF-FH-DE8"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="bottom" secondItem="C3G-wL-u7w" secondAttribute="top" constant="-8" id="zst-nc-VqA"/>
|
||||
</constraints>
|
||||
<animations/>
|
||||
</view>
|
||||
<point key="canvasLocation" x="112.5" y="308"/>
|
||||
</window>
|
||||
|
||||
@@ -42,14 +42,22 @@
|
||||
self.notificationManager = [[SNTNotificationManager alloc] init];
|
||||
|
||||
NSNotificationCenter *workspaceNotifications = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
[workspaceNotifications addObserver:self
|
||||
selector:@selector(killConnection)
|
||||
name:NSWorkspaceSessionDidResignActiveNotification
|
||||
object:nil];
|
||||
[workspaceNotifications addObserver:self
|
||||
selector:@selector(createConnection)
|
||||
name:NSWorkspaceSessionDidBecomeActiveNotification
|
||||
object:nil];
|
||||
|
||||
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidResignActiveNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue currentQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
self.listener.invalidationHandler = nil;
|
||||
self.listener.rejectedHandler = nil;
|
||||
[self.listener invalidate];
|
||||
self.listener = nil;
|
||||
}];
|
||||
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue currentQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
[self attemptReconnection];
|
||||
}];
|
||||
|
||||
[self createConnection];
|
||||
}
|
||||
@@ -63,29 +71,25 @@
|
||||
#pragma mark Connection handling
|
||||
|
||||
- (void)createConnection {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
dispatch_sync(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 = ^{
|
||||
[weakSelf attemptReconnection];
|
||||
};
|
||||
self.listener.invalidationHandler = self.listener.rejectedHandler;
|
||||
[self.listener resume];
|
||||
}
|
||||
|
||||
- (void)killConnection {
|
||||
self.listener.invalidationHandler = nil;
|
||||
[self.listener invalidate];
|
||||
self.listener = nil;
|
||||
self.listener = [[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
|
||||
options:NSXPCConnectionPrivileged];
|
||||
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.listener.exportedObject = self.notificationManager;
|
||||
self.listener.rejectedHandler = ^{
|
||||
[weakSelf attemptReconnection];
|
||||
};
|
||||
self.listener.invalidationHandler = self.listener.rejectedHandler;
|
||||
[self.listener resume];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)attemptReconnection {
|
||||
// TODO(rah): Make this smarter.
|
||||
sleep(10);
|
||||
[self performSelectorOnMainThread:@selector(createConnection) withObject:nil waitUntilDone:NO];
|
||||
sleep(5);
|
||||
[self createConnection];
|
||||
}
|
||||
|
||||
#pragma mark Menu Management
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
[NSAnimationContext beginGrouping];
|
||||
[[NSAnimationContext currentContext] setDuration:0.15f];
|
||||
[[NSAnimationContext currentContext] setCompletionHandler:^{
|
||||
[weakSelf.windowController windowWillClose:nil];
|
||||
[weakSelf orderOut:nil];
|
||||
[weakSelf.windowController windowWillClose:sender];
|
||||
[weakSelf orderOut:sender];
|
||||
[weakSelf setAlphaValue:1.f];
|
||||
}];
|
||||
[[self animator] setAlphaValue:0.f];
|
||||
|
||||
@@ -60,4 +60,10 @@
|
||||
///
|
||||
@property IBOutlet NSButton *openEventButton;
|
||||
|
||||
///
|
||||
/// Reference to the "Application Name" label in the XIB. Used to remove if application
|
||||
/// doesn't have a CFBundleName.
|
||||
///
|
||||
@property IBOutlet NSTextField *applicationNameLabel;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTMessageWindow.h"
|
||||
@@ -46,6 +46,10 @@
|
||||
[self.openEventButton setTitle:eventDetailText];
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.event.fileBundleName) {
|
||||
[self.applicationNameLabel removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(id)sender {
|
||||
@@ -63,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];
|
||||
}
|
||||
|
||||
@@ -105,7 +109,7 @@
|
||||
}
|
||||
|
||||
- (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];
|
||||
@@ -133,8 +137,11 @@
|
||||
if ([self.customMessage length] > 0) {
|
||||
message = self.customMessage;
|
||||
} else {
|
||||
message = @"The following application has been blocked from executing<br />"
|
||||
@"because its trustworthiness cannot be determined.";
|
||||
message = [[SNTConfigurator configurator] defaultBlockMessage];
|
||||
if (!message) {
|
||||
message = @"The following application has been blocked from executing<br />"
|
||||
@"because its trustworthiness cannot be determined.";
|
||||
}
|
||||
}
|
||||
|
||||
NSString *fullHTML = [NSString stringWithFormat:@"%@%@%@", htmlHeader, message, htmlFooter];
|
||||
|
||||
@@ -1,111 +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;
|
||||
|
||||
///
|
||||
/// 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
|
||||
@@ -1,361 +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]];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -31,19 +31,39 @@ extern NSString * const kDefaultConfigFilePath;
|
||||
@property(nonatomic) santa_clientmode_t clientMode;
|
||||
|
||||
///
|
||||
/// Whether or not to log all events, even for whitelisted binaries.
|
||||
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
|
||||
///
|
||||
@property(nonatomic) BOOL logAllEvents;
|
||||
/// The regex flags IXSM can be used, though the s (dotalL) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(nonatomic) NSRegularExpression *fileChangesRegex;
|
||||
|
||||
///
|
||||
/// The regex of whitelisted paths. Regexes are specified in ICU format.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
|
||||
/// pointless as a path only ever a single line.
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(nonatomic) NSRegularExpression *whitelistPathRegex;
|
||||
|
||||
///
|
||||
/// The regex of blacklisted paths. Regexes are specified in ICU format.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotall) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@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
|
||||
|
||||
///
|
||||
@@ -73,6 +93,12 @@ 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.
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *defaultBlockMessage;
|
||||
|
||||
#pragma mark - Sync Settings
|
||||
|
||||
///
|
||||
@@ -91,6 +117,16 @@ extern NSString * const kDefaultConfigFilePath;
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *machineOwner;
|
||||
|
||||
///
|
||||
/// The last date of successful sync.
|
||||
///
|
||||
@property(nonatomic) NSDate *syncLastSuccess;
|
||||
|
||||
///
|
||||
/// If YES a clean sync is required.
|
||||
///
|
||||
@property(nonatomic) BOOL syncCleanRequired;
|
||||
|
||||
///
|
||||
/// If set, this over-rides the default machine ID used for syncing.
|
||||
///
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
@property NSString *configFilePath;
|
||||
@property NSMutableDictionary *configData;
|
||||
|
||||
/// Creating NSRegularExpression objects is not fast, so cache it.
|
||||
/// Creating NSRegularExpression objects is not fast, so cache them.
|
||||
@property NSRegularExpression *cachedFileChangesRegex;
|
||||
@property NSRegularExpression *cachedWhitelistDirRegex;
|
||||
@property NSRegularExpression *cachedBlacklistDirRegex;
|
||||
|
||||
/// Array of keys that cannot be changed while santad is running if santad didn't make the change.
|
||||
@property(readonly) NSArray *protectedKeys;
|
||||
@@ -35,14 +37,19 @@ 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 kLogAllEventsKey = @"LogAllEvents";
|
||||
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 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";
|
||||
@@ -82,7 +89,8 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
#pragma mark Protected Keys
|
||||
|
||||
- (NSArray *)protectedKeys {
|
||||
return @[ kClientModeKey, kWhitelistRegexKey ];
|
||||
return @[ kClientModeKey, kWhitelistRegexKey, kBlacklistRegexKey,
|
||||
kFileChangesRegexKey, kSyncBaseURLKey ];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
@@ -110,7 +118,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedWhitelistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:nil];
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedWhitelistDirRegex;
|
||||
}
|
||||
@@ -125,15 +133,53 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
- (BOOL)logAllEvents {
|
||||
return [self.configData[kLogAllEventsKey] boolValue];
|
||||
- (NSRegularExpression *)blacklistPathRegex {
|
||||
if (!self.cachedBlacklistDirRegex && self.configData[kBlacklistRegexKey]) {
|
||||
NSString *re = self.configData[kBlacklistRegexKey];
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedBlacklistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedBlacklistDirRegex;
|
||||
}
|
||||
|
||||
- (void)setLogAllEvents:(BOOL)logAllEvents {
|
||||
self.configData[kLogAllEventsKey] = @(logAllEvents);
|
||||
- (void)setBlacklistPathRegex:(NSRegularExpression *)re {
|
||||
if (!re) {
|
||||
[self.configData removeObjectForKey:kBlacklistRegexKey];
|
||||
} else {
|
||||
self.configData[kBlacklistRegexKey] = [re pattern];
|
||||
}
|
||||
self.cachedBlacklistDirRegex = nil;
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)fileChangesRegex {
|
||||
if (!self.cachedFileChangesRegex && self.configData[kFileChangesRegexKey]) {
|
||||
NSString *re = self.configData[kFileChangesRegexKey];
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedFileChangesRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedFileChangesRegex;
|
||||
}
|
||||
|
||||
- (void)setFileChangesRegex:(NSRegularExpression *)re {
|
||||
if (!re) {
|
||||
[self.configData removeObjectForKey:kFileChangesRegexKey];
|
||||
} else {
|
||||
self.configData[kFileChangesRegexKey] = [re pattern];
|
||||
}
|
||||
self.cachedFileChangesRegex = nil;
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
- (BOOL)enablePageZeroProtection {
|
||||
NSNumber *keyValue = self.configData[kEnablePageZeroProtectionKey];
|
||||
return keyValue ? [keyValue boolValue] : YES;
|
||||
}
|
||||
|
||||
- (NSURL *)moreInfoURL {
|
||||
return [NSURL URLWithString:self.configData[kMoreInfoURLKey]];
|
||||
}
|
||||
@@ -146,6 +192,10 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return self.configData[kEventDetailTextKey];
|
||||
}
|
||||
|
||||
- (NSString *)defaultBlockMessage {
|
||||
return self.configData[kDefaultBlockMessage];
|
||||
}
|
||||
|
||||
- (NSURL *)syncBaseURL {
|
||||
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
|
||||
}
|
||||
@@ -174,6 +224,24 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return self.configData[kServerAuthRootsFileKey];
|
||||
}
|
||||
|
||||
- (NSDate *)syncLastSuccess {
|
||||
return self.configData[kSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)syncLastSuccess {
|
||||
self.configData[kSyncLastSuccess] = syncLastSuccess;
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
- (BOOL)syncCleanRequired {
|
||||
return [self.configData[kSyncCleanRequired] boolValue];
|
||||
}
|
||||
|
||||
- (void)setSyncCleanRequired:(BOOL)syncCleanRequired {
|
||||
self.configData[kSyncCleanRequired] = @(syncCleanRequired);
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
- (NSString *)machineOwner {
|
||||
NSString *machineOwner;
|
||||
|
||||
@@ -213,8 +281,6 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
}
|
||||
|
||||
- (void)reloadConfigData {
|
||||
if (!self.configData) self.configData = [NSMutableDictionary dictionary];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm fileExistsAtPath:self.configFilePath]) return;
|
||||
|
||||
@@ -229,7 +295,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
|
||||
NSDictionary *configData =
|
||||
[NSPropertyListSerialization propertyListWithData:readData
|
||||
options:kCFPropertyListImmutable
|
||||
options:NSPropertyListImmutable
|
||||
format:NULL
|
||||
error:&error];
|
||||
if (error) {
|
||||
@@ -237,20 +303,29 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 (self.configData[key] && configData[key] &&
|
||||
![self.configData[key] isEqual:configData[key]] && geteuid() == 0) {
|
||||
NSMutableDictionary *configDataMutable = [configData mutableCopy];
|
||||
configDataMutable[key] = self.configData[key];
|
||||
changed = YES;
|
||||
LOGD(@"Ignoring changed configuration key: %@", key);
|
||||
if (!self.configData) {
|
||||
self.configData = [configData mutableCopy];
|
||||
} else 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];
|
||||
}
|
||||
changed = YES;
|
||||
LOGI(@"Ignoring changed configuration key: %@", key);
|
||||
}
|
||||
}
|
||||
self.configData = configDataMutable;
|
||||
if (changed) [self saveConfigToDisk];
|
||||
}
|
||||
self.configData = configDataMutable;
|
||||
if (changed) [self saveConfigToDisk];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
@@ -92,6 +92,11 @@
|
||||
///
|
||||
- (BOOL)isScript;
|
||||
|
||||
///
|
||||
/// @return YES if this file has a bad/missing __PAGEZERO .
|
||||
///
|
||||
- (BOOL)isMissingPageZero;
|
||||
|
||||
///
|
||||
/// @return An NSBundle if this file is part of a bundle.
|
||||
///
|
||||
@@ -104,8 +109,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;
|
||||
|
||||
@@ -130,9 +135,28 @@
|
||||
- (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.
|
||||
///
|
||||
- (NSUInteger)fileSize;
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,21 +18,42 @@
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/swap.h>
|
||||
#include <sys/xattr.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
|
||||
@property NSData *data;
|
||||
@property uint32_t offset;
|
||||
- (instancetype)initWithData:(NSData *)data offset:(uint32_t)offset;
|
||||
@end
|
||||
@implementation MachHeaderWithOffset
|
||||
- (instancetype)initWithData:(NSData *)data offset:(uint32_t)offset {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_data = data;
|
||||
_offset = offset;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SNTFileInfo ()
|
||||
@property NSString *path;
|
||||
@property NSData *fileData;
|
||||
|
||||
// Dictionary of MachHeaderWithOffset objects where the keys are the architecture strings
|
||||
@property NSDictionary *machHeaders;
|
||||
|
||||
// Cached properties
|
||||
@property NSData *firstMachHeaderData;
|
||||
@property NSBundle *bundleRef;
|
||||
@property NSDictionary *infoDict;
|
||||
@property NSArray *architecturesArray;
|
||||
@property NSDictionary *quarantineDict;
|
||||
@end
|
||||
|
||||
@implementation SNTFileInfo
|
||||
|
||||
extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path error:(NSError **)error {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@@ -49,9 +70,10 @@
|
||||
}
|
||||
|
||||
_fileData = [NSData dataWithContentsOfFile:_path
|
||||
options:NSDataReadingMappedIfSafe
|
||||
options:NSDataReadingUncached
|
||||
error:error];
|
||||
if (_fileData.length == 0) return nil;
|
||||
[self parseMachHeaders];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -96,87 +118,70 @@
|
||||
}
|
||||
|
||||
- (NSArray *)architectures {
|
||||
if (!self.architecturesArray) {
|
||||
self.architecturesArray = (NSArray *)[NSNull null];
|
||||
|
||||
if ([self isFat]) {
|
||||
NSMutableArray *ret = [[NSMutableArray alloc] init];
|
||||
|
||||
// Retrieve just the fat_header, if possible.
|
||||
NSData *head = [self safeSubdataWithRange:NSMakeRange(0, sizeof(struct fat_header))];
|
||||
if (!head) return nil;
|
||||
struct fat_header *fat_header = (struct fat_header *)[head bytes];
|
||||
|
||||
// Get number of architectures in the binary
|
||||
uint32_t narch = NSSwapBigIntToHost(fat_header->nfat_arch);
|
||||
|
||||
// Retrieve just the fat_arch's, make a mutable copy and if necessary swap the bytes
|
||||
NSData *archs = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
|
||||
sizeof(struct fat_arch) * narch)];
|
||||
if (!archs) return nil;
|
||||
struct fat_arch *fat_archs = (struct fat_arch *)[archs bytes];
|
||||
|
||||
// For each arch, get the name of its architecture
|
||||
for (uint32_t i = 0; i < narch; ++i) {
|
||||
cpu_type_t cpu = (cpu_type_t)NSSwapBigIntToHost((unsigned int)fat_archs[i].cputype);
|
||||
[ret addObject:[self nameForCPUType:cpu]];
|
||||
}
|
||||
|
||||
self.architecturesArray = ret;
|
||||
} else if ([self firstMachHeader]) {
|
||||
struct mach_header *hdr = [self firstMachHeader];
|
||||
self.architecturesArray = @[ [self nameForCPUType:hdr->cputype] ];
|
||||
}
|
||||
}
|
||||
|
||||
return self.architecturesArray == (NSArray *)[NSNull null] ? nil : self.architecturesArray;
|
||||
return [self.machHeaders allKeys];
|
||||
}
|
||||
|
||||
- (BOOL)isDylib {
|
||||
struct mach_header *mach_header = [self firstMachHeader];
|
||||
if (mach_header && (mach_header->filetype == MH_DYLIB || mach_header->filetype == MH_FVMLIB)) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (mach_header && mach_header->filetype == MH_DYLIB) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isKext {
|
||||
struct mach_header *mach_header = [self firstMachHeader];
|
||||
if (mach_header && mach_header->filetype == MH_KEXT_BUNDLE) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (mach_header && mach_header->filetype == MH_KEXT_BUNDLE) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isMachO {
|
||||
return [self firstMachHeader] != nil;
|
||||
return ([self.machHeaders count] > 0);
|
||||
}
|
||||
|
||||
- (BOOL)isFat {
|
||||
return ([self isFatHeader:(struct fat_header *)[self.fileData bytes]]);
|
||||
return ([self.machHeaders count] > 1);
|
||||
}
|
||||
|
||||
- (BOOL)isScript {
|
||||
char magic[2];
|
||||
[self.fileData getBytes:&magic length:2];
|
||||
|
||||
const char *magic = (const char *)[[self safeSubdataWithRange:NSMakeRange(0, 2)] bytes];
|
||||
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 ||
|
||||
mach_header->filetype == MH_PRELOAD) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (mach_header->filetype == MH_OBJECT || mach_header->filetype == MH_EXECUTE) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (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"];
|
||||
if (!x86Header) return NO;
|
||||
|
||||
struct mach_header *mh = (struct mach_header *)[x86Header.data bytes];
|
||||
if (mh->filetype != MH_EXECUTE) return NO;
|
||||
|
||||
NSRange range = NSMakeRange(x86Header.offset + sizeof(struct mach_header),
|
||||
sizeof(struct segment_command));
|
||||
NSData *lcData = [self safeSubdataWithRange:range];
|
||||
if (!lcData) return NO;
|
||||
|
||||
// This code assumes the __PAGEZERO is always the first load-command in the file.
|
||||
// Given that the OS X ABI says "the static linker creates a __PAGEZERO segment
|
||||
// as the first segment of an executable file." this should be OK.
|
||||
struct load_command *lc = (struct load_command *)[lcData bytes];
|
||||
if (lc->cmd == LC_SEGMENT) {
|
||||
struct segment_command *segment = (struct segment_command *)lc;
|
||||
if (segment->vmaddr == 0 && segment->vmsize != 0 &&
|
||||
segment->initprot == 0 && segment->maxprot == 0 &&
|
||||
strcmp("__PAGEZERO", segment->segname) == 0) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark Bundle Information
|
||||
|
||||
///
|
||||
@@ -221,18 +226,19 @@
|
||||
|
||||
- (NSDictionary *)infoPlist {
|
||||
if (!self.infoDict) {
|
||||
self.infoDict = (NSDictionary *)[NSNull null];
|
||||
|
||||
if ([self bundle] && [self.bundle infoDictionary]) {
|
||||
self.infoDict = [self.bundle infoDictionary];
|
||||
} else {
|
||||
// Binaries with embedded Info.plist aren't in an NSBundle but
|
||||
// CFBundleCopyInfoDictionaryForURL will return the embedded info dict.
|
||||
NSURL *url = [NSURL fileURLWithPath:self.path isDirectory:NO];
|
||||
NSDictionary *infoDict =
|
||||
(__bridge_transfer NSDictionary *)CFBundleCopyInfoDictionaryForURL((__bridge CFURLRef)url);
|
||||
if (infoDict) self.infoDict = 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];
|
||||
}
|
||||
return self.infoDict == (NSDictionary *)[NSNull null] ? nil : self.infoDict;
|
||||
}
|
||||
@@ -253,73 +259,159 @@
|
||||
return [self.infoPlist objectForKey:@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
- (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;
|
||||
- (NSString *)quarantineDataURL {
|
||||
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineDataURLKey];
|
||||
return [url absoluteString];
|
||||
}
|
||||
|
||||
if (getxattr(path, "com.apple.metadata:kMDItemWhereFroms", value, size, 0, 0) == -1) {
|
||||
free(value);
|
||||
return nil;
|
||||
- (NSString *)quarantineRefererURL {
|
||||
NSURL *url = [self quarantineData][(__bridge NSString *)kLSQuarantineOriginURLKey];
|
||||
return [url absoluteString];
|
||||
}
|
||||
|
||||
- (NSString *)quarantineAgentBundleID {
|
||||
return [self quarantineData][(__bridge NSString *)kLSQuarantineAgentBundleIdentifierKey];
|
||||
}
|
||||
|
||||
- (NSDate *)quarantineTimestamp {
|
||||
return [self quarantineData][(__bridge NSString *)kLSQuarantineTimeStampKey];
|
||||
}
|
||||
|
||||
- (NSUInteger)fileSize {
|
||||
return self.fileData.length;
|
||||
}
|
||||
|
||||
#pragma mark Internal Methods
|
||||
|
||||
- (void)parseMachHeaders {
|
||||
if (self.machHeaders) return;
|
||||
|
||||
// Sanity check file length
|
||||
if (self.fileData.length < sizeof(struct mach_header)) {
|
||||
self.machHeaders = [NSDictionary dictionary];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *data = [NSData dataWithBytes:value length:size];
|
||||
free(value);
|
||||
NSMutableDictionary *machHeaders = [NSMutableDictionary dictionary];
|
||||
|
||||
if (data) {
|
||||
NSArray *urls = [NSPropertyListSerialization propertyListWithData:data
|
||||
options:NSPropertyListImmutable
|
||||
format:NULL
|
||||
error:NULL];
|
||||
return urls;
|
||||
NSData *machHeader = [self parseSingleMachHeader:self.fileData];
|
||||
if (machHeader) {
|
||||
struct mach_header *mh = (struct mach_header *)[machHeader bytes];
|
||||
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader offset:0];
|
||||
machHeaders[[self nameForCPUType:mh->cputype]] = mhwo;
|
||||
} else {
|
||||
NSRange range = NSMakeRange(0, sizeof(struct fat_header));
|
||||
NSData *fatHeader = [self safeSubdataWithRange:range];
|
||||
struct fat_header *fh = (struct fat_header *)[fatHeader bytes];
|
||||
|
||||
if (fatHeader && (fh->magic == FAT_MAGIC || fh->magic == FAT_CIGAM)) {
|
||||
int nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
|
||||
range = NSMakeRange(sizeof(struct fat_header), sizeof(struct fat_arch) * nfat_arch);
|
||||
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++) {
|
||||
int offset = OSSwapBigToHostInt32(fat_arch[i].offset);
|
||||
int size = OSSwapBigToHostInt32(fat_arch[i].size);
|
||||
int cputype = OSSwapBigToHostInt(fat_arch[i].cputype);
|
||||
|
||||
range = NSMakeRange(offset, size);
|
||||
NSData *machHeader = [self parseSingleMachHeader:[self safeSubdataWithRange:range]];
|
||||
if (machHeader) {
|
||||
NSString *key = [self nameForCPUType:cputype];
|
||||
MachHeaderWithOffset *mhwo = [[MachHeaderWithOffset alloc] initWithData:machHeader
|
||||
offset:offset];
|
||||
machHeaders[key] = mhwo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.machHeaders = [machHeaders copy];
|
||||
}
|
||||
|
||||
- (NSData *)parseSingleMachHeader:(NSData *)inputData {
|
||||
if (inputData.length < sizeof(struct mach_header)) return nil;
|
||||
struct mach_header *mh = (struct mach_header *)[inputData bytes];
|
||||
|
||||
if (mh->magic == MH_CIGAM || mh->magic == MH_CIGAM_64) {
|
||||
NSMutableData *mutableInput = [inputData mutableCopy];
|
||||
mh = (struct mach_header *)[mutableInput mutableBytes];
|
||||
swap_mach_header(mh, NXHostByteOrder());
|
||||
}
|
||||
|
||||
if (mh->magic == MH_MAGIC || mh->magic == MH_MAGIC_64) {
|
||||
return [NSData dataWithBytes:mh length:sizeof(struct mach_header)];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark Internal Methods
|
||||
///
|
||||
/// Locate an embedded plist in the file
|
||||
///
|
||||
- (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 (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;
|
||||
}
|
||||
|
||||
///
|
||||
/// Look through the file for the first mach_header. If the file is thin, this will be the
|
||||
/// header at the beginning of the file. If the file is fat, it will be the first
|
||||
/// architecture-specific header.
|
||||
/// Return one of the mach_header's in this file.
|
||||
///
|
||||
- (struct mach_header *)firstMachHeader {
|
||||
if (!self.firstMachHeaderData) {
|
||||
self.firstMachHeaderData = (NSData *)[NSNull null];
|
||||
return (struct mach_header *)([[[[self.machHeaders allValues] firstObject] data] bytes]);
|
||||
|
||||
if ([self isFatHeader:(struct fat_header *)[self.fileData bytes]]) {
|
||||
// Get the bytes for the fat_arch
|
||||
NSData *archHdr = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
|
||||
sizeof(struct fat_arch))];
|
||||
if (!archHdr) return NULL;
|
||||
struct fat_arch *fat_arch = (struct fat_arch *)[archHdr bytes];
|
||||
|
||||
// Get bytes for first mach_header
|
||||
NSData *machHdr = [self safeSubdataWithRange:NSMakeRange(NSSwapBigIntToHost(fat_arch->offset),
|
||||
sizeof(struct mach_header))];
|
||||
if (!machHdr || ![self isMachHeader:(struct mach_header *)machHdr.bytes]) return NULL;
|
||||
|
||||
self.firstMachHeaderData = [machHdr copy];
|
||||
} else if ([self isMachHeader:(struct mach_header *)[self.fileData bytes]]) {
|
||||
NSData *machHdr = [self safeSubdataWithRange:NSMakeRange(0, sizeof(struct mach_header))];
|
||||
if (!machHdr) return NULL;
|
||||
self.firstMachHeaderData = [machHdr copy];
|
||||
}
|
||||
}
|
||||
return (self.firstMachHeaderData == (NSData *)[NSNull null] ?
|
||||
NULL :
|
||||
(struct mach_header *)self.firstMachHeaderData.bytes);
|
||||
}
|
||||
|
||||
- (BOOL)isMachHeader:(struct mach_header *)header {
|
||||
return (header->magic == MH_MAGIC || header->magic == MH_MAGIC_64 ||
|
||||
header->magic == MH_CIGAM || header->magic == MH_CIGAM_64);
|
||||
}
|
||||
|
||||
- (BOOL)isFatHeader:(struct fat_header *)header {
|
||||
return (header->magic == FAT_MAGIC || header->magic == FAT_CIGAM);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -335,6 +427,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Retrieve quarantine data for a file
|
||||
///
|
||||
- (NSDictionary *)quarantineData {
|
||||
if (!self.quarantineDict && NSURLQuarantinePropertiesKey != NULL) {
|
||||
NSURL *url = [NSURL fileURLWithPath:self.path];
|
||||
NSDictionary *d = [url resourceValuesForKeys:@[ NSURLQuarantinePropertiesKey ] error:NULL];
|
||||
self.quarantineDict = d[NSURLQuarantinePropertiesKey];
|
||||
if (!self.quarantineDict) self.quarantineDict = (NSDictionary *)[NSNull null];
|
||||
}
|
||||
return (self.quarantineDict == (NSDictionary *)[NSNull null]) ? nil : self.quarantineDict;
|
||||
}
|
||||
|
||||
///
|
||||
/// Return a human-readable string for a cpu_type_t.
|
||||
///
|
||||
- (NSString *)nameForCPUType:(cpu_type_t)cpuType {
|
||||
switch (cpuType) {
|
||||
case CPU_TYPE_X86:
|
||||
|
||||
@@ -52,9 +52,12 @@ typedef enum {
|
||||
ACTION_RESPOND_CHECKBW_DENY = 12,
|
||||
|
||||
// NOTIFY
|
||||
ACTION_NOTIFY_EXEC_ALLOW_NODAEMON = 30,
|
||||
ACTION_NOTIFY_EXEC_ALLOW_CACHED = 31,
|
||||
ACTION_NOTIFY_EXEC_DENY_CACHED = 32,
|
||||
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,
|
||||
@@ -70,10 +73,17 @@ typedef enum {
|
||||
typedef struct {
|
||||
santa_action_t action;
|
||||
uint64_t vnode_id;
|
||||
uid_t userId;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
pid_t ppid;
|
||||
char path[MAXPATHLEN];
|
||||
char newpath[MAXPATHLEN];
|
||||
// For file events, this is the process name.
|
||||
// For exec requests, this is the parent process name.
|
||||
// While process names can technically be 4*MAXPATHLEN, that never
|
||||
// actually happens, so only take MAXPATHLEN and throw away any excess.
|
||||
char pname[MAXPATHLEN];
|
||||
} santa_message_t;
|
||||
|
||||
#endif // SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
@@ -32,10 +32,12 @@
|
||||
|
||||
#else // KERNEL
|
||||
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARN 2
|
||||
#define LOG_LEVEL_INFO 3
|
||||
#define LOG_LEVEL_DEBUG 4
|
||||
typedef enum : NSUInteger {
|
||||
LOG_LEVEL_ERROR,
|
||||
LOG_LEVEL_WARN,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_DEBUG
|
||||
} LogLevel;
|
||||
|
||||
///
|
||||
/// Logging function.
|
||||
@@ -45,7 +47,7 @@
|
||||
/// @param format the printf style format string
|
||||
/// @param ... the arguments to format.
|
||||
///
|
||||
void logMessage(int level, FILE *destination, NSString *format, ...)
|
||||
void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
|
||||
__attribute__((format(__NSString__, 3, 4)));
|
||||
|
||||
/// Simple logging macros
|
||||
|
||||
@@ -14,25 +14,32 @@
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
#import <sys/syslog.h>
|
||||
#import <asl.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
static int logLevel = LOG_LEVEL_DEBUG;
|
||||
static LogLevel logLevel = LOG_LEVEL_DEBUG;
|
||||
#else
|
||||
static int logLevel = LOG_LEVEL_INFO; // default to info
|
||||
static LogLevel logLevel = LOG_LEVEL_INFO; // default to info
|
||||
#endif
|
||||
|
||||
void logMessage(int level, FILE *destination, NSString *format, ...) {
|
||||
static NSString *binaryName;
|
||||
void logMessage(LogLevel level, FILE *destination, NSString *format, ...) {
|
||||
static BOOL useSyslog = NO;
|
||||
static const char *binaryName;
|
||||
static dispatch_once_t pred;
|
||||
|
||||
dispatch_once(&pred, ^{
|
||||
binaryName = [[NSProcessInfo processInfo] processName];
|
||||
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 requested, redirect output to syslog.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--syslog"] ||
|
||||
strcmp(binaryName, "santad") == 0) {
|
||||
useSyslog = YES;
|
||||
}
|
||||
});
|
||||
|
||||
if (logLevel < level) return;
|
||||
@@ -42,25 +49,22 @@ void logMessage(int level, FILE *destination, NSString *format, ...) {
|
||||
NSString *s = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
// Only prepend timestamp, severity and binary name if stdout is not a TTY
|
||||
if (isatty(fileno(destination))) {
|
||||
fprintf(destination, "%s\n", [s UTF8String]);
|
||||
} else {
|
||||
NSString *levelName;
|
||||
int syslogLevel = LOG_DEBUG;
|
||||
if (useSyslog) {
|
||||
aslclient client = asl_open(NULL, "com.google.santa", 0);
|
||||
asl_set_filter(client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
|
||||
|
||||
char *levelName;
|
||||
int syslogLevel = ASL_LEVEL_DEBUG;
|
||||
switch (level) {
|
||||
case LOG_LEVEL_ERROR: levelName = @"E"; syslogLevel = LOG_ERR; break;
|
||||
case LOG_LEVEL_WARN: levelName = @"W"; syslogLevel = LOG_WARNING; break;
|
||||
case LOG_LEVEL_INFO: levelName = @"I"; syslogLevel = LOG_INFO; break;
|
||||
case LOG_LEVEL_DEBUG: levelName = @"D"; syslogLevel = LOG_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;
|
||||
}
|
||||
|
||||
if (fileno(destination) == -1) {
|
||||
syslog(syslogLevel, "%s\n",
|
||||
[[NSString stringWithFormat:@"%@ %@: %@", levelName, binaryName, s] UTF8String]);
|
||||
} else {
|
||||
fprintf(destination, "%s\n",
|
||||
[[NSString stringWithFormat:@"%@ %@: %@", levelName, binaryName, s] UTF8String]);
|
||||
}
|
||||
asl_log(client, NULL, syslogLevel, "%s %s: %s", levelName, binaryName, [s UTF8String]);
|
||||
asl_close(client);
|
||||
} else {
|
||||
fprintf(destination, "%s\n", [s UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,4 +60,25 @@
|
||||
#undef DECODE
|
||||
#undef ENCODE
|
||||
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) return YES;
|
||||
if (![other isKindOfClass:[SNTRule class]]) return NO;
|
||||
SNTRule *o = other;
|
||||
return ([self.shasum isEqual:o.shasum] && self.state == o.state && self.type == o.type);
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger prime = 31;
|
||||
NSUInteger result = 1;
|
||||
result = prime * result + [self.shasum hash];
|
||||
result = prime * result + self.state;
|
||||
result = prime * result + self.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"SNTRule: SHA-256: %@, State: %d, Type: %d",
|
||||
self.shasum, self.state, self.type];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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,13 @@
|
||||
///
|
||||
@property NSString *parentName;
|
||||
|
||||
///
|
||||
/// Quarantine data about the executed file, if any.
|
||||
///
|
||||
@property NSString *quarantineDataURL;
|
||||
@property NSString *quarantineRefererURL;
|
||||
@property NSDate *quarantineTimestamp;
|
||||
@property NSString *quarantineAgentBundleID;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "MOLCertificate.h"
|
||||
|
||||
@implementation SNTStoredEvent
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
|
||||
ENCODE(self.loggedInUsers, @"loggedInUsers");
|
||||
ENCODE(self.currentSessions, @"currentSessions");
|
||||
|
||||
ENCODE(self.quarantineDataURL, @"quarantineDataURL");
|
||||
ENCODE(self.quarantineRefererURL, @"quarantineRefererURL");
|
||||
ENCODE(self.quarantineTimestamp, @"quarantineTiemstamp");
|
||||
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;
|
||||
}
|
||||
@@ -81,7 +91,7 @@
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) return YES;
|
||||
if (![other isKindOfClass:[SNTStoredEvent class]]) return NO;
|
||||
SNTStoredEvent *o;
|
||||
SNTStoredEvent *o = other;
|
||||
return ([self.fileSHA256 isEqual:o.fileSHA256] && [self.idx isEqual:o.idx]);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "SNTXPCConnection.h"
|
||||
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
|
||||
@protocol XPCConnectionValidityRequest
|
||||
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
|
||||
@@ -93,12 +93,12 @@
|
||||
|
||||
[connection resume];
|
||||
|
||||
__block BOOL verificationComplete = NO;
|
||||
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];
|
||||
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
|
||||
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
|
||||
|
||||
if (response && [otherCS signingInformationMatches:selfCS]) {
|
||||
[self.currentConnection suspend];
|
||||
@@ -107,20 +107,19 @@
|
||||
self.currentConnection.exportedObject = self.exportedObject;
|
||||
[self invokeAcceptedHandler];
|
||||
[self.currentConnection resume];
|
||||
verificationComplete = YES;
|
||||
dispatch_semaphore_signal(sema);
|
||||
} else {
|
||||
[self invokeRejectedHandler];
|
||||
[self.currentConnection invalidate];
|
||||
self.currentConnection = nil;
|
||||
verificationComplete = YES;
|
||||
dispatch_semaphore_signal(sema);
|
||||
}
|
||||
}];
|
||||
|
||||
// Wait for validation to complete, at most 5s
|
||||
for (int sleepLoops = 0; sleepLoops < 1000 && !verificationComplete; sleepLoops++) {
|
||||
usleep(5000);
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
|
||||
[self invalidate];
|
||||
}
|
||||
if (!verificationComplete) [self invalidate];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,8 +153,8 @@
|
||||
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block {
|
||||
pid_t pid = self.currentConnection.processIdentifier;
|
||||
|
||||
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
|
||||
SNTCodesignChecker *otherCS = [[SNTCodesignChecker alloc] initWithPID:pid];
|
||||
MOLCodesignChecker *selfCS = [[MOLCodesignChecker alloc] initWithSelf];
|
||||
MOLCodesignChecker *otherCS = [[MOLCodesignChecker alloc] initWithPID:pid];
|
||||
|
||||
if ([otherCS signingInformationMatches:selfCS]) {
|
||||
[self.currentConnection suspend];
|
||||
|
||||
@@ -32,8 +32,10 @@
|
||||
/// 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;
|
||||
@@ -44,9 +46,14 @@
|
||||
/// Config ops
|
||||
///
|
||||
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
|
||||
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply;
|
||||
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply;
|
||||
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
|
||||
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
|
||||
bool SantaDecisionManager::init() {
|
||||
if (!super::init()) return false;
|
||||
|
||||
sdm_lock_grp_ = lck_grp_alloc_init("santa-locks", lck_grp_attr_alloc_init());
|
||||
dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, lck_attr_alloc_init());
|
||||
cached_decisions_lock_ = lck_rw_alloc_init(sdm_lock_grp_,
|
||||
lck_attr_alloc_init());
|
||||
sdm_lock_grp_attr_ = lck_grp_attr_alloc_init();
|
||||
sdm_lock_grp_ = lck_grp_alloc_init("santa-locks", sdm_lock_grp_attr_);
|
||||
|
||||
sdm_lock_attr_ = lck_attr_alloc_init();
|
||||
|
||||
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_);
|
||||
|
||||
cached_decisions_ = OSDictionary::withCapacity(1000);
|
||||
|
||||
@@ -44,17 +47,27 @@ void SantaDecisionManager::free() {
|
||||
|
||||
if (cached_decisions_lock_) {
|
||||
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
|
||||
cached_decisions_lock_ = NULL;
|
||||
cached_decisions_lock_ = nullptr;
|
||||
}
|
||||
|
||||
if (dataqueue_lock_) {
|
||||
lck_mtx_free(dataqueue_lock_, sdm_lock_grp_);
|
||||
dataqueue_lock_ = NULL;
|
||||
dataqueue_lock_ = nullptr;
|
||||
}
|
||||
|
||||
if (sdm_lock_attr_) {
|
||||
lck_attr_free(sdm_lock_attr_);
|
||||
sdm_lock_attr_ = nullptr;
|
||||
}
|
||||
|
||||
if (sdm_lock_grp_) {
|
||||
lck_grp_free(sdm_lock_grp_);
|
||||
sdm_lock_grp_ = NULL;
|
||||
sdm_lock_grp_ = nullptr;
|
||||
}
|
||||
|
||||
if (sdm_lock_grp_attr_) {
|
||||
lck_grp_attr_free(sdm_lock_grp_attr_);
|
||||
sdm_lock_grp_attr_ = nullptr;
|
||||
}
|
||||
|
||||
super::free();
|
||||
@@ -80,14 +93,15 @@ void SantaDecisionManager::ConnectClient(mach_port_t port, pid_t pid) {
|
||||
|
||||
void SantaDecisionManager::DisconnectClient(bool itDied) {
|
||||
if (client_pid_ < 1) return;
|
||||
|
||||
client_pid_ = -1;
|
||||
client_pid_ = 0;
|
||||
|
||||
// Ask santad to shutdown, in case it's running.
|
||||
if (!itDied) {
|
||||
santa_message_t message = {.action = ACTION_REQUEST_SHUTDOWN};
|
||||
santa_message_t *message = new santa_message_t;
|
||||
message->action = ACTION_REQUEST_SHUTDOWN;
|
||||
PostToQueue(message);
|
||||
dataqueue_->setNotificationPort(NULL);
|
||||
delete message;
|
||||
dataqueue_->setNotificationPort(nullptr);
|
||||
} else {
|
||||
// If the client died, reset the data queue so when it reconnects
|
||||
// it doesn't get swamped straight away.
|
||||
@@ -101,7 +115,10 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::ClientConnected() {
|
||||
return client_pid_ > 0;
|
||||
proc_t p = proc_find(client_pid_);
|
||||
bool is_exiting = proc_exiting(p);
|
||||
proc_rele(p);
|
||||
return (client_pid_ > 0 && !is_exiting);
|
||||
}
|
||||
|
||||
IOMemoryDescriptor *SantaDecisionManager::GetMemoryDescriptor() {
|
||||
@@ -128,10 +145,10 @@ kern_return_t SantaDecisionManager::StartListener() {
|
||||
|
||||
kern_return_t SantaDecisionManager::StopListener() {
|
||||
kauth_unlisten_scope(vnode_listener_);
|
||||
vnode_listener_ = NULL;
|
||||
vnode_listener_ = nullptr;
|
||||
|
||||
kauth_unlisten_scope(fileop_listener_);
|
||||
fileop_listener_ = NULL;
|
||||
fileop_listener_ = nullptr;
|
||||
|
||||
// Wait for any active invocations to finish before returning
|
||||
do {
|
||||
@@ -150,37 +167,37 @@ kern_return_t SantaDecisionManager::StopListener() {
|
||||
|
||||
void SantaDecisionManager::AddToCache(
|
||||
const char *identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
lck_rw_lock_exclusive(cached_decisions_lock_);
|
||||
|
||||
if (cached_decisions_->getCount() > kMaxCacheSize) {
|
||||
// This could be made a _lot_ smarter, say only removing entries older
|
||||
// than a certain time period. However, with a kMaxCacheSize set
|
||||
// sufficiently large and a kMaxAllowCacheTimeMilliseconds set
|
||||
// sufficiently low, this should only ever occur if someone is purposefully
|
||||
// trying to make the cache grow.
|
||||
LOGD("Cache too large, flushing.");
|
||||
cached_decisions_->flushCollection();
|
||||
LOGI("Cache too large, flushing.");
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
if (decision == ACTION_REQUEST_CHECKBW) {
|
||||
SantaMessage *pending = new SantaMessage();
|
||||
pending->setAction(ACTION_REQUEST_CHECKBW, 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));
|
||||
if (pending) {
|
||||
pending->setAction(decision, microsecs);
|
||||
}
|
||||
lck_rw_unlock_exclusive(cached_decisions_lock_);
|
||||
}
|
||||
|
||||
lck_rw_unlock_exclusive(cached_decisions_lock_);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::CacheCheck(const char *identifier) {
|
||||
lck_rw_lock_shared(cached_decisions_lock_);
|
||||
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
|
||||
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
|
||||
if (shouldInvalidate) {
|
||||
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
|
||||
// shared_to_exclusive will return false if a previous reader upgraded
|
||||
@@ -249,11 +266,14 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
|
||||
}
|
||||
|
||||
santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
const santa_message_t message, const char *vnode_id_str) {
|
||||
santa_message_t *message, const char *vnode_id_str) {
|
||||
santa_action_t return_action = ACTION_UNSET;
|
||||
|
||||
// Wait for the daemon to respond or die.
|
||||
do {
|
||||
// Add pending request to cache.
|
||||
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
|
||||
|
||||
// Send request to daemon...
|
||||
if (!PostToQueue(message)) {
|
||||
OSIncrementAtomic(&failed_queue_requests_);
|
||||
@@ -261,8 +281,9 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
LOGE("Failed to queue more than %d requests, killing daemon",
|
||||
kMaxQueueFailures);
|
||||
proc_signal(client_pid_, SIGKILL);
|
||||
client_pid_ = 0;
|
||||
}
|
||||
LOGE("Failed to queue request for %s.", message.path);
|
||||
LOGE("Failed to queue request for %s.", message->path);
|
||||
CacheCheck(vnode_id_str);
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
@@ -276,7 +297,7 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
// If response is still not valid, the daemon exited
|
||||
if (!CHECKBW_RESPONSE_VALID(return_action)) {
|
||||
LOGE("Daemon process did not respond correctly. Allowing executions "
|
||||
"until it comes back.");
|
||||
"until it comes back. Executable path: %s", message->path);
|
||||
CacheCheck(vnode_id_str);
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
@@ -290,16 +311,14 @@ 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;
|
||||
|
||||
// Check to see if item is in cache
|
||||
return_action = GetFromCache(vnode_id_str);
|
||||
|
||||
// If item wasn in cache return it.
|
||||
// If item was in cache return it.
|
||||
if CHECKBW_RESPONSE_VALID(return_action) return return_action;
|
||||
|
||||
// Add pending request to cache.
|
||||
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
|
||||
|
||||
// Get path
|
||||
char path[MAXPATHLEN];
|
||||
int name_len = MAXPATHLEN;
|
||||
@@ -307,31 +326,37 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
path[0] = '\0';
|
||||
}
|
||||
|
||||
// Prepare message to send to daemon.
|
||||
santa_message_t message = {};
|
||||
strlcpy(message.path, path, sizeof(message.path));
|
||||
message.userId = kauth_cred_getuid(cred);
|
||||
message.pid = proc_selfpid();
|
||||
message.ppid = proc_selfppid();
|
||||
message.action = ACTION_REQUEST_CHECKBW;
|
||||
message.vnode_id = vnode_id;
|
||||
|
||||
if (ClientConnected()) {
|
||||
return GetFromDaemon(message, vnode_id_str);
|
||||
} else {
|
||||
LOGI("Execution request without daemon running: %s", path);
|
||||
message.action = ACTION_NOTIFY_EXEC_ALLOW_NODAEMON;
|
||||
PostToQueue(message);
|
||||
return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
}
|
||||
santa_message_t *message = NewMessage();
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
message->action = ACTION_REQUEST_CHECKBW;
|
||||
message->vnode_id = vnode_id;
|
||||
proc_name(message->ppid, message->pname, sizeof(message->pname));
|
||||
santa_action_t ret = GetFromDaemon(message, vnode_id_str);
|
||||
delete message;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#pragma mark Misc
|
||||
|
||||
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
|
||||
santa_message_t* SantaDecisionManager::NewMessage() {
|
||||
santa_message_t *message = new santa_message_t;
|
||||
message->uid = kauth_getuid();
|
||||
message->gid = kauth_getgid();
|
||||
message->pid = proc_selfpid();
|
||||
message->ppid = proc_selfppid();
|
||||
return message;
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::PostToQueue(santa_message_t *message) {
|
||||
bool kr = false;
|
||||
lck_mtx_lock(dataqueue_lock_);
|
||||
kr = dataqueue_->enqueue(&message, sizeof(message));
|
||||
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
|
||||
if (!kr) {
|
||||
// If enqueue failed, pop an item off the queue and try again.
|
||||
uint32_t dataSize = sizeof(santa_message_t);
|
||||
dataqueue_->dequeue(0, &dataSize);
|
||||
kr = dataqueue_->enqueue(message, sizeof(santa_message_t));
|
||||
}
|
||||
lck_mtx_unlock(dataqueue_lock_);
|
||||
return kr;
|
||||
}
|
||||
@@ -367,8 +392,7 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
const vnode_t vp,
|
||||
int *errno) {
|
||||
// Only operate on regular files (not directories, symlinks, etc.).
|
||||
vtype vt = vnode_vtype(vp);
|
||||
if (vt != VREG) return KAUTH_RESULT_DEFER;
|
||||
if (vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
|
||||
|
||||
// Get ID for the vnode and convert it to a string.
|
||||
uint64_t vnode_id = GetVnodeIDForVnode(ctx, vp);
|
||||
@@ -400,14 +424,53 @@ int SantaDecisionManager::VnodeCallback(const kauth_cred_t cred,
|
||||
}
|
||||
}
|
||||
|
||||
int SantaDecisionManager::FileOpCallback(const vnode_t vp) {
|
||||
vfs_context_t context = vfs_context_create(NULL);
|
||||
char vnode_id_str[MAX_VNODE_ID_STR];
|
||||
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
|
||||
GetVnodeIDForVnode(context, vp));
|
||||
CacheCheck(vnode_id_str);
|
||||
vfs_context_rele(context);
|
||||
return KAUTH_RESULT_DEFER;
|
||||
void SantaDecisionManager::FileOpCallback(
|
||||
const kauth_action_t action, const vnode_t vp,
|
||||
const char *path, const char *new_path) {
|
||||
if (vp) {
|
||||
vfs_context_t context = vfs_context_create(nullptr);
|
||||
uint64_t vnode_id = GetVnodeIDForVnode(context, vp);
|
||||
vfs_context_rele(context);
|
||||
|
||||
if (action == KAUTH_FILEOP_CLOSE) {
|
||||
char vnode_id_str[MAX_VNODE_ID_STR];
|
||||
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
|
||||
CacheCheck(vnode_id_str);
|
||||
} else if (action == KAUTH_FILEOP_EXEC) {
|
||||
santa_message_t *message = NewMessage();
|
||||
message->vnode_id = vnode_id;
|
||||
message->action = ACTION_NOTIFY_EXEC;
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
PostToQueue(message);
|
||||
delete message;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out modifications to locations that are definitely not useful.
|
||||
if (ClientConnected() && !strprefix(path, "/.") && !strprefix(path, "/dev")) {
|
||||
santa_message_t *message = NewMessage();
|
||||
strlcpy(message->path, path, sizeof(message->path));
|
||||
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
|
||||
proc_name(message->pid, message->pname, sizeof(message->pname));
|
||||
|
||||
switch (action) {
|
||||
case KAUTH_FILEOP_CLOSE:
|
||||
message->action = ACTION_NOTIFY_WRITE; break;
|
||||
case KAUTH_FILEOP_RENAME:
|
||||
message->action = ACTION_NOTIFY_RENAME; break;
|
||||
case KAUTH_FILEOP_LINK:
|
||||
message->action = ACTION_NOTIFY_LINK; break;
|
||||
case KAUTH_FILEOP_EXCHANGE:
|
||||
message->action = ACTION_NOTIFY_EXCHANGE; break;
|
||||
case KAUTH_FILEOP_DELETE:
|
||||
message->action = ACTION_NOTIFY_DELETE; break;
|
||||
default: delete message; return;
|
||||
}
|
||||
|
||||
PostToQueue(message);
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
|
||||
#undef super
|
||||
@@ -417,17 +480,35 @@ int SantaDecisionManager::FileOpCallback(const vnode_t vp) {
|
||||
extern "C" int fileop_scope_callback(
|
||||
kauth_cred_t credential, void *idata, kauth_action_t action,
|
||||
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
|
||||
if (action != KAUTH_FILEOP_CLOSE ||
|
||||
!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED) ||
|
||||
idata == NULL) {
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
|
||||
SantaDecisionManager *sdm = OSDynamicCast(
|
||||
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
|
||||
|
||||
vnode_t vp = nullptr;
|
||||
char *path = nullptr;
|
||||
char *new_path = nullptr;
|
||||
|
||||
switch (action) {
|
||||
case KAUTH_FILEOP_CLOSE:
|
||||
if (!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED)) return KAUTH_RESULT_DEFER;
|
||||
// Intentional fall-through
|
||||
case KAUTH_FILEOP_DELETE:
|
||||
case KAUTH_FILEOP_EXEC:
|
||||
vp = reinterpret_cast<vnode_t>(arg0);
|
||||
if (vp && vnode_vtype(vp) != VREG) return KAUTH_RESULT_DEFER;
|
||||
path = reinterpret_cast<char *>(arg1);
|
||||
break;
|
||||
case KAUTH_FILEOP_RENAME:
|
||||
case KAUTH_FILEOP_EXCHANGE:
|
||||
case KAUTH_FILEOP_LINK:
|
||||
path = reinterpret_cast<char *>(arg0);
|
||||
new_path = reinterpret_cast<char *>(arg1);
|
||||
break;
|
||||
default:
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
|
||||
sdm->IncrementListenerInvocations();
|
||||
sdm->FileOpCallback(reinterpret_cast<vnode_t>(arg0));
|
||||
sdm->FileOpCallback(action, vp, path, new_path);
|
||||
sdm->DecrementListenerInvocations();
|
||||
|
||||
return KAUTH_RESULT_DEFER;
|
||||
@@ -438,7 +519,7 @@ extern "C" int vnode_scope_callback(
|
||||
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
|
||||
if (action & KAUTH_VNODE_ACCESS ||
|
||||
!(action & KAUTH_VNODE_EXECUTE) ||
|
||||
idata == NULL) {
|
||||
idata == nullptr) {
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,9 +101,8 @@ class SantaDecisionManager : public OSObject {
|
||||
///
|
||||
/// FileOp Callback
|
||||
/// @param vp The Vnode for this request.
|
||||
/// @return int Should always be KAUTH_RESULT_DEFER.
|
||||
///
|
||||
int FileOpCallback(const vnode_t vp);
|
||||
void FileOpCallback(kauth_action_t action, const vnode_t vp, const char *path, const char *new_path);
|
||||
|
||||
protected:
|
||||
///
|
||||
@@ -151,7 +150,7 @@ class SantaDecisionManager : public OSObject {
|
||||
/// @param identifier The vnode ID string for this request
|
||||
/// @return santa_action_t The response for this request
|
||||
///
|
||||
santa_action_t GetFromDaemon(const santa_message_t message,
|
||||
santa_action_t GetFromDaemon(santa_message_t *message,
|
||||
const char *identifier);
|
||||
|
||||
///
|
||||
@@ -176,7 +175,7 @@ class SantaDecisionManager : public OSObject {
|
||||
/// @param message The message to send
|
||||
/// @return bool true if sending was successful.
|
||||
///
|
||||
bool PostToQueue(santa_message_t message);
|
||||
bool PostToQueue(santa_message_t *message);
|
||||
|
||||
///
|
||||
/// Fetches the vnode_id for a given vnode.
|
||||
@@ -187,11 +186,19 @@ class SantaDecisionManager : public OSObject {
|
||||
///
|
||||
uint64_t GetVnodeIDForVnode(const vfs_context_t ctx, const vnode_t vp);
|
||||
|
||||
///
|
||||
/// Creates a new santa_message_t with some fields pre-filled.
|
||||
///
|
||||
santa_message_t* NewMessage();
|
||||
|
||||
/// Returns the current system uptime in microseconds
|
||||
static uint64_t GetCurrentUptime();
|
||||
|
||||
private:
|
||||
lck_grp_t *sdm_lock_grp_;
|
||||
lck_grp_attr_t *sdm_lock_grp_attr_;
|
||||
|
||||
lck_attr_t *sdm_lock_attr_;
|
||||
lck_rw_t *cached_decisions_lock_;
|
||||
lck_mtx_t *dataqueue_lock_;
|
||||
|
||||
@@ -215,7 +222,7 @@ class SantaDecisionManager : public OSObject {
|
||||
/// @param action that was requested
|
||||
/// @param VFS context
|
||||
/// @param Vnode being operated on
|
||||
/// @param Parent Vnode. May be NULL.
|
||||
/// @param Parent Vnode. May be nullptr.
|
||||
/// @param Pointer to an errno-style error.
|
||||
///
|
||||
extern "C" int vnode_scope_callback(
|
||||
|
||||
@@ -27,7 +27,7 @@ bool SantaDriver::start(IOService *provider) {
|
||||
if (!santaDecisionManager->init() ||
|
||||
santaDecisionManager->StartListener() != kIOReturnSuccess) {
|
||||
santaDecisionManager->release();
|
||||
santaDecisionManager = NULL;
|
||||
santaDecisionManager = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ bool SantaDriver::start(IOService *provider) {
|
||||
void SantaDriver::stop(IOService *provider) {
|
||||
santaDecisionManager->StopListener();
|
||||
santaDecisionManager->release();
|
||||
santaDecisionManager = NULL;
|
||||
santaDecisionManager = nullptr;
|
||||
|
||||
LOGI("Unloaded.");
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ bool SantaDriverClient::start(IOService *provider) {
|
||||
|
||||
void SantaDriverClient::stop(IOService *provider) {
|
||||
super::stop(provider);
|
||||
myProvider = NULL;
|
||||
decisionManager = NULL;
|
||||
myProvider = nullptr;
|
||||
decisionManager = nullptr;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientClose() {
|
||||
@@ -124,8 +124,10 @@ IOReturn SantaDriverClient::static_allow_binary(
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
|
||||
|
||||
return target->allow_binary(
|
||||
*(static_cast<const uint64_t *>(arguments->scalarInput)));
|
||||
static_cast<const uint64_t>(*arguments->scalarInput));
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::deny_binary(const uint64_t vnode_id) {
|
||||
@@ -141,8 +143,10 @@ IOReturn SantaDriverClient::static_deny_binary(
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
|
||||
|
||||
return target->deny_binary(
|
||||
*(static_cast<const uint64_t *>(arguments->scalarInput)));
|
||||
static_cast<const uint64_t>(*arguments->scalarInput));
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clear_cache() {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTFileInfo.h"
|
||||
|
||||
@interface SNTCommandBinaryInfo : NSObject<SNTCommand>
|
||||
@@ -59,40 +59,54 @@ REGISTER_COMMAND_NAME(@"binaryinfo")
|
||||
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]);
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
|
||||
|
||||
[self printKey:@"Path" value:fileInfo.path];
|
||||
[self printKey:@"SHA-256" value:fileInfo.SHA256];
|
||||
[self printKey:@"SHA-1" value:fileInfo.SHA1];
|
||||
[self printKey:@"Bundle Name" value:fileInfo.bundleName];
|
||||
[self printKey:@"Bundle Version" value:fileInfo.bundleVersion];
|
||||
[self printKey:@"Bundle Version Str" value:fileInfo.bundleShortVersionString];
|
||||
|
||||
if (fileInfo.quarantineDataURL) {
|
||||
[self printKey:@"Download Referer URL" value:fileInfo.quarantineRefererURL];
|
||||
[self printKey:@"Download URL" value:fileInfo.quarantineDataURL];
|
||||
[self printKey:@"Download Timestamp"
|
||||
value:[dateFormatter stringFromDate:fileInfo.quarantineTimestamp]];
|
||||
[self printKey:@"Download Agent" value:fileInfo.quarantineAgentBundleID];
|
||||
}
|
||||
|
||||
NSArray *archs = [fileInfo architectures];
|
||||
if (archs) {
|
||||
printf("%-19s: %s (%s)\n", "Type",
|
||||
[[fileInfo machoType] UTF8String],
|
||||
[[archs componentsJoinedByString:@", "] UTF8String]);
|
||||
NSString *s = [NSString stringWithFormat:@"%@ (%@)",
|
||||
fileInfo.machoType, [archs componentsJoinedByString:@", "]];
|
||||
[self printKey:@"Type" value:s];
|
||||
} else {
|
||||
printf("%-19s: %s\n", "Type", [[fileInfo machoType] UTF8String]);
|
||||
[self printKey:@"Type" value:fileInfo.machoType];
|
||||
}
|
||||
|
||||
SNTCodesignChecker *csc = [[SNTCodesignChecker alloc] initWithBinaryPath:filePath];
|
||||
|
||||
printf("%-19s: %s\n", "Code-signed", (csc) ? "Yes" : "No");
|
||||
if ([fileInfo isMissingPageZero]) {
|
||||
[self printKey:@"Page Zero" value:@"__PAGEZERO segment missing/bad!"];
|
||||
}
|
||||
|
||||
MOLCodesignChecker *csc = [[MOLCodesignChecker alloc] initWithBinaryPath:filePath];
|
||||
[self printKey:@"Code-signed" value:(csc) ? @"Yes" : @"No"];
|
||||
if (csc) {
|
||||
printf("Signing chain:\n");
|
||||
|
||||
[csc.certificates enumerateObjectsUsingBlock:^(SNTCertificate *c,
|
||||
[csc.certificates enumerateObjectsUsingBlock:^(MOLCertificate *c,
|
||||
unsigned long idx,
|
||||
BOOL *stop) {
|
||||
idx++; // index from 1
|
||||
printf(" %2lu. %-20s: %s\n", idx, "SHA-256", [c.SHA256 UTF8String]);
|
||||
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", [[c.validFrom description] UTF8String]);
|
||||
printf(" %-20s: %s\n", "Valid Until", [[c.validUntil description] 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");
|
||||
}];
|
||||
}
|
||||
@@ -100,4 +114,8 @@ REGISTER_COMMAND_NAME(@"binaryinfo")
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+ (void)printKey:(NSString *)key value:(NSString *)value {
|
||||
printf("%-21s: %s\n", [key UTF8String], [value UTF8String]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -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"
|
||||
@@ -47,117 +47,107 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return (@"Usage: santactl rule {add|remove} [options]\n"
|
||||
@" --whitelist: add to whitelist\n"
|
||||
@" --blacklist: add to blacklist\n"
|
||||
@" --silent-blacklist: add to silent blacklist\n"
|
||||
@" --message {message}: custom message\n"
|
||||
@" --path {path}: path of binary to add\n"
|
||||
@" --sha256 {sha256}: hash to add\n");
|
||||
return (@"Usage: santactl rule [options]\n"
|
||||
@" One of:\n"
|
||||
@" --whitelist: add to whitelist\n"
|
||||
@" --blacklist: add to blacklist\n"
|
||||
@" --silent-blacklist: add to silent blacklist\n"
|
||||
@" --remove: remove existing rule\n"
|
||||
@"\n"
|
||||
@" One of:\n"
|
||||
@" --path {path}: path of binary/bundle to add/remove.\n"
|
||||
@" Will add the hash of the file currently at that path.\n"
|
||||
@" --sha256 {sha256}: hash to add/remove\n"
|
||||
@"\n"
|
||||
@" Optionally:\n"
|
||||
@" --certificate: add certificate rule instead of binary\n"
|
||||
@" --message {message}: custom message\n");
|
||||
}
|
||||
|
||||
+ (void)printErrorUsageAndExit:(NSString *)error {
|
||||
printf("%s\n\n", [error UTF8String]);
|
||||
printf("%s\n", [[self longHelpText] UTF8String]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
printf("Failed to drop root privileges.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ([config syncBaseURL] != nil) {
|
||||
printf("SyncBaseURL is set, rules are managed centrally.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
NSString *action = [arguments firstObject];
|
||||
|
||||
// add or remove
|
||||
if (!action) {
|
||||
printf("Missing action - add or remove?\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int state = RULESTATE_UNKNOWN;
|
||||
|
||||
if ([action compare:@"add" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
} else if ([action compare:@"remove" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
state = RULESTATE_REMOVE;
|
||||
} else {
|
||||
printf("Unknown action, expected add or remove.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
NSString *customMsg = @"";
|
||||
NSString *SHA256 = nil;
|
||||
NSString *filePath = nil;
|
||||
|
||||
// parse arguments
|
||||
for (NSUInteger i = 1; i < [arguments count] ; i++ ) {
|
||||
NSString* argument = [arguments objectAtIndex:i];
|
||||
|
||||
if ([argument compare:@"--whitelist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
state = RULESTATE_WHITELIST;
|
||||
} else if ([argument compare:@"--blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
state = RULESTATE_BLACKLIST;
|
||||
} else if ([argument compare:@"--silent-blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
state = RULESTATE_SILENT_BLACKLIST;
|
||||
} else if ([argument compare:@"--message" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
if (++i > ([arguments count])) {
|
||||
printf("No message specified.\n");
|
||||
}
|
||||
|
||||
customMsg = [arguments objectAtIndex:i];
|
||||
} else if ([argument compare:@"--path" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
if (++i > ([arguments count])) {
|
||||
printf("No path specified.\n");
|
||||
}
|
||||
|
||||
filePath = [arguments objectAtIndex:i];
|
||||
} else if ([argument compare:@"--sha256" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
|
||||
if (++i > ([arguments count])) {
|
||||
printf("No SHA-256 specified.\n");
|
||||
}
|
||||
|
||||
SHA256 = [arguments objectAtIndex:i];
|
||||
} else {
|
||||
printf("Unknown argument %s.\n", [argument UTF8String]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (state == RULESTATE_UNKNOWN) {
|
||||
printf("No state specified.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (filePath) {
|
||||
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
|
||||
if (!fileInfo) {
|
||||
printf("Not a regular file or executable bundle.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SHA256 = [fileInfo SHA256];
|
||||
} else if (SHA256) {
|
||||
} else {
|
||||
printf("No SHA-256 or binary specified.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.shasum = SHA256;
|
||||
newRule.state = state;
|
||||
newRule.state = RULESTATE_UNKNOWN;
|
||||
newRule.type = RULETYPE_BINARY;
|
||||
newRule.customMsg = customMsg;
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^{
|
||||
if (state == RULESTATE_REMOVE) {
|
||||
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
} else {
|
||||
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
|
||||
NSString *path;
|
||||
|
||||
// Parse arguments
|
||||
for (NSUInteger i = 0; i < arguments.count ; i++ ) {
|
||||
NSString *arg = arguments[i];
|
||||
|
||||
if ([arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
|
||||
newRule.state = RULESTATE_WHITELIST;
|
||||
} else if ([arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
|
||||
newRule.state = RULESTATE_BLACKLIST;
|
||||
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
|
||||
newRule.state = RULESTATE_SILENT_BLACKLIST;
|
||||
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
|
||||
newRule.state = RULESTATE_REMOVE;
|
||||
} else if ([arg caseInsensitiveCompare:@"--certificate"] == NSOrderedSame) {
|
||||
newRule.type = RULETYPE_CERT;
|
||||
} else if ([arg caseInsensitiveCompare:@"--path"] == NSOrderedSame) {
|
||||
if (++i > arguments.count - 1) {
|
||||
[self printErrorUsageAndExit:@"--path requires an argument"];
|
||||
}
|
||||
path = arguments[i];
|
||||
} else if ([arg caseInsensitiveCompare:@"--sha256"] == NSOrderedSame) {
|
||||
if (++i > arguments.count - 1) {
|
||||
[self printErrorUsageAndExit:@"--sha256 requires an argument"];
|
||||
}
|
||||
newRule.shasum = arguments[i];
|
||||
if (newRule.shasum.length != 64) {
|
||||
[self printErrorUsageAndExit:@"--sha256 requires a valid SHA-256 as the argument"];
|
||||
}
|
||||
} else if ([arg caseInsensitiveCompare:@"--message"] == NSOrderedSame) {
|
||||
if (++i > arguments.count - 1) {
|
||||
[self printErrorUsageAndExit:@"--message requires an argument"];
|
||||
}
|
||||
newRule.customMsg = arguments[i];
|
||||
} else {
|
||||
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:arg]];
|
||||
}
|
||||
}
|
||||
|
||||
if (path) {
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:path];
|
||||
if (newRule.type == RULETYPE_BINARY) {
|
||||
newRule.shasum = fi.SHA256;
|
||||
} else if (newRule.type == RULETYPE_CERT) {
|
||||
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:fi.path];
|
||||
newRule.shasum = cs.leafCertificate.SHA256;
|
||||
}
|
||||
}
|
||||
|
||||
if (newRule.state == RULESTATE_UNKNOWN) {
|
||||
[self printErrorUsageAndExit:@"No state specified"];
|
||||
} else if (!newRule.shasum) {
|
||||
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
|
||||
}
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^(BOOL success) {
|
||||
if (!success) {
|
||||
printf("Failed to modify rules.");
|
||||
exit(1);
|
||||
} 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);
|
||||
}
|
||||
exit(0);
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@@ -37,12 +38,17 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return nil;
|
||||
return (@"Provides details about Santa while it's running.\n"
|
||||
@" Use --json to output in JSON format");
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
// Daemon status
|
||||
__block NSString *clientMode;
|
||||
__block uint64_t cpuEvents, ramEvents;
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] clientMode:^(santa_clientmode_t cm) {
|
||||
switch (cm) {
|
||||
case CLIENTMODE_MONITOR:
|
||||
@@ -52,35 +58,102 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
default:
|
||||
clientMode = [NSString stringWithFormat:@"Unknown (%d)", cm]; break;
|
||||
}
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
do { usleep(5000); } while (!clientMode);
|
||||
printf(">>> Daemon Info\n");
|
||||
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] watchdogCPUEvents:^(uint64_t events) {
|
||||
cpuEvents = events;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] watchdogRAMEvents:^(uint64_t events) {
|
||||
ramEvents = events;
|
||||
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);
|
||||
}];
|
||||
do { usleep(5000); } while (cacheCount == -1);
|
||||
printf(">>> Kernel Info\n");
|
||||
printf(" %-25s | %lld\n", "Kernel cache count", cacheCount);
|
||||
|
||||
// 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);
|
||||
}];
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] databaseEventCount:^(int64_t count) {
|
||||
eventCount = count;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
|
||||
|
||||
printf(">>> Database Info\n");
|
||||
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
|
||||
// Sync status
|
||||
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
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];
|
||||
|
||||
// Wait a maximum of 5s for stats collected from daemon to arrive.
|
||||
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
|
||||
fprintf(stderr, "Failed to retrieve some stats from daemon\n\n");
|
||||
}
|
||||
|
||||
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
|
||||
},
|
||||
};
|
||||
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
NSString *statsStr = [[NSString alloc] initWithData:statsData encoding:NSUTF8StringEncoding];
|
||||
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(">>> Kernel Info\n");
|
||||
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
|
||||
printf(">>> Database Info\n");
|
||||
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
|
||||
|
||||
if (syncURLStr) {
|
||||
printf(">>> Sync Info\n");
|
||||
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
|
||||
printf(" %-22s | %s\n", "Last Successful Sync", [lastSyncSuccessStr UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "SNTAuthenticatingURLSession.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTDERDecoder.h"
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
[config setTLSMinimumSupportedProtocol:kTLSProtocol12];
|
||||
[config setHTTPShouldUsePipelining:YES];
|
||||
return [self initWithSessionConfiguration:config];
|
||||
@@ -193,7 +193,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
|
||||
MOLCertificate *clientCert = [[MOLCertificate alloc] initWithSecCertificateRef:certificate];
|
||||
CFRelease(certificate);
|
||||
|
||||
// Switch identity finding method depending on config
|
||||
@@ -239,7 +239,7 @@
|
||||
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
|
||||
@@ -277,11 +277,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 +304,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ 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;
|
||||
@@ -64,6 +65,10 @@ 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;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ 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";
|
||||
@@ -66,6 +67,10 @@ 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";
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "NSData+Zlib.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTCommandSyncConstants.h"
|
||||
#import "SNTCommandSyncState.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
@@ -97,6 +98,13 @@
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
[req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
NSData *compressed = [requestBody zlibCompressed];
|
||||
if (compressed) {
|
||||
requestBody = compressed;
|
||||
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
@@ -166,9 +174,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];
|
||||
MOLCertificate *cert = [event.signingChain objectAtIndex:i];
|
||||
|
||||
NSMutableDictionary *certDict = [NSMutableDictionary dictionary];
|
||||
ADDKEY(certDict, kCertSHA256, cert.SHA256);
|
||||
|
||||
@@ -44,11 +44,22 @@
|
||||
} else {
|
||||
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
if (syncState.newClientMode) {
|
||||
[[daemonConn remoteObjectProxy] setClientMode:syncState.newClientMode reply:^{}];
|
||||
}
|
||||
|
||||
NSString *backoffInterval = r[kBackoffInterval];
|
||||
if (backoffInterval) {
|
||||
[[daemonConn remoteObjectProxy] setNextSyncInterval:[backoffInterval intValue] reply:^{}];
|
||||
}
|
||||
|
||||
if (syncState.cleanSync) {
|
||||
[[daemonConn remoteObjectProxy] setSyncCleanRequired:NO reply:^{}];
|
||||
}
|
||||
|
||||
// Update last sync success
|
||||
[[daemonConn remoteObjectProxy] setSyncLastSuccess:[NSDate date] reply:^{}];
|
||||
|
||||
handler(YES);
|
||||
}
|
||||
}] resume];
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "NSData+Zlib.h"
|
||||
#import "SNTCommandSyncConstants.h"
|
||||
#import "SNTCommandSyncState.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTSystemInfo.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
@@ -40,17 +42,27 @@
|
||||
requestDict[kOSBuild] = [SNTSystemInfo osBuild];
|
||||
requestDict[kPrimaryUser] = syncState.machineOwner;
|
||||
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"]) {
|
||||
// If user requested it or we've never had a successful sync, try from a clean slate.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"] ||
|
||||
[[SNTConfigurator configurator] syncCleanRequired]) {
|
||||
requestDict[kRequestCleanSync] = @YES;
|
||||
}
|
||||
|
||||
NSData *requestBody = [NSJSONSerialization dataWithJSONObject:requestDict
|
||||
options:0
|
||||
error:nil];
|
||||
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
[req setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
NSData *compressed = [requestBody zlibCompressed];
|
||||
if (compressed) {
|
||||
requestBody = compressed;
|
||||
[req setValue:@"zlib" forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
NSURLResponse *response,
|
||||
@@ -68,15 +80,19 @@
|
||||
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
|
||||
|
||||
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
|
||||
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_MONITOR reply:^{}];
|
||||
syncState.newClientMode = CLIENTMODE_MONITOR;
|
||||
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
|
||||
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_LOCKDOWN reply:^{}];
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
if (!resp) {
|
||||
LOGE(@"Failed to decode server's response");
|
||||
handler(NO);
|
||||
return;
|
||||
}
|
||||
|
||||
NSArray *receivedRules = resp[kRules];
|
||||
@@ -90,9 +91,14 @@
|
||||
if (syncState.downloadedRules.count) {
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRules:syncState.downloadedRules
|
||||
cleanSlate:syncState.cleanSync
|
||||
reply:^{
|
||||
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
|
||||
handler(YES);
|
||||
reply:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Added %lu rule(s)", syncState.downloadedRules.count);
|
||||
handler(YES);
|
||||
} else {
|
||||
LOGE(@"Failed to add rules to database");
|
||||
handler(NO);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
handler(YES);
|
||||
@@ -107,6 +113,7 @@
|
||||
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.shasum = dict[kRuleSHA256];
|
||||
if (newRule.shasum.length != 64) return nil;
|
||||
|
||||
NSString *policyString = dict[kRulePolicy];
|
||||
if ([policyString isEqual:kRulePolicyWhitelist]) {
|
||||
@@ -131,7 +138,7 @@
|
||||
}
|
||||
|
||||
NSString *customMsg = dict[kRuleCustomMsg];
|
||||
if (customMsg) {
|
||||
if (customMsg.length) {
|
||||
newRule.customMsg = customMsg;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
/// An instance of this class is passed to each stage of the sync process for storing data
|
||||
/// that might be needed in later stages.
|
||||
@interface SNTCommandSyncState : NSObject
|
||||
@@ -27,6 +29,9 @@
|
||||
/// should be deleted before inserting any new rules.
|
||||
@property BOOL cleanSync;
|
||||
|
||||
/// New client mode sent from server
|
||||
@property santa_clientmode_t newClientMode;
|
||||
|
||||
/// Batch size for uploading events, sent from server
|
||||
@property int32_t eventBatchSize;
|
||||
|
||||
|
||||
@@ -41,14 +41,30 @@ REGISTER_COMMAND_NAME(@"version")
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return nil;
|
||||
return (@"Show versions of all Santa components.\n"
|
||||
@" Use --json to output in JSON format.");
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
|
||||
if ([arguments containsObject:@"--json"]) {
|
||||
NSDictionary *versions = @{
|
||||
@"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]);
|
||||
} else {
|
||||
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
|
||||
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -58,7 +74,7 @@ REGISTER_COMMAND_NAME(@"version")
|
||||
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
|
||||
);
|
||||
|
||||
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
|
||||
if (loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
|
||||
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#import "SNTDaemonControlController.h"
|
||||
#import "SNTDatabaseController.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTEventLog.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTExecutionController.h"
|
||||
#import "SNTFileWatcher.h"
|
||||
@@ -34,6 +35,7 @@
|
||||
|
||||
@interface SNTApplication ()
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTEventLog *eventLog;
|
||||
@property SNTEventTable *eventTable;
|
||||
@property SNTExecutionController *execController;
|
||||
@property SNTFileWatcher *configFileWatcher;
|
||||
@@ -93,11 +95,14 @@
|
||||
chmod([kDefaultConfigFilePath fileSystemRepresentation], 0644);
|
||||
}];
|
||||
|
||||
_eventLog = [[SNTEventLog alloc] init];
|
||||
|
||||
// Initialize the binary checker object
|
||||
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
|
||||
ruleTable:_ruleTable
|
||||
eventTable:_eventTable
|
||||
notifierConnection:_notifierConnection];
|
||||
notifierConnection:_notifierConnection
|
||||
eventLog:_eventLog];
|
||||
if (!_execController) return nil;
|
||||
}
|
||||
|
||||
@@ -119,15 +124,27 @@
|
||||
LOGI(@"Driver requested a shutdown");
|
||||
exit(0);
|
||||
}
|
||||
case ACTION_NOTIFY_EXEC_ALLOW_NODAEMON:
|
||||
case ACTION_NOTIFY_EXEC_ALLOW_CACHED:
|
||||
case ACTION_NOTIFY_EXEC_DENY_CACHED: {
|
||||
// TODO(rah): Implement.
|
||||
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: {
|
||||
// Validate the binary aynchronously on a concurrent queue so we don't
|
||||
// hold up other execution requests in the background.
|
||||
dispatch_async(q, ^{
|
||||
[self.execController validateBinaryWithMessage:message];
|
||||
});
|
||||
|
||||
32
Source/santad/SNTCachedDecision.h
Normal file
32
Source/santad/SNTCachedDecision.h
Normal 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.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
/// Store information about executions from decision making for later logging.
|
||||
///
|
||||
@interface SNTCachedDecision : NSObject
|
||||
|
||||
@property uint64_t vnodeId;
|
||||
@property santa_eventstate_t decision;
|
||||
@property NSString *decisionExtra;
|
||||
@property NSString *sha256;
|
||||
@property NSString *certSHA256;
|
||||
@property NSString *certCommonName;
|
||||
|
||||
@property NSString *customMsg;
|
||||
@property BOOL silentBlock;
|
||||
|
||||
@end
|
||||
18
Source/santad/SNTCachedDecision.m
Normal file
18
Source/santad/SNTCachedDecision.m
Normal file
@@ -0,0 +1,18 @@
|
||||
/// 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 "SNTCachedDecision.h"
|
||||
|
||||
@implementation SNTCachedDecision
|
||||
@end
|
||||
@@ -23,6 +23,10 @@
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
// Globals used by the santad watchdog thread
|
||||
uint64_t watchdogCPUEvents = 0;
|
||||
uint64_t watchdogRAMEvents = 0;
|
||||
|
||||
@interface SNTDaemonControlController ()
|
||||
@property dispatch_source_t syncTimer;
|
||||
@end
|
||||
@@ -35,7 +39,7 @@
|
||||
_driverManager = driverManager;
|
||||
|
||||
_syncTimer = [self createSyncTimer];
|
||||
[self rescheduleSyncSecondsFromNow:600];
|
||||
[self rescheduleSyncSecondsFromNow:30];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -46,22 +50,19 @@
|
||||
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];
|
||||
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(1);
|
||||
if (fork() == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(EPERM);
|
||||
}
|
||||
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--syslog", NULL));
|
||||
}
|
||||
|
||||
fclose(stdout);
|
||||
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", NULL));
|
||||
}
|
||||
});
|
||||
|
||||
dispatch_resume(syncTimerQ);
|
||||
@@ -93,12 +94,14 @@
|
||||
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];
|
||||
@@ -107,7 +110,7 @@
|
||||
[self.driverManager flushCache];
|
||||
}
|
||||
|
||||
reply();
|
||||
reply(success);
|
||||
}
|
||||
|
||||
- (void)databaseEventCount:(void (^)(int64_t count))reply {
|
||||
@@ -143,6 +146,16 @@
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setSyncCleanRequired:cleanReqd];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply {
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
@@ -151,4 +164,20 @@
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply {
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply {
|
||||
reply(watchdogCPUEvents);
|
||||
}
|
||||
|
||||
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply {
|
||||
reply(watchdogRAMEvents);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
#import "SNTDatabaseController.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTRuleTable.h"
|
||||
@@ -31,8 +34,10 @@ static NSString * const kEventsDatabaseName = @"events.db";
|
||||
[self createDatabasePath];
|
||||
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
|
||||
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
|
||||
chown([fullPath UTF8String], 0, 0);
|
||||
chmod([fullPath UTF8String], 0600);
|
||||
|
||||
#ifndef DEBUG
|
||||
#ifndef DEBUG
|
||||
[eventDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
|
||||
#endif
|
||||
});
|
||||
@@ -47,8 +52,10 @@ static NSString * const kEventsDatabaseName = @"events.db";
|
||||
[self createDatabasePath];
|
||||
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
|
||||
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
|
||||
chown([fullPath UTF8String], 0, 0);
|
||||
chmod([fullPath UTF8String], 0600);
|
||||
|
||||
#ifndef DEBUG
|
||||
#ifndef DEBUG
|
||||
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) { db.logsErrors = NO; }];
|
||||
#endif
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
// This is imported in the header rather than implementation to saves
|
||||
// This is imported in the header rather than implementation to save
|
||||
// classes that use this one from also having to import FMDB stuff.
|
||||
#import <FMDB/FMDB.h>
|
||||
|
||||
|
||||
@@ -27,7 +27,16 @@
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[db inDatabase:^(FMDatabase *db) {
|
||||
if (![db goodConnection]) {
|
||||
[db close];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[db databasePath] error:NULL];
|
||||
[db open];
|
||||
}
|
||||
}];
|
||||
|
||||
_dbQ = db;
|
||||
|
||||
[self updateTableSchema];
|
||||
}
|
||||
return self;
|
||||
@@ -51,7 +60,7 @@
|
||||
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
|
||||
if (newVersion < 1) return;
|
||||
|
||||
LOGD(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
|
||||
LOGI(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
|
||||
|
||||
[db setUserVersion:newVersion];
|
||||
}];
|
||||
|
||||
@@ -87,8 +87,6 @@ static const int MAX_DELAY = 15;
|
||||
|
||||
- (void)listenWithBlock:(void (^)(santa_message_t message))callback {
|
||||
kern_return_t kr;
|
||||
santa_message_t vdata;
|
||||
UInt32 dataSize;
|
||||
|
||||
mach_vm_address_t address = 0;
|
||||
mach_vm_size_t size = 0;
|
||||
@@ -120,10 +118,11 @@ static const int MAX_DELAY = 15;
|
||||
}
|
||||
|
||||
self.queueMemory = (IODataQueueMemory *)address;
|
||||
dataSize = sizeof(vdata);
|
||||
|
||||
do {
|
||||
while (IODataQueueDataAvailable(self.queueMemory)) {
|
||||
santa_message_t vdata;
|
||||
uint32_t dataSize = sizeof(vdata);
|
||||
kr = IODataQueueDequeue(self.queueMemory, &vdata, &dataSize);
|
||||
if (kr == kIOReturnSuccess) {
|
||||
callback(vdata);
|
||||
|
||||
31
Source/santad/SNTEventLog.h
Normal file
31
Source/santad/SNTEventLog.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/// 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 "SNTKernelCommon.h"
|
||||
|
||||
@class SNTCachedDecision;
|
||||
|
||||
///
|
||||
/// Logs execution and file write events to syslog
|
||||
///
|
||||
@interface SNTEventLog : NSObject
|
||||
|
||||
- (void)logFileModification:(santa_message_t)message;
|
||||
|
||||
- (void)saveDecisionDetails:(SNTCachedDecision *)cd;
|
||||
- (void)logDeniedExecution:(SNTCachedDecision *)cd withMessage:(santa_message_t)message;
|
||||
- (void)logAllowedExecution:(santa_message_t)message;
|
||||
|
||||
@end
|
||||
218
Source/santad/SNTEventLog.m
Normal file
218
Source/santad/SNTEventLog.m
Normal file
@@ -0,0 +1,218 @@
|
||||
/// 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 "SNTEventLog.h"
|
||||
|
||||
#include <libproc.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@interface SNTEventLog ()
|
||||
@property NSMutableDictionary *detailStore;
|
||||
@end
|
||||
|
||||
@implementation SNTEventLog
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_detailStore = [NSMutableDictionary dictionaryWithCapacity:10000];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)saveDecisionDetails:(SNTCachedDecision *)cd {
|
||||
self.detailStore[@(cd.vnodeId)] = cd;
|
||||
}
|
||||
|
||||
- (void)logFileModification:(santa_message_t)message {
|
||||
NSString *action, *path, *newpath, *sha256, *outStr;
|
||||
|
||||
path = @(message.path);
|
||||
|
||||
switch (message.action) {
|
||||
case ACTION_NOTIFY_DELETE: {
|
||||
action = @"DELETE";
|
||||
break;
|
||||
}
|
||||
case ACTION_NOTIFY_EXCHANGE: {
|
||||
action = @"EXCHANGE";
|
||||
newpath = @(message.newpath);
|
||||
break;
|
||||
}
|
||||
case ACTION_NOTIFY_LINK: {
|
||||
action = @"LINK";
|
||||
newpath = @(message.newpath);
|
||||
break;
|
||||
}
|
||||
case ACTION_NOTIFY_RENAME: {
|
||||
action = @"RENAME";
|
||||
newpath = @(message.newpath);
|
||||
break;
|
||||
}
|
||||
case ACTION_NOTIFY_WRITE: {
|
||||
action = @"WRITE";
|
||||
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
|
||||
if (fileInfo.fileSize < 1024 * 1024) {
|
||||
sha256 = fileInfo.SHA256;
|
||||
} else {
|
||||
sha256 = @"(too large)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: action = @"UNKNOWN"; break;
|
||||
}
|
||||
|
||||
outStr = [NSString stringWithFormat:@"action=%@|path=%@", action, [self sanitizeString:path]];
|
||||
if (newpath) {
|
||||
outStr = [outStr stringByAppendingFormat:@"|newpath=%@", [self sanitizeString:newpath]];
|
||||
}
|
||||
char ppath[PATH_MAX];
|
||||
if (proc_pidpath(message.pid, ppath, PATH_MAX) < 1) {
|
||||
strncpy(ppath, "(null)", 6);
|
||||
}
|
||||
outStr =
|
||||
[outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|gid=%d",
|
||||
message.pid, message.ppid, message.pname, ppath, message.uid, message.gid];
|
||||
if (sha256) {
|
||||
outStr = [outStr stringByAppendingFormat:@"|sha256=%@", sha256];
|
||||
}
|
||||
|
||||
LOGI(@"%@", outStr);
|
||||
}
|
||||
|
||||
- (void)logDeniedExecution:(SNTCachedDecision *)cd withMessage:(santa_message_t)message {
|
||||
[self logExecution:message withDecision:cd];
|
||||
}
|
||||
|
||||
- (void)logAllowedExecution:(santa_message_t)message {
|
||||
SNTCachedDecision *cd = self.detailStore[@(message.vnode_id)];
|
||||
[self logExecution:message withDecision:cd];
|
||||
}
|
||||
|
||||
- (void)logExecution:(santa_message_t)message withDecision:(SNTCachedDecision *)cd {
|
||||
NSString *d, *r, *args, *outLog;
|
||||
|
||||
switch (cd.decision) {
|
||||
case EVENTSTATE_ALLOW_BINARY:
|
||||
d = @"ALLOW"; r = @"BINARY"; args = [self argsForPid:message.pid]; break;
|
||||
case EVENTSTATE_ALLOW_CERTIFICATE:
|
||||
d = @"ALLOW"; r = @"CERTIFICATE"; args = [self argsForPid:message.pid]; break;
|
||||
case EVENTSTATE_ALLOW_SCOPE:
|
||||
d = @"ALLOW"; r = @"SCOPE"; args = [self argsForPid:message.pid]; break;
|
||||
case EVENTSTATE_ALLOW_UNKNOWN:
|
||||
d = @"ALLOW"; r = @"UNKNOWN"; args = [self argsForPid:message.pid]; break;
|
||||
case EVENTSTATE_BLOCK_BINARY:
|
||||
d = @"DENY"; r = @"BINARY"; break;
|
||||
case EVENTSTATE_BLOCK_CERTIFICATE:
|
||||
d = @"DENY"; r = @"CERT"; break;
|
||||
case EVENTSTATE_BLOCK_SCOPE:
|
||||
d = @"DENY"; r = @"SCOPE"; break;
|
||||
case EVENTSTATE_BLOCK_UNKNOWN:
|
||||
d = @"DENY"; r = @"UNKNOWN"; break;
|
||||
default:
|
||||
d = @"ALLOW"; r = @"NOTRUNNING"; args = [self argsForPid:message.pid]; break;
|
||||
}
|
||||
|
||||
outLog = [NSString stringWithFormat:@"action=EXEC|decision=%@|reason=%@", d, r];
|
||||
|
||||
if (cd.decisionExtra) {
|
||||
outLog = [outLog stringByAppendingFormat:@"|explain=%@", cd.decisionExtra];
|
||||
}
|
||||
|
||||
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:@"|pid=%d|ppid=%d|uid=%d|gid=%d",
|
||||
message.pid, message.ppid, message.uid, message.gid];
|
||||
|
||||
LOGI(@"%@", outLog);
|
||||
}
|
||||
|
||||
#pragma mark Helpers
|
||||
|
||||
- (NSString *)sanitizeString:(NSString *)inStr {
|
||||
inStr = [inStr stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
|
||||
inStr = [inStr stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
|
||||
inStr = [inStr stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
|
||||
return inStr;
|
||||
}
|
||||
|
||||
- (NSString *)argsForPid:(pid_t)pid {
|
||||
int mib[3];
|
||||
|
||||
// Get size of buffer required to store process arguments.
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_ARGMAX;
|
||||
int argmax;
|
||||
size_t size = sizeof(argmax);
|
||||
|
||||
if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) return nil;
|
||||
|
||||
// Create buffer to store args
|
||||
NSMutableData *argsdata = [NSMutableData dataWithCapacity:argmax];
|
||||
char *argsdatabytes = (char *)argsdata.mutableBytes;
|
||||
|
||||
// Fetch args
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROCARGS2;
|
||||
mib[2] = pid;
|
||||
size = (size_t)argmax;
|
||||
if (sysctl(mib, 3, argsdatabytes, &size, NULL, 0) == -1) return nil;
|
||||
|
||||
// Get argc
|
||||
int argc;
|
||||
memcpy(&argc, argsdatabytes, sizeof(argc));
|
||||
|
||||
// Get pointer to beginning of string space
|
||||
char *cp;
|
||||
cp = (char *) argsdatabytes + sizeof(argc);
|
||||
|
||||
// Skip over exec_path
|
||||
for (; cp < &argsdatabytes[size]; cp++) {
|
||||
if (*cp == '\0') {
|
||||
cp++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip trailing NULL bytes
|
||||
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++) {
|
||||
NSString *arg = @(cp);
|
||||
[args addObject:arg];
|
||||
|
||||
// Move the pointer past this string and the terminator at the end.
|
||||
cp += strlen(cp) + 1;
|
||||
}
|
||||
|
||||
// Return the args as a space-separated list
|
||||
return [args componentsJoinedByString:@" "];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "SNTEventTable.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@@ -41,10 +41,14 @@
|
||||
if (!event.fileSHA256 ||
|
||||
!event.filePath ||
|
||||
!event.occurrenceDate ||
|
||||
!event.executingUser ||
|
||||
!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) {
|
||||
@@ -74,6 +78,9 @@
|
||||
|
||||
if ([rs next]) {
|
||||
storedEvent = [self eventFromResultSet:rs];
|
||||
if (!storedEvent) {
|
||||
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
|
||||
}
|
||||
}
|
||||
|
||||
[rs close];
|
||||
@@ -93,8 +100,7 @@
|
||||
if (obj) {
|
||||
[pendingEvents addObject:obj];
|
||||
} else {
|
||||
NSNumber *idx = [rs objectForColumnName:@"idx"];
|
||||
[db executeUpdate:@"DELETE FROM events WHERE idx=?", idx];
|
||||
[db executeUpdate:@"DELETE FROM events WHERE idx=?", [rs objectForColumnName:@"idx"]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +114,12 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
#include "SNTCommonEnums.h"
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
@class SNTCodesignChecker;
|
||||
@class MOLCodesignChecker;
|
||||
@class SNTDriverManager;
|
||||
@class SNTEventLog;
|
||||
@class SNTEventTable;
|
||||
@class SNTRuleTable;
|
||||
@class SNTXPCConnection;
|
||||
@@ -30,19 +31,20 @@
|
||||
/// + (If denied or unknown) Storing details about the execution event to the database
|
||||
/// for upload and spwaning santactl to quickly try and send that to the server.
|
||||
/// + (If denied) Potentially sending a message to SantaGUI to notify the user
|
||||
/// + Logging the event to the log file
|
||||
///
|
||||
@interface SNTExecutionController : NSObject
|
||||
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTRuleTable *ruleTable;
|
||||
@property SNTEventLog *eventLog;
|
||||
@property SNTEventTable *eventTable;
|
||||
@property SNTRuleTable *ruleTable;
|
||||
@property SNTXPCConnection *notifierConnection;
|
||||
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
|
||||
ruleTable:(SNTRuleTable *)ruleTable
|
||||
eventTable:(SNTEventTable *)eventTable
|
||||
notifierConnection:(SNTXPCConnection *)notifierConn;
|
||||
notifierConnection:(SNTXPCConnection *)notifierConn
|
||||
eventLog:(SNTEventLog *)eventLog;
|
||||
|
||||
///
|
||||
/// Handles the logic of deciding whether to allow the binary to run or not, sends the response to
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTDropRootPrivs.h"
|
||||
#import "SNTEventLog.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTRule.h"
|
||||
@@ -41,86 +43,119 @@
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
|
||||
ruleTable:(SNTRuleTable *)ruleTable
|
||||
eventTable:(SNTEventTable *)eventTable
|
||||
notifierConnection:(SNTXPCConnection *)notifier {
|
||||
notifierConnection:(SNTXPCConnection *)notifier
|
||||
eventLog:(SNTEventLog *)eventLog {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_driverManager = driverManager;
|
||||
_ruleTable = ruleTable;
|
||||
_eventTable = eventTable;
|
||||
_notifierConnection = notifier;
|
||||
_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;
|
||||
}
|
||||
|
||||
#pragma mark Binary Validation
|
||||
|
||||
- (void)validateBinaryWithMessage:(santa_message_t)message {
|
||||
NSString *path = @(message.path);
|
||||
uint64_t vnodeId = message.vnode_id;
|
||||
|
||||
NSError *fileInfoError;
|
||||
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path error:&fileInfoError];
|
||||
NSString *sha256 = [binInfo SHA256];
|
||||
|
||||
// If we can't read the file and hash properly, log an error.
|
||||
if (!binInfo || !sha256) {
|
||||
LOGW(@"Failed to read file %@: %@", path, fileInfoError.localizedDescription);
|
||||
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vnodeId];
|
||||
[self logDecisionForEventState:EVENTSTATE_ALLOW_UNKNOWN sha256:nil path:path leafCert:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
// These will be filled in either in later steps
|
||||
santa_action_t respondedAction = ACTION_UNSET;
|
||||
SNTRule *rule;
|
||||
|
||||
// Get name of parent process. Do this before responding to be sure parent doesn't go away.
|
||||
char pname[PROC_PIDPATHINFO_MAXSIZE];
|
||||
proc_name(message.ppid, pname, PROC_PIDPATHINFO_MAXSIZE);
|
||||
|
||||
// Step 1 - binary rule?
|
||||
rule = [self.ruleTable binaryRuleForSHA256:sha256];
|
||||
- (santa_eventstate_t)makeDecision:(SNTCachedDecision *)cd binaryInfo:(SNTFileInfo *)fi {
|
||||
SNTRule *rule = [self.ruleTable binaryRuleForSHA256:cd.sha256];
|
||||
if (rule) {
|
||||
respondedAction = [self actionForRuleState:rule.state];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
}
|
||||
|
||||
SNTCodesignChecker *csInfo = [[SNTCodesignChecker alloc] initWithBinaryPath:path];
|
||||
|
||||
// Step 2 - cert rule?
|
||||
if (!rule) {
|
||||
rule = [self.ruleTable certificateRuleForSHA256:csInfo.leafCertificate.SHA256];
|
||||
if (rule) {
|
||||
respondedAction = [self actionForRuleState:rule.state];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
switch (rule.state) {
|
||||
case RULESTATE_WHITELIST:
|
||||
return EVENTSTATE_ALLOW_BINARY;
|
||||
case RULESTATE_SILENT_BLACKLIST:
|
||||
cd.silentBlock = YES;
|
||||
case RULESTATE_BLACKLIST:
|
||||
cd.customMsg = rule.customMsg;
|
||||
return EVENTSTATE_BLOCK_BINARY;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3 - in scope?
|
||||
if (!rule && ![self fileIsInScope:path]) {
|
||||
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vnodeId];
|
||||
[self logDecisionForEventState:EVENTSTATE_ALLOW_SCOPE sha256:sha256 path:path leafCert:nil];
|
||||
rule = [self.ruleTable certificateRuleForSHA256:cd.certSHA256];
|
||||
if (rule) {
|
||||
switch (rule.state) {
|
||||
case RULESTATE_WHITELIST:
|
||||
return EVENTSTATE_ALLOW_CERTIFICATE;
|
||||
case RULESTATE_SILENT_BLACKLIST:
|
||||
cd.silentBlock = YES;
|
||||
case RULESTATE_BLACKLIST:
|
||||
cd.customMsg = rule.customMsg;
|
||||
return EVENTSTATE_BLOCK_CERTIFICATE;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *msg = [self fileIsScopeBlacklisted:fi];
|
||||
if (msg) {
|
||||
cd.decisionExtra = msg;
|
||||
return EVENTSTATE_BLOCK_SCOPE;
|
||||
}
|
||||
|
||||
msg = [self fileIsScopeWhitelisted:fi];
|
||||
if (msg) {
|
||||
cd.decisionExtra = msg;
|
||||
return EVENTSTATE_ALLOW_SCOPE;
|
||||
}
|
||||
|
||||
switch ([[SNTConfigurator configurator] clientMode]) {
|
||||
case CLIENTMODE_MONITOR: return EVENTSTATE_ALLOW_UNKNOWN;
|
||||
case CLIENTMODE_LOCKDOWN: return EVENTSTATE_BLOCK_UNKNOWN;
|
||||
default: return EVENTSTATE_BLOCK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)validateBinaryWithMessage:(santa_message_t)message {
|
||||
// Get info about the file. If we can't get this info, allow execution and log an error.
|
||||
NSError *fileInfoError;
|
||||
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
|
||||
forVnodeID:message.vnode_id];
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 4 - default rule :-(
|
||||
if (!rule) {
|
||||
respondedAction = [self defaultDecision];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
}
|
||||
// Get codesigning info about the file.
|
||||
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path];
|
||||
|
||||
// Actually make the decision.
|
||||
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
|
||||
cd.sha256 = binInfo.SHA256;
|
||||
cd.certCommonName = csInfo.leafCertificate.commonName;
|
||||
cd.certSHA256 = csInfo.leafCertificate.SHA256;
|
||||
cd.vnodeId = message.vnode_id;
|
||||
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];
|
||||
|
||||
// Send the decision to the kernel.
|
||||
[self.driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
|
||||
|
||||
// Log to database if necessary.
|
||||
if (cd.decision != EVENTSTATE_ALLOW_BINARY &&
|
||||
cd.decision != EVENTSTATE_ALLOW_CERTIFICATE &&
|
||||
cd.decision != EVENTSTATE_ALLOW_SCOPE) {
|
||||
|
||||
// Step 5 - log to database and potentially alert user
|
||||
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY ||
|
||||
!rule ||
|
||||
[[SNTConfigurator configurator] logAllEvents]) {
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.fileSHA256 = sha256;
|
||||
se.filePath = path;
|
||||
se.occurrenceDate = [[NSDate alloc] init];
|
||||
se.fileSHA256 = cd.sha256;
|
||||
se.filePath = binInfo.path;
|
||||
se.decision = cd.decision;
|
||||
|
||||
se.signingChain = csInfo.certificates;
|
||||
se.pid = @(message.pid);
|
||||
se.ppid = @(message.ppid);
|
||||
se.parentName = @(message.pname);
|
||||
|
||||
se.fileBundleID = [binInfo bundleIdentifier];
|
||||
se.fileBundleName = [binInfo bundleName];
|
||||
|
||||
@@ -132,51 +167,38 @@
|
||||
se.fileBundleVersion = [binInfo bundleVersion];
|
||||
}
|
||||
|
||||
se.signingChain = csInfo.certificates;
|
||||
se.occurrenceDate = [[NSDate alloc] init];
|
||||
se.decision = [self eventStateForDecision:respondedAction type:rule.type];
|
||||
se.pid = @(message.pid);
|
||||
se.ppid = @(message.ppid);
|
||||
se.parentName = @(pname);
|
||||
|
||||
struct passwd *user = getpwuid(message.userId);
|
||||
struct passwd *user = getpwuid(message.uid);
|
||||
endpwent();
|
||||
NSString *userName;
|
||||
if (user) {
|
||||
userName = @(user->pw_name);
|
||||
se.executingUser = @(user->pw_name);
|
||||
}
|
||||
se.executingUser = userName;
|
||||
|
||||
NSArray *loggedInUsers, *currentSessions;
|
||||
[self loggedInUsers:&loggedInUsers sessions:¤tSessions];
|
||||
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 (respondedAction == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
// If binary was blocked, do the needful
|
||||
if (action != ACTION_RESPOND_CHECKBW_ALLOW) {
|
||||
[self.eventLog logDeniedExecution:cd withMessage:message];
|
||||
|
||||
// So the server has something to show the user straight away, initiate an event
|
||||
// upload for the blocked binary rather than waiting for the next sync.
|
||||
// The event upload is skipped if the full path is equal to that of santactl so that
|
||||
/// on the off chance that santactl is not whitelisted, we don't get into an infinite loop.
|
||||
if (![path isEqual:@(kSantaCtlPath)] &&
|
||||
[[SNTConfigurator configurator] syncBaseURL]
|
||||
&& ![[SNTConfigurator configurator] syncBackOff]) {
|
||||
[self initiateEventUploadForSHA256:sha256];
|
||||
}
|
||||
[self initiateEventUploadForEvent:se];
|
||||
|
||||
if (!rule || rule.state != RULESTATE_SILENT_BLACKLIST) {
|
||||
if (!cd.silentBlock) {
|
||||
[[self.notifierConnection remoteObjectProxy] postBlockNotification:se
|
||||
withCustomMessage:rule.customMsg];
|
||||
withCustomMessage:cd.customMsg];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6 - log to log file
|
||||
[self logDecisionForEventState:[self eventStateForDecision:respondedAction type:rule.type]
|
||||
sha256:sha256
|
||||
path:path
|
||||
leafCert:csInfo.leafCertificate];
|
||||
}
|
||||
|
||||
///
|
||||
@@ -188,118 +210,70 @@
|
||||
///
|
||||
/// @return @c YES if file is in scope, @c NO otherwise.
|
||||
///
|
||||
- (BOOL)fileIsInScope:(NSString *)path {
|
||||
- (NSString *)fileIsScopeWhitelisted:(SNTFileInfo *)fi {
|
||||
// Determine if file is within a whitelisted path
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] whitelistPathRegex];
|
||||
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
|
||||
return NO;
|
||||
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
|
||||
return @"Whitelist Regex";
|
||||
}
|
||||
|
||||
// If file is not a Mach-O file, we're not interested unless it's part of an install package.
|
||||
// TODO(rah): Consider adding an option to check all scripts.
|
||||
// TODO(rah): Consider adding an option to disable package script checks.
|
||||
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:path];
|
||||
if (![binInfo isMachO] && ![path hasPrefix:@"/private/tmp/PKInstallSandbox."]) {
|
||||
return NO;
|
||||
if (!fi.isMachO && ![fi.path hasPrefix:@"/private/tmp/PKInstallSandbox."]) {
|
||||
return @"Not a Mach-O";
|
||||
}
|
||||
|
||||
return YES;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (santa_eventstate_t)eventStateForDecision:(santa_action_t)decision type:(santa_ruletype_t)type {
|
||||
switch (decision) {
|
||||
case ACTION_RESPOND_CHECKBW_ALLOW:
|
||||
switch (type) {
|
||||
case RULETYPE_BINARY: return EVENTSTATE_ALLOW_BINARY;
|
||||
case RULETYPE_CERT: return EVENTSTATE_ALLOW_CERTIFICATE;
|
||||
default: return EVENTSTATE_ALLOW_UNKNOWN;
|
||||
|
||||
}
|
||||
case ACTION_RESPOND_CHECKBW_DENY:
|
||||
switch (type) {
|
||||
case RULETYPE_BINARY: return EVENTSTATE_BLOCK_BINARY;
|
||||
case RULETYPE_CERT: return EVENTSTATE_BLOCK_CERTIFICATE;
|
||||
default: return EVENTSTATE_BLOCK_UNKNOWN;
|
||||
}
|
||||
default: return EVENTSTATE_UNKNOWN;
|
||||
- (NSString *)fileIsScopeBlacklisted:(SNTFileInfo *)fi {
|
||||
NSRegularExpression *re = [[SNTConfigurator configurator] blacklistPathRegex];
|
||||
if ([re numberOfMatchesInString:fi.path options:0 range:NSMakeRange(0, fi.path.length)]) {
|
||||
return @"Blacklist Regex";
|
||||
}
|
||||
|
||||
if ([[SNTConfigurator configurator] enablePageZeroProtection] && fi.isMissingPageZero) {
|
||||
return @"Missing __PAGEZERO";
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)logDecisionForEventState:(santa_eventstate_t)eventState
|
||||
sha256:(NSString *)sha256
|
||||
path:(NSString *)path
|
||||
leafCert:(SNTCertificate *)cert {
|
||||
NSString *d, *r, *outLog;
|
||||
|
||||
switch (eventState) {
|
||||
case EVENTSTATE_ALLOW_BINARY:
|
||||
d = @"A"; r = @"B"; break;
|
||||
case EVENTSTATE_ALLOW_CERTIFICATE:
|
||||
d = @"A"; r = @"C"; break;
|
||||
case EVENTSTATE_ALLOW_SCOPE:
|
||||
d = @"A"; r = @"S"; break;
|
||||
case EVENTSTATE_ALLOW_UNKNOWN:
|
||||
d = @"A"; r = @"?"; break;
|
||||
case EVENTSTATE_BLOCK_BINARY:
|
||||
d = @"D"; r = @"B"; break;
|
||||
case EVENTSTATE_BLOCK_CERTIFICATE:
|
||||
d = @"D"; r = @"C"; break;
|
||||
case EVENTSTATE_BLOCK_UNKNOWN:
|
||||
d = @"D"; r = @"?"; break;
|
||||
default:
|
||||
d = @"?"; r = @"?"; break;
|
||||
}
|
||||
|
||||
// Ensure there are no pipes in the path name (as this will be confusing in the log)
|
||||
NSString *printPath = [path stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
|
||||
|
||||
if (cert && cert.SHA256 && cert.commonName) {
|
||||
// Also ensure there are no pipes in the cert's common name.
|
||||
NSString *printCommonName =
|
||||
[cert.commonName stringByReplacingOccurrencesOfString:@"|" withString:@"<pipe>"];
|
||||
outLog = [NSString stringWithFormat:@"%@|%@|%@|%@|%@|%@",
|
||||
d, r, sha256, printPath, cert.SHA256, printCommonName];
|
||||
} else {
|
||||
outLog = [NSString stringWithFormat:@"%@|%@|%@|%@", d, r, sha256, printPath];
|
||||
}
|
||||
|
||||
// Now make sure none of the log line has a newline in it.
|
||||
LOGI(@"%@", [[outLog componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]
|
||||
componentsJoinedByString:@" "]);
|
||||
}
|
||||
|
||||
- (void)initiateEventUploadForSHA256:(NSString *)sha256 {
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
- (void)initiateEventUploadForEvent:(SNTStoredEvent *)event {
|
||||
// The event upload is skipped if the full path is equal to that of santactl so that
|
||||
// on the off chance that santactl is not whitelisted, we don't get into an infinite loop.
|
||||
// It's also skipped if there isn't a server configured or the last sync caused a backoff.
|
||||
if ([event.filePath isEqual:@(kSantaCtlPath)] ||
|
||||
![[SNTConfigurator configurator] syncBaseURL] ||
|
||||
[[SNTConfigurator configurator] syncBackOff]) return;
|
||||
|
||||
if (fork() == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(1);
|
||||
_exit(EPERM);
|
||||
}
|
||||
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "singleevent", [sha256 UTF8String], NULL));
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "singleevent",
|
||||
[event.fileSHA256 UTF8String], NULL));
|
||||
}
|
||||
}
|
||||
|
||||
- (santa_action_t)defaultDecision {
|
||||
switch ([[SNTConfigurator configurator] clientMode]) {
|
||||
case CLIENTMODE_MONITOR: return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
case CLIENTMODE_LOCKDOWN: return ACTION_RESPOND_CHECKBW_DENY;
|
||||
default: return ACTION_RESPOND_CHECKBW_DENY; // This can't happen.
|
||||
}
|
||||
}
|
||||
|
||||
- (santa_action_t)actionForRuleState:(santa_rulestate_t)state {
|
||||
- (santa_action_t)actionForEventState:(santa_eventstate_t)state {
|
||||
switch (state) {
|
||||
case RULESTATE_WHITELIST:
|
||||
case EVENTSTATE_ALLOW_BINARY:
|
||||
case EVENTSTATE_ALLOW_CERTIFICATE:
|
||||
case EVENTSTATE_ALLOW_SCOPE:
|
||||
case EVENTSTATE_ALLOW_UNKNOWN:
|
||||
return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
case RULESTATE_BLACKLIST:
|
||||
case RULESTATE_SILENT_BLACKLIST:
|
||||
case EVENTSTATE_BLOCK_BINARY:
|
||||
case EVENTSTATE_BLOCK_CERTIFICATE:
|
||||
case EVENTSTATE_BLOCK_SCOPE:
|
||||
case EVENTSTATE_BLOCK_UNKNOWN:
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
default:
|
||||
return ACTION_ERROR;
|
||||
LOGW(@"Invalid event state %d", state);
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,25 @@
|
||||
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTRule.h"
|
||||
|
||||
@interface SNTRuleTable ()
|
||||
@property NSString *santadCertSHA;
|
||||
@property NSString *launchdCertSHA;
|
||||
@end
|
||||
|
||||
@implementation SNTRuleTable
|
||||
|
||||
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
|
||||
|
||||
// Save hashes of the signing certs for launchd and santad
|
||||
self.santadCertSHA = [[[[MOLCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
|
||||
self.launchdCertSHA = [[[[MOLCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
|
||||
|
||||
uint32_t newVersion = 0;
|
||||
|
||||
if (version < 1) {
|
||||
@@ -40,14 +51,14 @@
|
||||
// Insert the codesigning certs for the running santad and launchd into the initial database.
|
||||
// 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.
|
||||
NSString *santadSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
|
||||
NSString *launchdSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
|
||||
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
|
||||
santadSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
|
||||
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
|
||||
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
|
||||
launchdSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
|
||||
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
|
||||
|
||||
newVersion = 1;
|
||||
|
||||
[[SNTConfigurator configurator] setSyncCleanRequired:YES];
|
||||
}
|
||||
|
||||
return newVersion;
|
||||
@@ -121,14 +132,28 @@
|
||||
#pragma mark Adding
|
||||
|
||||
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate {
|
||||
__block BOOL failed = NO;
|
||||
|
||||
if (!rules || rules.count < 1) {
|
||||
LOGE(@"Received request to add rules with nil/empty array.");
|
||||
return NO;
|
||||
}
|
||||
|
||||
__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.");
|
||||
*rollback = failed = YES;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cleanSlate) {
|
||||
[db executeUpdate:@"DELETE FROM rules"];
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
#import "SNTApplication.h"
|
||||
|
||||
extern uint64_t watchdogCPUEvents;
|
||||
extern uint64_t watchdogRAMEvents;
|
||||
|
||||
/// 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;
|
||||
@@ -59,6 +62,7 @@ void *watchdogThreadFunction(__unused void *idata) {
|
||||
if (percentage > cpuWarnThreshold) {
|
||||
LOGW(@"Watchdog: potentially high CPU use, ~%.2f%% over last %d seconds.",
|
||||
percentage, timeInterval);
|
||||
watchdogCPUEvents++;
|
||||
}
|
||||
|
||||
// RAM
|
||||
@@ -67,6 +71,7 @@ void *watchdogThreadFunction(__unused void *idata) {
|
||||
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;
|
||||
}
|
||||
@@ -87,10 +92,6 @@ int main(int argc, const char *argv[]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Close stdout/stderr so logging goes to syslog
|
||||
fclose(stdout);
|
||||
fclose(stderr);
|
||||
|
||||
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
|
||||
|
||||
SNTApplication *s = [[SNTApplication alloc] init];
|
||||
|
||||
@@ -29,23 +29,30 @@
|
||||
///
|
||||
|
||||
#define TSTART(testName) \
|
||||
printf(" %-50s ", testName);
|
||||
do { printf(" %-50s ", testName); } while (0)
|
||||
#define TPASS() \
|
||||
printf("\x1b[32mPASS\x1b[0m\n");
|
||||
do { printf("\x1b[32mPASS\x1b[0m\n"); } while (0)
|
||||
#define TPASSINFO(fmt, ...) \
|
||||
printf("\x1b[32mPASS\x1b[0m\n " fmt "\n", ##__VA_ARGS__);
|
||||
do { printf("\x1b[32mPASS\x1b[0m\n " fmt "\n", ##__VA_ARGS__); } while (0)
|
||||
#define TFAIL() \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n"); \
|
||||
exit(1);
|
||||
do { \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n"); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
#define TFAILINFO(fmt, ...) \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
|
||||
exit(1);
|
||||
do { \
|
||||
printf("\x1b[31mFAIL\x1b[0m\n -> " fmt "\n\nTest failed.\n\n", ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
} while (0)
|
||||
|
||||
@interface SantaKernelTests : NSObject
|
||||
@property io_connect_t connection;
|
||||
@property int timesSeenLs;
|
||||
@property int timesSeenCat;
|
||||
@property int timesSeenCp;
|
||||
|
||||
@property int testExeIteration;
|
||||
@property int timesSeenTestExeIteration;
|
||||
- (void)runTests;
|
||||
@end
|
||||
|
||||
@@ -60,16 +67,15 @@
|
||||
t.standardInput = nil;
|
||||
t.standardOutput = nil;
|
||||
t.standardError = nil;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
- (NSString *)sha256ForPath:(NSString *)path {
|
||||
unsigned char sha256[CC_SHA256_DIGEST_LENGTH];
|
||||
NSData *psData = [NSData dataWithContentsOfFile:path
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:nil];
|
||||
CC_SHA256([psData bytes], (unsigned int)[psData length], sha256);
|
||||
NSData *fData = [NSData dataWithContentsOfFile:path
|
||||
options:NSDataReadingMappedIfSafe
|
||||
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]);
|
||||
@@ -188,8 +194,15 @@
|
||||
}
|
||||
TPASS();
|
||||
|
||||
// Fetch the SHA-256 of /bin/ps, as we'll be using that for the cache invalidation test.
|
||||
NSString *psSHA = [self sha256ForPath:@"/bin/ps"];
|
||||
// Fetch the SHA-256 of /bin/ed, as we'll be using that for the cache invalidation test.
|
||||
NSString *edSHA = [self sha256ForPath:@"/bin/ed"];
|
||||
|
||||
// Create the RE used for matching testexe's
|
||||
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
|
||||
NSString *pattern = [cwd stringByAppendingPathComponent:@"testexe\\.(\\d+)"];
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
|
||||
/// Begin listening for events
|
||||
queueMemory = (IODataQueueMemory *)address;
|
||||
@@ -198,7 +211,9 @@
|
||||
dataSize = sizeof(vdata);
|
||||
kr = IODataQueueDequeue(queueMemory, &vdata, &dataSize);
|
||||
if (kr == kIOReturnSuccess) {
|
||||
if ([[self sha256ForPath:@(vdata.path)] isEqual:psSHA]) {
|
||||
if (vdata.action != ACTION_REQUEST_CHECKBW) continue;
|
||||
|
||||
if ([[self sha256ForPath:@(vdata.path)] isEqual:edSHA]) {
|
||||
[self postToKernelAction:ACTION_RESPOND_CHECKBW_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];
|
||||
@@ -220,6 +235,26 @@
|
||||
}
|
||||
TPASSINFO("Received pid, ppid: %d, %d", vdata.pid, vdata.ppid);
|
||||
} else {
|
||||
NSString *path = @(vdata.path);
|
||||
|
||||
// If current executable is one of our test exe's from handlesLotsOfBinaries,
|
||||
// check that the number has increased.
|
||||
NSArray *matches = [re matchesInString:path
|
||||
options:0
|
||||
range:NSMakeRange(0, path.length)];
|
||||
if (matches.count == 1 && [matches[0] numberOfRanges] == 2) {
|
||||
NSUInteger count = [[path substringWithRange:[matches[0] rangeAtIndex:1]] intValue];
|
||||
if (count <= self.testExeIteration && count > 0) {
|
||||
self.timesSeenTestExeIteration++;
|
||||
if (self.timesSeenTestExeIteration > 2) {
|
||||
TFAILINFO("Saw same binary several times");
|
||||
}
|
||||
} else {
|
||||
self.timesSeenTestExeIteration = 0;
|
||||
self.testExeIteration = (int)count;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow everything not related to our testing.
|
||||
[self postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vdata.vnode_id];
|
||||
}
|
||||
@@ -239,11 +274,11 @@
|
||||
- (void)receiveAndBlockTests {
|
||||
TSTART("Blocks denied binaries");
|
||||
|
||||
NSTask *ps = [self taskWithPath:@"/bin/ps"];
|
||||
NSTask *ed = [self taskWithPath:@"/bin/ed"];
|
||||
|
||||
@try {
|
||||
[ps launch];
|
||||
[ps waitUntilExit];
|
||||
[ed launch];
|
||||
[ed waitUntilExit];
|
||||
TFAIL();
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
@@ -282,12 +317,12 @@
|
||||
|
||||
// Copy the ls binary to a new file
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"santakerneltests_tmp" error:nil]) {
|
||||
if (![fm copyItemAtPath:@"/bin/pwd" toPath:@"invalidacachetest_tmp" error:nil]) {
|
||||
TFAILINFO("Failed to create temp file");
|
||||
}
|
||||
|
||||
// Launch the new file to put it in the cache
|
||||
NSTask *pwd = [self taskWithPath:@"santakerneltests_tmp"];
|
||||
NSTask *pwd = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
[pwd launch];
|
||||
[pwd waitUntilExit];
|
||||
|
||||
@@ -296,10 +331,10 @@
|
||||
TFAILINFO("First launch of test binary failed");
|
||||
}
|
||||
|
||||
// Now replace the contents of the test file (which is cached) with the contents of /bin/ps,
|
||||
// Now replace the contents of the test file (which is cached) with the contents of /bin/ed,
|
||||
// which is 'blacklisted' by SHA-256 during the tests.
|
||||
FILE *infile = fopen("/bin/ps", "r");
|
||||
FILE *outfile = fopen("santakerneltests_tmp", "w");
|
||||
FILE *infile = fopen("/bin/ed", "r");
|
||||
FILE *outfile = fopen("invalidacachetest_tmp", "w");
|
||||
int ch;
|
||||
while ((ch = fgetc(infile)) != EOF) {
|
||||
fputc(ch, outfile);
|
||||
@@ -307,13 +342,13 @@
|
||||
fclose(infile);
|
||||
|
||||
// Now try running the temp file again. If it succeeds, the test failed.
|
||||
NSTask *ps = [self taskWithPath:@"santakerneltests_tmp"];
|
||||
NSTask *ed = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
|
||||
@try {
|
||||
[ps launch];
|
||||
[ps waitUntilExit];
|
||||
[ed launch];
|
||||
[ed waitUntilExit];
|
||||
TFAILINFO("Launched after write while file open");
|
||||
[fm removeItemAtPath:@"santakerneltests_tmp" error:nil];
|
||||
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
|
||||
} @catch (NSException *exception) {
|
||||
// This is a pass, but we have more to do.
|
||||
}
|
||||
@@ -322,16 +357,16 @@
|
||||
fclose(outfile);
|
||||
|
||||
// And try running the temp file again. If it succeeds, the test failed.
|
||||
ps = [self taskWithPath:@"santakerneltests_tmp"];
|
||||
ed = [self taskWithPath:@"invalidacachetest_tmp"];
|
||||
|
||||
@try {
|
||||
[ps launch];
|
||||
[ps waitUntilExit];
|
||||
[ed launch];
|
||||
[ed waitUntilExit];
|
||||
TFAILINFO("Launched after file closed");
|
||||
} @catch (NSException *exception) {
|
||||
TPASS();
|
||||
} @finally {
|
||||
[fm removeItemAtPath:@"santakerneltests_tmp" error:nil];
|
||||
[fm removeItemAtPath:@"invalidacachetest_tmp" error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,6 +423,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that the kernel can handle _lots_ of executions.
|
||||
- (void)handlesLotsOfBinaries {
|
||||
TSTART("Handles lots of binaries");
|
||||
|
||||
const int LIMIT = 12000;
|
||||
|
||||
for (int i = 0; i < LIMIT; i++) {
|
||||
printf("\033[s"); // save cursor position
|
||||
|
||||
printf("%d/%i", i+1, LIMIT);
|
||||
|
||||
NSString *fname = [@"testexe" stringByAppendingFormat:@".%i", i];
|
||||
[[NSFileManager defaultManager] copyItemAtPath:@"/bin/hostname" toPath:fname error:NULL];
|
||||
|
||||
@try {
|
||||
NSTask *testexec = [self taskWithPath:fname];
|
||||
[testexec launch];
|
||||
[testexec waitUntilExit];
|
||||
} @catch (NSException *e) {
|
||||
TFAILINFO("Failed to launch");
|
||||
}
|
||||
|
||||
unlink([fname UTF8String]);
|
||||
printf("\033[u"); // restore cursor position
|
||||
}
|
||||
printf("\033[K\033[u"); // clear line, restore cursor position
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
#pragma mark - Main
|
||||
|
||||
- (void)runTests {
|
||||
@@ -402,7 +467,7 @@
|
||||
[self performSelectorInBackground:@selector(beginListening) withObject:nil];
|
||||
|
||||
// Wait for driver to finish getting ready
|
||||
sleep(1.0);
|
||||
sleep(1);
|
||||
printf("\n-> Functional tests:\033[m\n");
|
||||
|
||||
[self receiveAndBlockTests];
|
||||
@@ -410,6 +475,7 @@
|
||||
[self invalidatesCacheTests];
|
||||
[self clearCacheTests];
|
||||
[self blocksDeniedTracedBinaries];
|
||||
[self handlesLotsOfBinaries];
|
||||
|
||||
printf("\nAll tests passed.\n\n");
|
||||
}
|
||||
|
||||
BIN
Tests/LogicTests/Resources/bad_pagezero
Executable file
BIN
Tests/LogicTests/Resources/bad_pagezero
Executable file
Binary file not shown.
BIN
Tests/LogicTests/Resources/missing_pagezero
Executable file
BIN
Tests/LogicTests/Resources/missing_pagezero
Executable file
Binary file not shown.
@@ -1,239 +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"]);
|
||||
|
||||
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"]);
|
||||
}
|
||||
|
||||
- (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
|
||||
@@ -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
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
@@ -24,8 +24,8 @@
|
||||
/// Adding/Retrieving events relies on SNTStoredEvent coding to work correctly
|
||||
/// so if that is broken, these tests will fail.
|
||||
///
|
||||
/// Depends on on SNTFileInfo and SNTCodesignChecker (and by extension
|
||||
/// SNTCertificate) to avoid duplicating code into these tests.
|
||||
/// Depends on on SNTFileInfo and MOLCodesignChecker (and by extension
|
||||
/// MOLCertificate) to avoid duplicating code into these tests.
|
||||
///
|
||||
@interface SNTEventTableTest : XCTestCase
|
||||
@property SNTEventTable *sut;
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
- (SNTStoredEvent *)createTestEvent {
|
||||
SNTFileInfo *binInfo = [[SNTFileInfo alloc] initWithPath:@"/usr/bin/false"];
|
||||
SNTCodesignChecker *csInfo = [[SNTCodesignChecker alloc] initWithBinaryPath:@"/usr/bin/false"];
|
||||
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:@"/usr/bin/false"];
|
||||
SNTStoredEvent *event;
|
||||
event = [[SNTStoredEvent alloc] init];
|
||||
event.filePath = @"/usr/bin/false";
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
#import "SNTExecutionController.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCertificate.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTEventTable.h"
|
||||
@@ -26,10 +26,6 @@
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
@interface SNTExecutionController (Testing)
|
||||
- (BOOL)fileIsInScope:(NSString *)path;
|
||||
@end
|
||||
|
||||
@interface SNTExecutionControllerTest : XCTestCase
|
||||
@property id mockConfigurator;
|
||||
@property id mockCodesignChecker;
|
||||
@@ -48,14 +44,13 @@
|
||||
|
||||
fclose(stdout);
|
||||
|
||||
self.mockCodesignChecker = OCMClassMock([SNTCodesignChecker class]);
|
||||
self.mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
|
||||
OCMStub([self.mockCodesignChecker alloc]).andReturn(self.mockCodesignChecker);
|
||||
OCMStub([self.mockCodesignChecker initWithBinaryPath:OCMOCK_ANY])
|
||||
.andReturn(self.mockCodesignChecker);
|
||||
|
||||
self.mockConfigurator = OCMClassMock([SNTConfigurator class]);
|
||||
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
|
||||
OCMStub([self.mockConfigurator configurator]).andReturn(self.mockConfigurator);
|
||||
|
||||
self.mockDriverManager = OCMClassMock([SNTDriverManager class]);
|
||||
|
||||
@@ -70,7 +65,8 @@
|
||||
self.sut = [[SNTExecutionController alloc] initWithDriverManager:self.mockDriverManager
|
||||
ruleTable:self.mockRuleDatabase
|
||||
eventTable:self.mockEventDatabase
|
||||
notifierConnection:nil];
|
||||
notifierConnection:nil
|
||||
eventLog:nil];
|
||||
}
|
||||
|
||||
/// Return a pre-configured santa_message_ t for testing with.
|
||||
@@ -94,14 +90,12 @@
|
||||
}
|
||||
|
||||
- (void)testBinaryWhitelistRule {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
|
||||
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = RULESTATE_WHITELIST;
|
||||
OCMExpect([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
|
||||
OCMStub([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
@@ -110,14 +104,12 @@
|
||||
}
|
||||
|
||||
- (void)testBinaryBlacklistRule {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
|
||||
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = RULESTATE_BLACKLIST;
|
||||
OCMExpect([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
|
||||
OCMStub([self.mockRuleDatabase binaryRuleForSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
@@ -126,16 +118,16 @@
|
||||
}
|
||||
|
||||
- (void)testCertificateWhitelistRule {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
id cert = OCMClassMock([SNTCertificate class]);
|
||||
OCMExpect([self.mockCodesignChecker leafCertificate]).andReturn(cert);
|
||||
OCMExpect([cert SHA256]).andReturn(@"a");
|
||||
id cert = OCMClassMock([MOLCertificate class]);
|
||||
OCMStub([self.mockCodesignChecker leafCertificate]).andReturn(cert);
|
||||
OCMStub([cert SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = RULESTATE_WHITELIST;
|
||||
OCMExpect([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
|
||||
OCMStub([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
@@ -144,18 +136,16 @@
|
||||
}
|
||||
|
||||
- (void)testCertificateBlacklistRule {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
id cert = OCMClassMock([SNTCertificate class]);
|
||||
OCMExpect([self.mockCodesignChecker leafCertificate]).andReturn(cert);
|
||||
OCMExpect([cert SHA256]).andReturn(@"a");
|
||||
id cert = OCMClassMock([MOLCertificate class]);
|
||||
OCMStub([self.mockCodesignChecker leafCertificate]).andReturn(cert);
|
||||
OCMStub([cert SHA256]).andReturn(@"a");
|
||||
|
||||
SNTRule *rule = [[SNTRule alloc] init];
|
||||
rule.state = RULESTATE_BLACKLIST;
|
||||
OCMExpect([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
|
||||
OCMStub([self.mockRuleDatabase certificateRuleForSHA256:@"a"]).andReturn(rule);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
|
||||
@@ -164,16 +154,14 @@
|
||||
}
|
||||
|
||||
- (void)testDefaultDecision {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
|
||||
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_MONITOR);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
forVnodeID:1234]);
|
||||
|
||||
OCMExpect([self.mockFileInfo SHA256]).andReturn(@"a");
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
|
||||
@@ -181,10 +169,9 @@
|
||||
}
|
||||
|
||||
- (void)testOutOfScope {
|
||||
id mockSut = OCMPartialMock(self.sut);
|
||||
OCMStub([mockSut fileIsInScope:OCMOCK_ANY]).andReturn(NO);
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(NO);
|
||||
|
||||
OCMExpect([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
|
||||
OCMStub([self.mockConfigurator clientMode]).andReturn(CLIENTMODE_LOCKDOWN);
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
forVnodeID:1234]);
|
||||
@@ -194,7 +181,15 @@
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW
|
||||
forVnodeID:1234]);
|
||||
}
|
||||
|
||||
- (void)testPageZero {
|
||||
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
|
||||
OCMStub([self.mockFileInfo isMissingPageZero]).andReturn(YES);
|
||||
|
||||
[self.sut validateBinaryWithMessage:[self getMessage]];
|
||||
OCMVerify([self.mockDriverManager postToKernelAction:ACTION_RESPOND_CHECKBW_DENY
|
||||
forVnodeID:1234]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,17 +35,24 @@
|
||||
}
|
||||
|
||||
- (void)testSHA1 {
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/sbin/launchd"];
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
|
||||
ofType:@""];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
|
||||
XCTAssertNotNil(sut.SHA1);
|
||||
XCTAssertEqual(sut.SHA1.length, 40);
|
||||
XCTAssertEqualObjects(sut.SHA1, @"3a865bf47b4ceba20496e0e66e39e4cfa101ffe6");
|
||||
}
|
||||
|
||||
- (void)testSHA256 {
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:@"/sbin/launchd"];
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
|
||||
ofType:@""];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
|
||||
XCTAssertNotNil(sut.SHA256);
|
||||
XCTAssertEqual(sut.SHA256.length, 64);
|
||||
XCTAssertEqualObjects(sut.SHA256,
|
||||
@"5e089b65a1e7a4696d84a34510710b6993d1de21250c41daaec63d9981083eba");
|
||||
}
|
||||
|
||||
- (void)testExecutable {
|
||||
@@ -60,6 +67,20 @@
|
||||
XCTAssertFalse(sut.isScript);
|
||||
}
|
||||
|
||||
- (void)testPageZero {
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"missing_pagezero"
|
||||
ofType:@""];
|
||||
SNTFileInfo *sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
XCTAssertTrue(sut.isMissingPageZero);
|
||||
|
||||
path = [[NSBundle bundleForClass:[self class]] pathForResource:@"bad_pagezero" ofType:@""];
|
||||
sut = [[SNTFileInfo alloc] initWithPath:path];
|
||||
XCTAssertTrue(sut.isMissingPageZero);
|
||||
|
||||
sut = [[SNTFileInfo alloc] initWithPath:@"/usr/sbin/bless"];
|
||||
XCTAssertFalse(sut.isMissingPageZero);
|
||||
}
|
||||
|
||||
- (void)testKext {
|
||||
SNTFileInfo *sut =
|
||||
[[SNTFileInfo alloc] initWithPath:
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
@interface SNTRuleTable (Testing)
|
||||
@property NSString *santadCertSHA;
|
||||
@property NSString *launchdCertSHA;
|
||||
@end
|
||||
|
||||
/// This test case actually tests SNTRuleTable and SNTRule
|
||||
@interface SNTRuleTableTest : XCTestCase
|
||||
@property SNTRuleTable *sut;
|
||||
@@ -60,22 +65,36 @@
|
||||
}
|
||||
|
||||
- (void)testAddRulesClean {
|
||||
// If SNTRuleTable doesn't start with some rules, this test doesn't work properly.
|
||||
XCTAssert(self.sut.ruleCount);
|
||||
// Assert that insert without 'self' and launchd cert hashes fails
|
||||
XCTAssertFalse([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES]);
|
||||
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES];
|
||||
// Now add a binary rule without clean slate
|
||||
XCTAssertTrue([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO]);
|
||||
|
||||
XCTAssertEqual(self.sut.ruleCount, 1);
|
||||
XCTAssertEqual(self.sut.binaryRuleCount, 1);
|
||||
// Now add a cert rule + the required rules as a clean slate,
|
||||
// assert that the binary rule was removed
|
||||
SNTRule *r1 = [[SNTRule alloc] init];
|
||||
r1.shasum = self.sut.launchdCertSHA;
|
||||
r1.state = RULESTATE_WHITELIST;
|
||||
r1.type = RULETYPE_CERT;
|
||||
SNTRule *r2 = [[SNTRule alloc] init];
|
||||
r2.shasum = self.sut.santadCertSHA;
|
||||
r2.state = RULESTATE_WHITELIST;
|
||||
r2.type = RULETYPE_CERT;
|
||||
|
||||
XCTAssertTrue(([self.sut addRules:@[ [self _exampleCertRule], r1, r2 ] cleanSlate:YES]));
|
||||
XCTAssertEqual([self.sut binaryRuleCount], 0);
|
||||
}
|
||||
|
||||
- (void)testAddMultipleRules {
|
||||
NSUInteger ruleCount = self.sut.ruleCount;
|
||||
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule],
|
||||
[self _exampleCertRule],
|
||||
[self _exampleBinaryRule] ]
|
||||
cleanSlate:YES];
|
||||
cleanSlate:NO];
|
||||
|
||||
XCTAssertEqual(self.sut.ruleCount, 2);
|
||||
XCTAssertEqual(self.sut.ruleCount, ruleCount + 2);
|
||||
}
|
||||
|
||||
- (void)testAddRulesEmptyArray {
|
||||
@@ -87,7 +106,7 @@
|
||||
}
|
||||
|
||||
- (void)testFetchBinaryRule {
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
|
||||
|
||||
SNTRule *r = [self.sut binaryRuleForSHA256:@"a"];
|
||||
XCTAssertNotNil(r);
|
||||
@@ -99,7 +118,7 @@
|
||||
}
|
||||
|
||||
- (void)testFetchCertificateRule {
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
|
||||
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
|
||||
|
||||
SNTRule *r = [self.sut certificateRuleForSHA256:@"b"];
|
||||
XCTAssertNotNil(r);
|
||||
@@ -110,4 +129,18 @@
|
||||
XCTAssertNil(r);
|
||||
}
|
||||
|
||||
- (void)testBadDatabase {
|
||||
NSString *dbPath = [NSTemporaryDirectory() stringByAppendingString:@"sntruletabletest_baddb.db"];
|
||||
[@"some text" writeToFile:dbPath atomically:YES encoding:NSUTF8StringEncoding error:NULL];
|
||||
|
||||
FMDatabaseQueue *dbq = [[FMDatabaseQueue alloc] initWithPath:dbPath];
|
||||
SNTRuleTable *sut = [[SNTRuleTable alloc] initWithDatabaseQueue:dbq];
|
||||
|
||||
[sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO];
|
||||
|
||||
XCTAssertGreaterThan(sut.ruleCount, 0);
|
||||
|
||||
[[NSFileManager defaultManager] removeItemAtPath:dbPath error:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#import <OCMock/OCMock.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
|
||||
@interface SNTXPCConnection (Testing)
|
||||
@@ -100,7 +100,7 @@
|
||||
|
||||
OCMExpect([self.mockConnection processIdentifier]).andReturn(1);
|
||||
|
||||
id mockCodesignChecker = OCMClassMock([SNTCodesignChecker class]);
|
||||
id mockCodesignChecker = OCMClassMock([MOLCodesignChecker class]);
|
||||
OCMExpect([mockCodesignChecker alloc]).andReturn(mockCodesignChecker);
|
||||
OCMExpect([mockCodesignChecker signingInformationMatches:OCMOCK_ANY]).andReturn(NO);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user