Compare commits

...

33 Commits
0.9.1 ... 0.9.3

Author SHA1 Message Date
Russell Hancox
61c817c9cb Tests: Fix SNTRuleTable tests 2015-10-09 15:14:15 -04:00
Russell Hancox
2ed384f677 santactl/sync: Only update client mode at end of sync 2015-10-09 13:12:25 -04:00
Russell Hancox
7a851cb080 santad: Typo in comment 2015-10-08 19:54:23 -04:00
Russell Hancox
13aa889633 SNTFileInfo: Add fileSize method, use it in SNTEventLog 2015-10-08 17:57:02 -04:00
Russell Hancox
5c3fba5f41 santad: Prevent user/server from accidentally deleting rules that would kill the system. 2015-10-08 17:45:39 -04:00
Russell Hancox
145d9216bf Project: Don't bother with "xcodebuild clean" for Rakefile clean rule 2015-10-08 17:43:59 -04:00
Russell Hancox
84f46de940 Driver/Daemon: Collect process name in-kernel for file events, parent name for exec requests. For file events log process name and path, if possible. 2015-10-05 17:09:33 -04:00
Russell Hancox
cb9a5b6fbe santactl: Add --json option to both status and version commands. 2015-10-05 14:15:10 -04:00
Russell Hancox
d9718faba4 SNTFileInfo: Return non-embedded dict if locating embedded fails 2015-10-05 14:13:40 -04:00
Russell Hancox
5472ff41f0 santactl/status: Show timezone as UTF offset rather than name 2015-10-05 13:00:55 -04:00
Russell Hancox
4f94c3b310 santactl/status: Use fixed format for sync date output but still include TZ. 2015-10-03 19:57:19 -04:00
Russell Hancox
420f1efa50 santad: For file write events, print process name as well as pid. 2015-10-03 18:16:06 -04:00
Russell Hancox
5d2ce17817 santactl/status: When printing last sync date, use local timezone and locale settings 2015-10-03 18:15:41 -04:00
Russell Hancox
053cb823a1 santa-driver: Change C++ std to C++11
This is mostly just to quiet the warning about override not being set on getMetaClass, which is part of the OSDeclareDefaultStructors macro.
2015-10-03 18:15:11 -04:00
Russell Hancox
18a7992372 Config: Add more protected keys, only protect if a server is set 2015-10-02 16:35:30 -04:00
Russell Hancox
9e935f5bfb GUI: Include CFBundleName as first item in UI, if available. 2015-10-01 18:53:58 -04:00
Russell Hancox
9f49e24dc5 santad: Update file changes logging to use a configurable regex 2015-10-01 17:57:07 -04:00
Russell Hancox
dbf60f16bc santactl/sync: Fix typo causing clean sync on every run 2015-09-30 16:00:39 -04:00
Russell Hancox
0f3a228788 santactl/rule: Make help text a little clearer 2015-09-28 17:46:30 -04:00
Russell Hancox
d905f5b095 santactl/rule: Add ability to add certificate rules. Re-write argument parsing. 2015-09-28 17:20:34 -04:00
Russell Hancox
1c310486c7 santactl/status, santad: Show watchdog events in status output 2015-09-28 16:41:33 -04:00
Russell Hancox
4b01c6da91 santactl/status: Report some sync statuses. 2015-09-28 16:14:45 -04:00
Russell Hancox
5782378616 santactl/sync, santad: Add clean sync and last success options, use to initiate clean sync when database is re-created 2015-09-28 16:11:17 -04:00
Russell Hancox
64c97ebfba santad: If database open fails, delete and re-create. 2015-09-28 16:09:05 -04:00
Russell Hancox
5fd4d56b00 santactl/sync: Add ability to sync blacklist regex 2015-09-28 16:08:11 -04:00
Russell Hancox
e658b5167e Project: Update README a little 2015-09-24 18:15:03 -04:00
Russell Hancox
cea698d720 SNTCertificate: Add serialNumber and isCa properties. 2015-09-21 17:48:47 -04:00
Russell Hancox
c07f41c312 santad: Stop closing stdout/stderr 2015-09-21 15:59:32 -04:00
Russell Hancox
a837aa0334 santactl/status: Use dispatch group instead of sleeping 2015-09-21 15:59:20 -04:00
Russell Hancox
0050724e22 SNTXPCConnection: Use semaphore instead of variable & sleep. 2015-09-21 15:58:54 -04:00
Russell Hancox
adac4ac75c SantaGUI: windowWillClose and orderOut are being marked nonnull 2015-09-21 15:51:36 -04:00
Russell Hancox
718f37024a SNTConfigurator: Use NSPropertyListImmutable instead of kCFPropertyListImmutable 2015-09-21 15:51:03 -04:00
Russell Hancox
fcb3008539 Rakefile: Handle xcpretty missing better 2015-09-21 15:50:22 -04:00
39 changed files with 587 additions and 206 deletions

View File

