mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3b3d722b4 | ||
|
|
a82428958b | ||
|
|
b185632bda | ||
|
|
e7a0c3d25b | ||
|
|
ab33de2c15 | ||
|
|
a1031cdc27 | ||
|
|
e3ab3ca506 | ||
|
|
b4cd1ccbee | ||
|
|
14573a5714 | ||
|
|
96150a9668 | ||
|
|
c10c1303ed | ||
|
|
7852e69685 | ||
|
|
094880af50 | ||
|
|
c3db518aca | ||
|
|
41ee0c5fdb | ||
|
|
ae178bc146 | ||
|
|
a2a660d483 | ||
|
|
8684cc34f7 | ||
|
|
0aba8b78ba | ||
|
|
5e735aa8d5 | ||
|
|
a2d6338400 | ||
|
|
5e4b8350ab | ||
|
|
4a65b646df | ||
|
|
24c715aae9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@ Santa.xcodeproj/xcuserdata
|
||||
Santa.xcodeproj/project.xcworkspace
|
||||
Santa.xcworkspace/xcuserdata
|
||||
Santa.xcworkspace/xcshareddata
|
||||
Source/DevelopmentTeam.xcconfig
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copy this file to /etc/asl to log all messages from santa-driver to the log file
|
||||
> /var/log/santa.log format="[$((Time)(ISO8601Z.3))] $Message" mode=0644 rotate=seq compress file_max=25M all_max=100M uid=0 gid=0
|
||||
> /var/db/santa/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 kernel] [S= Message santa-driver:] file /var/db/santa/santa.log
|
||||
? [= Facility com.google.santa] claim
|
||||
? [= Facility com.google.santa] file /var/log/santa.log
|
||||
? [= Facility com.google.santa] file /var/db/santa/santa.log
|
||||
|
||||
7
Podfile
7
Podfile
@@ -11,6 +11,11 @@ target :santad do
|
||||
pod 'FMDB'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
target :santabs do
|
||||
pod 'FMDB'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
end
|
||||
end
|
||||
|
||||
target :santactl do
|
||||
@@ -18,7 +23,7 @@ target :santactl do
|
||||
pod 'MOLAuthenticatingURLSession'
|
||||
pod 'MOLCertificate'
|
||||
pod 'MOLCodesignChecker'
|
||||
pod 'MOLFCMClient'
|
||||
pod 'MOLFCMClient', '~> 1.3'
|
||||
end
|
||||
|
||||
target :LogicTests do
|
||||
|
||||
18
Podfile.lock
18
Podfile.lock
@@ -2,31 +2,31 @@ PODS:
|
||||
- FMDB (2.6.2):
|
||||
- FMDB/standard (= 2.6.2)
|
||||
- FMDB/standard (2.6.2)
|
||||
- MOLAuthenticatingURLSession (2.1):
|
||||
- MOLAuthenticatingURLSession (2.2):
|
||||
- MOLCertificate (~> 1.5)
|
||||
- MOLCertificate (1.5)
|
||||
- MOLCodesignChecker (1.5):
|
||||
- MOLCertificate (~> 1.3)
|
||||
- MOLFCMClient (1.1):
|
||||
- MOLFCMClient (1.3):
|
||||
- MOLAuthenticatingURLSession (~> 2.1)
|
||||
- OCMock (3.3.1)
|
||||
- OCMock (3.4)
|
||||
|
||||
DEPENDENCIES:
|
||||
- FMDB
|
||||
- MOLAuthenticatingURLSession
|
||||
- MOLCertificate
|
||||
- MOLCodesignChecker
|
||||
- MOLFCMClient
|
||||
- MOLFCMClient (~> 1.3)
|
||||
- OCMock
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
FMDB: 854a0341b4726e53276f2a8996f06f1b80f9259a
|
||||
MOLAuthenticatingURLSession: 2f0fd35f641bc857ee1b026021dbd759955adaa3
|
||||
MOLAuthenticatingURLSession: 5a5e31eb73248c3e92c79b9a285f031194e8404c
|
||||
MOLCertificate: c39cae866d24d36fbc78032affff83d401b5384a
|
||||
MOLCodesignChecker: fc9c64147811d7b0d0739127003e0630dff9213a
|
||||
MOLFCMClient: f1684219facbffdb060ff4ab18b1825bcd4c75bc
|
||||
OCMock: f3f61e6eaa16038c30caa5798c5e49d3307b6f22
|
||||
MOLFCMClient: 13d8b42db9d750e772f09cc38fc453922fece09f
|
||||
OCMock: 35ae71d6a8fcc1b59434d561d1520b9dd4f15765
|
||||
|
||||
PODFILE CHECKSUM: b20628b5933f54525daf0dcc5534512b1cb134c8
|
||||
PODFILE CHECKSUM: acd378b3727c923d912e09812da344f7375c14fe
|
||||
|
||||
COCOAPODS: 1.0.1
|
||||
COCOAPODS: 1.2.1
|
||||
|
||||
12
Rakefile
12
Rakefile
@@ -1,3 +1,5 @@
|
||||
require 'openssl'
|
||||
|
||||
WORKSPACE = 'Santa.xcworkspace'
|
||||
DEFAULT_SCHEME = 'All'
|
||||
OUTPUT_PATH = 'Build'
|
||||
@@ -5,6 +7,8 @@ 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"
|
||||
DEVTEAM_FILE = 'Source/DevelopmentTeam.xcconfig'
|
||||
DEVTEAM_CERT_CN = 'Mac Developer'
|
||||
$DISABLE_XCPRETTY = false
|
||||
|
||||
task :default do
|
||||
@@ -44,6 +48,13 @@ task :init do
|
||||
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
|
||||
$DISABLE_XCPRETTY = true
|
||||
end
|
||||
cert_pem = `security find-certificate -p -c '#{DEVTEAM_CERT_CN}'`
|
||||
cert = OpenSSL::X509::Certificate.new cert_pem
|
||||
team_id = cert.subject.to_a.find {|f| f[0] == "OU"}[1]
|
||||
File.open(DEVTEAM_FILE, 'w') { |f|
|
||||
f.puts("// This file is auto-generated. Do not edit manually")
|
||||
f.puts("DEVELOPMENT_TEAM = #{team_id}")
|
||||
}
|
||||
end
|
||||
|
||||
task :remove_existing do
|
||||
@@ -95,6 +106,7 @@ namespace :install do
|
||||
system 'sudo cp conf/com.google.santad.plist /Library/LaunchDaemons'
|
||||
system 'sudo cp conf/com.google.santagui.plist /Library/LaunchAgents'
|
||||
system 'sudo cp conf/com.google.santa.asl.conf /etc/asl'
|
||||
system '/usr/bin/killall -HUP syslogd'
|
||||
Rake::Task['build:build'].invoke(config)
|
||||
puts "Installing with configuration: #{config}"
|
||||
Rake::Task['remove_existing'].invoke()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
80
Santa.xcodeproj/xcshareddata/xcschemes/santabs.xcscheme
Normal file
80
Santa.xcodeproj/xcshareddata/xcschemes/santabs.xcscheme
Normal file
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0730"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C78227531E1C3C58006EB2D6"
|
||||
BuildableName = "santabs.xpc"
|
||||
BlueprintName = "santabs"
|
||||
ReferencedContainer = "container:Santa.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14E46" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16D32" 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="10117"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTAboutWindowController">
|
||||
@@ -16,7 +16,7 @@
|
||||
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16E195" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<development version="6300" identifier="xcode"/>
|
||||
@@ -9,6 +9,9 @@
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
|
||||
<connections>
|
||||
<outlet property="applicationNameLabel" destination="qgf-Jf-cJr" id="1JX-X8-03v"/>
|
||||
<outlet property="bundleHashLabel" destination="xP7-jE-NF8" id="i8B-Gs-2E3"/>
|
||||
<outlet property="foundFileCountLabel" destination="LHV-gV-vyf" id="Sr0-T2-xGx"/>
|
||||
<outlet property="hashingIndicator" destination="VyY-Yg-JOe" id="Yq4-tZ-9ep"/>
|
||||
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
|
||||
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
|
||||
</connections>
|
||||
@@ -17,14 +20,14 @@
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Santa Blocked Execution" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
|
||||
<windowStyleMask key="styleMask" utility="YES"/>
|
||||
<rect key="contentRect" x="167" y="107" width="497" height="439"/>
|
||||
<rect key="contentRect" x="167" y="107" width="540" height="479"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="Iwq-Lx-rLv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="497" height="439"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="540" height="479"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kiB-jZ-69S">
|
||||
<rect key="frame" x="-6" y="411" width="37" height="32"/>
|
||||
<rect key="frame" x="16" y="451" width="37" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Hidden Button" alternateTitle="This button exists so neither of the other two buttons is pre-selected when the dialog opens." bezelStyle="rounded" alignment="center" borderStyle="border" focusRingType="none" transparent="YES" imageScaling="proportionallyDown" inset="2" id="XGa-Sl-F4t">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -37,7 +40,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
|
||||
<rect key="frame" x="206" y="368" width="85" height="41"/>
|
||||
<rect key="frame" x="228" y="408" width="85" height="41"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" refusesFirstResponder="YES" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
|
||||
<font key="font" metaFont="systemUltraLight" size="34"/>
|
||||
<color key="textColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
@@ -51,7 +54,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="22" y="329" width="454" height="17"/>
|
||||
<rect key="frame" x="43" y="369" width="454" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
|
||||
</constraints>
|
||||
@@ -65,58 +68,10 @@
|
||||
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="VC7-bE-uHc"/>
|
||||
</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="132" width="1" height="167"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
|
||||
</constraints>
|
||||
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<font key="titleFont" metaFont="system"/>
|
||||
</box>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC" userLabel="Label: Application">
|
||||
<rect key="frame" x="8" y="282" width="120" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSIsNil</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField toolTip="Application Name" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="282" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="Pav-ZA-iAu"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H" userLabel="Label: Path">
|
||||
<rect key="frame" x="8" y="257" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="297" width="142" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
|
||||
<constraint firstAttribute="width" constant="138" id="Kqd-nX-7df"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Filename" id="KgY-X1-ESG">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
@@ -128,9 +83,9 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YNz-ka-cBi" userLabel="Label: Path">
|
||||
<rect key="frame" x="8" y="232" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="272" width="142" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="116" id="3wU-P0-gAC"/>
|
||||
<constraint firstAttribute="width" constant="138" id="3wU-P0-gAC"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="adC-be-Beh">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
@@ -141,10 +96,10 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField toolTip="Binary Path" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="257" width="294" height="17"/>
|
||||
<textField toolTip="Binary Name" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="187" y="297" width="315" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
|
||||
<constraint firstAttribute="width" constant="311" id="xVR-j3-dLw"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Name" id="E7T-9h-ofr">
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -156,7 +111,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5" userLabel="Label: Publisher">
|
||||
<rect key="frame" x="8" y="207" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="247" width="142" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Publisher" id="yL9-yD-JXX">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -167,9 +122,9 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField toolTip="Publisher" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w" userLabel="Publisher" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="207" width="294" height="17"/>
|
||||
<rect key="frame" x="187" y="247" width="315" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
|
||||
<constraint firstAttribute="width" constant="311" id="Dem-wH-KHm"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -185,7 +140,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS" userLabel="Publisher Certs Button">
|
||||
<rect key="frame" x="40" y="208" width="15" height="15"/>
|
||||
<rect key="frame" x="62" y="248" width="15" height="15"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
|
||||
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
|
||||
@@ -207,7 +162,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y" userLabel="Label: Identifier">
|
||||
<rect key="frame" x="8" y="182" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="222" width="142" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Identifier" id="eKN-Ic-5zy">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -217,8 +172,8 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
|
||||
<rect key="frame" x="165" y="182" width="219" height="17"/>
|
||||
<textField toolTip="SHA-256" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
|
||||
<rect key="frame" x="187" y="222" width="219" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="215" id="4hh-R2-86s"/>
|
||||
</constraints>
|
||||
@@ -234,8 +189,26 @@
|
||||
<binding destination="-2" name="value" keyPath="self.event.fileSHA256" id="9KB-0b-qLV"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MhO-U0-MLR" userLabel="Label: Bundle Identifier">
|
||||
<rect key="frame" x="8" y="197" width="142" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Bundle Identifier" id="LEe-u0-52o">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.event.needsBundleHash" id="2kb-3z-Kyn">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSNegateBoolean</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J" userLabel="Label: Parent">
|
||||
<rect key="frame" x="8" y="157" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="157" width="142" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Parent" id="gze-4A-1w5">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -246,7 +219,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField toolTip="Parent Process" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="f1p-GL-O3o" userLabel="Parent" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="157" width="294" height="17"/>
|
||||
<rect key="frame" x="187" y="157" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
|
||||
</constraints>
|
||||
@@ -269,7 +242,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL" userLabel="Label: User">
|
||||
<rect key="frame" x="8" y="132" width="120" height="17"/>
|
||||
<rect key="frame" x="8" y="132" width="142" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="User" id="1ut-uT-hQD">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -279,26 +252,8 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField toolTip="Executing User" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="h6f-PY-cc0" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="132" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="IcM-Lt-xTT">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="7ua-5a-uSd">
|
||||
<rect key="frame" x="132" y="33" width="112" height="25"/>
|
||||
<rect key="frame" x="154" y="33" width="112" height="25"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
|
||||
</constraints>
|
||||
@@ -315,8 +270,35 @@ DQ
|
||||
<outlet property="nextKeyView" destination="BbV-3h-mmL" id="Xkz-va-iGc"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField toolTip="Binary Path" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bDE-Tl-UHg" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="187" y="272" width="315" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="311" id="p1W-f9-KBX"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="H1b-Ui-CYo">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.filePath" id="Sry-KY-HDb"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="5D8-GP-a4l">
|
||||
<rect key="frame" x="113" y="80" width="315" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="25" id="KvD-X6-CsO"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Prevent future notifications for this application for a day" bezelStyle="regularSquare" imagePosition="left" alignment="center" inset="2" id="R5Y-Uc-rEP">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.silenceFutureNotifications" id="tEb-2A-sht"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL" userLabel="Dismiss Button">
|
||||
<rect key="frame" x="256" y="33" width="110" height="25"/>
|
||||
<rect key="frame" x="278" y="33" width="110" height="25"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
|
||||
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
|
||||
@@ -335,61 +317,138 @@ DQ
|
||||
<outlet property="nextKeyView" destination="7ua-5a-uSd" id="4KL-Z2-1op"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField toolTip="Binary Path" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bDE-Tl-UHg" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="165" y="232" width="294" height="17"/>
|
||||
<textField toolTip="Executing User" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="h6f-PY-cc0" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="187" y="132" width="294" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="290" id="p1W-f9-KBX"/>
|
||||
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="H1b-Ui-CYo">
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.filePath" id="Sry-KY-HDb"/>
|
||||
<binding destination="-2" name="value" keyPath="self.event.executingUser" id="IcM-Lt-xTT">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="5D8-GP-a4l">
|
||||
<rect key="frame" x="91" y="80" width="315" height="29"/>
|
||||
<progressIndicator wantsLayer="YES" canDrawConcurrently="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="1" bezeled="NO" controlSize="small" style="bar" translatesAutoresizingMaskIntoConstraints="NO" id="VyY-Yg-JOe">
|
||||
<rect key="frame" x="187" y="199" width="217" height="12"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="25" id="KvD-X6-CsO"/>
|
||||
<constraint firstAttribute="width" constant="217" id="M22-Dv-KIP"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Prevent future notifications for this application for a day" bezelStyle="regularSquare" imagePosition="left" alignment="center" inset="2" id="R5Y-Uc-rEP">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
</buttonCell>
|
||||
</progressIndicator>
|
||||
<textField toolTip="Bundle SHA-256" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xP7-jE-NF8">
|
||||
<rect key="frame" x="187" y="197" width="219" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="215" id="s7W-o9-2nN"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="Calculating..." id="yJa-yL-X9a">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.silenceFutureNotifications" id="tEb-2A-sht"/>
|
||||
<binding destination="-2" name="value" keyPath="self.event.fileBundleHash" id="CnT-q6-bot"/>
|
||||
</connections>
|
||||
</button>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LHV-gV-vyf">
|
||||
<rect key="frame" x="187" y="182" width="219" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="215" id="LUu-Vd-peN"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1000 related binaries" id="AVM-vB-hB8">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
|
||||
<rect key="frame" x="168" y="132" width="1" height="207"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
|
||||
</constraints>
|
||||
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<font key="titleFont" metaFont="system"/>
|
||||
</box>
|
||||
<textField toolTip="Application Name" verticalHuggingPriority="900" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr" customClass="SNTAccessibleTextField">
|
||||
<rect key="frame" x="187" y="322" width="315" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="311" id="Pav-ZA-iAu"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Unknown</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC" userLabel="Label: Application">
|
||||
<rect key="frame" x="8" y="322" width="142" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="138" id="8mA-zi-Ev7"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="accessibilityElement" value="NO"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSIsNil</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="0AD-PS-5V1"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" priority="500" constant="193" id="2uo-Cm-Tfp"/>
|
||||
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="f1p-GL-O3o" secondAttribute="bottom" constant="8" id="496-VQ-Fx5"/>
|
||||
<constraint firstItem="xP7-jE-NF8" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="5Mr-By-PAU"/>
|
||||
<constraint firstItem="pDa-fA-vnC" firstAttribute="centerY" secondItem="qgf-Jf-cJr" secondAttribute="centerY" id="AKX-pe-hEX"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="ALv-0v-szi"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="E6D-7P-17g"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="FPe-Rd-G4n"/>
|
||||
<constraint firstItem="cJf-k6-OxS" firstAttribute="centerY" secondItem="C3G-wL-u7w" secondAttribute="centerY" id="FdL-ZZ-Vbe"/>
|
||||
<constraint firstItem="t8c-Fx-e5h" firstAttribute="top" secondItem="Iwq-Lx-rLv" secondAttribute="top" constant="30" id="FuB-GX-0jg"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="G0I-O2-S91"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-45" id="GD2-Ka-deo"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-67" id="GD2-Ka-deo"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="5D8-GP-a4l" secondAttribute="bottom" priority="900" constant="25" id="GT2-tO-2td"/>
|
||||
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pDa-fA-vnC" secondAttribute="top" id="Gd4-Nr-n5G"/>
|
||||
<constraint firstItem="xP7-jE-NF8" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="HUT-MI-jsR"/>
|
||||
<constraint firstItem="qgf-Jf-cJr" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="Ht4-Lg-U5N"/>
|
||||
<constraint firstItem="LHV-gV-vyf" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="IA0-dy-2be"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="IwX-ja-ZIs"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" priority="500" id="JY4-N1-j8e"/>
|
||||
<constraint firstItem="YNz-ka-cBi" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="KmX-kX-VCN"/>
|
||||
<constraint firstItem="5D8-GP-a4l" firstAttribute="centerX" secondItem="Iwq-Lx-rLv" secondAttribute="centerX" id="LkH-F4-Ncm"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="950" constant="30" id="Nsl-zf-poH"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="950" constant="55" id="Nsl-zf-poH"/>
|
||||
<constraint firstItem="YNz-ka-cBi" firstAttribute="centerY" secondItem="bDE-Tl-UHg" secondAttribute="centerY" id="ObQ-RA-S5V"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="SCl-Ky-VmT"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="centerY" secondItem="pc8-G9-4pJ" secondAttribute="centerY" id="SLv-F7-w5k"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="KEB-eH-x2Y" secondAttribute="trailing" constant="20" id="Seb-c0-MUL"/>
|
||||
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="V0a-Py-iEc"/>
|
||||
<constraint firstItem="LHV-gV-vyf" firstAttribute="top" secondItem="VyY-Yg-JOe" secondAttribute="bottom" id="Vjr-NX-j8V"/>
|
||||
<constraint firstItem="MhO-U0-MLR" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="Vly-VE-BwU"/>
|
||||
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="leading" priority="999" id="Z6G-l9-G4a"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="top" secondItem="bDE-Tl-UHg" secondAttribute="bottom" constant="8" id="ZoS-xV-2WA"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="aMJ-Wb-vRS"/>
|
||||
@@ -401,22 +460,28 @@ DQ
|
||||
<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="f1p-GL-O3o" firstAttribute="top" secondItem="LHV-gV-vyf" secondAttribute="bottom" constant="8" id="h4h-K3-BTd"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" priority="700" constant="8" id="hXw-6Z-lb2"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="7ua-5a-uSd" secondAttribute="trailing" constant="12" id="ioO-NJ-Jqo"/>
|
||||
<constraint firstItem="bDE-Tl-UHg" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="jdk-ak-soQ"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="lvJ-Rk-UT5" secondAttribute="centerY" id="jfs-YI-7Ae"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="trailing" constant="20" id="kOG-Cj-hFG"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="MhO-U0-MLR" secondAttribute="trailing" constant="20" id="ke9-wW-5fr"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="qgf-Jf-cJr" secondAttribute="bottom" constant="8" id="lWU-tC-vWg"/>
|
||||
<constraint firstItem="5D8-GP-a4l" firstAttribute="top" secondItem="h6f-PY-cc0" secondAttribute="bottom" constant="25" id="lYd-VZ-lBs"/>
|
||||
<constraint firstItem="VyY-Yg-JOe" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="18" id="lei-uP-T8m"/>
|
||||
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="xP7-jE-NF8" secondAttribute="bottom" priority="701" constant="8" id="oY4-e7-lsz"/>
|
||||
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="5D8-GP-a4l" secondAttribute="bottom" priority="900" constant="25" id="pCX-eX-erN"/>
|
||||
<constraint firstAttribute="centerX" secondItem="7ua-5a-uSd" secondAttribute="centerX" constant="61" id="phL-j9-rPq"/>
|
||||
<constraint firstItem="xP7-jE-NF8" firstAttribute="centerY" secondItem="MhO-U0-MLR" secondAttribute="centerY" id="pdC-x8-Nao"/>
|
||||
<constraint firstAttribute="centerX" secondItem="7ua-5a-uSd" secondAttribute="centerX" constant="60" id="phL-j9-rPq"/>
|
||||
<constraint firstItem="bDE-Tl-UHg" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="bottom" constant="8" id="pis-of-f93"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="20" id="qKi-KT-jzJ"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="bottom" secondItem="PXc-xv-A28" secondAttribute="top" constant="-8" id="snd-8T-LjC"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="trailing" constant="20" id="stz-Vm-Kxo"/>
|
||||
<constraint firstItem="PXc-xv-A28" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="tAa-1s-xVZ"/>
|
||||
<constraint firstItem="eQb-0a-76J" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="u1y-6V-moc"/>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="width" secondItem="eQb-0a-76J" secondAttribute="width" id="u4p-1B-x5B"/>
|
||||
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="35" id="ukF-FH-DE8"/>
|
||||
<constraint firstItem="VyY-Yg-JOe" firstAttribute="centerY" secondItem="MhO-U0-MLR" secondAttribute="centerY" id="vB8-c5-pfO"/>
|
||||
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="YNz-ka-cBi" secondAttribute="trailing" constant="20" id="vfq-83-tKI"/>
|
||||
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="z6s-ga-iAk"/>
|
||||
</constraints>
|
||||
@@ -424,8 +489,9 @@ DQ
|
||||
<connections>
|
||||
<outlet property="initialFirstResponder" destination="kiB-jZ-69S" id="I96-dS-lq5"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="302.5" y="304.5"/>
|
||||
<point key="canvasLocation" x="274" y="326.5"/>
|
||||
</window>
|
||||
<userDefaultsController representsSharedInstance="YES" id="iXx-cu-WYe"/>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSInfo" width="32" height="32"/>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
@interface SNTAboutWindowController : NSWindowController
|
||||
|
||||
@property IBOutlet NSButton *moreInfoButton;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
/**
|
||||
An NSTextField subclass that provides an accessiblity label equal to:
|
||||
(self.toolTip + self.stringValue) where available. It also sets the
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
///
|
||||
/// Initiates and manages the connection to santad
|
||||
///
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@property SNTFileWatcher *configFileWatcher;
|
||||
@property SNTNotificationManager *notificationManager;
|
||||
@property SNTXPCConnection *listener;
|
||||
@property SNTXPCConnection *daemonListener;
|
||||
@property SNTXPCConnection *bundleListener;
|
||||
@end
|
||||
|
||||
@implementation SNTAppDelegate
|
||||
@@ -49,18 +50,19 @@
|
||||
object:nil
|
||||
queue:[NSOperationQueue currentQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
self.listener.invalidationHandler = nil;
|
||||
[self.listener invalidate];
|
||||
self.listener = nil;
|
||||
self.daemonListener.invalidationHandler = nil;
|
||||
[self.daemonListener invalidate];
|
||||
self.daemonListener = nil;
|
||||
}];
|
||||
[workspaceNotifications addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue currentQueue]
|
||||
usingBlock:^(NSNotification *note) {
|
||||
[self attemptReconnection];
|
||||
[self attemptDaemonReconnection];
|
||||
}];
|
||||
|
||||
[self createConnection];
|
||||
[self createDaemonConnection];
|
||||
[self createBundleConnection];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
|
||||
@@ -71,24 +73,24 @@
|
||||
|
||||
#pragma mark Connection handling
|
||||
|
||||
- (void)createConnection {
|
||||
- (void)createDaemonConnection {
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
|
||||
WEAKIFY(self);
|
||||
|
||||
// Create listener for return connection from daemon.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
|
||||
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.listener.exportedObject = self.notificationManager;
|
||||
self.listener.acceptedHandler = ^{
|
||||
self.daemonListener = [[SNTXPCConnection alloc] initServerWithListener:listener];
|
||||
self.daemonListener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.daemonListener.exportedObject = self.notificationManager;
|
||||
self.daemonListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
};
|
||||
self.listener.invalidationHandler = ^{
|
||||
self.daemonListener.invalidationHandler = ^{
|
||||
STRONGIFY(self);
|
||||
[self attemptReconnection];
|
||||
[self attemptDaemonReconnection];
|
||||
};
|
||||
[self.listener resume];
|
||||
[self.daemonListener resume];
|
||||
|
||||
// Tell daemon to connect back to the above listener.
|
||||
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
@@ -97,12 +99,46 @@
|
||||
|
||||
// Now wait for the connection to come in.
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
|
||||
[self attemptReconnection];
|
||||
[self attemptDaemonReconnection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)attemptReconnection {
|
||||
[self performSelectorInBackground:@selector(createConnection) withObject:nil];
|
||||
- (void)attemptDaemonReconnection {
|
||||
[self performSelectorInBackground:@selector(createDaemonConnection) withObject:nil];
|
||||
}
|
||||
|
||||
- (void)createBundleConnection {
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
|
||||
WEAKIFY(self);
|
||||
|
||||
// Create listener for return connection from the bundle service.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.bundleListener = [[SNTXPCConnection alloc] initServerWithListener:listener];
|
||||
self.bundleListener.exportedInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
self.bundleListener.exportedObject = self.notificationManager;
|
||||
self.bundleListener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
};
|
||||
self.bundleListener.invalidationHandler = ^{
|
||||
STRONGIFY(self);
|
||||
[self attemptBundleReconnection];
|
||||
};
|
||||
[self.bundleListener resume];
|
||||
|
||||
// Tell santabs to connect back to the above listener.
|
||||
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
[daemonConn resume];
|
||||
[[daemonConn remoteObjectProxy] setBundleNotificationListener:listener.endpoint];
|
||||
|
||||
// Now wait for the connection to come in.
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
|
||||
[self attemptBundleReconnection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)attemptBundleReconnection {
|
||||
[self performSelectorInBackground:@selector(createBundleConnection) withObject:nil];
|
||||
}
|
||||
|
||||
#pragma mark Menu Management
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
///
|
||||
/// An NSPanel that can become key/main and can fade in/out.
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
@protocol SNTMessageWindowControllerDelegate
|
||||
@@ -29,6 +31,36 @@
|
||||
- (IBAction)closeWindow:(id)sender;
|
||||
- (IBAction)showCertInfo:(id)sender;
|
||||
|
||||
/// Reference to the "Bundle Hash" label in the XIB. Used to remove if application
|
||||
/// doesn't have a bundle hash.
|
||||
@property(weak) IBOutlet NSTextField *bundleHashLabel;
|
||||
|
||||
///
|
||||
/// Is displayed if calculating the bundle hash is taking a bit.
|
||||
///
|
||||
@property(weak) IBOutlet NSProgressIndicator *hashingIndicator;
|
||||
|
||||
///
|
||||
/// Is displayed if calculating the bundle hash is taking a bit.
|
||||
///
|
||||
@property(weak) IBOutlet NSTextField *foundFileCountLabel;
|
||||
|
||||
///
|
||||
/// Reference to the "Open Event" button in the XIB. Used to either remove the button
|
||||
/// if it isn't needed or set its title if it is.
|
||||
///
|
||||
@property(weak) IBOutlet NSButton *openEventButton;
|
||||
|
||||
///
|
||||
/// The execution event that this window is for
|
||||
///
|
||||
@property(readonly) SNTStoredEvent *event;
|
||||
|
||||
///
|
||||
/// The root progress object. Child nodes are vended to santad to report on work being done.
|
||||
///
|
||||
@property NSProgress *progress;
|
||||
|
||||
///
|
||||
/// The delegate to inform when the notification is dismissed
|
||||
///
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "SNTMessageWindowController.h"
|
||||
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
@import SecurityInterface.SFCertificatePanel;
|
||||
|
||||
#import "MOLCertificate.h"
|
||||
#import "SNTBlockMessage.h"
|
||||
@@ -24,9 +24,6 @@
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@interface SNTMessageWindowController ()
|
||||
/// The execution event that this window is for
|
||||
@property SNTStoredEvent *event;
|
||||
|
||||
/// The custom message to display for this event
|
||||
@property(copy) NSString *customMessage;
|
||||
|
||||
@@ -36,13 +33,9 @@
|
||||
/// An optional message to display with this block.
|
||||
@property(readonly, nonatomic) NSAttributedString *attributedCustomMessage;
|
||||
|
||||
/// Reference to the "Open Event" button in the XIB. Used to either remove the button
|
||||
/// if it isn't needed or set its title if it is.
|
||||
@property IBOutlet NSButton *openEventButton;
|
||||
|
||||
/// Reference to the "Application Name" label in the XIB. Used to remove if application
|
||||
/// doesn't have a CFBundleName.
|
||||
@property IBOutlet NSTextField *applicationNameLabel;
|
||||
@property(weak) IBOutlet NSTextField *applicationNameLabel;
|
||||
|
||||
/// Linked to checkbox in UI to prevent future notifications for this binary.
|
||||
@property BOOL silenceFutureNotifications;
|
||||
@@ -55,10 +48,35 @@
|
||||
if (self) {
|
||||
_event = event;
|
||||
_customMessage = message;
|
||||
_progress = [NSProgress discreteProgressWithTotalUnitCount:1];
|
||||
[_progress addObserver:self
|
||||
forKeyPath:@"fractionCompleted"
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_progress removeObserver:self forKeyPath:@"fractionCompleted"];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"fractionCompleted"]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSProgress *progress = object;
|
||||
if (progress.fractionCompleted != 0.0) {
|
||||
self.hashingIndicator.indeterminate = NO;
|
||||
[self.foundFileCountLabel removeFromSuperview];
|
||||
}
|
||||
self.hashingIndicator.doubleValue = progress.fractionCompleted;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadWindow {
|
||||
[super loadWindow];
|
||||
[self.window setLevel:NSPopUpMenuWindowLevel];
|
||||
@@ -73,6 +91,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.event.needsBundleHash) {
|
||||
[self.bundleHashLabel removeFromSuperview];
|
||||
[self.hashingIndicator removeFromSuperview];
|
||||
[self.foundFileCountLabel removeFromSuperview];
|
||||
} else {
|
||||
self.openEventButton.enabled = NO;
|
||||
self.hashingIndicator.indeterminate = YES;
|
||||
[self.hashingIndicator startAnimation:self];
|
||||
self.bundleHashLabel.hidden = YES;
|
||||
self.foundFileCountLabel.stringValue = @"";
|
||||
}
|
||||
|
||||
if (!self.event.fileBundleName) {
|
||||
[self.applicationNameLabel removeFromSuperview];
|
||||
}
|
||||
@@ -83,6 +113,7 @@
|
||||
}
|
||||
|
||||
- (IBAction)closeWindow:(id)sender {
|
||||
[self.progress cancel];
|
||||
[(SNTMessageWindow *)self.window fadeOut:sender];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,15 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
#import "SNTMessageWindowController.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
///
|
||||
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.
|
||||
///
|
||||
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate, SNTNotifierXPC>
|
||||
@interface SNTNotificationManager : NSObject<SNTMessageWindowControllerDelegate,
|
||||
SNTNotifierXPC, SNTBundleNotifierXPC>
|
||||
|
||||
@end
|
||||
|
||||
@@ -18,13 +18,27 @@
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTNotificationManager ()
|
||||
|
||||
/// The currently displayed notification
|
||||
@property SNTMessageWindowController *currentWindowController;
|
||||
|
||||
/// The queue of pending notifications
|
||||
@property(readonly) NSMutableArray *pendingNotifications;
|
||||
|
||||
/// The connection to the bundle service
|
||||
@property SNTXPCConnection *bundleServiceConnection;
|
||||
|
||||
/// A semaphore to block bundle hashing until a connection is established
|
||||
@property dispatch_semaphore_t bundleServiceSema;
|
||||
|
||||
// A serial queue for holding hashBundleBinaries requests
|
||||
@property dispatch_queue_t hashBundleBinariesQueue;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SNTNotificationManager
|
||||
@@ -35,6 +49,9 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_pendingNotifications = [[NSMutableArray alloc] init];
|
||||
_bundleServiceSema = dispatch_semaphore_create(0);
|
||||
_hashBundleBinariesQueue = dispatch_queue_create("com.google.santagui.hashbundlebinaries",
|
||||
DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -48,7 +65,16 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
if ([self.pendingNotifications count]) {
|
||||
self.currentWindowController = [self.pendingNotifications firstObject];
|
||||
[self.currentWindowController showWindow:self];
|
||||
if (self.currentWindowController.event.needsBundleHash) {
|
||||
dispatch_async(self.hashBundleBinariesQueue, ^{
|
||||
[self hashBundleBinariesForEvent:self.currentWindowController.event];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Tear down the bundle service
|
||||
self.bundleServiceSema = dispatch_semaphore_create(0);
|
||||
[self.bundleServiceConnection invalidate];
|
||||
self.bundleServiceConnection = nil;
|
||||
[NSApp hide:self];
|
||||
}
|
||||
}
|
||||
@@ -65,7 +91,7 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
[ud setObject:d forKey:silencedNotificationsKey];
|
||||
}
|
||||
|
||||
#pragma mark SNTNotifierXPC protocol method
|
||||
#pragma mark SNTNotifierXPC protocol methods
|
||||
|
||||
- (void)postClientModeNotification:(SNTClientMode)clientmode {
|
||||
NSUserNotification *un = [[NSUserNotification alloc] init];
|
||||
@@ -131,6 +157,11 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
if (!self.currentWindowController) {
|
||||
self.currentWindowController = pendingMsg;
|
||||
[pendingMsg showWindow:nil];
|
||||
if (self.currentWindowController.event.needsBundleHash) {
|
||||
dispatch_async(self.hashBundleBinariesQueue, ^{
|
||||
[self hashBundleBinariesForEvent:self.currentWindowController.event];
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -143,4 +174,85 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:un];
|
||||
}
|
||||
|
||||
#pragma mark SNTBundleNotifierXPC protocol methods
|
||||
|
||||
- (void)updateCountsForEvent:(SNTStoredEvent *)event
|
||||
binaryCount:(uint64_t)binaryCount
|
||||
fileCount:(uint64_t)fileCount {
|
||||
if ([self.currentWindowController.event.idx isEqual:event.idx]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.currentWindowController.foundFileCountLabel.stringValue =
|
||||
[NSString stringWithFormat:@"%llu binaries / %llu files", binaryCount, fileCount];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener {
|
||||
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
|
||||
c.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
[c resume];
|
||||
self.bundleServiceConnection = c;
|
||||
dispatch_semaphore_signal(self.bundleServiceSema);
|
||||
}
|
||||
|
||||
#pragma mark SNTBundleNotifierXPC helper methods
|
||||
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event {
|
||||
// Wait a max of 6 secs for the bundle service. Should the bundle service fall over, it will
|
||||
// reconnect within 5 secs. Otherwise abandon bundle hashing and display the blockable event.
|
||||
if (dispatch_semaphore_wait(self.bundleServiceSema,
|
||||
dispatch_time(DISPATCH_TIME_NOW, 6 * NSEC_PER_SEC))) {
|
||||
[self updateBlockNotification:event withBundleHash:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
// Let all future requests flow, until the connection is terminated and we go back to waiting.
|
||||
dispatch_semaphore_signal(self.bundleServiceSema);
|
||||
|
||||
// NSProgress becomes current for this thread. XPC messages vend a child node to the receiver.
|
||||
[self.currentWindowController.progress becomeCurrentWithPendingUnitCount:1];
|
||||
|
||||
// Start hashing. Progress is reported to the root NSProgress (currentWindowController.progress).
|
||||
[[self.bundleServiceConnection remoteObjectProxy]
|
||||
hashBundleBinariesForEvent:event
|
||||
reply:^(NSString *bh, NSArray<SNTStoredEvent *> *events, NSNumber *ms) {
|
||||
// Revert to displaying the blockable event if we fail to calculate the bundle hash
|
||||
if (!bh) return [self updateBlockNotification:event withBundleHash:nil];
|
||||
|
||||
event.fileBundleHash = bh;
|
||||
event.fileBundleBinaryCount = @(events.count);
|
||||
event.fileBundleHashMilliseconds = ms;
|
||||
for (SNTStoredEvent *se in events) {
|
||||
se.fileBundleHash = bh;
|
||||
se.fileBundleBinaryCount = @(events.count);
|
||||
se.fileBundleHashMilliseconds = ms;
|
||||
}
|
||||
|
||||
// Send the results to santad. It will decide if they need to be synced.
|
||||
SNTXPCConnection *daemonConn = [SNTXPCControlInterface configuredConnection];
|
||||
[daemonConn resume];
|
||||
[[daemonConn remoteObjectProxy] syncBundleEvent:event relatedEvents:events];
|
||||
|
||||
// Update the UI with the bundle hash. Also make the openEventButton available.
|
||||
[self updateBlockNotification:event withBundleHash:bh];
|
||||
}];
|
||||
[self.currentWindowController.progress resignCurrent];
|
||||
}
|
||||
|
||||
- (void)updateBlockNotification:(SNTStoredEvent *)event withBundleHash:(NSString *)bundleHash {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([self.currentWindowController.event.idx isEqual:event.idx]) {
|
||||
if (bundleHash) {
|
||||
[self.currentWindowController.bundleHashLabel setHidden:NO];
|
||||
} else {
|
||||
[self.currentWindowController.bundleHashLabel removeFromSuperview];
|
||||
}
|
||||
self.currentWindowController.event.fileBundleHash = bundleHash;
|
||||
[self.currentWindowController.foundFileCountLabel removeFromSuperview];
|
||||
[self.currentWindowController.hashingIndicator setHidden:YES];
|
||||
[self.currentWindowController.openEventButton setEnabled:YES];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Cocoa;
|
||||
|
||||
#import "SNTAppDelegate.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifdef SANTAGUI
|
||||
@import Cocoa;
|
||||
#else
|
||||
@import Foundation;
|
||||
#endif
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
@interface SNTBlockMessage : NSObject
|
||||
|
||||
@@ -109,8 +109,9 @@
|
||||
if (!formatStr.length) return nil;
|
||||
|
||||
if (event.fileSHA256) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
|
||||
withString:event.fileSHA256];
|
||||
formatStr =
|
||||
[formatStr stringByReplacingOccurrencesOfString:@"%file_sha%"
|
||||
withString:event.fileBundleHash ?: event.fileSHA256];
|
||||
}
|
||||
if (event.executingUser) {
|
||||
formatStr = [formatStr stringByReplacingOccurrencesOfString:@"%username%"
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// These enums are used in various places throughout the Santa client code.
|
||||
/// The integer values are also stored in the database and so shouldn't be changed.
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
@@ -131,21 +133,20 @@ extern NSString *const kDefaultConfigFilePath;
|
||||
///
|
||||
@property(readonly, nonatomic) NSURL *syncBaseURL;
|
||||
|
||||
///
|
||||
/// If YES, mid-execution event uploads are skipped.
|
||||
/// This property is never stored on disk.
|
||||
///
|
||||
@property BOOL syncBackOff;
|
||||
|
||||
///
|
||||
/// The machine owner.
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *machineOwner;
|
||||
|
||||
///
|
||||
/// The last date of successful sync.
|
||||
/// The last date of a successful full sync.
|
||||
///
|
||||
@property(nonatomic) NSDate *syncLastSuccess;
|
||||
@property(nonatomic) NSDate *fullSyncLastSuccess;
|
||||
|
||||
///
|
||||
/// The last date of a successful rule sync.
|
||||
///
|
||||
@property(nonatomic) NSDate *ruleSyncLastSuccess;
|
||||
|
||||
///
|
||||
/// If YES a clean sync is required.
|
||||
@@ -157,6 +158,12 @@ extern NSString *const kDefaultConfigFilePath;
|
||||
///
|
||||
@property(readonly, nonatomic) NSString *machineID;
|
||||
|
||||
///
|
||||
/// If YES, enables bundle detection for blocked events. This property is not stored on disk.
|
||||
/// Its value is set by a sync server that supports bundles. Defaults to NO.
|
||||
///
|
||||
@property BOOL bundlesEnabled;
|
||||
|
||||
#pragma mark Server Auth Settings
|
||||
|
||||
///
|
||||
|
||||
@@ -52,7 +52,8 @@ static NSString *const kModeNotificationMonitor = @"ModeNotificationMonitor";
|
||||
static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
|
||||
|
||||
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
|
||||
static NSString *const kSyncLastSuccess = @"SyncLastSuccess";
|
||||
static NSString *const kFullSyncLastSuccess = @"FullSyncLastSuccess";
|
||||
static NSString *const kRuleSyncLastSuccess = @"RuleSyncLastSuccess";
|
||||
static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
|
||||
static NSString *const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
|
||||
@@ -111,7 +112,7 @@ static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return (SNTClientMode)cm;
|
||||
} else {
|
||||
LOGE(@"Client mode was set to bad value: %ld. Resetting to MONITOR.", cm);
|
||||
self.configData[kClientModeKey] = @(SNTClientModeMonitor);
|
||||
self.clientMode = SNTClientModeMonitor;
|
||||
return SNTClientModeMonitor;
|
||||
}
|
||||
}
|
||||
@@ -259,12 +260,22 @@ static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
return self.configData[kServerAuthRootsFileKey];
|
||||
}
|
||||
|
||||
- (NSDate *)syncLastSuccess {
|
||||
return self.configData[kSyncLastSuccess];
|
||||
- (NSDate *)fullSyncLastSuccess {
|
||||
return self.configData[kFullSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)syncLastSuccess {
|
||||
self.configData[kSyncLastSuccess] = syncLastSuccess;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)fullSyncLastSuccess {
|
||||
self.configData[kFullSyncLastSuccess] = fullSyncLastSuccess;
|
||||
[self saveConfigToDisk];
|
||||
self.ruleSyncLastSuccess = fullSyncLastSuccess;
|
||||
}
|
||||
|
||||
- (NSDate *)ruleSyncLastSuccess {
|
||||
return self.configData[kRuleSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)ruleSyncLastSuccess {
|
||||
self.configData[kRuleSyncLastSuccess] = ruleSyncLastSuccess;
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
|
||||
@@ -317,7 +328,14 @@ static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
|
||||
- (void)reloadConfigData {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
if (![fm fileExistsAtPath:self.configFilePath]) return;
|
||||
if (![fm fileExistsAtPath:self.configFilePath]) {
|
||||
// As soon as saveConfigToDisk is called, reloadConfigData will be called again because
|
||||
// of the SNTFileWatchers on the config path. No need to use dictionaryWithCapacity: here.
|
||||
self.configData = [NSMutableDictionary dictionary];
|
||||
self.configData[kClientModeKey] = @(SNTClientModeMonitor);
|
||||
[self saveConfigToDisk];
|
||||
return;
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
NSData *readData = [NSData dataWithContentsOfFile:self.configFilePath
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// Simple function to check and drop root privileges.
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// Represents a binary on disk, providing access to details about that binary
|
||||
/// such as the SHA-1, SHA-256, Info.plist and the Mach-O data.
|
||||
@@ -114,6 +116,21 @@
|
||||
///
|
||||
- (BOOL)isMissingPageZero;
|
||||
|
||||
///
|
||||
/// If set to YES, the bundle* and infoPlist methods will search for and use the highest NSBundle
|
||||
/// found in the tree. Defaults to NO, which uses the first found bundle, if any.
|
||||
///
|
||||
/// @example:
|
||||
/// An SNTFileInfo object that represents
|
||||
/// /Applications/Photos.app/Contents/XPCServices/com.apple.Photos.librarychooserservice.xpc
|
||||
/// useAncestorBundle is set to YES
|
||||
/// /Applications/Photos.app will be used to get data backing all the bundle methods
|
||||
///
|
||||
/// @note: The NSBundle object backing the bundle* and infoPlist methods is cached once found.
|
||||
/// Setting the useAncestorBundle propery will clear this cache and force a re-search.
|
||||
///
|
||||
@property(nonatomic) BOOL useAncestorBundle;
|
||||
|
||||
///
|
||||
/// @return An NSBundle if this file is part of a bundle.
|
||||
///
|
||||
|
||||
@@ -280,34 +280,34 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
///
|
||||
/// Rationale: An NSBundle has a method executablePath for discovering the main binary within a
|
||||
/// bundle but provides no way to get an NSBundle object when only the executablePath is known.
|
||||
/// Also a bundle can contain multiple binaries within the MacOS folder and we want any of these
|
||||
/// Also a bundle can contain multiple binaries within its subdirectories and we want any of these
|
||||
/// to count as being part of the bundle.
|
||||
///
|
||||
/// This method relies on executable bundles being laid out as follows:
|
||||
/// This method walks up the path until a bundle is found, if any.
|
||||
///
|
||||
/// @code
|
||||
/// Bundle.app/
|
||||
/// Contents/
|
||||
/// MacOS/
|
||||
/// executable
|
||||
/// @endcode
|
||||
///
|
||||
/// If @c self.path is the full path to @c executable above, this method would return an
|
||||
/// NSBundle reference for Bundle.app.
|
||||
/// @param ancestor YES this will return the highest NSBundle found in the tree. No will return the
|
||||
/// the lowest.
|
||||
///
|
||||
-(NSBundle *)findBundleWithAncestor:(BOOL)ancestor {
|
||||
NSBundle *bundle;
|
||||
NSMutableArray *pathComponents = [[self.path pathComponents] mutableCopy];
|
||||
|
||||
// Ignore the root path "/", for some reason this is considered a bundle.
|
||||
while (pathComponents.count > 1) {
|
||||
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
|
||||
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) {
|
||||
bundle = bndl;
|
||||
if (!ancestor) break;
|
||||
}
|
||||
[pathComponents removeLastObject];
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
- (NSBundle *)bundle {
|
||||
if (!self.bundleRef) {
|
||||
self.bundleRef = (NSBundle *)[NSNull null];
|
||||
|
||||
// Check that the full path is at least 4-levels deep:
|
||||
// e.g: /Calendar.app/Contents/MacOS/Calendar
|
||||
NSArray *pathComponents = [self.path pathComponents];
|
||||
NSUInteger pathComponentsCount = pathComponents.count;
|
||||
if (pathComponentsCount < 4) return nil;
|
||||
|
||||
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, pathComponentsCount - 3)];
|
||||
NSBundle *bndl = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
|
||||
if (bndl && [bndl objectForInfoDictionaryKey:@"CFBundleIdentifier"]) self.bundleRef = bndl;
|
||||
self.bundleRef =
|
||||
[self findBundleWithAncestor:self.useAncestorBundle] ?: (NSBundle *)[NSNull null];
|
||||
}
|
||||
return self.bundleRef == (NSBundle *)[NSNull null] ? nil : self.bundleRef;
|
||||
}
|
||||
@@ -316,6 +316,14 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
return [self.bundle bundlePath];
|
||||
}
|
||||
|
||||
- (void)setUseAncestorBundle:(BOOL)useAncestorBundle {
|
||||
if (self.useAncestorBundle != useAncestorBundle) {
|
||||
self.bundleRef = nil;
|
||||
self.infoDict = nil;
|
||||
}
|
||||
_useAncestorBundle = useAncestorBundle;
|
||||
}
|
||||
|
||||
- (NSDictionary *)infoPlist {
|
||||
if (!self.infoDict) {
|
||||
NSDictionary *d = [self embeddedPlist];
|
||||
@@ -638,7 +646,7 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
BOOL directory;
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&directory]) {
|
||||
return nil;
|
||||
} else if (directory) {
|
||||
} else if (directory && ![path isEqualToString:@"/"]) {
|
||||
NSBundle *bndl = [NSBundle bundleWithPath:path];
|
||||
if (bundle) *bundle = bndl;
|
||||
return [bndl executablePath];
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// Simple file watching class using dispatch sources. Will automatically
|
||||
/// reload the watch if the file is deleted and continue watching for
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
@interface SNTFileWatcher ()
|
||||
@property NSString *filePath;
|
||||
@property(strong) void (^handler)(unsigned long);
|
||||
@property(copy) void (^handler)(unsigned long);
|
||||
|
||||
@property dispatch_source_t source;
|
||||
@end
|
||||
@@ -52,7 +52,8 @@
|
||||
|
||||
dispatch_async(queue, ^{
|
||||
int fd = -1;
|
||||
while ((fd = open([self.filePath fileSystemRepresentation], O_EVTONLY | O_CLOEXEC)) < 0) {
|
||||
const char *filePath = [self.filePath fileSystemRepresentation];
|
||||
while ((fd = open(filePath, O_EVTONLY | O_CLOEXEC)) < 0) {
|
||||
usleep(200000); // wait 200ms
|
||||
}
|
||||
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, queue);
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#else // KERNEL
|
||||
|
||||
@import Foundation;
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
LOG_LEVEL_ERROR,
|
||||
LOG_LEVEL_WARN,
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
@@ -34,6 +36,28 @@
|
||||
///
|
||||
@property NSString *filePath;
|
||||
|
||||
///
|
||||
/// Set to YES if the event is a part of a bundle. When an event is passed to SantaGUI this propery
|
||||
/// will be used as an indicator to to kick off bundle hashing as necessary. Default value is NO.
|
||||
///
|
||||
@property BOOL needsBundleHash;
|
||||
|
||||
///
|
||||
/// If the executed file was part of a bundle, this is the calculated hash of all the nested
|
||||
/// executables within the bundle.
|
||||
///
|
||||
@property NSString *fileBundleHash;
|
||||
|
||||
///
|
||||
/// If the executed file was part of a bundle, this is the time in ms it took to hash the bundle.
|
||||
///
|
||||
@property NSNumber *fileBundleHashMilliseconds;
|
||||
|
||||
///
|
||||
/// If the executed file was part of a bundle, this is the total count of related mach-o binaries.
|
||||
///
|
||||
@property NSNumber *fileBundleBinaryCount;
|
||||
|
||||
///
|
||||
/// If the executed file was part of the bundle, this is the CFBundleDisplayName, if it exists
|
||||
/// or the CFBundleName if not.
|
||||
|
||||
@@ -33,6 +33,10 @@
|
||||
ENCODE(self.fileSHA256, @"fileSHA256");
|
||||
ENCODE(self.filePath, @"filePath");
|
||||
|
||||
ENCODE(@(self.needsBundleHash), @"needsBundleHash");
|
||||
ENCODE(self.fileBundleHash, @"fileBundleHash");
|
||||
ENCODE(self.fileBundleHashMilliseconds, @"fileBundleHashMilliseconds");
|
||||
ENCODE(self.fileBundleBinaryCount, @"fileBundleBinaryCount");
|
||||
ENCODE(self.fileBundleName, @"fileBundleName");
|
||||
ENCODE(self.fileBundlePath, @"fileBundlePath");
|
||||
ENCODE(self.fileBundleID, @"fileBundleID");
|
||||
@@ -72,6 +76,10 @@
|
||||
_fileSHA256 = DECODE(NSString, @"fileSHA256");
|
||||
_filePath = DECODE(NSString, @"filePath");
|
||||
|
||||
_needsBundleHash = [DECODE(NSNumber, @"needsBundleHash") boolValue];
|
||||
_fileBundleHash = DECODE(NSString, @"fileBundleHash");
|
||||
_fileBundleHashMilliseconds = DECODE(NSNumber, @"fileBundleHashMilliseconds");
|
||||
_fileBundleBinaryCount = DECODE(NSNumber, @"fileBundleBinaryCount");
|
||||
_fileBundleName = DECODE(NSString, @"fileBundleName");
|
||||
_fileBundlePath = DECODE(NSString, @"fileBundlePath");
|
||||
_fileBundleID = DECODE(NSString, @"fileBundleID");
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// Simple class for fetching system information
|
||||
///
|
||||
|
||||
56
Source/common/SNTXPCBundleServiceInterface.h
Normal file
56
Source/common/SNTXPCBundleServiceInterface.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/// Copyright 2017 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 Foundation;
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
/// A block that takes the calculated bundle hash, associated events and hashing time in ms.
|
||||
typedef void (^SNTBundleHashBlock)(NSString *, NSArray<SNTStoredEvent *> *, NSNumber *);
|
||||
|
||||
/// Protocol implemented by santabs and utilized by SantaGUI for bundle hashing
|
||||
@protocol SNTBundleServiceXPC
|
||||
|
||||
///
|
||||
/// @param listener The listener to connect back to the SantaGUI.
|
||||
///
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Hash a bundle for an event. The SNTBundleHashBlock will be called with nil parameters if a
|
||||
/// failure or cancellation occurs.
|
||||
///
|
||||
/// @param event The event that includes the fileBundlePath to be hashed.
|
||||
/// @param reply A SNTBundleHashBlock to be executed upon completion or cancellation.
|
||||
///
|
||||
/// @note If there is a current NSProgress when called this method will report back its progress.
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCBundleServiceInterface : NSObject
|
||||
|
||||
///
|
||||
/// Returns an initialized NSXPCInterface for the SNTBundleServiceXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning.
|
||||
///
|
||||
+ (NSXPCInterface *)bundleServiceInterface;
|
||||
|
||||
///
|
||||
/// Returns the MachService ID for this service.
|
||||
///
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
@end
|
||||
36
Source/common/SNTXPCBundleServiceInterface.m
Normal file
36
Source/common/SNTXPCBundleServiceInterface.m
Normal file
@@ -0,0 +1,36 @@
|
||||
/// Copyright 2017 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 "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCBundleServiceInterface
|
||||
|
||||
+ (NSXPCInterface *)bundleServiceInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTBundleServiceXPC)];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(hashBundleBinariesForEvent:reply:)
|
||||
argumentIndex:1
|
||||
ofReply:YES];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
+ (NSString *)serviceId {
|
||||
return @"com.google.santabs";
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
/**
|
||||
A wrapper around NSXPCListener and NSXPCConnection to provide client multiplexing, signature
|
||||
validation of connecting clients and forced connection establishment.
|
||||
@@ -63,13 +65,20 @@
|
||||
- (nullable instancetype)initServerWithName:(nonnull NSString *)name;
|
||||
|
||||
/**
|
||||
Initializer a new client to a service exported by a LaunchDaemon.
|
||||
Initialize a new client to a service exported by a LaunchDaemon.
|
||||
|
||||
@param name MachService name
|
||||
@param privileged Use YES if the server is running as root.
|
||||
*/
|
||||
- (nullable instancetype)initClientWithName:(nonnull NSString *)name privileged:(BOOL)privileged;
|
||||
|
||||
/**
|
||||
Initialize a new client to a service within a bundle.
|
||||
|
||||
@param name service name
|
||||
*/
|
||||
- (nullable instancetype)initClientWithServiceName:(nonnull NSString *)name;
|
||||
|
||||
/**
|
||||
Initialize a new client with a listener endpoint sent from another process.
|
||||
|
||||
|
||||
@@ -95,6 +95,17 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initClientWithServiceName:(NSString *)name {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_currentConnection = [[NSXPCConnection alloc] initWithServiceName:name];
|
||||
if (!_currentConnection) return nil;
|
||||
_validationInterface =
|
||||
[NSXPCInterface interfaceWithProtocol:@protocol(SNTXPCConnectionProtocol)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
@@ -113,8 +124,10 @@
|
||||
// send a message to the listener to finish establishing the connection
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
self.currentConnection.remoteObjectInterface = self.validationInterface;
|
||||
self.currentConnection.interruptionHandler = self.invalidationHandler;
|
||||
self.currentConnection.invalidationHandler = self.invalidationHandler;
|
||||
self.currentConnection.interruptionHandler = self.currentConnection.invalidationHandler = ^{
|
||||
STRONGIFY(self);
|
||||
if (self.invalidationHandler) self.invalidationHandler();
|
||||
};
|
||||
[self.currentConnection resume];
|
||||
[[self.currentConnection remoteObjectProxy] connectWithReply:^{
|
||||
STRONGIFY(self);
|
||||
|
||||
@@ -12,11 +12,14 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
|
||||
#import "SNTCachedDecision.h"
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
@@ -55,13 +58,13 @@
|
||||
/// @param filePath A Path to the file, can be nil.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param signingCertificate A MOLCertificate object, can be nil.
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
///
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
signingCertificate:(MOLCertificate *)signingCertificate
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply;
|
||||
|
||||
///
|
||||
@@ -73,23 +76,32 @@
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)())reply;
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)setRuleSyncLastSuccess:(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;
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply;
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)())reply;
|
||||
|
||||
///
|
||||
/// GUI Ops
|
||||
///
|
||||
- (void)setNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener;
|
||||
|
||||
///
|
||||
/// Syncd Ops
|
||||
///
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener;
|
||||
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply;
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message reply:(void (^)())reply;
|
||||
|
||||
///
|
||||
/// Bundle Ops
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event reply:(SNTBundleHashBlock)reply;
|
||||
- (void)syncBundleEvent:(SNTStoredEvent *)event relatedEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCControlInterface : NSObject
|
||||
|
||||
@@ -37,6 +37,16 @@
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(hashBundleBinariesForEvent:reply:)
|
||||
argumentIndex:1
|
||||
ofReply:YES];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(syncBundleEvent:relatedEvents:)
|
||||
argumentIndex:1
|
||||
ofReply:NO];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
|
||||
@@ -23,6 +26,14 @@
|
||||
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message;
|
||||
@end
|
||||
|
||||
/// Protocol implemented by SantaGUI and utilized by santabs
|
||||
@protocol SNTBundleNotifierXPC
|
||||
- (void)updateCountsForEvent:(SNTStoredEvent *)event
|
||||
binaryCount:(uint64_t)binaryCount
|
||||
fileCount:(uint64_t)fileCount;
|
||||
- (void)setBundleServiceListener:(NSXPCListenerEndpoint *)listener;
|
||||
@end
|
||||
|
||||
@interface SNTXPCNotifierInterface : NSObject
|
||||
|
||||
///
|
||||
@@ -31,4 +42,10 @@
|
||||
///
|
||||
+ (NSXPCInterface *)notifierInterface;
|
||||
|
||||
///
|
||||
/// @return an initialized NSXPCInterface for the SNTBundleNotifierXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning
|
||||
///
|
||||
+ (NSXPCInterface *)bundleNotifierInterface;
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,4 +20,8 @@
|
||||
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)bundleNotifierInterface {
|
||||
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTBundleNotifierXPC)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
@class SNTStoredEvent;
|
||||
@@ -19,7 +21,8 @@
|
||||
/// Protocol implemented by santactl and utilized by santad
|
||||
@protocol SNTSyncdXPC
|
||||
- (void)postEventToSyncServer:(SNTStoredEvent *)event;
|
||||
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds;
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event reply:(void (^)(BOOL))reply;
|
||||
- (void)postBundleEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events;
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply;
|
||||
@end
|
||||
|
||||
|
||||
@@ -14,10 +14,19 @@
|
||||
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCSyncdInterface
|
||||
|
||||
+ (NSXPCInterface *)syncdInterface {
|
||||
return [NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncdXPC)];
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTSyncdXPC)];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(postBundleEventsToSyncServer:)
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -247,7 +247,7 @@ template<class T> class SantaCache {
|
||||
/**
|
||||
Holder for a 'zero' entry for the current type
|
||||
*/
|
||||
T zero_ = {};
|
||||
const T zero_ = T();
|
||||
|
||||
/**
|
||||
Special bucket used when automatically clearing due to size
|
||||
|
||||
@@ -206,11 +206,13 @@ void SantaDecisionManager::AddToCache(
|
||||
decision_cache_->remove(identifier);
|
||||
}
|
||||
|
||||
if (unlikely(!identifier)) return;
|
||||
wakeup((void *)identifier);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::RemoveFromCache(uint64_t identifier) {
|
||||
decision_cache_->remove(identifier);
|
||||
if (unlikely(!identifier)) return;
|
||||
wakeup((void *)identifier);
|
||||
}
|
||||
|
||||
@@ -237,13 +239,8 @@ santa_action_t SantaDecisionManager::GetFromCache(uint64_t identifier) {
|
||||
|
||||
if (RESPONSE_VALID(result)) {
|
||||
if (result == ACTION_RESPOND_DENY) {
|
||||
auto diff_time = GetCurrentUptime();
|
||||
if ((kMaxDenyCacheTimeMilliseconds * 1000) > diff_time) {
|
||||
diff_time = 0;
|
||||
} else {
|
||||
diff_time -= (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
}
|
||||
if (decision_time < diff_time) {
|
||||
auto expiry_time = decision_time + (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
if (expiry_time < GetCurrentUptime()) {
|
||||
decision_cache_->remove(identifier);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
|
||||
31
Source/santabs/Resources/santabs-Info.plist
Normal file
31
Source/santabs/Resources/santabs-Info.plist
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>santabs</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>XPCService</key>
|
||||
<dict>
|
||||
<key>ServiceType</key>
|
||||
<string>Application</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
20
Source/santabs/SNTBundleService.h
Normal file
20
Source/santabs/SNTBundleService.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/// Copyright 2017 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 Foundation;
|
||||
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
|
||||
@interface SNTBundleService : NSObject<SNTBundleServiceXPC>
|
||||
@end
|
||||
313
Source/santabs/SNTBundleService.m
Normal file
313
Source/santabs/SNTBundleService.m
Normal file
@@ -0,0 +1,313 @@
|
||||
/// Copyright 2017 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 "SNTBundleService.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <pthread/pthread.h>
|
||||
|
||||
#import "MOLCertificate.h"
|
||||
#import "MOLCodesignChecker.h"
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
@interface SNTBundleService ()
|
||||
@property SNTXPCConnection *notifierConnection;
|
||||
@property SNTXPCConnection *listener;
|
||||
@end
|
||||
|
||||
@implementation SNTBundleService
|
||||
|
||||
#pragma mark Connection handling
|
||||
|
||||
// Create a listener for SantaGUI to connect
|
||||
- (void)createConnection {
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
|
||||
// Create listener for return connection from SantaGUI.
|
||||
NSXPCListener *listener = [NSXPCListener anonymousListener];
|
||||
self.listener = [[SNTXPCConnection alloc] initServerWithListener:listener];
|
||||
self.listener.exportedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
self.listener.exportedObject = self;
|
||||
self.listener.acceptedHandler = ^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
};
|
||||
|
||||
// Exit when SantaGUI is done with us.
|
||||
self.listener.invalidationHandler = ^{
|
||||
exit(0);
|
||||
};
|
||||
|
||||
[self.listener resume];
|
||||
|
||||
// Tell SantaGUI to connect back to the above listener.
|
||||
[[self.notifierConnection remoteObjectProxy] setBundleServiceListener:listener.endpoint];
|
||||
|
||||
// Now wait for the connection to come in.
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
|
||||
[self attemptReconnection];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)attemptReconnection {
|
||||
[self performSelectorInBackground:@selector(createConnection) withObject:nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark SNTBundleServiceXPC Methods
|
||||
|
||||
// Connect to the SantaGUI
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener {
|
||||
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
|
||||
c.remoteInterface = [SNTXPCNotifierInterface bundleNotifierInterface];
|
||||
[c resume];
|
||||
self.notifierConnection = c;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
[self createConnection];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
|
||||
reply:(SNTBundleHashBlock)reply {
|
||||
NSProgress *progress =
|
||||
[NSProgress currentProgress] ? [NSProgress progressWithTotalUnitCount:1] : nil;
|
||||
|
||||
NSDate *startTime = [NSDate date];
|
||||
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
// Use the highest bundle we can find. Save and reuse the bundle infomation when creating
|
||||
// the related binary events.
|
||||
SNTFileInfo *b = [[SNTFileInfo alloc] initWithPath:event.fileBundlePath];
|
||||
b.useAncestorBundle = YES;
|
||||
event.fileBundlePath = b.bundlePath;
|
||||
event.fileBundleID = b.bundleIdentifier;
|
||||
event.fileBundleName = b.bundleName;
|
||||
event.fileBundleVersion = b.bundleVersion;
|
||||
event.fileBundleVersionString = b.bundleShortVersionString;
|
||||
|
||||
NSArray *relatedBinaries = [self findRelatedBinaries:event progress:progress];
|
||||
NSString *bundleHash = [self calculateBundleHashFromEvents:relatedBinaries];
|
||||
NSNumber *ms = [NSNumber numberWithDouble:[startTime timeIntervalSinceNow] * -1000.0];
|
||||
if (bundleHash) LOGD(@"hashed %@ in %@ ms", event.fileBundlePath, ms);
|
||||
reply(bundleHash, relatedBinaries, ms);
|
||||
dispatch_semaphore_signal(sema);
|
||||
});
|
||||
|
||||
// Master timeout of 10 min. Don't block the calling thread. NSProgress updates will be coming
|
||||
// in over this thread.
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 600 * NSEC_PER_SEC))) {
|
||||
LOGD(@"hashBundleBinariesForEvent timeout");
|
||||
[progress cancel];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark Internal Methods
|
||||
|
||||
/**
|
||||
Find binaries within a bundle given the bundle's event. It will run until a timeout occurs,
|
||||
or until the NSProgress is cancelled. Search is done within the bundle concurrently.
|
||||
|
||||
@param event The SNTStoredEvent to begin searching underneath
|
||||
@return An array of SNTStoredEvent's
|
||||
|
||||
@note The first stage gathers a set of executables. 60 sec / max thread timeout.
|
||||
@note The second stage hashes the executables. 300 sec / max thread timeout.
|
||||
*/
|
||||
- (NSArray *)findRelatedBinaries:(SNTStoredEvent *)event progress:(NSProgress *)progress {
|
||||
// For storing the generated events, with a simple lock for writing.
|
||||
NSMutableArray *relatedEvents = [NSMutableArray array];
|
||||
|
||||
// For storing files to be hashed
|
||||
NSMutableSet<SNTFileInfo *> *fis = [NSMutableSet set];
|
||||
|
||||
// Limit the number of threads that can process files at once to keep CPU usage down.
|
||||
dispatch_semaphore_t sema =
|
||||
dispatch_semaphore_create([[NSProcessInfo processInfo] processorCount] / 2);
|
||||
|
||||
// Group the processing into a single group so we can wait on the whole group after each stage.
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
// Directory enumerator
|
||||
NSDirectoryEnumerator *dirEnum =
|
||||
[[NSFileManager defaultManager] enumeratorAtPath:event.fileBundlePath];
|
||||
|
||||
// Locks for accessing the enumerator and adding file and events between threads.
|
||||
__block pthread_mutex_t enumeratorMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
__block pthread_mutex_t eventsMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Counts used as additional progress information in SantaGUI
|
||||
__block uint64_t binaryCount = 0;
|
||||
__block uint64_t sentBinaryCount = 0;
|
||||
__block uint64_t fileCount = 0;
|
||||
|
||||
__block BOOL breakDir = NO;
|
||||
|
||||
// In the first stage iterate over every file in the tree checking if it is a binary. If so add
|
||||
// it to the fis set for the second stage. Hashing the file while iterating over the filesystem
|
||||
// causes performance issues. Do them separately.
|
||||
while (1) {
|
||||
@autoreleasepool {
|
||||
if (breakDir || progress.isCancelled) break;
|
||||
|
||||
// Wait for a processing thread to become available. At this stage we are only reading the
|
||||
// mach_header. If all processing threads are blocking for more than 60 sec bail.
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC))) {
|
||||
LOGD(@"isExecutable processing threads timeout");
|
||||
return nil;
|
||||
}
|
||||
|
||||
dispatch_group_async(group,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
pthread_mutex_lock(&enumeratorMutex);
|
||||
NSString *file = [dirEnum nextObject];
|
||||
fileCount++;
|
||||
pthread_mutex_unlock(&enumeratorMutex);
|
||||
|
||||
if (!file) {
|
||||
breakDir = YES;
|
||||
dispatch_semaphore_signal(sema);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([dirEnum fileAttributes][NSFileType] != NSFileTypeRegular) {
|
||||
dispatch_semaphore_signal(sema);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *newFile = [event.fileBundlePath stringByAppendingPathComponent:file];
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:newFile];
|
||||
if (!fi.isExecutable) {
|
||||
dispatch_semaphore_signal(sema);
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&eventsMutex);
|
||||
[fis addObject:fi];
|
||||
binaryCount++;
|
||||
pthread_mutex_unlock(&eventsMutex);
|
||||
|
||||
dispatch_semaphore_signal(sema);
|
||||
});
|
||||
if (progress && ((fileCount % 500) == 0 || binaryCount > sentBinaryCount)) {
|
||||
sentBinaryCount = binaryCount;
|
||||
[[self.notifierConnection remoteObjectProxy] updateCountsForEvent:event
|
||||
binaryCount:binaryCount
|
||||
fileCount:fileCount];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (progress.isCancelled) return nil;
|
||||
|
||||
// Wait for all the processing threads to finish
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
|
||||
NSProgress *p;
|
||||
if (progress) {
|
||||
[progress becomeCurrentWithPendingUnitCount:1];
|
||||
p = [NSProgress progressWithTotalUnitCount:fis.count];
|
||||
}
|
||||
|
||||
// In the second stage perform SHA256 hashing on all of the found binaries.
|
||||
for (SNTFileInfo *fi in fis) {
|
||||
@autoreleasepool {
|
||||
if (progress.isCancelled) break;
|
||||
|
||||
// Wait for a processing thread to become available. Here we are hashing the entire file.
|
||||
// If all processing threads are blocking for more than 5 min bail.
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 300 * NSEC_PER_SEC))) {
|
||||
LOGD(@"SHA256 processing threads timeout");
|
||||
return nil;
|
||||
}
|
||||
|
||||
dispatch_group_async(group,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
@autoreleasepool {
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.filePath = fi.path;
|
||||
se.fileSHA256 = fi.SHA256;
|
||||
se.occurrenceDate = [NSDate distantFuture];
|
||||
se.decision = SNTEventStateBundleBinary;
|
||||
|
||||
se.fileBundlePath = event.fileBundlePath;
|
||||
se.fileBundleID = event.fileBundleID;
|
||||
se.fileBundleName = event.fileBundleName;
|
||||
se.fileBundleVersion = event.fileBundleVersion;
|
||||
se.fileBundleVersionString = event.fileBundleVersionString;
|
||||
|
||||
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:se.filePath];
|
||||
se.signingChain = cs.certificates;
|
||||
|
||||
pthread_mutex_lock(&eventsMutex);
|
||||
[relatedEvents addObject:se];
|
||||
p.completedUnitCount++;
|
||||
pthread_mutex_unlock(&eventsMutex);
|
||||
|
||||
dispatch_semaphore_signal(sema);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all the processing threads to finish
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
|
||||
pthread_mutex_destroy(&enumeratorMutex);
|
||||
pthread_mutex_destroy(&eventsMutex);
|
||||
|
||||
return progress.isCancelled ? nil : relatedEvents;
|
||||
}
|
||||
|
||||
- (NSString *)calculateBundleHashFromEvents:(NSArray<SNTStoredEvent *> *)events {
|
||||
if (!events) return nil;
|
||||
NSMutableArray *eventSHA256Hashes = [NSMutableArray arrayWithCapacity:events.count];
|
||||
for (SNTStoredEvent *event in events) {
|
||||
if (!event.fileSHA256) return nil;
|
||||
[eventSHA256Hashes addObject:event.fileSHA256];
|
||||
}
|
||||
|
||||
[eventSHA256Hashes sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
|
||||
NSString *sha256Hashes = [eventSHA256Hashes componentsJoinedByString:@""];
|
||||
|
||||
CC_SHA256_CTX c256;
|
||||
CC_SHA256_Init(&c256);
|
||||
CC_SHA256_Update(&c256, (const void *)sha256Hashes.UTF8String, (CC_LONG)sha256Hashes.length);
|
||||
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
|
||||
CC_SHA256_Final(digest, &c256);
|
||||
|
||||
NSString *const SHA256FormatString =
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x";
|
||||
|
||||
NSString *sha256 = [[NSString alloc] initWithFormat:SHA256FormatString,
|
||||
digest[0], digest[1], digest[2], digest[3],
|
||||
digest[4], digest[5], digest[6], digest[7],
|
||||
digest[8], digest[9], digest[10], digest[11],
|
||||
digest[12], digest[13], digest[14], digest[15],
|
||||
digest[16], digest[17], digest[18], digest[19],
|
||||
digest[20], digest[21], digest[22], digest[23],
|
||||
digest[24], digest[25], digest[26], digest[27],
|
||||
digest[28], digest[29], digest[30], digest[31]];
|
||||
|
||||
return sha256;
|
||||
}
|
||||
|
||||
@end
|
||||
27
Source/santabs/main.m
Normal file
27
Source/santabs/main.m
Normal file
@@ -0,0 +1,27 @@
|
||||
/// Copyright 2017 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 Foundation;
|
||||
|
||||
#import "SNTBundleService.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
SNTXPCConnection *c =
|
||||
[[SNTXPCConnection alloc] initServerWithListener:[NSXPCListener serviceListener]];
|
||||
c.exportedInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
c.exportedObject = [[SNTBundleService alloc] init];
|
||||
[c resume];
|
||||
}
|
||||
79
Source/santactl/Commands/SNTCommandBundleInfo.m
Normal file
79
Source/santactl/Commands/SNTCommandBundleInfo.m
Normal file
@@ -0,0 +1,79 @@
|
||||
/// Copyright 2017 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTFileInfo.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandBundleInfo : NSObject<SNTCommand>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandBundleInfo
|
||||
|
||||
#ifdef DEBUG
|
||||
REGISTER_COMMAND_NAME(@"bundleinfo")
|
||||
#endif
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)requiresDaemonConn {
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Searches a bundle for binaries";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return @"Searches a bundle for binaries";
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
NSError *error;
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:arguments.firstObject error:&error];
|
||||
if (!fi) {
|
||||
printf("%s\n", error.description.UTF8String);
|
||||
exit(1);
|
||||
} else if (!fi.bundle) {
|
||||
printf("Not a bundle\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.fileBundlePath = fi.bundlePath;
|
||||
|
||||
[[daemonConn remoteObjectProxy] hashBundleBinariesForEvent:se
|
||||
reply:^(NSString *hash,
|
||||
NSArray<SNTStoredEvent *> *events,
|
||||
NSNumber *time) {
|
||||
|
||||
printf("Hashing time: %llu ms\n", time.unsignedLongLongValue);
|
||||
printf("%lu events found\n", events.count);
|
||||
printf("BundleHash: %s\n", hash.UTF8String);
|
||||
|
||||
for (SNTStoredEvent *event in events) {
|
||||
printf("BundleID: %s \n\tSHA-256: %s \n\tPath: %s\n",
|
||||
event.fileBundleID.UTF8String, event.fileSHA256.UTF8String, event.filePath.UTF8String);
|
||||
}
|
||||
exit(0);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import <MOLCertificate/MOLCertificate.h>
|
||||
@@ -279,7 +281,7 @@ REGISTER_COMMAND_NAME(@"fileinfo")
|
||||
}
|
||||
[[fi.daemonConn remoteObjectProxy] decisionForFilePath:fi.path(fi)
|
||||
fileSHA256:fi.propertyMap[kSHA256](fi)
|
||||
signingCertificate:fi.csc.leafCertificate
|
||||
certificateSHA256:fi.csc.leafCertificate.SHA256
|
||||
reply:^(SNTEventState state) {
|
||||
if (state) s = state;
|
||||
dispatch_semaphore_signal(sema);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#include "SNTLogging.h"
|
||||
@@ -42,7 +44,7 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Manually add/remove rules.";
|
||||
return @"Manually add/remove/check rules.";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
@@ -52,14 +54,17 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
@" --blacklist: add to blacklist\n"
|
||||
@" --silent-blacklist: add to silent blacklist\n"
|
||||
@" --remove: remove existing rule\n"
|
||||
@" --check: check for an 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"
|
||||
@" Does not work with --check. Use the fileinfo verb to check.\n"
|
||||
@" the rule state of a file.\n"
|
||||
@" --sha256 {sha256}: hash to add/remove/check\n"
|
||||
@"\n"
|
||||
@" Optionally:\n"
|
||||
@" --certificate: add certificate rule instead of binary\n"
|
||||
@" --certificate: add or check a certificate sha256 rule instead of binary\n"
|
||||
@" --message {message}: custom message\n");
|
||||
}
|
||||
|
||||
@@ -71,7 +76,7 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
if ([config syncBaseURL] != nil) {
|
||||
if ([config syncBaseURL] && ![arguments containsObject:@"--check"]) {
|
||||
printf("SyncBaseURL is set, rules are managed centrally.\n");
|
||||
exit(1);
|
||||
}
|
||||
@@ -81,6 +86,7 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
newRule.type = SNTRuleTypeBinary;
|
||||
|
||||
NSString *path;
|
||||
BOOL check = NO;
|
||||
|
||||
// Parse arguments
|
||||
for (NSUInteger i = 0; i < arguments.count; ++i) {
|
||||
@@ -94,6 +100,8 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
newRule.state = SNTRuleStateSilentBlacklist;
|
||||
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
|
||||
newRule.state = SNTRuleStateRemove;
|
||||
} else if ([arg caseInsensitiveCompare:@"--check"] == NSOrderedSame) {
|
||||
check = YES;
|
||||
} else if ([arg caseInsensitiveCompare:@"--certificate"] == NSOrderedSame) {
|
||||
newRule.type = SNTRuleTypeCertificate;
|
||||
} else if ([arg caseInsensitiveCompare:@"--path"] == NSOrderedSame) {
|
||||
@@ -119,6 +127,11 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
}
|
||||
}
|
||||
|
||||
if (check) {
|
||||
if (!newRule.shasum) return [self printErrorUsageAndExit:@"--check requires --sha256"];
|
||||
return [self printStateOfRule:newRule daemonConnection:daemonConn];
|
||||
}
|
||||
|
||||
if (path) {
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:path];
|
||||
if (!fi.path) {
|
||||
@@ -157,4 +170,58 @@ REGISTER_COMMAND_NAME(@"rule")
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)printStateOfRule:(SNTRule *)rule daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
NSString *fileSHA256 = (rule.type == SNTRuleTypeBinary) ? rule.shasum : nil;
|
||||
NSString *certificateSHA256 = (rule.type == SNTRuleTypeCertificate) ? rule.shasum : nil;
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter(group);
|
||||
__block NSMutableString *output;
|
||||
[[daemonConn remoteObjectProxy] decisionForFilePath:nil
|
||||
fileSHA256:fileSHA256
|
||||
certificateSHA256:certificateSHA256
|
||||
reply:^(SNTEventState s) {
|
||||
output = (SNTEventStateAllow & s) ? @"Whitelisted".mutableCopy : @"Blacklisted".mutableCopy;
|
||||
switch (s) {
|
||||
case SNTEventStateAllowUnknown:
|
||||
case SNTEventStateBlockUnknown:
|
||||
[output appendString:@" (Unknown)"];
|
||||
break;
|
||||
case SNTEventStateAllowBinary:
|
||||
case SNTEventStateBlockBinary:
|
||||
[output appendString:@" (Binary)"];
|
||||
break;
|
||||
case SNTEventStateAllowCertificate:
|
||||
case SNTEventStateBlockCertificate:
|
||||
[output appendString:@" (Certificate)"];
|
||||
break;
|
||||
case SNTEventStateAllowScope:
|
||||
case SNTEventStateBlockScope:
|
||||
[output appendString:@" (Scope)"];
|
||||
break;
|
||||
default:
|
||||
output = @"None".mutableCopy;
|
||||
break;
|
||||
}
|
||||
if (isatty(STDOUT_FILENO)) {
|
||||
if ((SNTEventStateAllow & s)) {
|
||||
[output insertString:@"\033[32m" atIndex:0];
|
||||
[output appendString:@"\033[0m"];
|
||||
} else if ((SNTEventStateBlock & s)) {
|
||||
[output insertString:@"\033[31m" atIndex:0];
|
||||
[output appendString:@"\033[0m"];
|
||||
} else {
|
||||
[output insertString:@"\033[33m" atIndex:0];
|
||||
[output appendString:@"\033[0m"];
|
||||
}
|
||||
}
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
|
||||
printf("Cannot communicate with daemon");
|
||||
exit(1);
|
||||
}
|
||||
printf("%s\n", output.UTF8String);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
@@ -102,15 +104,30 @@ REGISTER_COMMAND_NAME(@"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];
|
||||
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] fullSyncLastSuccess];
|
||||
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
|
||||
NSDate *lastRuleSyncSuccess = [[SNTConfigurator configurator] ruleSyncLastSuccess];
|
||||
NSString *lastRuleSyncSuccessStr =
|
||||
[dateFormatter stringFromDate:lastRuleSyncSuccess] ?: lastSyncSuccessStr;
|
||||
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
|
||||
__block BOOL pushNotifications;
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] pushNotifications:^(BOOL response) {
|
||||
pushNotifications = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block BOOL pushNotifications = NO;
|
||||
if ([[SNTConfigurator configurator] syncBaseURL]) {
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] pushNotifications:^(BOOL response) {
|
||||
pushNotifications = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
}
|
||||
|
||||
__block BOOL bundlesEnabled = NO;
|
||||
if ([[SNTConfigurator configurator] syncBaseURL]) {
|
||||
dispatch_group_enter(group);
|
||||
[[daemonConn remoteObjectProxy] bundlesEnabled:^(BOOL response) {
|
||||
bundlesEnabled = response;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
}
|
||||
|
||||
// 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))) {
|
||||
@@ -138,8 +155,10 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"sync" : @{
|
||||
@"server" : syncURLStr ?: @"null",
|
||||
@"clean_required" : @(syncCleanReqd),
|
||||
@"last_successful" : lastSyncSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"true" : @"false"
|
||||
@"last_successful_full" : lastSyncSuccessStr ?: @"null",
|
||||
@"last_successful_rule" : lastRuleSyncSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"Connected" : @"Disconnected",
|
||||
@"bundle_scanning" : @(bundlesEnabled)
|
||||
},
|
||||
};
|
||||
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
|
||||
@@ -149,23 +168,26 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
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 (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-22s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
|
||||
printf(" %-25s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
|
||||
printf(" %-25s | %lld (Peak: %.2f%%)\n", "Watchdog CPU Events", cpuEvents, cpuPeak);
|
||||
printf(" %-25s | %lld (Peak: %.2fMB)\n", "Watchdog RAM Events", ramEvents, ramPeak);
|
||||
printf(">>> Kernel Info\n");
|
||||
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
|
||||
printf(" %-25s | %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);
|
||||
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
|
||||
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
|
||||
printf(" %-25s | %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]);
|
||||
printf(" %-22s | %s\n", "Push Notifications", (pushNotifications ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Last Successful Full Sync", [lastSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Last Successful Rule Sync", [lastRuleSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Push Notifications",
|
||||
(pushNotifications ? "Connected" : "Disconnected"));
|
||||
printf(" %-25s | %s\n", "Bundle Scanning", (bundlesEnabled ? "Yes" : "No"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,11 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#include <IOKit/kext/KextManager.h>
|
||||
@import IOKit.kext;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTFileInfo.h"
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
/// Category on NSData providing the option of getting zlib or gzip compressed data.
|
||||
@interface NSData (Zlib)
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#import "SNTCommandSyncManager.h"
|
||||
@@ -59,6 +61,11 @@ REGISTER_COMMAND_NAME(@"sync")
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) {
|
||||
LOGE(@"Missing SyncBaseURL. Exiting.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SNTCommandSync *s = [[self alloc] init];
|
||||
[daemonConn resume];
|
||||
BOOL daemon = [arguments containsObject:@"--daemon"];
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
extern NSString *const kXSRFToken;
|
||||
|
||||
extern NSString *const kSerialNumber;
|
||||
@@ -32,6 +34,9 @@ extern NSString *const kBlacklistRegex;
|
||||
extern NSString *const kBinaryRuleCount;
|
||||
extern NSString *const kCertificateRuleCount;
|
||||
extern NSString *const kFCMToken;
|
||||
extern NSString *const kFCMFullSyncInterval;
|
||||
extern NSString *const kFCMGlobalRuleSyncDeadline;
|
||||
extern NSString *const kBundlesEnabled;
|
||||
|
||||
extern NSString *const kEvents;
|
||||
extern NSString *const kFileSHA256;
|
||||
@@ -57,6 +62,9 @@ extern NSString *const kFileBundlePath;
|
||||
extern NSString *const kFileBundleName;
|
||||
extern NSString *const kFileBundleVersion;
|
||||
extern NSString *const kFileBundleShortVersionString;
|
||||
extern NSString *const kFileBundleHash;
|
||||
extern NSString *const kFileBundleHashMilliseconds;
|
||||
extern NSString *const kFileBundleBinaryCount;
|
||||
extern NSString *const kPID;
|
||||
extern NSString *const kPPID;
|
||||
extern NSString *const kParentName;
|
||||
@@ -94,3 +102,17 @@ extern NSString *const kFullSync;
|
||||
extern NSString *const kRuleSync;
|
||||
extern NSString *const kConfigSync;
|
||||
extern NSString *const kLogSync;
|
||||
|
||||
extern const NSUInteger kDefaultEventBatchSize;
|
||||
|
||||
///
|
||||
/// kDefaultFullSyncInterval
|
||||
/// kDefaultFCMFullSyncInterval
|
||||
/// kDefaultFCMGlobalRuleSyncDeadline
|
||||
///
|
||||
/// Are represented in seconds
|
||||
///
|
||||
extern const NSUInteger kDefaultFullSyncInterval;
|
||||
extern const NSUInteger kDefaultFCMFullSyncInterval;
|
||||
extern const NSUInteger kDefaultFCMGlobalRuleSyncDeadline;
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ NSString *const kBlacklistRegex = @"blacklist_regex";
|
||||
NSString *const kBinaryRuleCount = @"binary_rule_count";
|
||||
NSString *const kCertificateRuleCount = @"certificate_rule_count";
|
||||
NSString *const kFCMToken = @"fcm_token";
|
||||
NSString *const kFCMFullSyncInterval = @"fcm_full_sync_interval";
|
||||
NSString *const kFCMGlobalRuleSyncDeadline = @"fcm_global_rule_sync_deadline";
|
||||
NSString *const kBundlesEnabled = @"bundles_enabled";
|
||||
|
||||
NSString *const kEvents = @"events";
|
||||
NSString *const kFileSHA256 = @"file_sha256";
|
||||
@@ -59,6 +62,9 @@ NSString *const kFileBundlePath = @"file_bundle_path";
|
||||
NSString *const kFileBundleName = @"file_bundle_name";
|
||||
NSString *const kFileBundleVersion = @"file_bundle_version";
|
||||
NSString *const kFileBundleShortVersionString = @"file_bundle_version_string";
|
||||
NSString *const kFileBundleHash = @"file_bundle_hash";
|
||||
NSString *const kFileBundleHashMilliseconds = @"file_bundle_hash_millis";
|
||||
NSString *const kFileBundleBinaryCount = @"file_bundle_binary_count";
|
||||
NSString *const kPID = @"pid";
|
||||
NSString *const kPPID = @"ppid";
|
||||
NSString *const kParentName = @"parent_name";
|
||||
@@ -96,3 +102,8 @@ NSString *const kFullSync = @"full_sync";
|
||||
NSString *const kRuleSync = @"rule_sync";
|
||||
NSString *const kConfigSync = @"config_sync";
|
||||
NSString *const kLogSync = @"log_sync";
|
||||
|
||||
const NSUInteger kDefaultEventBatchSize = 50;
|
||||
const NSUInteger kDefaultFullSyncInterval = 600;
|
||||
const NSUInteger kDefaultFCMFullSyncInterval = 14400;
|
||||
const NSUInteger kDefaultFCMGlobalRuleSyncDeadline = 600;
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
|
||||
@interface SNTCommandSyncEventUpload : SNTCommandSyncStage
|
||||
|
||||
- (BOOL)uploadEvents:(NSArray *)events;
|
||||
|
||||
- (BOOL)syncBundleEvents;
|
||||
|
||||
@end
|
||||
|
||||
@@ -44,28 +44,6 @@
|
||||
return (dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER) == 0);
|
||||
}
|
||||
|
||||
- (BOOL)syncBundleEvents {
|
||||
NSMutableArray *newEvents = [NSMutableArray array];
|
||||
for (NSString *bundlePath in [NSSet setWithArray:self.syncState.bundleBinaryRequests]) {
|
||||
__block NSArray *relatedBinaries;
|
||||
__block BOOL shouldCancel = NO;
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
|
||||
relatedBinaries = [self findRelatedBinaries:bundlePath shouldCancel:&shouldCancel];
|
||||
dispatch_semaphore_signal(sema);
|
||||
});
|
||||
|
||||
// Give the search up to 5m to run
|
||||
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 300))) {
|
||||
LOGD(@"Timed out while searching for related binaries at path %@", bundlePath);
|
||||
shouldCancel = YES;
|
||||
} else {
|
||||
[newEvents addObjectsFromArray:relatedBinaries];
|
||||
}
|
||||
}
|
||||
return [self uploadEvents:newEvents];
|
||||
}
|
||||
|
||||
- (BOOL)uploadEvents:(NSArray *)events {
|
||||
NSMutableArray *uploadEvents = [[NSMutableArray alloc] init];
|
||||
|
||||
@@ -79,7 +57,7 @@
|
||||
NSDictionary *r = [self performRequest:[self requestWithDictionary:@{ kEvents: uploadEvents }]];
|
||||
if (!r) return NO;
|
||||
|
||||
// Keep track of bundle search requests
|
||||
// A list of bundle hashes that require their related binary events to be uploaded.
|
||||
self.syncState.bundleBinaryRequests = r[kEventUploadBundleBinaries];
|
||||
|
||||
LOGI(@"Uploaded %lu events", uploadEvents.count);
|
||||
@@ -122,7 +100,10 @@
|
||||
ADDKEY(newEvent, kDecision, kDecisionBlockCertificate);
|
||||
break;
|
||||
case SNTEventStateBlockScope: ADDKEY(newEvent, kDecision, kDecisionBlockScope); break;
|
||||
case SNTEventStateBundleBinary: ADDKEY(newEvent, kDecision, kDecisionBundleBinary); break;
|
||||
case SNTEventStateBundleBinary:
|
||||
ADDKEY(newEvent, kDecision, kDecisionBundleBinary);
|
||||
[newEvent removeObjectForKey:kExecutionTime];
|
||||
break;
|
||||
default: ADDKEY(newEvent, kDecision, kDecisionUnknown);
|
||||
}
|
||||
|
||||
@@ -131,6 +112,9 @@
|
||||
ADDKEY(newEvent, kFileBundleName, event.fileBundleName);
|
||||
ADDKEY(newEvent, kFileBundleVersion, event.fileBundleVersion);
|
||||
ADDKEY(newEvent, kFileBundleShortVersionString, event.fileBundleVersionString);
|
||||
ADDKEY(newEvent, kFileBundleHash, event.fileBundleHash);
|
||||
ADDKEY(newEvent, kFileBundleHashMilliseconds, event.fileBundleHashMilliseconds);
|
||||
ADDKEY(newEvent, kFileBundleBinaryCount, event.fileBundleBinaryCount);
|
||||
|
||||
ADDKEY(newEvent, kPID, event.pid);
|
||||
ADDKEY(newEvent, kPPID, event.ppid);
|
||||
@@ -161,73 +145,4 @@
|
||||
#undef ADDKEY
|
||||
}
|
||||
|
||||
/**
|
||||
Find binaries within a bundle given the bundle's path. Will run until completion, however long
|
||||
that might be. Search is done within the bundle concurrently, using up to 25 threads at once.
|
||||
|
||||
@param path, the path to begin searching underneath
|
||||
@param shouldCancel, if YES, the search is cancelled part way through.
|
||||
@return array of SNTStoredEvent's
|
||||
*/
|
||||
- (NSArray *)findRelatedBinaries:(NSString *)path shouldCancel:(BOOL *)shouldCancel {
|
||||
// For storing the generated events, with a simple lock for writing.
|
||||
NSMutableArray *relatedEvents = [NSMutableArray array];
|
||||
NSLock *relatedEventsLock = [[NSLock alloc] init];
|
||||
|
||||
// Limit the number of threads that can process files at once to keep CPU usage down.
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(25);
|
||||
|
||||
// Group the processing into a single group so we can wait on the whole group at the end.
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
|
||||
while (1) {
|
||||
@autoreleasepool {
|
||||
if (*shouldCancel) break;
|
||||
NSString *file = [dirEnum nextObject];
|
||||
if (!file) break;
|
||||
if ([dirEnum fileAttributes][NSFileType] != NSFileTypeRegular) continue;
|
||||
|
||||
// Wait for a processing thread to become available
|
||||
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
|
||||
|
||||
dispatch_group_async(group,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),
|
||||
^{
|
||||
@autoreleasepool {
|
||||
NSString *newFile = [path stringByAppendingPathComponent:file];
|
||||
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:newFile];
|
||||
if (!fi.isExecutable) {
|
||||
dispatch_semaphore_signal(sema);
|
||||
return;
|
||||
}
|
||||
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.filePath = fi.path;
|
||||
se.fileSHA256 = fi.SHA256;
|
||||
se.decision = SNTEventStateBundleBinary;
|
||||
se.fileBundleID = fi.bundleIdentifier;
|
||||
se.fileBundleName = fi.bundleName;
|
||||
se.fileBundlePath = fi.bundlePath;
|
||||
se.fileBundleVersion = fi.bundleVersion;
|
||||
se.fileBundleVersionString = fi.bundleShortVersionString;
|
||||
|
||||
MOLCodesignChecker *cs = [[MOLCodesignChecker alloc] initWithBinaryPath:se.filePath];
|
||||
se.signingChain = cs.certificates;
|
||||
|
||||
[relatedEventsLock lock];
|
||||
[relatedEvents addObject:se];
|
||||
[relatedEventsLock unlock];
|
||||
|
||||
dispatch_semaphore_signal(sema);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
|
||||
return relatedEvents;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
|
||||
@interface SNTCommandSyncLogUpload : SNTCommandSyncStage
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#import "SNTCommandSyncManager.h"
|
||||
|
||||
@import SystemConfiguration;
|
||||
|
||||
#import <MOLAuthenticatingURLSession.h>
|
||||
#import <MOLFCMClient/MOLFCMClient.h>
|
||||
|
||||
@@ -26,26 +28,50 @@
|
||||
#import "SNTCommandSyncRuleDownload.h"
|
||||
#import "SNTCommandSyncState.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
|
||||
// Syncing time constants
|
||||
const uint64_t kFullSyncInterval = 600;
|
||||
const uint64_t kFullSyncFCMInterval = 14400;
|
||||
const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
@interface SNTCommandSyncManager () {
|
||||
SCNetworkReachabilityRef _reachability;
|
||||
}
|
||||
|
||||
@interface SNTCommandSyncManager ()
|
||||
@property(nonatomic) dispatch_source_t fullSyncTimer;
|
||||
@property(nonatomic) dispatch_source_t ruleSyncTimer;
|
||||
|
||||
@property(nonatomic) NSCache *dispatchLock;
|
||||
@property(nonatomic) NSCache *ruleSyncCache;
|
||||
|
||||
@property NSUInteger FCMFullSyncInterval;
|
||||
@property NSUInteger FCMGlobalRuleSyncDeadline;
|
||||
@property NSUInteger eventBatchSize;
|
||||
|
||||
@property MOLFCMClient *FCMClient;
|
||||
|
||||
@property(nonatomic) SNTXPCConnection *daemonConn;
|
||||
|
||||
@property BOOL targetedRuleSync;
|
||||
|
||||
@property(nonatomic) BOOL reachable;
|
||||
|
||||
@end
|
||||
|
||||
// Called when the network state changes
|
||||
static void reachabilityHandler(
|
||||
SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) {
|
||||
// Put this check and set on the main thread to ensure serial access.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
SNTCommandSyncManager *commandSyncManager = (__bridge SNTCommandSyncManager *)info;
|
||||
// Only call the setter when there is a change. This will filter out the redundant calls to this
|
||||
// callback whenever the network interface states change.
|
||||
if (commandSyncManager.reachable != (flags & kSCNetworkReachabilityFlagsReachable)) {
|
||||
commandSyncManager.reachable = (flags & kSCNetworkReachabilityFlagsReachable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@implementation SNTCommandSyncManager
|
||||
|
||||
#pragma mark init
|
||||
@@ -56,7 +82,7 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
_daemonConn = daemonConn;
|
||||
_daemon = daemon;
|
||||
_fullSyncTimer = [self createSyncTimerWithBlock:^{
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncFCMInterval];
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:self.FCMFullSyncInterval];
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) return;
|
||||
[self lockAction:kFullSync];
|
||||
[self preflight];
|
||||
@@ -81,12 +107,44 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
}];
|
||||
_dispatchLock = [[NSCache alloc] init];
|
||||
_ruleSyncCache = [[NSCache alloc] init];
|
||||
|
||||
_eventBatchSize = kDefaultEventBatchSize;
|
||||
_FCMFullSyncInterval = kDefaultFCMFullSyncInterval;
|
||||
_FCMGlobalRuleSyncDeadline = kDefaultFCMGlobalRuleSyncDeadline;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark SNTSyncdXPC protocol methods
|
||||
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event reply:(void (^)(BOOL))reply {
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if (event && [p uploadEvents:@[event]]) {
|
||||
BOOL needsRelatedEvents = [syncState.bundleBinaryRequests containsObject:event.fileBundleHash];
|
||||
reply(needsRelatedEvents);
|
||||
if (needsRelatedEvents) {
|
||||
LOGD(@"Needs related events");
|
||||
} else {
|
||||
LOGD(@"Bundle event upload complete");
|
||||
}
|
||||
} else {
|
||||
reply(NO);
|
||||
LOGE(@"Bundle event upload failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)postBundleEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events {
|
||||
SNTCommandSyncState *syncState = [self createSyncState];
|
||||
syncState.eventBatchSize = self.eventBatchSize;
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if (events && [p uploadEvents:events]) {
|
||||
LOGD(@"Bundle events upload complete");
|
||||
} else {
|
||||
LOGE(@"Bundle events upload failed");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)postEventToSyncServer:(SNTStoredEvent *)event {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc]
|
||||
initWithState:[self createSyncState]];
|
||||
@@ -97,10 +155,6 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)rescheduleSyncSecondsFromNow:(uint64_t)seconds {
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:seconds];
|
||||
}
|
||||
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply {
|
||||
reply((self.FCMClient.FCMToken != nil));
|
||||
}
|
||||
@@ -134,7 +188,7 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
LOGE(@"FCM connection error: %@", error);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncInterval];
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kDefaultFullSyncInterval];
|
||||
};
|
||||
|
||||
self.FCMClient.loggingBlock = ^(NSString *log) {
|
||||
@@ -145,24 +199,16 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
}
|
||||
|
||||
- (void)processFCMMessage:(NSDictionary *)FCMmessage withMachineID:(NSString *)machineID {
|
||||
NSData *entryData;
|
||||
NSData *messageData = [self extractMessageDataFrom:FCMmessage];
|
||||
|
||||
// Sort through the entries in the FCM message.
|
||||
for (NSDictionary *entry in FCMmessage[@"data"]) {
|
||||
if ([entry[@"key"] isEqualToString:@"blob"]) {
|
||||
entryData = [entry[@"value"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryData) {
|
||||
if (!messageData) {
|
||||
LOGD(@"Push notification message is not in the expected format...dropping message");
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *actionMessage = [NSJSONSerialization JSONObjectWithData:entryData
|
||||
options:NSJSONReadingAllowFragments
|
||||
NSDictionary *actionMessage = [NSJSONSerialization JSONObjectWithData:messageData
|
||||
options:0
|
||||
error:&error];
|
||||
if (!actionMessage) {
|
||||
LOGD(@"Unable to parse push notification message value: %@", error);
|
||||
@@ -193,8 +239,8 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
self.targetedRuleSync = YES;
|
||||
[self ruleSync];
|
||||
} else {
|
||||
uint32_t delaySeconds = arc4random_uniform(kGlobalRuleSyncLeeway);
|
||||
LOGD(@"Staggering rule download, %u second delay for this machine", delaySeconds);
|
||||
uint32_t delaySeconds = arc4random_uniform((uint32_t)self.FCMGlobalRuleSyncDeadline);
|
||||
LOGD(@"Staggering rule download: %u second delay", delaySeconds);
|
||||
[self ruleSyncSecondsFromNow:delaySeconds];
|
||||
}
|
||||
} else if ([action isEqualToString:kConfigSync]) {
|
||||
@@ -206,6 +252,12 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)extractMessageDataFrom:(NSDictionary *)FCMmessage {
|
||||
if (![FCMmessage[@"data"] isKindOfClass:[NSDictionary class]]) return nil;
|
||||
if (![FCMmessage[@"data"][@"blob"] isKindOfClass:[NSString class]]) return nil;
|
||||
return [FCMmessage[@"data"][@"blob"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
#pragma mark sync timer control
|
||||
|
||||
- (void)fullSync {
|
||||
@@ -246,15 +298,21 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
if ([p sync]) {
|
||||
LOGD(@"Preflight complete");
|
||||
|
||||
// Start listening for push notifications with a full sync every kFullSyncFCMInterval or
|
||||
// revert to full syncing every kFullSyncInterval.
|
||||
// Clean up reachability if it was started for a non-network error
|
||||
[self stopReachability];
|
||||
|
||||
self.eventBatchSize = syncState.eventBatchSize;
|
||||
|
||||
// Start listening for push notifications with a full sync every FCMFullSyncInterval
|
||||
if (syncState.daemon && syncState.FCMToken) {
|
||||
self.FCMFullSyncInterval = syncState.FCMFullSyncInterval;
|
||||
self.FCMGlobalRuleSyncDeadline = syncState.FCMGlobalRuleSyncDeadline;
|
||||
[self listenForPushNotificationsWithSyncState:syncState];
|
||||
} else if (syncState.daemon) {
|
||||
LOGD(@"FCMToken not provided. Sync every %llu min.", kFullSyncInterval / 60);
|
||||
LOGD(@"FCMToken not provided. Sync every %lu min.", kDefaultFullSyncInterval / 60);
|
||||
[self.FCMClient disconnect];
|
||||
self.FCMClient = nil;
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kFullSyncInterval];
|
||||
[self rescheduleTimerQueue:self.fullSyncTimer secondsFromNow:kDefaultFullSyncInterval];
|
||||
}
|
||||
|
||||
if (syncState.uploadLogURL) {
|
||||
@@ -263,8 +321,13 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
}
|
||||
} else {
|
||||
LOGE(@"Preflight failed, aborting run");
|
||||
if (!syncState.daemon) exit(1);
|
||||
if (!syncState.daemon) {
|
||||
LOGE(@"Preflight failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
LOGE(@"Preflight failed, will try again once %@ is reachable",
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
[self startReachability];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,9 +356,6 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
SNTCommandSyncRuleDownload *p = [[SNTCommandSyncRuleDownload alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
LOGD(@"Rule download complete");
|
||||
if (syncState.bundleBinaryRequests.count) {
|
||||
return [self eventUploadBundleBinariesWithSyncState:syncState];
|
||||
}
|
||||
return [self postflightWithSyncState:syncState];
|
||||
} else {
|
||||
LOGE(@"Rule download failed, aborting run");
|
||||
@@ -303,16 +363,6 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)eventUploadBundleBinariesWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncEventUpload *p = [[SNTCommandSyncEventUpload alloc] initWithState:syncState];
|
||||
if ([p syncBundleEvents]) {
|
||||
LOGD(@"Event upload for bundle binaries complete");
|
||||
} else {
|
||||
LOGW(@"Event upload for bundle binary search failed");
|
||||
}
|
||||
return [self postflightWithSyncState:syncState];
|
||||
}
|
||||
|
||||
- (void)postflightWithSyncState:(SNTCommandSyncState *)syncState {
|
||||
SNTCommandSyncPostflight *p = [[SNTCommandSyncPostflight alloc] initWithState:syncState];
|
||||
if ([p sync]) {
|
||||
@@ -361,8 +411,11 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
LOGW(@"Missing Machine Owner.");
|
||||
}
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] xsrfToken:^(NSString *token) {
|
||||
syncState.xsrfToken = token;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
MOLAuthenticatingURLSession *authURLSession = [[MOLAuthenticatingURLSession alloc] init];
|
||||
@@ -397,7 +450,8 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
syncState.session = [authURLSession session];
|
||||
syncState.daemonConn = self.daemonConn;
|
||||
syncState.daemon = self.daemon;
|
||||
|
||||
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
return syncState;
|
||||
}
|
||||
|
||||
@@ -413,4 +467,39 @@ const uint64_t kGlobalRuleSyncLeeway = 600;
|
||||
return ([self.dispatchLock objectForKey:action] == nil);
|
||||
}
|
||||
|
||||
#pragma mark reachability methods
|
||||
|
||||
- (void)setReachable:(BOOL)reachable {
|
||||
_reachable = reachable;
|
||||
if (reachable) {
|
||||
[self stopReachability];
|
||||
[self fullSync];
|
||||
}
|
||||
}
|
||||
|
||||
// Start listening for network state changes on a background thread
|
||||
- (void)startReachability {
|
||||
if (_reachability) return;
|
||||
const char *nodename = [[SNTConfigurator configurator] syncBaseURL].host.UTF8String;
|
||||
_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, nodename);
|
||||
SCNetworkReachabilityContext context = {
|
||||
.info = (__bridge void *)self
|
||||
};
|
||||
if (SCNetworkReachabilitySetCallback(_reachability, reachabilityHandler, &context)) {
|
||||
SCNetworkReachabilitySetDispatchQueue(
|
||||
_reachability, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
} else {
|
||||
[self stopReachability];
|
||||
}
|
||||
}
|
||||
|
||||
// Stop listening for network state changes
|
||||
- (void)stopReachability {
|
||||
if (_reachability) {
|
||||
SCNetworkReachabilitySetDispatchQueue(_reachability, NULL);
|
||||
if (_reachability) CFRelease(_reachability);
|
||||
_reachability = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
|
||||
@interface SNTCommandSyncPostflight : SNTCommandSyncStage
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
}
|
||||
|
||||
- (BOOL)sync {
|
||||
NSDictionary *r = [self performRequest:[self requestWithDictionary:nil]];
|
||||
[self performRequest:[self requestWithDictionary:nil]];
|
||||
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
void (^replyBlock)() = ^{
|
||||
@@ -43,14 +43,6 @@
|
||||
reply:replyBlock];
|
||||
}
|
||||
|
||||
// Update backoff interval
|
||||
NSString *backoffInterval = r[kBackoffInterval];
|
||||
if (backoffInterval) {
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setNextSyncInterval:[backoffInterval intValue]
|
||||
reply:replyBlock];
|
||||
}
|
||||
|
||||
// Remove clean sync flag if we did a clean sync
|
||||
if (self.syncState.cleanSync) {
|
||||
dispatch_group_enter(group);
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
|
||||
@interface SNTCommandSyncPreflight : SNTCommandSyncStage
|
||||
|
||||
@@ -74,9 +74,21 @@
|
||||
|
||||
if (!resp) return NO;
|
||||
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setBundlesEnabled:[resp[kBundlesEnabled] boolValue] reply:^{
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] unsignedIntegerValue] ?: kDefaultEventBatchSize;
|
||||
self.syncState.FCMToken = resp[kFCMToken];
|
||||
|
||||
self.syncState.eventBatchSize = [resp[kBatchSize] intValue] ?: 50;
|
||||
// Don't let these go too low
|
||||
NSUInteger value = [resp[kFCMFullSyncInterval] unsignedIntegerValue];
|
||||
self.syncState.FCMFullSyncInterval =
|
||||
(value < kDefaultFullSyncInterval) ? kDefaultFCMFullSyncInterval : value;
|
||||
value = [resp[kFCMGlobalRuleSyncDeadline] unsignedIntegerValue];
|
||||
self.syncState.FCMGlobalRuleSyncDeadline =
|
||||
(value < 60) ? kDefaultFCMGlobalRuleSyncDeadline : value;
|
||||
|
||||
self.syncState.uploadLogURL = [NSURL URLWithString:resp[kUploadLogsURL]];
|
||||
|
||||
@@ -99,6 +111,7 @@
|
||||
self.syncState.cleanSync = YES;
|
||||
}
|
||||
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandSyncStage.h"
|
||||
|
||||
@interface SNTCommandSyncRuleDownload : SNTCommandSyncStage
|
||||
|
||||
@@ -67,18 +67,24 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
sema = dispatch_semaphore_create(0);
|
||||
[[self.daemonConn remoteObjectProxy] setRuleSyncLastSuccess:[NSDate date] reply:^{
|
||||
dispatch_semaphore_signal(sema);
|
||||
}];
|
||||
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
|
||||
LOGI(@"Added %lu rules", self.syncState.downloadedRules.count);
|
||||
|
||||
if (self.syncState.targetedRuleSync) {
|
||||
NSString *fileName;
|
||||
for (SNTRule *r in self.syncState.downloadedRules) {
|
||||
fileName = [[self.syncState.ruleSyncCache objectForKey:r.shasum] copy];
|
||||
NSString *fileName = [[self.syncState.ruleSyncCache objectForKey:r.shasum] copy];
|
||||
[self.syncState.ruleSyncCache removeObjectForKey:r.shasum];
|
||||
if (fileName) break;
|
||||
if (fileName) {
|
||||
NSString *message = [NSString stringWithFormat:@"%@ can now be run", fileName];
|
||||
[[self.daemonConn remoteObjectProxy]
|
||||
postRuleSyncNotificationWithCustomMessage:message reply:^{}];
|
||||
}
|
||||
}
|
||||
NSString *message = fileName ? [NSString stringWithFormat:@"%@ can now be run", fileName] : nil;
|
||||
[[self.daemonConn remoteObjectProxy]
|
||||
postRuleSyncNotificationWithCustomMessage:message reply:^{}];
|
||||
}
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@class SNTCommandSyncState;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
@class SNTCommandSyncManager;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
/// An instance of this class is passed to each stage of the sync process for storing data
|
||||
@@ -32,9 +35,15 @@
|
||||
/// An XSRF token to send in the headers with each request.
|
||||
@property NSString *xsrfToken;
|
||||
|
||||
/// A FCM token to subscribe to push notifications.
|
||||
/// An FCM token to subscribe to push notifications.
|
||||
@property(copy) NSString *FCMToken;
|
||||
|
||||
/// Full sync interval in seconds while listening for FCM messages.
|
||||
@property NSUInteger FCMFullSyncInterval;
|
||||
|
||||
/// Leeway time in seconds when receiving a global rule sync message.
|
||||
@property NSUInteger FCMGlobalRuleSyncDeadline;
|
||||
|
||||
/// Machine identifier and owner.
|
||||
@property(copy) NSString *machineID;
|
||||
@property(copy) NSString *machineOwner;
|
||||
@@ -53,7 +62,7 @@
|
||||
/// Log upload URL sent from server. If set, LogUpload phase needs to happen.
|
||||
@property NSURL *uploadLogURL;
|
||||
|
||||
/// Array of bundle paths to find binaries for.
|
||||
/// Array of bundle IDs to find binaries for.
|
||||
@property NSArray *bundleBinaryRequests;
|
||||
|
||||
/// Rules downloaded from server.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@class SNTXPCConnection;
|
||||
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
// 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>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTDatabaseTable.h"
|
||||
|
||||
@class SNTNotificationMessage;
|
||||
@@ -30,6 +32,14 @@
|
||||
///
|
||||
- (BOOL)addStoredEvent:(SNTStoredEvent *)event;
|
||||
|
||||
///
|
||||
/// Add events to the database.
|
||||
///
|
||||
/// @param events the events to store.
|
||||
/// @return YES if events were successfully stored.
|
||||
///
|
||||
- (BOOL)addStoredEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
|
||||
///
|
||||
/// Retrieves all events in the database
|
||||
///
|
||||
|
||||
@@ -83,24 +83,37 @@
|
||||
#pragma mark Loading / Storing
|
||||
|
||||
- (BOOL)addStoredEvent:(SNTStoredEvent *)event {
|
||||
if (!event.idx ||
|
||||
!event.fileSHA256 ||
|
||||
!event.filePath ||
|
||||
!event.occurrenceDate ||
|
||||
!event.decision) return NO;
|
||||
return [self addStoredEvents:@[event]];
|
||||
}
|
||||
|
||||
NSData *eventData;
|
||||
@try {
|
||||
eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
|
||||
} @catch (NSException *exception) {
|
||||
return NO;
|
||||
- (BOOL)addStoredEvents:(NSArray<SNTStoredEvent *> *)events {
|
||||
NSMutableDictionary *eventsData = [NSMutableDictionary dictionaryWithCapacity:events.count];
|
||||
for (SNTStoredEvent *event in events) {
|
||||
if (!event.idx ||
|
||||
!event.fileSHA256 ||
|
||||
!event.filePath ||
|
||||
!event.occurrenceDate ||
|
||||
!event.decision) continue;
|
||||
|
||||
NSData *eventData;
|
||||
@try {
|
||||
eventData = [NSKeyedArchiver archivedDataWithRootObject:event];
|
||||
} @catch (NSException *exception) {
|
||||
continue;
|
||||
}
|
||||
eventsData[eventData] = event;
|
||||
}
|
||||
|
||||
__block BOOL success = NO;
|
||||
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
||||
success = [db executeUpdate:@"INSERT INTO 'events' (idx, filesha256, eventdata)"
|
||||
@"VALUES (?, ?, ?)",
|
||||
event.idx, event.fileSHA256, eventData];
|
||||
[eventsData enumerateKeysAndObjectsUsingBlock:^(NSData * eventData,
|
||||
SNTStoredEvent * event,
|
||||
BOOL *stop) {
|
||||
success = [db executeUpdate:@"INSERT INTO 'events' (idx, filesha256, eventdata)"
|
||||
@"VALUES (?, ?, ?)",
|
||||
event.idx, event.fileSHA256, eventData];
|
||||
if (!success) *stop = YES;
|
||||
}];
|
||||
}];
|
||||
|
||||
return success;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTDatabaseTable.h"
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
///
|
||||
/// The main controller class for santad
|
||||
///
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
#import "SNTApplication.h"
|
||||
|
||||
#import <DiskArbitration/DiskArbitration.h>
|
||||
@import DiskArbitration;
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@class SNTDriverManager;
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
#import "SNTPolicyProcessor.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTSyncdQueue.h"
|
||||
#import "SNTXPCBundleServiceInterface.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
#import "SNTXPCSyncdInterface.h"
|
||||
@@ -112,15 +115,19 @@ double watchdogRAMPeak = 0;
|
||||
|
||||
- (void)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
signingCertificate:(MOLCertificate *)signingCertificate
|
||||
certificateSHA256:(NSString *)certificateSHA256
|
||||
reply:(void (^)(SNTEventState))reply {
|
||||
reply([self.policyProcessor decisionForFilePath:filePath
|
||||
fileSHA256:fileSHA256
|
||||
signingCertificate:signingCertificate].decision);
|
||||
certificateSHA256:certificateSHA256].decision);
|
||||
}
|
||||
|
||||
#pragma mark Config Ops
|
||||
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply {
|
||||
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
|
||||
}
|
||||
|
||||
- (void)clientMode:(void (^)(SNTClientMode))reply {
|
||||
reply([[SNTConfigurator configurator] clientMode]);
|
||||
}
|
||||
@@ -143,7 +150,12 @@ double watchdogRAMPeak = 0;
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setSyncLastSuccess:date];
|
||||
[[SNTConfigurator configurator] setFullSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setRuleSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -157,6 +169,8 @@ double watchdogRAMPeak = 0;
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setWhitelistPathRegex:re];
|
||||
LOGI(@"Received new whitelist regex, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -165,11 +179,18 @@ double watchdogRAMPeak = 0;
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
|
||||
LOGI(@"Received new blacklist regex, flushing cache");
|
||||
[self.driverManager flushCache];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)watchdogInfo:(void (^)(uint64_t, uint64_t, double, double))reply {
|
||||
reply(watchdogCPUEvents, watchdogRAMEvents, watchdogCPUPeak, watchdogRAMPeak);
|
||||
- (void)bundlesEnabled:(void (^)(BOOL))reply {
|
||||
reply([SNTConfigurator configurator].bundlesEnabled);
|
||||
}
|
||||
|
||||
- (void)setBundlesEnabled:(BOOL)bundlesEnabled reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setBundlesEnabled:bundlesEnabled];
|
||||
reply();
|
||||
}
|
||||
|
||||
#pragma mark GUI Ops
|
||||
@@ -181,13 +202,23 @@ double watchdogRAMPeak = 0;
|
||||
self.notQueue.notifierConnection = c;
|
||||
}
|
||||
|
||||
- (void)setBundleNotificationListener:(NSXPCListenerEndpoint *)listener {
|
||||
SNTXPCConnection *bs = [[SNTXPCConnection alloc] initClientWithServiceName:@"com.google.santabs"];
|
||||
bs.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
[bs resume];
|
||||
[[bs remoteObjectProxy] setBundleNotificationListener:listener];
|
||||
}
|
||||
|
||||
#pragma mark syncd Ops
|
||||
|
||||
- (void)setSyncdListener:(NSXPCListenerEndpoint *)listener {
|
||||
// Only allow one active syncd connection
|
||||
if (self.syncdQueue.syncdConnection) return;
|
||||
SNTXPCConnection *c = [[SNTXPCConnection alloc] initClientWithListener:listener];
|
||||
c.remoteInterface = [SNTXPCSyncdInterface syncdInterface];
|
||||
c.invalidationHandler = ^{
|
||||
[self.syncdQueue stopSyncingEvents];
|
||||
self.syncdQueue.syncdConnection = nil;
|
||||
self.syncdQueue.invalidationHandler();
|
||||
};
|
||||
c.acceptedHandler = ^{
|
||||
@@ -197,12 +228,6 @@ double watchdogRAMPeak = 0;
|
||||
self.syncdQueue.syncdConnection = c;
|
||||
}
|
||||
|
||||
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply {
|
||||
[[self.syncdQueue.syncdConnection remoteObjectProxy] rescheduleSyncSecondsFromNow:seconds];
|
||||
[[SNTConfigurator configurator] setSyncBackOff:YES];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)pushNotifications:(void (^)(BOOL))reply {
|
||||
[self.syncdQueue.syncdConnection.remoteObjectProxy isFCMListening:^(BOOL response) {
|
||||
reply(response);
|
||||
@@ -215,4 +240,57 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
#pragma mark Bundle ops
|
||||
|
||||
///
|
||||
/// This method is only used for santactl's bundleinfo command. For blocked executions, SantaGUI
|
||||
/// calls on santabs directly.
|
||||
///
|
||||
/// Hash a bundle for an event. The SNTBundleHashBlock will be called with nil parameters if a
|
||||
/// failure or cancellation occurs.
|
||||
///
|
||||
/// @param event The event that includes the fileBundlePath to be hashed.
|
||||
/// @param reply A SNTBundleHashBlock to be executed upon completion or cancellation.
|
||||
///
|
||||
/// @note If there is a current NSProgress when called this method will report back it's progress.
|
||||
///
|
||||
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
|
||||
reply:(SNTBundleHashBlock)reply {
|
||||
SNTXPCConnection *bs =
|
||||
[[SNTXPCConnection alloc] initClientWithServiceName:[SNTXPCBundleServiceInterface serviceId]];
|
||||
bs.remoteInterface = [SNTXPCBundleServiceInterface bundleServiceInterface];
|
||||
[bs resume];
|
||||
[[bs remoteObjectProxy] hashBundleBinariesForEvent:event reply:reply];
|
||||
}
|
||||
|
||||
///
|
||||
/// Used by SantaGUI sync the offending event and potentially all the related events,
|
||||
/// if the sync server has not seen them before.
|
||||
///
|
||||
/// @param event The offending event, fileBundleHash & fileBundleBinaryCount need to be populated.
|
||||
/// @param relatedEvents Nexted bundle events.
|
||||
///
|
||||
- (void)syncBundleEvent:(SNTStoredEvent *)event
|
||||
relatedEvents:(NSArray<SNTStoredEvent *> *)events {
|
||||
SNTEventTable *eventTable = [SNTDatabaseController eventTable];
|
||||
|
||||
// Delete the event cached by the execution controller.
|
||||
[eventTable deleteEventWithId:event.idx];
|
||||
|
||||
// Add the updated event.
|
||||
[eventTable addStoredEvent:event];
|
||||
|
||||
WEAKIFY(self);
|
||||
|
||||
// Sync the updated event. If the sync server needs the related events, add them to the eventTable
|
||||
// and upload them too.
|
||||
[self.syncdQueue addBundleEvent:event reply:^(BOOL needRelatedEvents) {
|
||||
STRONGIFY(self);
|
||||
if (needRelatedEvents) {
|
||||
[eventTable addStoredEvents:events];
|
||||
[self.syncdQueue addBundleEvents:events];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
// This is imported in the header rather than implementation to saves
|
||||
// classes that use this one from also having to import FMDB stuff.
|
||||
#import <FMDB/FMDB.h>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
@class SNTNotificationMessage;
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
#import "SNTDriverManager.h"
|
||||
|
||||
#import <IOKit/IODataQueueClient.h>
|
||||
#import <IOKit/Kext/KextManager.h>
|
||||
@import IOKit.kext;
|
||||
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTKernelCommon.h"
|
||||
|
||||
@class SNTCachedDecision;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@
|
||||
// Actually make the decision.
|
||||
SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo
|
||||
fileSHA256:nil
|
||||
signingCertificate:csInfo.leafCertificate];
|
||||
certificateSHA256:csInfo.leafCertificate.SHA256];
|
||||
cd.certCommonName = csInfo.leafCertificate.commonName;
|
||||
cd.vnodeId = message.vnode_id;
|
||||
|
||||
// Formulate an action from the decision
|
||||
@@ -176,11 +177,19 @@
|
||||
if (action != ACTION_RESPOND_ALLOW) {
|
||||
[_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.
|
||||
dispatch_async(_eventQueue, ^{
|
||||
[self initiateEventUploadForEvent:se];
|
||||
});
|
||||
if ([[SNTConfigurator configurator] bundlesEnabled] && binInfo.bundle) {
|
||||
// If the binary is apart of a bundle, find and hash all the related binaries in the bundle.
|
||||
// Let the GUI know hashing is needed. Once the hashing is complete the GUI will send a
|
||||
// message to santad to perform the upload logic for bundles.
|
||||
// See syncBundleEvent:relatedEvents: for more info.
|
||||
se.needsBundleHash = YES;
|
||||
} else if ([[SNTConfigurator configurator] syncBaseURL]) {
|
||||
// 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.
|
||||
dispatch_async(_eventQueue, ^{
|
||||
[_syncdQueue addEvent:se];
|
||||
});
|
||||
}
|
||||
|
||||
if (!cd.silentBlock) {
|
||||
// Let the user know what happened, both on the terminal and in the GUI.
|
||||
@@ -245,24 +254,6 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
/**
|
||||
This sends the event that was just saved to santactl for immediate upload, so that the user
|
||||
has something to vote in straight away.
|
||||
|
||||
This method is always called on a serial queue to keep this low-priority method
|
||||
away from the high-priority decision making threads.
|
||||
*/
|
||||
- (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;
|
||||
|
||||
[self.syncdQueue addEvent:event];
|
||||
}
|
||||
|
||||
- (void)printMessage:(NSString *)msg toTTYForPID:(pid_t)pid {
|
||||
if (pid < 2) return; // don't bother even looking for launchd.
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@class SNTStoredEvent;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
|
||||
@@ -37,22 +39,24 @@
|
||||
/// @param fileInfo A SNTFileInfo object.
|
||||
/// @param fileSHA256 The pre-calculated SHA256 hash for the file, can be nil. If nil the hash will
|
||||
/// be calculated by this method from the filePath.
|
||||
/// @param signingCertificate A MOLCertificate object, can be nil.
|
||||
/// @note If fileInfo and signingCertificate are both passed in, the most specific rule will be
|
||||
/// @param certificateSHA256 A SHA256 hash of the signing certificate, can be nil.
|
||||
/// @note If fileSHA256 and certificateSHA256 are both passed in, the most specific rule will be
|
||||
/// returned. Binary rules take precedence over cert rules.
|
||||
/// @note This method can also be used to generate a SNTCachedDecision object without any
|
||||
/// artifacts on disk. Simply pass nil to fileInfo and pass in the desired SHA256s.
|
||||
///
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nullable SNTFileInfo *)fileInfo
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
signingCertificate:(nullable MOLCertificate *)signingCertificate;
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256;
|
||||
|
||||
///
|
||||
/// A wrapper for decisionForFileInfo:fileSHA256:signingCertificate:. This method is slower as it
|
||||
/// A wrapper for decisionForFileInfo:fileSHA256:certificateSHA256:. This method is slower as it
|
||||
/// has to create the SNTFileInfo object. This is mainly used by the santactl binary because
|
||||
/// SNTFileInfo is not SecureCoding compliant. If the SHA256 hash of the file has already been
|
||||
/// calculated, use the fileSHA256 parameter to save a second calculation of the hash.
|
||||
///
|
||||
- (nonnull SNTCachedDecision *)decisionForFilePath:(nonnull NSString *)filePath
|
||||
- (nonnull SNTCachedDecision *)decisionForFilePath:(nullable NSString *)filePath
|
||||
fileSHA256:(nullable NSString *)fileSHA256
|
||||
signingCertificate:(nullable MOLCertificate *)signingCertificate;
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256;
|
||||
|
||||
@end
|
||||
|
||||
@@ -38,14 +38,11 @@
|
||||
|
||||
- (SNTCachedDecision *)decisionForFileInfo:(SNTFileInfo *)fileInfo
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
signingCertificate:(MOLCertificate *)signingCertificate {
|
||||
certificateSHA256:(NSString *)certificateSHA256 {
|
||||
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
|
||||
cd.sha256 = fileSHA256 ?: fileInfo.SHA256;
|
||||
cd.certSHA256 = certificateSHA256;
|
||||
cd.quarantineURL = fileInfo.quarantineDataURL;
|
||||
if (signingCertificate) {
|
||||
cd.certCommonName = signingCertificate.commonName;
|
||||
cd.certSHA256 = signingCertificate.SHA256;
|
||||
}
|
||||
|
||||
SNTRule *rule = [self.ruleTable ruleForBinarySHA256:cd.sha256
|
||||
certificateSHA256:cd.certSHA256];
|
||||
@@ -113,13 +110,16 @@
|
||||
|
||||
- (SNTCachedDecision *)decisionForFilePath:(NSString *)filePath
|
||||
fileSHA256:(NSString *)fileSHA256
|
||||
signingCertificate:(MOLCertificate *)signingCertificate {
|
||||
NSError *error;
|
||||
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
|
||||
if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);
|
||||
certificateSHA256:(NSString *)certificateSHA256 {
|
||||
SNTFileInfo *fileInfo;
|
||||
if (filePath) {
|
||||
NSError *error;
|
||||
fileInfo = [[SNTFileInfo alloc] initWithPath:filePath error:&error];
|
||||
if (!fileInfo) LOGW(@"Failed to read file %@: %@", filePath, error.localizedDescription);
|
||||
}
|
||||
return [self decisionForFileInfo:fileInfo
|
||||
fileSHA256:fileSHA256
|
||||
signingCertificate:signingCertificate];
|
||||
certificateSHA256:certificateSHA256];
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
@class SNTStoredEvent;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@@ -22,6 +24,8 @@
|
||||
@property(copy) void (^acceptedHandler)();
|
||||
|
||||
- (void)addEvent:(SNTStoredEvent *)event;
|
||||
- (void)addBundleEvents:(NSArray<SNTStoredEvent *> *)events;
|
||||
- (void)addBundleEvent:(SNTStoredEvent *)event reply:(void (^)(BOOL))reply;
|
||||
- (void)startSyncingEvents;
|
||||
- (void)stopSyncingEvents;
|
||||
|
||||
|
||||
@@ -38,27 +38,31 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addBundleEvents:(NSArray<SNTStoredEvent *> *)events {
|
||||
if (![self backoffForBundleHash:events.firstObject.fileBundleHash]) return;
|
||||
[self dispatchBlockOnSyncdQueue:^{
|
||||
[self.syncdConnection.remoteObjectProxy postBundleEventsToSyncServer:events];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addBundleEvent:(SNTStoredEvent *)event reply:(void (^)(BOOL))reply {
|
||||
if (![self backoffForBundleHash:event.fileBundleHash]) return;
|
||||
[self dispatchBlockOnSyncdQueue:^{
|
||||
[self.syncdConnection.remoteObjectProxy postBundleEventToSyncServer:event
|
||||
reply:^(BOOL needRelatedEvents) {
|
||||
// Remove the backoff entry for the inital block event. The same event will be included in the
|
||||
// related events synced using addBundleEvents:.
|
||||
if (needRelatedEvents) [self.uploadBackoff removeObjectForKey:event.fileBundleHash];
|
||||
reply(needRelatedEvents);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addEvent:(SNTStoredEvent *)event {
|
||||
// The event upload is skipped if an event upload has been initiated for it in the
|
||||
// last 10 minutes.
|
||||
NSDate *backoff = [self.uploadBackoff objectForKey:event.fileSHA256];
|
||||
NSDate *now = [NSDate date];
|
||||
if (([now timeIntervalSince1970] - [backoff timeIntervalSince1970]) < 600) return;
|
||||
[self.uploadBackoff setObject:now forKey:event.fileSHA256];
|
||||
|
||||
// Hold events for a few seconds to allow santad and santactl to establish connections.
|
||||
// If the connections are not established in time drop the event from the queue.
|
||||
// They will be uploaded during a full sync.
|
||||
dispatch_async(self.syncdQueue, ^{
|
||||
if (!dispatch_semaphore_wait(self.sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
|
||||
[self.syncdConnection.remoteObjectProxy postEventToSyncServer:event];
|
||||
|
||||
// Let em flow
|
||||
dispatch_semaphore_signal(self.sema);
|
||||
} else {
|
||||
LOGI(@"Dropping event %@ from com.google.syncd_queue", event.fileSHA256);
|
||||
}
|
||||
});
|
||||
if (![self backoffForEvent:event]) return;
|
||||
[self dispatchBlockOnSyncdQueue:^{
|
||||
[self.syncdConnection.remoteObjectProxy postEventToSyncServer:event];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)startSyncingEvents {
|
||||
@@ -69,4 +73,38 @@
|
||||
self.sema = dispatch_semaphore_create(0);
|
||||
}
|
||||
|
||||
// Hold events for a few seconds to allow santad and santactl to establish connections.
|
||||
// If the connections are not established in time drop the event from the queue.
|
||||
// They will be uploaded during a full sync.
|
||||
- (void)dispatchBlockOnSyncdQueue:(void (^)())block {
|
||||
dispatch_async(self.syncdQueue, ^{
|
||||
if (!dispatch_semaphore_wait(self.sema, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC))) {
|
||||
if (block) block();
|
||||
dispatch_semaphore_signal(self.sema);
|
||||
} else {
|
||||
LOGD(@"Dropping block %@ from com.google.syncd_queue", block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// The event upload is skipped if an event upload has been initiated for it in the
|
||||
// last 10 minutes.
|
||||
- (BOOL)backoffForEvent:(SNTStoredEvent *)event {
|
||||
NSDate *backoff = [self.uploadBackoff objectForKey:event.fileSHA256];
|
||||
NSDate *now = [NSDate date];
|
||||
if (([now timeIntervalSince1970] - [backoff timeIntervalSince1970]) < 600) return NO;
|
||||
[self.uploadBackoff setObject:now forKey:event.fileSHA256];
|
||||
return YES;
|
||||
}
|
||||
|
||||
// The bundle event upload is skipped if a bundle event has been initiated for it in the
|
||||
// last 10 minutes.
|
||||
- (BOOL)backoffForBundleHash:(NSString *)bundleHash {
|
||||
NSDate *backoff = [self.uploadBackoff objectForKey:bundleHash];
|
||||
NSDate *now = [NSDate date];
|
||||
if (([now timeIntervalSince1970] - [backoff timeIntervalSince1970]) < 600) return NO;
|
||||
[self.uploadBackoff setObject:now forKey:bundleHash];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#include <mach/task.h>
|
||||
#include <pthread/pthread.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user