@@ -48,9 +48,23 @@ continue to work across OS versions.
Intentions and Expectations
===========================
No single system or process will stop *all* attacks, or provide 100% security. Santa is written with the intention of helping protect users from themselves. People often download malware and trust it, giving the malware credentials, or allowing unknown software to exfiltrate more data about your system. As a centrally managed component, Santa can help stop the spread of malware among a larger fleet of machines. Additionally, Santa can aid in analyzing what is running in your fleet.
No single system or process will stop *all* attacks, or provide 100% security.
Santa is written with the intention of helping protect users from themselves.
People often download malware and trust it, giving the malware credentials, or
allowing unknown software to exfiltrate more data about your system. As a
centrally managed component, Santa can help stop the spread of malware among a
larger fleet of machines. Additionally, Santa can aid in analyzing what is
running in your fleet.
Santa is part of a defense-in-depth strategy, and you should continue to protect hosts in whatever other ways you see fit.
Santa is part of a defense-in-depth strategy, and you should continue to protect
hosts in whatever other ways you see fit.
Get Help
========
If you have questions or need help getting started, the
[santa-dev](https://groups.google.com/forum/#!forum/santa-dev) group is the
best place to start.
Known Issues
============
@@ -58,7 +72,7 @@ Santa is not yet a 1.0 and we have some known issues to be aware of:
* Santa only blocks execution (execve and variants), it doesn't protect against
dynamic libraries loaded with dlopen, libraries on disk that have been replaced or
libraries loaded using DYLD_INSERT_LIBRARIES. We are working on also protecting
libraries loaded using `DYLD_INSERT_LIBRARIES`. We are working on also protecting
against these avenues of attack.
* Kext communication security: the kext will only accept a connection from a
@@ -107,11 +121,10 @@ and for security-reasons parts of Santa will not operate properly if not signed.
Kext Signing
============
10.9 requires a special Developer ID certificate to sign kernel extensions and
if the kext is not signed with one of these special certificates a warning will
be shown when loading the kext for the first time. In 10.10 this is a hard error
and the kext will not load at all unless the machine is booted with a debug
boot-arg.
Kernel extensions on OS X 10.9 and later must be signed using an Apple-provided
Developer ID certificate with a kernel extension flag. Without it, the only way
to load an extension is to enable kext-dev-mode or disable SIP, depending on the
OS version.
There are two possible solutions for this, for distribution purposes:
@@ -126,10 +139,6 @@ and distribute a new version of the pre-signed kext.
Apple will only grant this for broad distribution within an organization, they
won't issue them just for testing purposes.
If you just want to locally test changes to the kext code, you should enable
kext-dev mode, instructions for which can be found on the Apple developer site.
Contributing
============
Patches to this project are very much welcome. Please see the [CONTRIBUTING](https://github.com/google/santa/blob/master/CONTRIBUTING.md)

View File

@@ -6,15 +6,19 @@ BINARIES = ['Santa.app', 'santa-driver.kext']
DSYMS = ['Santa.app.dSYM', 'santa-driver.kext.dSYM', 'santad.dSYM', 'santactl.dSYM']
XCPRETTY_DEFAULTS = '-sc'
XCODEBUILD_DEFAULTS = "-workspace #{WORKSPACE} -derivedDataPath #{OUTPUT_PATH} -parallelizeTargets"
$DISABLE_XCPRETTY = false
task :default do
system("rake -sT")
end
def xcodebuild(opts)
if system "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts} | " \
"xcpretty #{XCPRETTY_DEFAULTS} && " \
"exit ${PIPESTATUS[0]}"
command = "xcodebuild #{XCODEBUILD_DEFAULTS} #{opts}"
if not $DISABLE_XCPRETTY
command << " | xcpretty #{XCPRETTY_DEFAULTS} && exit ${PIPESTATUS[0]}"
end
if system command
puts "\e[32mPass\e[0m"
else
raise "\e[31mFail\e[0m"
@@ -28,6 +32,7 @@ task :init do
end
unless system 'xcpretty -v >/dev/null 2>&1'
puts "xcpretty is not installed. Install with 'sudo gem install xcpretty'"
$DISABLE_XCPRETTY = true
end
end
@@ -39,7 +44,6 @@ end
desc "Clean"
task :clean => :init do
puts "Cleaning"
xcodebuild("-scheme All clean")
FileUtils.rm_rf(OUTPUT_PATH)
FileUtils.rm_rf(DIST_PATH)
end

View File

@@ -1647,7 +1647,10 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
PROVISIONING_PROFILE = "";
@@ -1667,8 +1670,11 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_WARN_CXX0X_EXTENSIONS = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = c99;
MACOSX_DEPLOYMENT_TARGET = 10.9;
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;

View File

@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14E46" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="8191" systemVersion="15A282b" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="8191"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
<connections>
<outlet property="applicationNameLabel" destination="qgf-Jf-cJr" id="1JX-X8-03v"/>
<outlet property="openEventButton" destination="7ua-5a-uSd" id="9s4-ZA-Vlo"/>
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
</connections>
@@ -15,14 +16,15 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="none" id="9Bq-yh-54f" customClass="SNTMessageWindow">
<windowStyleMask key="styleMask" utility="YES"/>
<rect key="contentRect" x="167" y="107" width="497" height="356"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1600"/>
<rect key="contentRect" x="167" y="107" width="497" height="381"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="Iwq-Lx-rLv">
<rect key="frame" x="0.0" y="0.0" width="497" height="356"/>
<rect key="frame" x="0.0" y="0.0" width="497" height="381"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
<rect key="frame" x="207" y="286" width="83" height="40"/>
<rect key="frame" x="207" y="311" width="83" height="40"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="7YA-iB-Zma">
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
<color key="textColor" red="0.18696189413265307" green="0.18696189413265307" blue="0.18696189413265307" alpha="1" colorSpace="calibratedRGB"/>
@@ -30,10 +32,11 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
<rect key="frame" x="22" y="239" width="454" height="17"/>
<rect key="frame" x="22" y="264" width="454" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="450" id="XgJ-EV-tBa"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
@@ -48,6 +51,7 @@
<constraints>
<constraint firstAttribute="width" constant="290" id="xVR-j3-dLw"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Binary Path" id="E7T-9h-ofr">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
@@ -57,11 +61,31 @@
<binding destination="-2" name="value" keyPath="self.event.filePath" id="qfp-sR-Nmu"/>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qgf-Jf-cJr">
<rect key="frame" x="165" y="217" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="Pav-ZA-iAu"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="Application Name" id="3UG-ca-d1k">
<font key="font" metaFont="systemBold"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="value" keyPath="self.event.fileBundleName" id="enC-Cl-UWt">
<dictionary key="options">
<string key="NSNullPlaceholder">Unknown</string>
</dictionary>
</binding>
</connections>
</textField>
<textField horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
<rect key="frame" x="165" y="142" width="294" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="290" id="4hh-R2-86s"/>
</constraints>
<animations/>
<textFieldCell key="cell" lineBreakMode="charWrapping" selectable="YES" sendsActionOnEndEditing="YES" title="Part of SHA-256" id="X4W-9e-eIu">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
@@ -76,6 +100,7 @@
<constraints>
<constraint firstAttribute="width" constant="290" id="Dem-wH-KHm"/>
</constraints>
<animations/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Code signing information" placeholderString="" id="ztA-La-XgT">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="deviceWhite"/>
@@ -91,6 +116,7 @@
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="oFj-ol-xpL">
<rect key="frame" x="8" y="92" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="User" id="1ut-uT-hQD">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -99,6 +125,7 @@
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eQb-0a-76J">
<rect key="frame" x="8" y="117" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Parent" id="gze-4A-1w5">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -107,6 +134,7 @@
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5">
<rect key="frame" x="8" y="167" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Publisher" id="yL9-yD-JXX">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -118,6 +146,7 @@
<constraints>
<constraint firstAttribute="width" constant="116" id="Kqd-nX-7df"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Path" id="KgY-X1-ESG">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -126,6 +155,7 @@
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y">
<rect key="frame" x="8" y="142" width="120" height="17"/>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Identifier" id="eKN-Ic-5zy">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@@ -137,6 +167,7 @@
<constraints>
<constraint firstAttribute="width" constant="290" id="on6-pj-m2k"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Executing User" id="HRT-Be-ePf">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -151,10 +182,11 @@
</connections>
</textField>
<box horizontalHuggingPriority="750" title="Line" boxType="custom" borderType="line" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="4Li-ul-zIi">
<rect key="frame" x="146" y="92" width="1" height="117"/>
<rect key="frame" x="146" y="92" width="1" height="142"/>
<constraints>
<constraint firstAttribute="width" constant="1" id="0o1-Jh-epf"/>
</constraints>
<animations/>
<color key="borderColor" white="0.0" alpha="0.17999999999999999" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<font key="titleFont" metaFont="system"/>
@@ -165,6 +197,7 @@
<constraint firstAttribute="width" constant="15" id="QTm-Iv-m5p"/>
<constraint firstAttribute="height" constant="15" id="YwG-0s-jop"/>
</constraints>
<animations/>
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSInfo" imagePosition="overlaps" alignment="center" refusesFirstResponder="YES" imageScaling="proportionallyDown" inset="2" id="R72-Qy-Xbb">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -184,6 +217,7 @@
<constraint firstAttribute="width" constant="110" id="6Uh-Bd-N64"/>
<constraint firstAttribute="height" constant="22" id="GH6-nw-6rD"/>
</constraints>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Dismiss" bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="XR6-Xa-gP4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -201,6 +235,7 @@ DQ
<constraints>
<constraint firstAttribute="width" priority="900" constant="112" id="Pec-Pa-4aZ"/>
</constraints>
<animations/>
<buttonCell key="cell" type="roundTextured" title="Open Event..." bezelStyle="texturedRounded" alignment="center" refusesFirstResponder="YES" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="X1b-TF-1TL">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@@ -218,6 +253,7 @@ DQ
<constraints>
<constraint firstAttribute="width" constant="290" id="h3Y-mO-38F"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Parent Name" id="ieo-WK-aDD">
<font key="font" metaFont="system"/>
<color key="textColor" white="0.0" alpha="0.5" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -236,6 +272,25 @@ DQ
</binding>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pDa-fA-vnC">
<rect key="frame" x="8" y="217" width="120" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="116" id="8mA-zi-Ev7"/>
</constraints>
<animations/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Application" id="Hy7-WF-6xW">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<connections>
<binding destination="-2" name="hidden" keyPath="self.event.fileBundleName" id="r2Q-hh-Uy5">
<dictionary key="options">
<string key="NSValueTransformerName">NSIsNil</string>
</dictionary>
</binding>
</connections>
</textField>
</subviews>
<constraints>
<constraint firstItem="f1p-GL-O3o" firstAttribute="centerY" secondItem="eQb-0a-76J" secondAttribute="centerY" id="2Aq-1E-Ybz"/>
@@ -243,6 +298,7 @@ DQ
<constraint firstItem="h6f-PY-cc0" firstAttribute="top" secondItem="f1p-GL-O3o" secondAttribute="bottom" constant="8" id="496-VQ-Fx5"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="-116" id="6Q5-Oo-1cI"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="7K6-bY-Rn6"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="centerY" secondItem="qgf-Jf-cJr" secondAttribute="centerY" id="AKX-pe-hEX"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="ALv-0v-szi"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="8" id="E6D-7P-17g"/>
<constraint firstItem="cJf-k6-OxS" firstAttribute="centerY" secondItem="C3G-wL-u7w" secondAttribute="centerY" id="FdL-ZZ-Vbe"/>
@@ -250,10 +306,12 @@ DQ
<constraint firstItem="oFj-ol-xpL" firstAttribute="bottom" secondItem="4Li-ul-zIi" secondAttribute="bottom" id="G0I-O2-S91"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="cJf-k6-OxS" secondAttribute="trailing" constant="-45" id="GD2-Ka-deo"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="centerY" secondItem="oFj-ol-xpL" secondAttribute="centerY" id="GXI-pT-FM1"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="top" secondItem="pDa-fA-vnC" secondAttribute="top" id="Gd4-Nr-n5G"/>
<constraint firstItem="qgf-Jf-cJr" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="Ht4-Lg-U5N"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="10" id="IwX-ja-ZIs"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" id="JY4-N1-j8e"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="top" secondItem="4Li-ul-zIi" secondAttribute="top" priority="500" id="JY4-N1-j8e"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="leading" priority="999" id="MVr-jY-GDj"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" priority="750" constant="30" id="Nsl-zf-poH"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="SCl-Ky-VmT"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="centerY" secondItem="pc8-G9-4pJ" secondAttribute="centerY" id="SLv-F7-w5k"/>
<constraint firstItem="7ua-5a-uSd" firstAttribute="top" secondItem="oFj-ol-xpL" secondAttribute="bottom" constant="35" id="Scq-zQ-Sao"/>
@@ -261,17 +319,20 @@ DQ
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="V0a-Py-iEc"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="leading" priority="999" id="Z6G-l9-G4a"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="top" secondItem="eQb-0a-76J" secondAttribute="bottom" constant="8" id="abm-cM-PN0"/>
<constraint firstItem="pDa-fA-vnC" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" id="asc-Ga-WHD"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="eQb-0a-76J" secondAttribute="trailing" constant="20" id="b0B-3w-grH"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="leading" priority="999" id="b5A-M7-ZsD"/>
<constraint firstItem="KEB-eH-x2Y" firstAttribute="centerY" secondItem="PXc-xv-A28" secondAttribute="centerY" id="cHe-pZ-0Oq"/>
<constraint firstItem="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="30" id="dYg-zP-wh2"/>
<constraint firstItem="h6f-PY-cc0" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="eSz-lz-Fdh"/>
<constraint firstItem="qgf-Jf-cJr" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="30" id="esg-lX-BAT"/>
<constraint firstItem="f1p-GL-O3o" firstAttribute="leading" secondItem="4Li-ul-zIi" secondAttribute="trailing" constant="20" id="fGd-YS-phP"/>
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" id="h3d-Kc-q88"/>
<constraint firstItem="BbV-3h-mmL" firstAttribute="leading" secondItem="7ua-5a-uSd" secondAttribute="trailing" constant="12" id="ioO-NJ-Jqo"/>
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="lvJ-Rk-UT5" secondAttribute="centerY" id="jfs-YI-7Ae"/>
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="jlD-Lo-abc"/>
<constraint firstItem="4Li-ul-zIi" firstAttribute="leading" secondItem="oFj-ol-xpL" secondAttribute="trailing" constant="20" id="kOG-Cj-hFG"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="qgf-Jf-cJr" secondAttribute="bottom" constant="8" id="lWU-tC-vWg"/>
<constraint firstItem="oFj-ol-xpL" firstAttribute="trailing" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" id="lse-kg-lA2"/>
<constraint firstItem="eQb-0a-76J" firstAttribute="top" secondItem="KEB-eH-x2Y" secondAttribute="bottom" constant="8" id="m2z-1O-ifB"/>
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="trailing" secondItem="KEB-eH-x2Y" secondAttribute="trailing" id="pdq-a6-Y73"/>
@@ -284,6 +345,7 @@ DQ
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="35" id="ukF-FH-DE8"/>
<constraint firstItem="pc8-G9-4pJ" firstAttribute="bottom" secondItem="C3G-wL-u7w" secondAttribute="top" constant="-8" id="zst-nc-VqA"/>
</constraints>
<animations/>
</view>
<point key="canvasLocation" x="112.5" y="308"/>
</window>

View File

@@ -43,8 +43,8 @@
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration:0.15f];
[[NSAnimationContext currentContext] setCompletionHandler:^{
[weakSelf.windowController windowWillClose:nil];
[weakSelf orderOut:nil];
[weakSelf.windowController windowWillClose:sender];
[weakSelf orderOut:sender];
[weakSelf setAlphaValue:1.f];
}];
[[self animator] setAlphaValue:0.f];

View File

@@ -60,4 +60,10 @@
///
@property IBOutlet NSButton *openEventButton;
///
/// Reference to the "Application Name" label in the XIB. Used to remove if application
/// doesn't have a CFBundleName.
///
@property IBOutlet NSTextField *applicationNameLabel;
@end

View File

@@ -46,6 +46,10 @@
[self.openEventButton setTitle:eventDetailText];
}
}
if (!self.event.fileBundleName) {
[self.applicationNameLabel removeFromSuperview];
}
}
- (IBAction)showWindow:(id)sender {

View File

@@ -90,6 +90,16 @@
///
@property(readonly, nonatomic) NSString *orgUnit;
///
/// Is this cert a CA?
///
@property(readonly, nonatomic) BOOL isCA;
///
/// The cert serial number
///
@property(readonly, nonatomic) NSString *serialNumber;
///
/// Issuer details, same fields as above.
///

View File

@@ -357,5 +357,19 @@ static NSString *const kCertDataKey = @"certData";
}];
}
- (BOOL)isCA {
return [[self memoizedSelector:_cmd forBlock:^id{
NSDictionary *dict = [self allCertificateValues][(__bridge NSString *)kSecOIDBasicConstraints];
return [self x509ValueForLabel:@"Certificate Authority"
fromDictionary:dict];
}] isEqual:@"Yes"];
}
- (NSString *)serialNumber {
return [self memoizedSelector:_cmd forBlock:^id{
NSDictionary *dict = [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SerialNumber];
return dict[(__bridge NSString *)kSecPropertyKeyValue];
}];
}
@end

View File

@@ -31,9 +31,13 @@ extern NSString * const kDefaultConfigFilePath;
@property(nonatomic) santa_clientmode_t clientMode;
///
/// If set to NO, file writes won't be logged. Defaults to YES.
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
///
@property(nonatomic) BOOL logFileChanges;
/// The regex flags IXSM can be used, though the s (dotalL) and m (multiline) flags are
/// pointless as a path only ever has a single line.
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
///
@property(nonatomic) NSRegularExpression *fileChangesRegex;
///
/// The regex of whitelisted paths. Regexes are specified in ICU format.
@@ -106,6 +110,16 @@ extern NSString * const kDefaultConfigFilePath;
///
@property(readonly, nonatomic) NSString *machineOwner;
///
/// The last date of successful sync.
///
@property(nonatomic) NSDate *syncLastSuccess;
///
/// If YES a clean sync is required.
///
@property(nonatomic) BOOL syncCleanRequired;
///
/// If set, this over-rides the default machine ID used for syncing.
///

View File

@@ -22,6 +22,7 @@
@property NSMutableDictionary *configData;
/// Creating NSRegularExpression objects is not fast, so cache them.
@property NSRegularExpression *cachedFileChangesRegex;
@property NSRegularExpression *cachedWhitelistDirRegex;
@property NSRegularExpression *cachedBlacklistDirRegex;
@@ -36,9 +37,9 @@ NSString * const kDefaultConfigFilePath = @"/var/db/santa/config.plist";
/// The keys in the config file
static NSString * const kClientModeKey = @"ClientMode";
static NSString * const kFileChangesRegexKey = @"FileChangesRegex";
static NSString * const kWhitelistRegexKey = @"WhitelistRegex";
static NSString * const kBlacklistRegexKey = @"BlacklistRegex";
static NSString * const kLogFileChangesKey = @"LogFileChanges";
static NSString * const kMoreInfoURLKey = @"MoreInfoURL";
static NSString * const kEventDetailURLKey = @"EventDetailURL";
@@ -46,6 +47,8 @@ static NSString * const kEventDetailTextKey = @"EventDetailText";
static NSString * const kDefaultBlockMessage = @"DefaultBlockMessage";
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
static NSString * const kSyncLastSuccess = @"SyncLastSuccess";
static NSString * const kSyncCleanRequired = @"SyncCleanRequired";
static NSString * const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
static NSString * const kClientAuthCertificatePasswordKey = @"ClientAuthCertificatePassword";
static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
@@ -85,7 +88,8 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
#pragma mark Protected Keys
- (NSArray *)protectedKeys {
return @[ kClientModeKey, kWhitelistRegexKey, kBlacklistRegexKey, kLogFileChangesKey ];
return @[ kClientModeKey, kWhitelistRegexKey, kBlacklistRegexKey,
kFileChangesRegexKey, kSyncBaseURLKey ];
}
#pragma mark Public Interface
@@ -149,12 +153,24 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
[self saveConfigToDisk];
}
- (BOOL)logFileChanges {
return [self.configData[kLogFileChangesKey] boolValue];
- (NSRegularExpression *)fileChangesRegex {
if (!self.cachedFileChangesRegex && self.configData[kFileChangesRegexKey]) {
NSString *re = self.configData[kFileChangesRegexKey];
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
self.cachedFileChangesRegex = [NSRegularExpression regularExpressionWithPattern:re
options:0
error:NULL];
}
return self.cachedFileChangesRegex;
}
- (void)setLogFileChanges:(BOOL)logFileChanges {
self.configData[kLogFileChangesKey] = @(logFileChanges);
- (void)setFileChangesRegex:(NSRegularExpression *)re {
if (!re) {
[self.configData removeObjectForKey:kFileChangesRegexKey];
} else {
self.configData[kFileChangesRegexKey] = [re pattern];
}
self.cachedFileChangesRegex = nil;
[self saveConfigToDisk];
}
@@ -202,6 +218,24 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
return self.configData[kServerAuthRootsFileKey];
}
- (NSDate *)syncLastSuccess {
return self.configData[kSyncLastSuccess];
}
- (void)setSyncLastSuccess:(NSDate *)syncLastSuccess {
self.configData[kSyncLastSuccess] = syncLastSuccess;
[self saveConfigToDisk];
}
- (BOOL)syncCleanRequired {
return [self.configData[kSyncCleanRequired] boolValue];
}
- (void)setSyncCleanRequired:(BOOL)syncCleanRequired {
self.configData[kSyncCleanRequired] = @(syncCleanRequired);
[self saveConfigToDisk];
}
- (NSString *)machineOwner {
NSString *machineOwner;
@@ -255,7 +289,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
NSDictionary *configData =
[NSPropertyListSerialization propertyListWithData:readData
options:kCFPropertyListImmutable
options:NSPropertyListImmutable
format:NULL
error:&error];
if (error) {
@@ -265,7 +299,7 @@ static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
if (!self.configData) {
self.configData = [configData mutableCopy];
} else {
} else if (self.syncBaseURL) {
// Ensure no-one is trying to change protected keys behind our back.
NSMutableDictionary *configDataMutable = [configData mutableCopy];
BOOL changed = NO;

View File

@@ -140,4 +140,9 @@
///
- (NSArray *)downloadURLs;
///
/// @return The size of the file in bytes.
///
- (NSUInteger)fileSize;
@end

View File

@@ -235,10 +235,10 @@
// This could (and used to) use CFBundleCopyInfoDictionaryForURL but that uses mmap to read
// the file and so can cause SIGBUS if the file is deleted/truncated while it's working.
MachHeaderWithOffset *mhwo = [[self.machHeaders allValues] firstObject];
if (!mhwo) return nil;
if (!mhwo) return self.infoDict;
struct mach_header *mh = (struct mach_header *)mhwo.data.bytes;
if (mh->filetype != MH_EXECUTE) return nil;
if (mh->filetype != MH_EXECUTE) return self.infoDict;
BOOL is64 = (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64);
uint32_t ncmds = mh->ncmds;
uint32_t nsects = 0;
@@ -253,7 +253,7 @@
// Loop through the load commands looking for the segment named __TEXT
for (uint32_t i = 0; i < ncmds; i++) {
NSData *cmdData = [self safeSubdataWithRange:NSMakeRange(offset, sz_segment)];
if (!cmdData) return nil;
if (!cmdData) return self.infoDict;
struct segment_command_64 *lc = (struct segment_command_64 *)[cmdData bytes];
if (lc->cmd == LC_SEGMENT || lc->cmd == LC_SEGMENT_64) {
if (strncmp(lc->segname, "__TEXT", 6) == 0) {
@@ -268,18 +268,18 @@
// Loop through the sections in the __TEXT segment looking for an __info_plist section.
for (uint32_t i = 0; i < nsects; i++) {
NSData *sectData = [self safeSubdataWithRange:NSMakeRange(offset, sz_section)];
if (!sectData) return nil;
if (!sectData) return self.infoDict;
struct section_64 *sect = (struct section_64 *)[sectData bytes];
if (strncmp(sect->sectname, "__info_plist", 12) == 0 && sect->size < 2000000) {
NSData *plistData = [self safeSubdataWithRange:NSMakeRange(sect->offset, sect->size)];
if (!plistData) return nil;
if (!plistData) return self.infoDict;
NSDictionary *plist;
plist = [NSPropertyListSerialization propertyListWithData:plistData
options:NSPropertyListImmutable
format:NULL
error:NULL];
if (plist) self.infoDict = plist;
break;
return self.infoDict;
}
offset += sz_section;
}
@@ -328,6 +328,10 @@
return nil;
}
- (NSUInteger)fileSize {
return self.fileData.length;
}
#pragma mark Internal Methods
- (void)parseMachHeaders {

View File

@@ -79,6 +79,11 @@ typedef struct {
pid_t ppid;
char path[MAXPATHLEN];
char newpath[MAXPATHLEN];
// For file events, this is the process name.
// For exec requests, this is the parent process name.
// While process names can technically be 4*MAXPATHLEN, that never
// actually happens, so only take MAXPATHLEN and throw away any excess.
char pname[MAXPATHLEN];
} santa_message_t;
#endif // SANTA__COMMON__KERNELCOMMON_H

View File

@@ -93,7 +93,7 @@
[connection resume];
__block BOOL verificationComplete = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[connection remoteObjectProxy] isConnectionValidWithBlock:^void(BOOL response) {
pid_t pid = self.currentConnection.processIdentifier;
@@ -107,20 +107,19 @@
self.currentConnection.exportedObject = self.exportedObject;
[self invokeAcceptedHandler];
[self.currentConnection resume];
verificationComplete = YES;
dispatch_semaphore_signal(sema);
} else {
[self invokeRejectedHandler];
[self.currentConnection invalidate];
self.currentConnection = nil;
verificationComplete = YES;
dispatch_semaphore_signal(sema);
}
}];
// Wait for validation to complete, at most 5s
for (int sleepLoops = 0; sleepLoops < 1000 && !verificationComplete; sleepLoops++) {
usleep(5000);
if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC))) {
[self invalidate];
}
if (!verificationComplete) [self invalidate];
}
}

View File

@@ -44,9 +44,14 @@
/// Config ops
///
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply;
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply;
- (void)setClientMode:(santa_clientmode_t)mode reply:(void (^)())reply;
- (void)setNextSyncInterval:(uint64_t)seconds reply:(void (^)())reply;
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
@end

View File

@@ -47,27 +47,27 @@ void SantaDecisionManager::free() {
if (cached_decisions_lock_) {
lck_rw_free(cached_decisions_lock_, sdm_lock_grp_);
cached_decisions_lock_ = NULL;
cached_decisions_lock_ = nullptr;
}
if (dataqueue_lock_) {
lck_mtx_free(dataqueue_lock_, sdm_lock_grp_);
dataqueue_lock_ = NULL;
dataqueue_lock_ = nullptr;
}
if (sdm_lock_attr_) {
lck_attr_free(sdm_lock_attr_);
sdm_lock_attr_ = NULL;
sdm_lock_attr_ = nullptr;
}
if (sdm_lock_grp_) {
lck_grp_free(sdm_lock_grp_);
sdm_lock_grp_ = NULL;
sdm_lock_grp_ = nullptr;
}
if (sdm_lock_grp_attr_) {
lck_grp_attr_free(sdm_lock_grp_attr_);
sdm_lock_grp_attr_ = NULL;
sdm_lock_grp_attr_ = nullptr;
}
super::free();
@@ -101,7 +101,7 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
message->action = ACTION_REQUEST_SHUTDOWN;
PostToQueue(message);
delete message;
dataqueue_->setNotificationPort(NULL);
dataqueue_->setNotificationPort(nullptr);
} else {
// If the client died, reset the data queue so when it reconnects
// it doesn't get swamped straight away.
@@ -145,10 +145,10 @@ kern_return_t SantaDecisionManager::StartListener() {
kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;
vnode_listener_ = nullptr;
kauth_unlisten_scope(fileop_listener_);
fileop_listener_ = NULL;
fileop_listener_ = nullptr;
// Wait for any active invocations to finish before returning
do {
@@ -197,7 +197,7 @@ void SantaDecisionManager::AddToCache(
void SantaDecisionManager::CacheCheck(const char *identifier) {
lck_rw_lock_shared(cached_decisions_lock_);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != nullptr);
if (shouldInvalidate) {
if (!lck_rw_lock_shared_to_exclusive(cached_decisions_lock_)) {
// shared_to_exclusive will return false if a previous reader upgraded
@@ -330,6 +330,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
strlcpy(message->path, path, sizeof(message->path));
message->action = ACTION_REQUEST_CHECKBW;
message->vnode_id = vnode_id;
proc_name(message->ppid, message->pname, sizeof(message->pname));
santa_action_t ret = GetFromDaemon(message, vnode_id_str);
delete message;
return ret;
@@ -427,7 +428,7 @@ void SantaDecisionManager::FileOpCallback(
const kauth_action_t action, const vnode_t vp,
const char *path, const char *new_path) {
if (vp) {
vfs_context_t context = vfs_context_create(NULL);
vfs_context_t context = vfs_context_create(nullptr);
uint64_t vnode_id = GetVnodeIDForVnode(context, vp);
vfs_context_rele(context);
@@ -446,15 +447,12 @@ void SantaDecisionManager::FileOpCallback(
}
}
// Filter out modifications to locations that are not that useful.
if (ClientConnected() &&
!strprefix(path, "/private/tmp") &&
!strprefix(path, "/private/var/folders") &&
!strprefix(path, "/.") &&
!strprefix(path, "/dev")) {
// Filter out modifications to locations that are definitely not useful.
if (ClientConnected() && !strprefix(path, "/.") && !strprefix(path, "/dev")) {
santa_message_t *message = NewMessage();
strlcpy(message->path, path, sizeof(message->path));
if (new_path) strlcpy(message->newpath, new_path, sizeof(message->newpath));
proc_name(message->pid, message->pname, sizeof(message->pname));
switch (action) {
case KAUTH_FILEOP_CLOSE:
@@ -485,9 +483,9 @@ extern "C" int fileop_scope_callback(
SantaDecisionManager *sdm = OSDynamicCast(
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
vnode_t vp = NULL;
char *path = NULL;
char *new_path = NULL;
vnode_t vp = nullptr;
char *path = nullptr;
char *new_path = nullptr;
switch (action) {
case KAUTH_FILEOP_CLOSE:
@@ -521,7 +519,7 @@ extern "C" int vnode_scope_callback(
uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3) {
if (action & KAUTH_VNODE_ACCESS ||
!(action & KAUTH_VNODE_EXECUTE) ||
idata == NULL) {
idata == nullptr) {
return KAUTH_RESULT_DEFER;
}

View File

@@ -222,7 +222,7 @@ class SantaDecisionManager : public OSObject {
/// @param action that was requested
/// @param VFS context
/// @param Vnode being operated on
/// @param Parent Vnode. May be NULL.
/// @param Parent Vnode. May be nullptr.
/// @param Pointer to an errno-style error.
///
extern "C" int vnode_scope_callback(

View File

@@ -27,7 +27,7 @@ bool SantaDriver::start(IOService *provider) {
if (!santaDecisionManager->init() ||
santaDecisionManager->StartListener() != kIOReturnSuccess) {
santaDecisionManager->release();
santaDecisionManager = NULL;
santaDecisionManager = nullptr;
return false;
}
@@ -41,7 +41,7 @@ bool SantaDriver::start(IOService *provider) {
void SantaDriver::stop(IOService *provider) {
santaDecisionManager->StopListener();
santaDecisionManager->release();
santaDecisionManager = NULL;
santaDecisionManager = nullptr;
LOGI("Unloaded.");

View File

@@ -49,8 +49,8 @@ bool SantaDriverClient::start(IOService *provider) {
void SantaDriverClient::stop(IOService *provider) {
super::stop(provider);
myProvider = NULL;
decisionManager = NULL;
myProvider = nullptr;
decisionManager = nullptr;
}
IOReturn SantaDriverClient::clientClose() {
@@ -124,7 +124,7 @@ IOReturn SantaDriverClient::static_allow_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == NULL) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->allow_binary(
static_cast<const uint64_t>(*arguments->scalarInput));
@@ -143,7 +143,7 @@ IOReturn SantaDriverClient::static_deny_binary(
void *reference,
IOExternalMethodArguments *arguments) {
if (!target) return kIOReturnBadArgument;
if (arguments->scalarInput == NULL) return kIOReturnBadArgument;
if (arguments->scalarInput == nullptr) return kIOReturnBadArgument;
return target->deny_binary(
static_cast<const uint64_t>(*arguments->scalarInput));

View File

@@ -47,13 +47,21 @@ REGISTER_COMMAND_NAME(@"rule")
}
+ (NSString *)longHelpText {
return (@"Usage: santactl rule {add|remove} [options]\n"
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --message {message}: custom message\n"
@" --path {path}: path of binary to add\n"
@" --sha256 {sha256}: hash to add\n");
return (@"Usage: santactl rule [options]\n"
@" One of:\n"
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --remove: remove existing rule\n"
@"\n"
@" One of:\n"
@" --path {path}: path of binary/bundle to add/remove.\n"
@" Will add the hash of the file currently at that path.\n"
@" --sha256 {sha256}: hash to add/remove\n"
@"\n"
@" Optionally:\n"
@" --certificate: add certificate rule instead of binary\n"
@" --message {message}: custom message\n");
}
+ (void)printErrorUsageAndExit:(NSString *)error {
@@ -64,95 +72,72 @@ REGISTER_COMMAND_NAME(@"rule")
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
SNTConfigurator *config = [SNTConfigurator configurator];
// Ensure we have no privileges
if (!DropRootPrivileges()) {
printf("Failed to drop root privileges.\n");
exit(1);
}
if ([config syncBaseURL] != nil) {
printf("SyncBaseURL is set, rules are managed centrally.\n");
exit(1);
}
NSString *action = [arguments firstObject];
SNTRule *newRule = [[SNTRule alloc] init];
newRule.state = RULESTATE_UNKNOWN;
newRule.type = RULETYPE_BINARY;
// add or remove
if (!action) {
[self printErrorUsageAndExit:@"Missing action"];
}
NSString *path;
int state = RULESTATE_UNKNOWN;
// Parse arguments
for (NSUInteger i = 0; i < arguments.count ; i++ ) {
NSString *arg = arguments[i];
if ([action compare:@"add" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
} else if ([action compare:@"remove" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_REMOVE;
} else {
[self printErrorUsageAndExit:@"Unknown action"];
}
NSString *customMsg = @"";
NSString *SHA256 = nil;
NSString *filePath = nil;
// parse arguments
for (NSUInteger i = 1; i < [arguments count] ; i++ ) {
NSString* argument = [arguments objectAtIndex:i];
if ([argument compare:@"--whitelist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_WHITELIST;
} else if ([argument compare:@"--blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_BLACKLIST;
} else if ([argument compare:@"--silent-blacklist" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
state = RULESTATE_SILENT_BLACKLIST;
} else if ([argument compare:@"--message" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
if ([arg caseInsensitiveCompare:@"--whitelist"] == NSOrderedSame) {
newRule.state = RULESTATE_WHITELIST;
} else if ([arg caseInsensitiveCompare:@"--blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_BLACKLIST;
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
newRule.state = RULESTATE_SILENT_BLACKLIST;
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
newRule.state = RULESTATE_REMOVE;
} else if ([arg caseInsensitiveCompare:@"--certificate"] == NSOrderedSame) {
newRule.type = RULETYPE_CERT;
} else if ([arg caseInsensitiveCompare:@"--path"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"No message specified"];
[self printErrorUsageAndExit:@"--path requires an argument"];
}
customMsg = [arguments objectAtIndex:i];
} else if ([argument compare:@"--path" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
path = arguments[i];
} else if ([arg caseInsensitiveCompare:@"--sha256"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"No path specified"];
[self printErrorUsageAndExit:@"--sha256 requires an argument"];
}
filePath = [arguments objectAtIndex:i];
} else if ([argument compare:@"--sha256" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
newRule.shasum = arguments[i];
if (newRule.shasum.length != 64) {
[self printErrorUsageAndExit:@"--sha256 requires a valid SHA-256 as the argument"];
}
} else if ([arg caseInsensitiveCompare:@"--message"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"No SHA-256 specified"];
[self printErrorUsageAndExit:@"--message requires an argument"];
}
SHA256 = [arguments objectAtIndex:i];
newRule.customMsg = arguments[i];
} else {
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:argument]];
[self printErrorUsageAndExit:[@"Unknown argument: %@" stringByAppendingString:arg]];
}
}
if (state == RULESTATE_UNKNOWN) {
if (path) {
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithPath:path];
if (newRule.type == RULETYPE_BINARY) {
newRule.shasum = fi.SHA256;
} else if (newRule.type == RULETYPE_CERT) {
SNTCodesignChecker *cs = [[SNTCodesignChecker alloc] initWithBinaryPath:fi.path];
newRule.shasum = cs.leafCertificate.SHA256;
}
}
if (newRule.state == RULESTATE_UNKNOWN) {
[self printErrorUsageAndExit:@"No state specified"];
}
if (filePath) {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:filePath];
if (!fileInfo) {
[self printErrorUsageAndExit:@"Provided path is not a regular file or executable bundle"];
}
SHA256 = [fileInfo SHA256];
} else if (SHA256) {
} else {
} else if (!newRule.shasum) {
[self printErrorUsageAndExit:@"Either SHA-256 or path to file must be specified"];
}
SNTRule *newRule = [[SNTRule alloc] init];
newRule.shasum = SHA256;
newRule.state = state;
newRule.type = RULETYPE_BINARY;
newRule.customMsg = customMsg;
[[daemonConn remoteObjectProxy] databaseRuleAddRule:newRule cleanSlate:NO reply:^{
if (state == RULESTATE_REMOVE) {
if (newRule.state == RULESTATE_REMOVE) {
printf("Removed rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);
} else {
printf("Added rule for SHA-256: %s.\n", [newRule.shasum UTF8String]);

View File

@@ -14,6 +14,7 @@
#import "SNTCommandController.h"
#import "SNTConfigurator.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -37,12 +38,17 @@ REGISTER_COMMAND_NAME(@"status")
}
+ (NSString *)longHelpText {
return nil;
return (@"Provides details about Santa while it's running.\n"
@" Use --json to output in JSON format");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
dispatch_group_t group = dispatch_group_create();
// Daemon status
__block NSString *clientMode;
__block uint64_t cpuEvents, ramEvents;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] clientMode:^(santa_clientmode_t cm) {
switch (cm) {
case CLIENTMODE_MONITOR:
@@ -52,35 +58,102 @@ REGISTER_COMMAND_NAME(@"status")
default:
clientMode = [NSString stringWithFormat:@"Unknown (%d)", cm]; break;
}
dispatch_group_leave(group);
}];
do { usleep(5000); } while (!clientMode);
printf(">>> Daemon Info\n");
printf(" %-25s | %s\n", "Mode", [clientMode UTF8String]);
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogCPUEvents:^(uint64_t events) {
cpuEvents = events;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] watchdogRAMEvents:^(uint64_t events) {
ramEvents = events;
dispatch_group_leave(group);
}];
BOOL fileLogging = ([[SNTConfigurator configurator] fileChangesRegex] != nil);
// Kext status
__block int64_t cacheCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] cacheCount:^(int64_t count) {
cacheCount = count;
dispatch_group_leave(group);
}];
do { usleep(5000); } while (cacheCount == -1);
printf(">>> Kernel Info\n");
printf(" %-25s | %lld\n", "Kernel cache count", cacheCount);
// Database counts
__block int64_t eventCount = -1, binaryRuleCount = -1, certRuleCount = -1;
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(int64_t binary, int64_t certificate) {
binaryRuleCount = binary;
certRuleCount = certificate;
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[daemonConn remoteObjectProxy] databaseEventCount:^(int64_t count) {
eventCount = count;
dispatch_group_leave(group);
}];
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
printf(">>> Database Info\n");
printf(" %-25s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-25s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-25s | %lld\n", "Events Pending Upload", eventCount);
// Sync status
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"YYYY/MM/dd HH:mm:ss Z";
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] syncLastSuccess];
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
// Wait a maximum of 5s for stats collected from daemon to arrive.
if (dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5))) {
fprintf(stderr, "Failed to retrieve some stats from daemon\n\n");
}
if ([arguments containsObject:@"--json"]) {
NSDictionary *stats = @{
@"daemon": @{
@"mode": clientMode,
@"file_logging": @(fileLogging),
@"watchdog_cpu_events": @(cpuEvents),
@"watchdog_ram_events": @(ramEvents),
},
@"kernel": @{
@"cache_count": @(cacheCount),
},
@"database": @{
@"binary_rules": @(binaryRuleCount),
@"certificate_rules": @(certRuleCount),
@"events_pending_upload": @(eventCount),
},
@"sync": @{
@"server": syncURLStr,
@"clean_required": @(syncCleanReqd),
@"last_successful": lastSyncSuccessStr
},
};
NSData *statsData = [NSJSONSerialization dataWithJSONObject:stats
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *statsStr = [[NSString alloc] initWithData:statsData encoding:NSUTF8StringEncoding];
printf("%s\n", [statsStr UTF8String]);
} else {
printf(">>> Daemon Info\n");
printf(" %-22s | %s\n", "Mode", [clientMode UTF8String]);
printf(" %-22s | %s\n", "File Logging", (fileLogging ? "Yes" : "No"));
printf(" %-22s | %lld\n", "Watchdog CPU Events", cpuEvents);
printf(" %-22s | %lld\n", "Watchdog RAM Events", ramEvents);
printf(">>> Kernel Info\n");
printf(" %-22s | %lld\n", "Kernel cache count", cacheCount);
printf(">>> Database Info\n");
printf(" %-22s | %lld\n", "Binary Rules", binaryRuleCount);
printf(" %-22s | %lld\n", "Certificate Rules", certRuleCount);
printf(" %-22s | %lld\n", "Events Pending Upload", eventCount);
if (syncURLStr) {
printf(">>> Sync Info\n");
printf(" %-22s | %s\n", "Sync Server", [syncURLStr UTF8String]);
printf(" %-22s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
printf(" %-22s | %s\n", "Last Successful Sync", [lastSyncSuccessStr UTF8String]);
}
}
exit(0);
}

View File

@@ -31,6 +31,7 @@ extern NSString * const kClientModeMonitor;
extern NSString * const kClientModeLockdown;
extern NSString * const kCleanSync;
extern NSString * const kWhitelistRegex;
extern NSString * const kBlacklistRegex;
extern NSString * const kEvents;
extern NSString * const kFileSHA256;

View File

@@ -33,6 +33,7 @@ NSString * const kClientModeMonitor = @"MONITOR";
NSString * const kClientModeLockdown = @"LOCKDOWN";
NSString * const kCleanSync = @"clean_sync";
NSString * const kWhitelistRegex = @"whitelist_regex";
NSString * const kBlacklistRegex = @"blacklist_regex";
NSString * const kEvents = @"events";
NSString * const kFileSHA256 = @"file_sha256";

View File

@@ -44,11 +44,22 @@
} else {
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (syncState.newClientMode) {
[[daemonConn remoteObjectProxy] setClientMode:syncState.newClientMode reply:^{}];
}
NSString *backoffInterval = r[kBackoffInterval];
if (backoffInterval) {
[[daemonConn remoteObjectProxy] setNextSyncInterval:[backoffInterval intValue] reply:^{}];
}
if (syncState.cleanSync) {
[[daemonConn remoteObjectProxy] setSyncCleanRequired:NO reply:^{}];
}
// Update last sync success
[[daemonConn remoteObjectProxy] setSyncLastSuccess:[NSDate date] reply:^{}];
handler(YES);
}
}] resume];

View File

@@ -20,6 +20,7 @@
#import "NSData+Zlib.h"
#import "SNTCommandSyncConstants.h"
#import "SNTCommandSyncState.h"
#import "SNTConfigurator.h"
#import "SNTSystemInfo.h"
#import "SNTXPCConnection.h"
#import "SNTXPCControlInterface.h"
@@ -41,7 +42,9 @@
requestDict[kOSBuild] = [SNTSystemInfo osBuild];
requestDict[kPrimaryUser] = syncState.machineOwner;
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"]) {
// If user requested it or we've never had a successful sync, try from a clean slate.
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"] ||
[[SNTConfigurator configurator] syncCleanRequired]) {
requestDict[kRequestCleanSync] = @YES;
}
@@ -77,15 +80,19 @@
syncState.uploadLogURL = [NSURL URLWithString:r[kUploadLogsURL]];
if ([r[kClientMode] isEqual:kClientModeMonitor]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_MONITOR reply:^{}];
syncState.newClientMode = CLIENTMODE_MONITOR;
} else if ([r[kClientMode] isEqual:kClientModeLockdown]) {
[[daemonConn remoteObjectProxy] setClientMode:CLIENTMODE_LOCKDOWN reply:^{}];
syncState.newClientMode = CLIENTMODE_LOCKDOWN;
}
if ([r[kWhitelistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setWhitelistPathRegex:r[kWhitelistRegex] reply:^{}];
}
if ([r[kBlacklistRegex] isKindOfClass:[NSString class]]) {
[[daemonConn remoteObjectProxy] setBlacklistPathRegex:r[kBlacklistRegex] reply:^{}];
}
if ([r[kCleanSync] boolValue]) {
syncState.cleanSync = YES;
}

View File

@@ -12,6 +12,8 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "SNTCommonEnums.h"
/// An instance of this class is passed to each stage of the sync process for storing data
/// that might be needed in later stages.
@interface SNTCommandSyncState : NSObject
@@ -27,6 +29,9 @@
/// should be deleted before inserting any new rules.
@property BOOL cleanSync;
/// New client mode sent from server
@property santa_clientmode_t newClientMode;
/// Batch size for uploading events, sent from server
@property int32_t eventBatchSize;

View File

@@ -41,14 +41,30 @@ REGISTER_COMMAND_NAME(@"version")
}
+ (NSString *)longHelpText {
return nil;
return (@"Show versions of all Santa components.\n"
@" Use --json to output in JSON format.");
}
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
if ([arguments containsObject:@"--json"]) {
NSDictionary *versions = @{
@"santa-driver": [self santaKextVersion],
@"santad": [self santadVersion],
@"santactl": [self santactlVersion],
@"SantaGUI": [self santaAppVersion],
};
NSData *versionsData = [NSJSONSerialization dataWithJSONObject:versions
options:NSJSONWritingPrettyPrinted
error:nil];
NSString *versionsStr = [[NSString alloc] initWithData:versionsData
encoding:NSUTF8StringEncoding];
printf("%s\n", [versionsStr UTF8String]);
} else {
printf("%-15s | %s\n", "santa-driver", [[self santaKextVersion] UTF8String]);
printf("%-15s | %s\n", "santad", [[self santadVersion] UTF8String]);
printf("%-15s | %s\n", "santactl", [[self santactlVersion] UTF8String]);
printf("%-15s | %s\n", "SantaGUI", [[self santaAppVersion] UTF8String]);
}
exit(0);
}
@@ -58,7 +74,7 @@ REGISTER_COMMAND_NAME(@"version")
(__bridge CFArrayRef)@[ @"CFBundleVersion" ])
);
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
if (loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
}

View File

@@ -130,7 +130,9 @@
case ACTION_NOTIFY_RENAME:
case ACTION_NOTIFY_WRITE: {
dispatch_async(q, ^{
if ([[SNTConfigurator configurator] logFileChanges]) {
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self.eventLog logFileModification:message];
}
});

View File

@@ -23,6 +23,10 @@
#import "SNTRule.h"
#import "SNTRuleTable.h"
// Globals used by the santad watchdog thread
uint64_t watchdogCPUEvents = 0;
uint64_t watchdogRAMEvents = 0;
@interface SNTDaemonControlController ()
@property dispatch_source_t syncTimer;
@end
@@ -140,6 +144,16 @@
reply();
}
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
[[SNTConfigurator configurator] setSyncLastSuccess:date];
reply();
}
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply {
[[SNTConfigurator configurator] setSyncCleanRequired:cleanReqd];
reply();
}
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply {
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
@@ -148,4 +162,20 @@
reply();
}
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply {
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
options:0
error:NULL];
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
reply();
}
- (void)watchdogCPUEvents:(void (^)(uint64_t))reply {
reply(watchdogCPUEvents);
}
- (void)watchdogRAMEvents:(void (^)(uint64_t))reply {
reply(watchdogRAMEvents);
}
@end

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
// This is imported in the header rather than implementation to saves
// This is imported in the header rather than implementation to save
// classes that use this one from also having to import FMDB stuff.
#import <FMDB/FMDB.h>

View File

@@ -27,7 +27,16 @@
self = [super init];
if (self) {
[db inDatabase:^(FMDatabase *db) {
if (![db goodConnection]) {
[db close];
[[NSFileManager defaultManager] removeItemAtPath:[db databasePath] error:NULL];
[db open];
}
}];
_dbQ = db;
[self updateTableSchema];
}
return self;
@@ -51,7 +60,7 @@
uint32_t newVersion = [self initializeDatabase:db fromVersion:currentVersion];
if (newVersion < 1) return;
LOGD(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
LOGI(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
[db setUserVersion:newVersion];
}];

View File

@@ -14,6 +14,7 @@
#import "SNTKernelCommon.h"
@class SNTCachedDecision;
///

View File

@@ -14,7 +14,7 @@
#import "SNTEventLog.h"
#include <sys/stat.h>
#include <libproc.h>
#include <sys/sysctl.h>
#import "SNTCachedDecision.h"
@@ -69,11 +69,8 @@
}
case ACTION_NOTIFY_WRITE: {
action = @"WRITE";
struct stat filestat;
stat(message.path, &filestat);
if (filestat.st_size < 1024 * 1024) {
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
SNTFileInfo *fileInfo = [[SNTFileInfo alloc] initWithPath:path];
if (fileInfo.fileSize < 1024 * 1024) {
sha256 = fileInfo.SHA256;
} else {
sha256 = @"(too large)";
@@ -87,8 +84,13 @@
if (newpath) {
outStr = [outStr stringByAppendingFormat:@"|newpath=%@", [self sanitizeString:newpath]];
}
outStr = [outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|uid=%d|gid=%d",
message.pid, message.ppid, message.uid, message.gid];
char ppath[PATH_MAX];
if (proc_pidpath(message.pid, ppath, PATH_MAX) < 1) {
strncpy(ppath, "(null)", 6);
}
outStr =
[outStr stringByAppendingFormat:@"|pid=%d|ppid=%d|process=%s|processpath=%s|uid=%d|gid=%d",
message.pid, message.ppid, message.pname, ppath, message.uid, message.gid];
if (sha256) {
outStr = [outStr stringByAppendingFormat:@"|sha256=%@", sha256];
}

View File

@@ -137,10 +137,6 @@
santa_action_t action = [self actionForEventState:cd.decision];
if (action == ACTION_RESPOND_CHECKBW_ALLOW) [self.eventLog saveDecisionDetails:cd];
// Get name of parent process. Do this before responding to be sure parent doesn't go away.
char pname[PROC_PIDPATHINFO_MAXSIZE];
proc_name(message.ppid, pname, PROC_PIDPATHINFO_MAXSIZE);
// Send the decision to the kernel.
[self.driverManager postToKernelAction:action forVnodeID:cd.vnodeId];
@@ -158,7 +154,7 @@
se.signingChain = csInfo.certificates;
se.pid = @(message.pid);
se.ppid = @(message.ppid);
se.parentName = @(pname);
se.parentName = @(message.pname);
se.fileBundleID = [binInfo bundleIdentifier];
se.fileBundleName = [binInfo bundleName];

View File

@@ -16,12 +16,23 @@
#import "SNTCertificate.h"
#import "SNTCodesignChecker.h"
#import "SNTConfigurator.h"
#import "SNTLogging.h"
#import "SNTRule.h"
@interface SNTRuleTable ()
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
@implementation SNTRuleTable
- (uint32_t)initializeDatabase:(FMDatabase *)db fromVersion:(uint32_t)version {
// Save hashes of the signing certs for launchd and santad
self.santadCertSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
self.launchdCertSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
uint32_t newVersion = 0;
if (version < 1) {
@@ -40,14 +51,14 @@
// Insert the codesigning certs for the running santad and launchd into the initial database.
// This helps prevent accidentally denying critical system components while the database
// is empty. This 'initial database' will then be cleared on the first successful sync.
NSString *santadSHA = [[[[SNTCodesignChecker alloc] initWithSelf] leafCertificate] SHA256];
NSString *launchdSHA = [[[[SNTCodesignChecker alloc] initWithPID:1] leafCertificate] SHA256];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
santadSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.santadCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
[db executeUpdate:@"INSERT INTO rules (shasum, state, type) VALUES (?, ?, ?)",
launchdSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
self.launchdCertSHA, @(RULESTATE_WHITELIST), @(RULETYPE_CERT)];
newVersion = 1;
[[SNTConfigurator configurator] setSyncCleanRequired:YES];
}
return newVersion;
@@ -121,14 +132,28 @@
#pragma mark Adding
- (BOOL)addRules:(NSArray *)rules cleanSlate:(BOOL)cleanSlate {
__block BOOL failed = NO;
if (!rules || rules.count < 1) {
LOGE(@"Received request to add rules with nil/empty array.");
return NO;
}
__block BOOL failed = NO;
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Protect rules for santad/launchd certificates.
NSPredicate *p = [NSPredicate predicateWithFormat:
@"(SELF.shasum = %@ OR SELF.shasum = %@) AND SELF.type = %d",
self.santadCertSHA, self.launchdCertSHA, RULETYPE_CERT];
NSArray *requiredHashes = [rules filteredArrayUsingPredicate:p];
p = [NSPredicate predicateWithFormat:@"SELF.state == %d", RULESTATE_WHITELIST];
NSArray *requiredHashesWhitelist = [requiredHashes filteredArrayUsingPredicate:p];
if ((cleanSlate && requiredHashesWhitelist.count != 2) ||
(requiredHashes.count != requiredHashesWhitelist.count)) {
LOGE(@"Received request to remove whitelist for launchd/santad ceritifcates.");
*rollback = failed = YES;
return;
}
if (cleanSlate) {
[db executeUpdate:@"DELETE FROM rules"];
}

View File

@@ -19,6 +19,9 @@
#import "SNTApplication.h"
extern uint64_t watchdogCPUEvents;
extern uint64_t watchdogRAMEvents;
/// Converts a timeval struct to double, converting the microseconds value to seconds.
static inline double timeval_to_double(struct timeval tv) {
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
@@ -59,6 +62,7 @@ void *watchdogThreadFunction(__unused void *idata) {
if (percentage > cpuWarnThreshold) {
LOGW(@"Watchdog: potentially high CPU use, ~%.2f%% over last %d seconds.",
percentage, timeInterval);
watchdogCPUEvents++;
}
// RAM
@@ -67,6 +71,7 @@ void *watchdogThreadFunction(__unused void *idata) {
double ramUseMB = (double) taskInfo.resident_size / 1024 / 1024;
if (ramUseMB > memWarnThreshold && ramUseMB > prevRamUseMB) {
LOGW(@"Watchdog: potentially high RAM use, RSS is %.2fMB.", ramUseMB);
watchdogRAMEvents++;
}
prevRamUseMB = ramUseMB;
}
@@ -87,10 +92,6 @@ int main(int argc, const char *argv[]) {
return 0;
}
// Close stdout/stderr so logging goes to syslog
fclose(stdout);
fclose(stderr);
LOGI(@"Started, version %@", infoDict[@"CFBundleVersion"]);
SNTApplication *s = [[SNTApplication alloc] init];

View File

@@ -114,6 +114,8 @@
@"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-04-05 15:15:55 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-04-04 15:15:55 +0000"]);
XCTAssertTrue(sut.isCA);
XCTAssertEqualObjects(sut.serialNumber, @"146025");
sut = [[SNTCertificate alloc] initWithCertificateDataPEM:self.testDataPEM2];
XCTAssertNotNil(sut);
@@ -128,6 +130,8 @@
@"129d39ff4384197dc2bcbe1a83a69b3405b7df33254b1b1ee29a23847a23555a");
XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithString:@"2013-11-14 00:00:00 +0000"]);
XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithString:@"2015-11-14 23:59:59 +0000"]);
XCTAssertFalse(sut.isCA);
XCTAssertEqualObjects(sut.serialNumber, @"5E FA 67 0E 99 E4 AB 88 E0 F2 0B 33 86 7B 78 4D");
}
- (void)testInitWithValidPEMAfterKey {

View File

@@ -17,6 +17,11 @@
#import "SNTRule.h"
#import "SNTRuleTable.h"
@interface SNTRuleTable (Testing)
@property NSString *santadCertSHA;
@property NSString *launchdCertSHA;
@end
/// This test case actually tests SNTRuleTable and SNTRule
@interface SNTRuleTableTest : XCTestCase
@property SNTRuleTable *sut;
@@ -60,22 +65,36 @@
}
- (void)testAddRulesClean {
// If SNTRuleTable doesn't start with some rules, this test doesn't work properly.
XCTAssert(self.sut.ruleCount);
// Assert that insert without 'self' and launchd cert hashes fails
XCTAssertFalse([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES]);
[self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:YES];
// Now add a binary rule without clean slate
XCTAssertTrue([self.sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO]);
XCTAssertEqual(self.sut.ruleCount, 1);
XCTAssertEqual(self.sut.binaryRuleCount, 1);
// Now add a cert rule + the required rules as a clean slate,
// assert that the binary rule was removed
SNTRule *r1 = [[SNTRule alloc] init];
r1.shasum = self.sut.launchdCertSHA;
r1.state = RULESTATE_WHITELIST;
r1.type = RULETYPE_CERT;
SNTRule *r2 = [[SNTRule alloc] init];
r2.shasum = self.sut.santadCertSHA;
r2.state = RULESTATE_WHITELIST;
r2.type = RULETYPE_CERT;
XCTAssertTrue(([self.sut addRules:@[ [self _exampleCertRule], r1, r2 ] cleanSlate:YES]));
XCTAssertEqual([self.sut binaryRuleCount], 0);
}
- (void)testAddMultipleRules {
NSUInteger ruleCount = self.sut.ruleCount;
[self.sut addRules:@[ [self _exampleBinaryRule],
[self _exampleCertRule],
[self _exampleBinaryRule] ]
cleanSlate:YES];
cleanSlate:NO];
XCTAssertEqual(self.sut.ruleCount, 2);
XCTAssertEqual(self.sut.ruleCount, ruleCount + 2);
}
- (void)testAddRulesEmptyArray {
@@ -87,7 +106,7 @@
}
- (void)testFetchBinaryRule {
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
SNTRule *r = [self.sut binaryRuleForSHA256:@"a"];
XCTAssertNotNil(r);
@@ -99,7 +118,7 @@
}
- (void)testFetchCertificateRule {
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:YES];
[self.sut addRules:@[ [self _exampleBinaryRule], [self _exampleCertRule] ] cleanSlate:NO];
SNTRule *r = [self.sut certificateRuleForSHA256:@"b"];
XCTAssertNotNil(r);
@@ -110,4 +129,18 @@
XCTAssertNil(r);
}
- (void)testBadDatabase {
NSString *dbPath = [NSTemporaryDirectory() stringByAppendingString:@"sntruletabletest_baddb.db"];
[@"some text" writeToFile:dbPath atomically:YES encoding:NSUTF8StringEncoding error:NULL];
FMDatabaseQueue *dbq = [[FMDatabaseQueue alloc] initWithPath:dbPath];
SNTRuleTable *sut = [[SNTRuleTable alloc] initWithDatabaseQueue:dbq];
[sut addRules:@[ [self _exampleBinaryRule] ] cleanSlate:NO];
XCTAssertGreaterThan(sut.ruleCount, 0);
[[NSFileManager defaultManager] removeItemAtPath:dbPath error:NULL];
}
@end