mirror of
https://github.com/google/santa.git
synced 2026-04-24 03:00:12 -04:00
Initial commit
This commit is contained in:
73
Source/SantaGUI/Resources/AboutWindow.xib
Normal file
73
Source/SantaGUI/Resources/AboutWindow.xib
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTAboutWindowController">
|
||||
<connections>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Santa" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="200"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1578"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BnL-ZS-kXw">
|
||||
<rect key="frame" x="199" y="140" width="83" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="UK2-2L-lPx"/>
|
||||
<constraint firstAttribute="width" constant="79" id="lDf-D7-qlY"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Santa" id="VVj-gU-bzy">
|
||||
<font key="font" size="34" name="HelveticaNeue-UltraLight"/>
|
||||
<color key="textColor" red="0.1869618941" green="0.1869618941" blue="0.1869618941" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uh6-q0-RzL">
|
||||
<rect key="frame" x="18" y="65" width="444" height="60"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="CcT-ul-1eA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="title">Santa is a binary whitelisting system for Mac OS X.
|
||||
|
||||
There are no user-configurable settings.</string>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Udo-BY-n7e">
|
||||
<rect key="frame" x="196" y="21" width="88" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="76" id="2Xc-ax-2bV"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="push" title="OK" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="uSw-o1-lWW">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="orderOut:" target="-1" id="6oW-zI-zn5"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="BnL-ZS-kXw" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="Fj1-SG-mzF"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Udo-BY-n7e" secondAttribute="bottom" constant="28" id="bpF-hC-haN"/>
|
||||
<constraint firstItem="BnL-ZS-kXw" firstAttribute="centerX" secondItem="Udo-BY-n7e" secondAttribute="centerX" constant="0.5" id="csK-2p-W94"/>
|
||||
<constraint firstItem="BnL-ZS-kXw" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="kez-S0-6Gg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="323" y="317"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "128x128",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "256x256",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-512.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "santa-hat-icon-512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "512x512",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
202
Source/SantaGUI/Resources/MessageWindow.xib
Normal file
202
Source/SantaGUI/Resources/MessageWindow.xib
Normal file
@@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6250" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6250"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="SNTMessageWindowController">
|
||||
<connections>
|
||||
<outlet property="window" destination="9Bq-yh-54f" id="Uhs-WF-TV9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<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="550" height="275"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
|
||||
<view key="contentView" id="Iwq-Lx-rLv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="550" height="275"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t8c-Fx-e5h">
|
||||
<rect key="frame" x="234" y="210" width="83" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="KoG-v6-GfK"/>
|
||||
<constraint firstAttribute="width" constant="79" id="oS3-CE-1vv"/>
|
||||
</constraints>
|
||||
<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"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cD5-Su-lXR">
|
||||
<rect key="frame" x="23" y="168" width="504" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="500" id="q9O-xW-hnS"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" title="A message to the user goes here..." allowsEditingTextAttributes="YES" id="5tH-bG-UJA">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" red="0.40000000000000002" green="0.40000000000000002" blue="0.40000000000000002" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.attributedCustomMessage" id="NH1-gV-Cor">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">The following application has been blocked from executing because its trustworthiness cannot be determined.</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="GYD-v8-fqH">
|
||||
<rect key="frame" x="31" y="91" width="32" height="32"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSBonjour" id="jKM-qY-7mp"/>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.bundleIcon" id="X4L-aD-P21">
|
||||
<dictionary key="options">
|
||||
<bool key="NSConditionallySetsEnabled" value="NO"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d9e-Wv-Y5H">
|
||||
<rect key="frame" x="111" y="126" width="34" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Path" id="KgY-X1-ESG">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="pc8-G9-4pJ">
|
||||
<rect key="frame" x="154" y="126" width="350" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="346" id="BYY-2q-Lmb"/>
|
||||
</constraints>
|
||||
<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"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.event.path" id="4Nh-Ue-aCb"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEB-eH-x2Y">
|
||||
<rect key="frame" x="96" y="99" width="46" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="SHA-1" id="eKN-Ic-5zy">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PXc-xv-A28">
|
||||
<rect key="frame" x="155" y="99" width="88" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Binary SHA-1" id="X4W-9e-eIu">
|
||||
<font key="font" metaFont="system"/>
|
||||
<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.SHA1" id="KuE-WW-9av"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" verticalCompressionResistancePriority="499" translatesAutoresizingMaskIntoConstraints="NO" id="lvJ-Rk-UT5">
|
||||
<rect key="frame" x="78" y="72" width="66" height="17"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Publisher" id="yL9-yD-JXX">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button toolTip="Show code signing certificate chain" translatesAutoresizingMaskIntoConstraints="NO" id="cJf-k6-OxS">
|
||||
<rect key="frame" x="322" y="75" width="10" height="10"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="10" id="c3b-iv-bWa"/>
|
||||
<constraint firstAttribute="width" constant="10" id="fXl-na-Lwx"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="regularSquare" image="NSFollowLinkFreestandingTemplate" 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"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showCertInfo:" target="-2" id="dB0-a3-X31"/>
|
||||
<binding destination="-2" name="hidden" keyPath="self.binaryCert" id="xpJ-jl-aUN">
|
||||
<dictionary key="options">
|
||||
<string key="NSValueTransformerName">NSIsNil</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="BbV-3h-mmL">
|
||||
<rect key="frame" x="220" y="23" width="110" height="25"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="110" id="HdL-6x-X4f"/>
|
||||
<constraint firstAttribute="height" constant="22" id="YYm-GI-ojT"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="roundTextured" title="OK" 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"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
DQ
|
||||
</string>
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="closeWindow:" target="-2" id="qQq-gh-8lw"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="C3G-wL-u7w">
|
||||
<rect key="frame" x="154" y="72" width="159" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="17" id="re0-7U-qcL"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" 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"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="self.binaryCert" id="eFt-oy-SXL">
|
||||
<dictionary key="options">
|
||||
<string key="NSNullPlaceholder">Not code-signed</string>
|
||||
</dictionary>
|
||||
</binding>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="d9e-Wv-Y5H" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="113" id="3oY-g4-wHW"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" priority="800" constant="52" id="42Z-62-hKo"/>
|
||||
<constraint firstItem="cD5-Su-lXR" firstAttribute="top" secondItem="t8c-Fx-e5h" secondAttribute="bottom" constant="25" id="4Hn-vu-fva"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="bottom" constant="10" id="7Pr-bA-HgG"/>
|
||||
<constraint firstItem="PXc-xv-A28" firstAttribute="top" secondItem="pc8-G9-4pJ" secondAttribute="bottom" constant="10" id="8LX-e8-bKv"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="d9e-Wv-Y5H" secondAttribute="top" id="94E-d6-Jrg"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="leading" secondItem="d9e-Wv-Y5H" secondAttribute="trailing" constant="13" id="A6N-gA-dt5"/>
|
||||
<constraint firstItem="KEB-eH-x2Y" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="98" id="CYj-Gm-XZp"/>
|
||||
<constraint firstItem="PXc-xv-A28" firstAttribute="leading" secondItem="KEB-eH-x2Y" secondAttribute="trailing" constant="17" id="IGi-bx-nBP"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="80" id="O3p-RO-0ZJ"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="centerY" secondItem="cJf-k6-OxS" secondAttribute="centerY" constant="-1" id="R0U-iV-5Fx"/>
|
||||
<constraint firstAttribute="centerX" secondItem="t8c-Fx-e5h" secondAttribute="centerX" id="SHu-BF-01V"/>
|
||||
<constraint firstAttribute="centerX" secondItem="BbV-3h-mmL" secondAttribute="centerX" id="UAx-Xk-9DE"/>
|
||||
<constraint firstItem="KEB-eH-x2Y" firstAttribute="top" secondItem="PXc-xv-A28" secondAttribute="top" id="YiW-o8-HZ2"/>
|
||||
<constraint firstItem="BbV-3h-mmL" firstAttribute="top" secondItem="C3G-wL-u7w" secondAttribute="bottom" constant="25" id="Zxm-Pa-Ryj"/>
|
||||
<constraint firstItem="lvJ-Rk-UT5" firstAttribute="top" secondItem="C3G-wL-u7w" secondAttribute="top" id="adm-oT-FAf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="BbV-3h-mmL" secondAttribute="bottom" constant="25" id="awW-Dh-Xl4"/>
|
||||
<constraint firstItem="GYD-v8-fqH" firstAttribute="leading" secondItem="Iwq-Lx-rLv" secondAttribute="leading" constant="31" id="btT-jY-NXw"/>
|
||||
<constraint firstItem="GYD-v8-fqH" firstAttribute="centerY" secondItem="KEB-eH-x2Y" secondAttribute="centerY" id="cOS-EE-Mw8"/>
|
||||
<constraint firstItem="C3G-wL-u7w" firstAttribute="leading" secondItem="lvJ-Rk-UT5" secondAttribute="trailing" constant="14" id="ewf-Pg-nRK"/>
|
||||
<constraint firstAttribute="centerX" secondItem="cD5-Su-lXR" secondAttribute="centerX" id="goV-ub-zwi"/>
|
||||
<constraint firstItem="t8c-Fx-e5h" firstAttribute="top" secondItem="Iwq-Lx-rLv" secondAttribute="top" constant="25" id="mY6-FP-uEK"/>
|
||||
<constraint firstItem="pc8-G9-4pJ" firstAttribute="top" secondItem="cD5-Su-lXR" secondAttribute="bottom" constant="25" id="pfg-1u-Yfj"/>
|
||||
<constraint firstItem="cJf-k6-OxS" firstAttribute="leading" secondItem="C3G-wL-u7w" secondAttribute="trailing" constant="11" id="sMW-KK-A28"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSBonjour" width="32" height="32"/>
|
||||
<image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
|
||||
</resources>
|
||||
</document>
|
||||
32
Source/SantaGUI/Resources/Santa-Info.plist
Normal file
32
Source/SantaGUI/Resources/Santa-Info.plist
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.${PRODUCT_NAME:rfc1034identifier}GUI</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Google, Inc.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
3
Source/SantaGUI/Resources/Santa-Prefix.pch
Normal file
3
Source/SantaGUI/Resources/Santa-Prefix.pch
Normal file
@@ -0,0 +1,3 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
17
Source/SantaGUI/SNTAboutWindowController.h
Normal file
17
Source/SantaGUI/SNTAboutWindowController.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
@interface SNTAboutWindowController : NSWindowController
|
||||
|
||||
@end
|
||||
23
Source/SantaGUI/SNTAboutWindowController.m
Normal file
23
Source/SantaGUI/SNTAboutWindowController.m
Normal file
@@ -0,0 +1,23 @@
|
||||
/// Copyright 2014 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 "SNTAboutWindowController.h"
|
||||
|
||||
@implementation SNTAboutWindowController
|
||||
|
||||
- (instancetype)init {
|
||||
return [super initWithWindowNibName:@"AboutWindow"];
|
||||
}
|
||||
|
||||
@end
|
||||
17
Source/SantaGUI/SNTAppDelegate.h
Normal file
17
Source/SantaGUI/SNTAppDelegate.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Initiates and manages the connection to santad
|
||||
@interface SNTAppDelegate : NSObject<NSApplicationDelegate>
|
||||
@end
|
||||
83
Source/SantaGUI/SNTAppDelegate.m
Normal file
83
Source/SantaGUI/SNTAppDelegate.m
Normal file
@@ -0,0 +1,83 @@
|
||||
/// Copyright 2014 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 "SNTAppDelegate.h"
|
||||
|
||||
#import "SNTAboutWindowController.h"
|
||||
#import "SNTNotificationManager.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
|
||||
@interface SNTAppDelegate ()
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@property SNTNotificationManager *notificationManager;
|
||||
@property SNTXPCConnection *listener;
|
||||
@end
|
||||
|
||||
@implementation SNTAppDelegate
|
||||
|
||||
#pragma mark App Delegate methods
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[self setupMenu];
|
||||
self.aboutWindowController = [[SNTAboutWindowController alloc] init];
|
||||
self.notificationManager = [[SNTNotificationManager alloc] init];
|
||||
[self createConnection];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag {
|
||||
[self.aboutWindowController showWindow:self];
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark Connection handling
|
||||
|
||||
- (void)createConnection {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
||||
self.listener =
|
||||
[[SNTXPCConnection alloc] initClientWithName:[SNTXPCNotifierInterface serviceId]
|
||||
options:NSXPCConnectionPrivileged];
|
||||
self.listener.exportedInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
self.listener.exportedObject = self.notificationManager;
|
||||
self.listener.rejectedHandler = ^{
|
||||
[weakSelf performSelectorInBackground:@selector(attemptReconnection)
|
||||
withObject:nil];
|
||||
};
|
||||
self.listener.invalidationHandler = self.listener.rejectedHandler;
|
||||
[self.listener resume];
|
||||
}
|
||||
|
||||
- (void)attemptReconnection {
|
||||
// TODO(rah): Make this smarter.
|
||||
sleep(10);
|
||||
[self createConnection];
|
||||
}
|
||||
|
||||
#pragma mark Menu Management
|
||||
|
||||
- (void)setupMenu {
|
||||
// Whilst the user will never see the menu, having one with the Copy and Select All options
|
||||
// allows the shortcuts for these items to work, which is useful for being able to copy
|
||||
// information from notifications. The mainMenu must have a nested menu for this to work properly.
|
||||
NSMenu *mainMenu = [[NSMenu alloc] init];
|
||||
NSMenu *editMenu = [[NSMenu alloc] init];
|
||||
[editMenu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
|
||||
[editMenu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
|
||||
NSMenuItem *editMenuItem = [[NSMenuItem alloc] init];
|
||||
[editMenuItem setSubmenu:editMenu];
|
||||
[mainMenu addItem:editMenuItem];
|
||||
[NSApp setMainMenu:mainMenu];
|
||||
}
|
||||
|
||||
@end
|
||||
24
Source/SantaGUI/SNTMessageWindow.h
Normal file
24
Source/SantaGUI/SNTMessageWindow.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// An NSPanel that can become key/main and can fade in/out.
|
||||
@interface SNTMessageWindow : NSPanel
|
||||
|
||||
/// Fade the window in
|
||||
- (IBAction)fadeIn:(id)sender;
|
||||
|
||||
/// Fade the window out
|
||||
- (IBAction)fadeOut:(id)sender;
|
||||
|
||||
@end
|
||||
53
Source/SantaGUI/SNTMessageWindow.m
Normal file
53
Source/SantaGUI/SNTMessageWindow.m
Normal file
@@ -0,0 +1,53 @@
|
||||
/// Copyright 2014 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 "SNTMessageWindow.h"
|
||||
|
||||
@implementation SNTMessageWindow
|
||||
|
||||
- (BOOL)canBecomeKeyWindow {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)canBecomeMainWindow {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (IBAction)fadeIn:(id)sender {
|
||||
[self setAlphaValue:0.f];
|
||||
[self makeKeyAndOrderFront:sender];
|
||||
[NSAnimationContext beginGrouping];
|
||||
[[NSAnimationContext currentContext] setDuration:0.15f];
|
||||
[[NSAnimationContext currentContext] setCompletionHandler:^{
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
}];
|
||||
[[self animator] setAlphaValue:1.f];
|
||||
[NSAnimationContext endGrouping];
|
||||
}
|
||||
|
||||
- (IBAction)fadeOut:(id)sender {
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
|
||||
[NSAnimationContext beginGrouping];
|
||||
[[NSAnimationContext currentContext] setDuration:0.15f];
|
||||
[[NSAnimationContext currentContext] setCompletionHandler:^{
|
||||
[weakSelf.windowController windowWillClose:nil];
|
||||
[weakSelf orderOut:nil];
|
||||
[weakSelf setAlphaValue:1.f];
|
||||
}];
|
||||
[[self animator] setAlphaValue:0.f];
|
||||
[NSAnimationContext endGrouping];
|
||||
}
|
||||
|
||||
@end
|
||||
45
Source/SantaGUI/SNTMessageWindowController.h
Normal file
45
Source/SantaGUI/SNTMessageWindowController.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTNotificationMessage;
|
||||
|
||||
@protocol SNTMessageWindowControllerDelegate
|
||||
- (void)windowDidClose;
|
||||
@end
|
||||
|
||||
/// Controller for a single message window.
|
||||
@interface SNTMessageWindowController : NSWindowController
|
||||
|
||||
- (instancetype)initWithEvent:(SNTNotificationMessage *)event;
|
||||
|
||||
- (IBAction)showWindow:(id)sender;
|
||||
- (IBAction)closeWindow:(id)sender;
|
||||
- (IBAction)showCertInfo:(id)sender;
|
||||
|
||||
/// The execution event that this window is for
|
||||
@property SNTNotificationMessage *event;
|
||||
|
||||
/// The delegate to inform when the notification is dismissed
|
||||
@property(weak) id<SNTMessageWindowControllerDelegate> delegate;
|
||||
|
||||
/// A 'friendly' string representing the certificate information
|
||||
@property(readonly) IBOutlet NSString *binaryCert;
|
||||
|
||||
/// An optional message to display with this block.
|
||||
@property(readonly) IBOutlet NSAttributedString *attributedCustomMessage;
|
||||
|
||||
/// If the binary is part of a bundle, this is the icon for that bundle
|
||||
@property(readonly) IBOutlet NSImage *bundleIcon;
|
||||
|
||||
@end
|
||||
118
Source/SantaGUI/SNTMessageWindowController.m
Normal file
118
Source/SantaGUI/SNTMessageWindowController.m
Normal file
@@ -0,0 +1,118 @@
|
||||
/// Copyright 2014 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 "SNTMessageWindowController.h"
|
||||
|
||||
#import <SecurityInterface/SFCertificatePanel.h>
|
||||
|
||||
#import "SNTBinaryInfo.h"
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTMessageWindow.h"
|
||||
#import "SNTNotificationMessage.h"
|
||||
|
||||
@implementation SNTMessageWindowController
|
||||
|
||||
- (instancetype)initWithEvent:(SNTNotificationMessage *)event {
|
||||
self = [super initWithWindowNibName:@"MessageWindow"];
|
||||
if (self) {
|
||||
_event = event;
|
||||
[self.window setMovableByWindowBackground:NO];
|
||||
[self.window setLevel:NSPopUpMenuWindowLevel];
|
||||
[self.window center];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(id)sender {
|
||||
[(SNTMessageWindow *)self.window fadeIn:sender];
|
||||
}
|
||||
|
||||
- (IBAction)closeWindow:(id)sender {
|
||||
[(SNTMessageWindow *)self.window fadeOut:sender];
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(NSNotification *)notification {
|
||||
if (self.delegate) [self.delegate windowDidClose];
|
||||
}
|
||||
|
||||
- (IBAction)showCertInfo:(id)sender {
|
||||
// SFCertificatePanel expects an NSArray of SecCertificateRef's
|
||||
NSMutableArray *certArray = [NSMutableArray arrayWithCapacity:[self.event.certificates count]];
|
||||
for (SNTCertificate *cert in self.event.certificates) {
|
||||
[certArray addObject:(id)cert.certRef];
|
||||
}
|
||||
|
||||
[[[SFCertificatePanel alloc] init] beginSheetForWindow:self.window
|
||||
modalDelegate:nil
|
||||
didEndSelector:nil
|
||||
contextInfo:nil
|
||||
certificates:certArray
|
||||
showGroup:YES];
|
||||
}
|
||||
|
||||
#pragma mark Generated properties
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
|
||||
if (! [key isEqualToString:@"event"]) {
|
||||
return [NSSet setWithObject:@"event"];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)binaryCert {
|
||||
SNTCertificate *leafCert = self.event.leafCertificate;
|
||||
|
||||
if (leafCert.commonName && leafCert.orgName) {
|
||||
return [NSString stringWithFormat:@"%@ - %@", leafCert.commonName, leafCert.orgName];
|
||||
} else if (leafCert.commonName) {
|
||||
return leafCert.commonName;
|
||||
} else if (leafCert.orgName) {
|
||||
return leafCert.orgName;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedCustomMessage {
|
||||
if (self.event.customMessage) {
|
||||
NSString *htmlHeader = @"<html><head><style>"
|
||||
@"body {"
|
||||
@" font-family: 'Lucida Grande', 'Helvetica', sans-serif;"
|
||||
@" font-size: 13px;"
|
||||
@" color: #666;"
|
||||
@" text-align: center;"
|
||||
@"}"
|
||||
@"</style></head><body>";
|
||||
NSString *htmlFooter = @"</body></html>";
|
||||
NSString *fullHtml = [NSString stringWithFormat:@"%@%@%@", htmlHeader,
|
||||
self.event.customMessage, htmlFooter];
|
||||
NSData *htmlData = [fullHtml dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSAttributedString *returnStr = [[NSAttributedString alloc] initWithHTML:htmlData
|
||||
documentAttributes:NULL];
|
||||
return returnStr;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSImage *)bundleIcon {
|
||||
SNTBinaryInfo *bi = [[SNTBinaryInfo alloc] initWithPath:self.event.path];
|
||||
|
||||
if (!bi || !bi.bundle) return nil;
|
||||
|
||||
return [[NSWorkspace sharedWorkspace] iconForFile:bi.bundlePath];
|
||||
}
|
||||
|
||||
@end
|
||||
22
Source/SantaGUI/SNTNotificationManager.h
Normal file
22
Source/SantaGUI/SNTNotificationManager.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/// Copyright 2014 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 "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>
|
||||
|
||||
|
||||
@end
|
||||
81
Source/SantaGUI/SNTNotificationManager.m
Normal file
81
Source/SantaGUI/SNTNotificationManager.m
Normal file
@@ -0,0 +1,81 @@
|
||||
/// Copyright 2014 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 "SNTNotificationManager.h"
|
||||
|
||||
#import "SNTNotificationMessage.h"
|
||||
|
||||
@interface SNTNotificationManager ()
|
||||
/// The currently displayed notification
|
||||
@property SNTMessageWindowController *currentWindowController;
|
||||
|
||||
/// The queue of pending notifications
|
||||
@property(readonly) NSMutableArray *pendingNotifications;
|
||||
@end
|
||||
|
||||
@implementation SNTNotificationManager
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_pendingNotifications = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)windowDidClose {
|
||||
[self.pendingNotifications removeObject:self.currentWindowController];
|
||||
self.currentWindowController = nil;
|
||||
|
||||
if ([self.pendingNotifications count]) {
|
||||
self.currentWindowController = [self.pendingNotifications firstObject];
|
||||
[self.currentWindowController showWindow:self];
|
||||
} else {
|
||||
[NSApp hide:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark SNTNotifierXPC protocol methods
|
||||
|
||||
- (void)postBlockNotification:(SNTNotificationMessage *)event {
|
||||
// See if this binary is already in the list of pending notifications.
|
||||
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"event.SHA1==%@", event.SHA1];
|
||||
if ([[self.pendingNotifications filteredArrayUsingPredicate:predicate] count]) return;
|
||||
|
||||
// Notifications arrive on a background thread but UI updates must happen on the main thread.
|
||||
// This includes making windows.
|
||||
[self performSelectorOnMainThread:@selector(postBlockNotificationMainThread:)
|
||||
withObject:event
|
||||
waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)postBlockNotificationMainThread:(SNTNotificationMessage *)event {
|
||||
// Create message window
|
||||
SNTMessageWindowController *pendingMsg = [[SNTMessageWindowController alloc] initWithEvent:event];
|
||||
pendingMsg.delegate = self;
|
||||
[self.pendingNotifications addObject:pendingMsg];
|
||||
|
||||
// If a notification isn't currently being displayed, display the incoming one.
|
||||
if (!self.currentWindowController) {
|
||||
self.currentWindowController = pendingMsg;
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
|
||||
// It's quite likely that we're currently on a background thread, and GUI code should always be
|
||||
// on main thread. Open the window on the main thread so any code it runs is also.
|
||||
[pendingMsg showWindow:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
25
Source/SantaGUI/main.m
Normal file
25
Source/SantaGUI/main.m
Normal file
@@ -0,0 +1,25 @@
|
||||
/// Copyright 2014 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 "SNTAppDelegate.h"
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
@autoreleasepool {
|
||||
NSApplication *app = [NSApplication sharedApplication];
|
||||
SNTAppDelegate *delegate = [[SNTAppDelegate alloc] init];
|
||||
[app setDelegate:delegate];
|
||||
[app finishLaunching];
|
||||
[app run];
|
||||
}
|
||||
}
|
||||
73
Source/common/SNTBinaryInfo.h
Normal file
73
Source/common/SNTBinaryInfo.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// SNTBinaryInfo represents a binary on disk, providing access to details about that binary such as
|
||||
/// the SHA-1, the Info.plist and the Mach-O data.
|
||||
@interface SNTBinaryInfo : NSObject
|
||||
|
||||
/// Designated initializer
|
||||
- (instancetype)initWithPath:(NSString *)path;
|
||||
|
||||
/// Return SHA-1 hash of this binary
|
||||
- (NSString *)SHA1;
|
||||
|
||||
/// Returns the type of Mach-O file:
|
||||
/// Dynamic Library, Kernel Extension, Fat Binary, Thin Binary
|
||||
- (NSString *)machoType;
|
||||
|
||||
/// Returns the architectures included in this binary (e.g. x86_64, ppc)
|
||||
- (NSArray *)architectures;
|
||||
|
||||
/// Returns YES if this file is a Mach-O file
|
||||
- (BOOL)isMachO;
|
||||
|
||||
/// Returns YES if this file contains multiple architectures
|
||||
- (BOOL)isFat;
|
||||
|
||||
/// Returns YES if this file is an executable Mach-O file
|
||||
- (BOOL)isExecutable;
|
||||
|
||||
/// Returns YES if this file is a dynamic library
|
||||
- (BOOL)isDylib;
|
||||
|
||||
/// Returns YES if this file is a kernel extension
|
||||
- (BOOL)isKext;
|
||||
|
||||
/// Returns YES if this file is a script (e.g. it begins #!)
|
||||
- (BOOL)isScript;
|
||||
|
||||
/// Returns an NSBundle if this file is part of a bundle.
|
||||
- (NSBundle *)bundle;
|
||||
|
||||
/// Returns the path to the bundle this file is a part of, if any.
|
||||
- (NSString *)bundlePath;
|
||||
|
||||
/// Returns either the Info.plist in the bundle this file is part of, or an embedded plist if there
|
||||
/// is one. In the odd case that a file has both an embedded Info.plist and is part of a bundle,
|
||||
/// the Info.plist from the bundle will be returned.
|
||||
- (NSDictionary *)infoPlist;
|
||||
|
||||
/// Returns the CFBundleIdentifier from this file's Info.plist
|
||||
- (NSString *)bundleIdentifier;
|
||||
|
||||
/// Returns the CFBundleName from this file's Info.plist
|
||||
- (NSString *)bundleName;
|
||||
|
||||
/// Returns the CFBundleVersion from this file's Info.plist
|
||||
- (NSString *)bundleVersion;
|
||||
|
||||
/// Returns the CFBundleShortVersionString from this file's Info.plist
|
||||
- (NSString *)bundleShortVersionString;
|
||||
|
||||
@end
|
||||
291
Source/common/SNTBinaryInfo.m
Normal file
291
Source/common/SNTBinaryInfo.m
Normal file
@@ -0,0 +1,291 @@
|
||||
/// Copyright 2014 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 "SNTBinaryInfo.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/swap.h>
|
||||
|
||||
@interface SNTBinaryInfo ()
|
||||
@property NSString *path;
|
||||
@property NSData *fileData;
|
||||
@property NSBundle *bundleRef;
|
||||
@property NSDictionary *infoDict;
|
||||
@end
|
||||
|
||||
@implementation SNTBinaryInfo
|
||||
|
||||
- (instancetype)initWithPath:(NSString *)path {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_path = path;
|
||||
|
||||
_fileData = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:nil];
|
||||
if (!_fileData) return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)SHA1 {
|
||||
unsigned char sha1[CC_SHA1_DIGEST_LENGTH];
|
||||
CC_SHA1([self.fileData bytes], (unsigned int)[self.fileData length], sha1);
|
||||
|
||||
// Convert the binary SHA into hex
|
||||
NSMutableString *buf = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
|
||||
[buf appendFormat:@"%02x", (unsigned char)sha1[i]];
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
- (NSString *)machoType {
|
||||
if ([self isDylib]) { return @"Dynamic Library"; }
|
||||
if ([self isKext]) { return @"Kernel Extension"; }
|
||||
if ([self isFat]) { return @"Fat Binary"; }
|
||||
if ([self isMachO]) { return @"Thin Binary"; }
|
||||
if ([self isScript]) { return @"Script"; }
|
||||
return @"Unknown (not executable?)";
|
||||
}
|
||||
|
||||
- (NSArray *)architectures {
|
||||
if (![self isMachO]) return nil;
|
||||
|
||||
if ([self isFat]) {
|
||||
NSMutableArray *ret = [[NSMutableArray alloc] init];
|
||||
|
||||
// Retrieve just the fat_header, if possible.
|
||||
NSData *head = [self safeSubdataWithRange:NSMakeRange(0, sizeof(struct fat_header))];
|
||||
if (!head) return nil;
|
||||
struct fat_header *fat_header = (struct fat_header *)[head bytes];
|
||||
|
||||
// Get number of architectures in the binary
|
||||
uint32_t narch = NSSwapBigIntToHost(fat_header->nfat_arch);
|
||||
|
||||
// Retrieve just the fat_arch's, make a mutable copy and if necessary swap the bytes
|
||||
NSData *archs = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
|
||||
sizeof(struct fat_arch) * narch)];
|
||||
if (!archs) return nil;
|
||||
struct fat_arch *fat_archs = (struct fat_arch *)[archs bytes];
|
||||
|
||||
// For each arch, get the name of it's architecture
|
||||
for (int i = 0; i < narch; ++i) {
|
||||
[ret addObject:[self nameForCPUType:NSSwapBigIntToHost(fat_archs[i].cputype)]];
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
struct mach_header *hdr = [self firstMachHeader];
|
||||
return @[ [self nameForCPUType:hdr->cputype] ];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)isDylib {
|
||||
struct mach_header *mach_header = [self firstMachHeader];
|
||||
if (!mach_header) return NO;
|
||||
if (mach_header->filetype == MH_DYLIB ||
|
||||
mach_header->filetype == MH_FVMLIB) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isKext {
|
||||
struct mach_header *mach_header = [self firstMachHeader];
|
||||
if (!mach_header) return NO;
|
||||
if (mach_header->filetype == MH_KEXT_BUNDLE) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isMachO {
|
||||
return ([self.fileData length] >= 160 &&
|
||||
([self isMachHeader:(struct mach_header *)[self.fileData bytes]] || [self isFat]));
|
||||
}
|
||||
|
||||
- (BOOL)isFat {
|
||||
return ([self isFatHeader:(struct fat_header *)[self.fileData bytes]]);
|
||||
}
|
||||
|
||||
- (BOOL)isScript {
|
||||
if ([self.fileData length] < 1) return NO;
|
||||
|
||||
char magic[2];
|
||||
[self.fileData getBytes:&magic length:2];
|
||||
|
||||
return (strncmp("#!", magic, 2) == 0);
|
||||
}
|
||||
|
||||
- (BOOL)isExecutable {
|
||||
struct mach_header *mach_header = [self firstMachHeader];
|
||||
if (!mach_header) return NO;
|
||||
if (mach_header->filetype == MH_OBJECT ||
|
||||
mach_header->filetype == MH_EXECUTE ||
|
||||
mach_header->filetype == MH_PRELOAD) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
# pragma mark Bundle Information
|
||||
|
||||
/**
|
||||
* Try and determine the bundle that the represented executable is contained within, if any.
|
||||
*
|
||||
* 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 to count
|
||||
* as being part of the bundle.
|
||||
*
|
||||
* This method relies on executable bundles being laid out as follows:
|
||||
*
|
||||
*@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.
|
||||
*/
|
||||
- (NSBundle *)bundle {
|
||||
if (self.bundleRef) return self.bundleRef;
|
||||
|
||||
NSArray *pathComponents = [self.path pathComponents];
|
||||
|
||||
// Check that the full path is at least 4-levels deep:
|
||||
// e.g: /Calendar.app/Contents/MacOS/Calendar
|
||||
if ([pathComponents count] < 4) return nil;
|
||||
|
||||
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 3)];
|
||||
self.bundleRef = [NSBundle bundleWithPath:[NSString pathWithComponents:pathComponents]];
|
||||
|
||||
// Clear the bundle if it doesn't have a bundle ID
|
||||
if (![self.bundleRef objectForInfoDictionaryKey:@"CFBundleIdentifier"]) self.bundleRef = nil;
|
||||
|
||||
return self.bundleRef;
|
||||
}
|
||||
|
||||
- (NSString *)bundlePath {
|
||||
return [self.bundle bundlePath];
|
||||
}
|
||||
|
||||
- (NSDictionary *)infoPlist {
|
||||
if (self.infoDict) return self.infoDict;
|
||||
|
||||
if ([self bundle]) {
|
||||
self.infoDict = [[self bundle] infoDictionary];
|
||||
return self.infoDict;
|
||||
}
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:self.path isDirectory:NO];
|
||||
self.infoDict =
|
||||
(__bridge_transfer NSDictionary*)CFBundleCopyInfoDictionaryForURL((__bridge CFURLRef) url);
|
||||
return self.infoDict;
|
||||
}
|
||||
|
||||
- (NSString *)bundleIdentifier {
|
||||
return [[self infoPlist] objectForKey:@"CFBundleIdentifier"];
|
||||
}
|
||||
|
||||
- (NSString *)bundleName {
|
||||
return [[self infoPlist] objectForKey:@"CFBundleName"];
|
||||
}
|
||||
|
||||
- (NSString *)bundleVersion {
|
||||
return [[self infoPlist] objectForKey:@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
- (NSString *)bundleShortVersionString {
|
||||
return [[self infoPlist] objectForKey:@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
# pragma mark Internal Methods
|
||||
|
||||
/// Look through the file for the first mach_header. If the file is thin, this will be the
|
||||
/// header at the beginning of the file. If the file is fat, it will be the first
|
||||
/// architecture-specific header.
|
||||
- (struct mach_header *)firstMachHeader {
|
||||
if (![self isMachO]) return NULL;
|
||||
|
||||
struct mach_header *mach_header = (struct mach_header *)[self.fileData bytes];
|
||||
struct fat_header *fat_header = (struct fat_header *)[self.fileData bytes];
|
||||
|
||||
if ([self isFatHeader:fat_header]) {
|
||||
// Get the bytes for the fat_arch
|
||||
NSData *archHdr = [self safeSubdataWithRange:NSMakeRange(sizeof(struct fat_header),
|
||||
sizeof(struct fat_arch))];
|
||||
if (!archHdr) return nil;
|
||||
struct fat_arch *fat_arch = (struct fat_arch *)[archHdr bytes];
|
||||
|
||||
// Get bytes for first mach_header
|
||||
NSData *machHdr = [self safeSubdataWithRange:NSMakeRange(NSSwapBigIntToHost(fat_arch->offset),
|
||||
sizeof(struct mach_header))];
|
||||
if (!machHdr) return nil;
|
||||
mach_header = (struct mach_header *)[machHdr bytes];
|
||||
}
|
||||
|
||||
if ([self isMachHeader:mach_header]) {
|
||||
return mach_header;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (BOOL)isMachHeader:(struct mach_header *)header {
|
||||
return (header->magic == MH_MAGIC || header->magic == MH_MAGIC_64 ||
|
||||
header->magic == MH_CIGAM || header->magic == MH_CIGAM_64);
|
||||
}
|
||||
|
||||
- (BOOL)isFatHeader:(struct fat_header *)header {
|
||||
return (header->magic == FAT_MAGIC || header->magic == FAT_CIGAM);
|
||||
}
|
||||
|
||||
/// Wrap subdataWithRange: in a @try/@catch, returning nil on exception.
|
||||
/// Useful for when the range is beyond the end of the file.
|
||||
- (NSData *)safeSubdataWithRange:(NSRange)range {
|
||||
@try {
|
||||
return [self.fileData subdataWithRange:range];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)nameForCPUType:(cpu_type_t)cpuType {
|
||||
switch (cpuType) {
|
||||
case CPU_TYPE_X86:
|
||||
return @"i386";
|
||||
case CPU_TYPE_X86_64:
|
||||
return @"x86-64";
|
||||
case CPU_TYPE_POWERPC:
|
||||
return @"ppc";
|
||||
case CPU_TYPE_POWERPC64:
|
||||
return @"ppc64";
|
||||
default:
|
||||
return @"unknown";
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
67
Source/common/SNTCertificate.h
Normal file
67
Source/common/SNTCertificate.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
/// SNTCertificate wraps a @c SecCertificateRef to provide Objective-C accessors to
|
||||
/// commonly used certificate data. Accessors cache data for repeated access.
|
||||
@interface SNTCertificate : NSObject<NSSecureCoding>
|
||||
|
||||
/// Initialize a SNTCertificate object with a valid SecCertificateRef. Designated initializer.
|
||||
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef;
|
||||
|
||||
/// Initialize a SNTCertificate object with certificate data in DER format.
|
||||
/// Returns nil if |certData| is invalid.
|
||||
- (instancetype)initWithCertificateDataDER:(NSData *)certData;
|
||||
|
||||
/// Initialize a SNTCertificate object with certificate data in PEM format.
|
||||
/// If multiple PEM certificates exist within the string, the first is used.
|
||||
/// Returns nil if |certData| is invalid.
|
||||
- (instancetype)initWithCertificateDataPEM:(NSString *)certData;
|
||||
|
||||
/// Returns an array of SNTCertificate's for all of the certificates in |pemData|.
|
||||
+ (NSArray *)certificatesFromPEM:(NSString *)pemData;
|
||||
|
||||
/// Access the underlying certificate ref.
|
||||
@property(readonly) SecCertificateRef certRef;
|
||||
|
||||
/// SHA-1 hash of the certificate data.
|
||||
@property(readonly) NSString *SHA1;
|
||||
|
||||
/// Certificate data.
|
||||
@property(readonly) NSData *certData;
|
||||
|
||||
/// Common Name e.g: "Software Signing"
|
||||
@property(readonly) NSString *commonName;
|
||||
|
||||
/// Country Name e.g: "US"
|
||||
@property(readonly) NSString *countryName;
|
||||
|
||||
/// Organizational Name e.g: "Apple Inc."
|
||||
@property(readonly) NSString *orgName;
|
||||
|
||||
/// Organizational Unit Name e.g: "Apple Software"
|
||||
@property(readonly) NSString *orgUnit;
|
||||
|
||||
/// Issuer details, same fields as above.
|
||||
@property(readonly) NSString *issuerCommonName;
|
||||
@property(readonly) NSString *issuerCountryName;
|
||||
@property(readonly) NSString *issuerOrgName;
|
||||
@property(readonly) NSString *issuerOrgUnit;
|
||||
|
||||
/// Validity Not Before
|
||||
@property(readonly) NSDate *validFrom;
|
||||
|
||||
/// Validity Not After
|
||||
@property(readonly) NSDate *validUntil;
|
||||
|
||||
@end
|
||||
336
Source/common/SNTCertificate.m
Normal file
336
Source/common/SNTCertificate.m
Normal file
@@ -0,0 +1,336 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <Security/Security.h>
|
||||
|
||||
@interface SNTCertificate ()
|
||||
/// A container for cached property values
|
||||
@property NSMutableDictionary *memoizedData;
|
||||
@end
|
||||
|
||||
@implementation SNTCertificate
|
||||
|
||||
static NSString *const kCertDataKey = @"certData";
|
||||
|
||||
#pragma mark Init/Dealloc
|
||||
|
||||
- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_certRef = certRef;
|
||||
CFRetain(_certRef);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCertificateDataDER:(NSData *)certData {
|
||||
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
|
||||
|
||||
if (cert) {
|
||||
// Despite the header file claiming that SecCertificateCreateWithData will return NULL if
|
||||
// |certData| doesn't contain a valid DER-encoded X509 cert, this isn't always true.
|
||||
// radar://problem/16124651
|
||||
// To workaround, check that the certificate serial number can be retrieved.
|
||||
NSData *ser = CFBridgingRelease(SecCertificateCopySerialNumber(cert, NULL));
|
||||
if (ser) {
|
||||
self = [self initWithSecCertificateRef:cert];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
CFRelease(cert); // was retained in initWithSecCertificateRef
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCertificateDataPEM:(NSString *)certData {
|
||||
// Find the PEM and extract the base64-encoded DER data from within
|
||||
NSScanner *scanner = [NSScanner scannerWithString:certData];
|
||||
NSString *base64der;
|
||||
|
||||
// Locate and parse DER data into |base64der|
|
||||
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
|
||||
if (!([scanner scanString:@"-----BEGIN CERTIFICATE-----" intoString:NULL] &&
|
||||
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&base64der] &&
|
||||
[scanner scanString:@"-----END CERTIFICATE-----" intoString:NULL])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// base64-decode the DER
|
||||
SecTransformRef transform = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
|
||||
if (!transform) return nil;
|
||||
NSData *input = [base64der dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *output = nil;
|
||||
|
||||
if (SecTransformSetAttribute(transform,
|
||||
kSecTransformInputAttributeName,
|
||||
(__bridge CFDataRef)input,
|
||||
NULL)) {
|
||||
output = CFBridgingRelease(SecTransformExecute(transform, NULL));
|
||||
}
|
||||
if (transform) CFRelease(transform);
|
||||
|
||||
return [self initWithCertificateDataDER:output];
|
||||
}
|
||||
|
||||
+ (NSArray *)certificatesFromPEM:(NSString *)pemData {
|
||||
NSScanner *scanner = [NSScanner scannerWithString:pemData];
|
||||
NSMutableArray *certs = [[NSMutableArray alloc] init];
|
||||
|
||||
while (YES) {
|
||||
NSString *curCert;
|
||||
|
||||
[scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL];
|
||||
[scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&curCert];
|
||||
|
||||
// If there was no data, break.
|
||||
if (!curCert) break;
|
||||
|
||||
curCert = [curCert stringByAppendingString:@"-----END CERTIFICATE-----"];
|
||||
SNTCertificate *cert = [[SNTCertificate alloc] initWithCertificateDataPEM:curCert];
|
||||
|
||||
// If the data couldn't be turned into a valid SNTCertificate, continue.
|
||||
if (!cert) continue;
|
||||
|
||||
[certs addObject:cert];
|
||||
}
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_certRef) CFRelease(_certRef);
|
||||
}
|
||||
|
||||
#pragma mark Equality & description
|
||||
|
||||
- (BOOL)isEqual:(SNTCertificate *)other {
|
||||
if (self == other) return YES;
|
||||
if (![other isKindOfClass:[SNTCertificate class]]) return NO;
|
||||
|
||||
return [self.certData isEqual:other.certData];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [self.certData hash];
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@",
|
||||
self.orgName,
|
||||
self.orgUnit,
|
||||
self.commonName];
|
||||
}
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeObject:self.certData forKey:kCertDataKey];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
NSData *certData = [decoder decodeObjectOfClass:[NSData class] forKey:kCertDataKey];
|
||||
if ([certData length] == 0) return nil;
|
||||
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
|
||||
self = [self initWithSecCertificateRef:cert];
|
||||
if (cert) CFRelease(cert);
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Private Accessors
|
||||
|
||||
/// For a given selector, caches the value that selector would return on subsequent invocations,
|
||||
/// using the provided block to get the value on the first invocation.
|
||||
/// Assumes the selector's value will never change.
|
||||
- (id)memoizedSelector:(SEL)selector forBlock:(id (^)(void))block {
|
||||
NSString *selName = NSStringFromSelector(selector);
|
||||
|
||||
if (!self.memoizedData) {
|
||||
self.memoizedData = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
if (!self.memoizedData[selName]) {
|
||||
id val = block();
|
||||
if (val) {
|
||||
self.memoizedData[selName] = val;
|
||||
} else {
|
||||
self.memoizedData[selName] = [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
// Return the value if there is one, or nil if the value is NSNull
|
||||
return self.memoizedData[selName] != [NSNull null] ? self.memoizedData[selName] : nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)allCertificateValues {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return CFBridgingRelease(SecCertificateCopyValues(self.certRef, NULL, NULL));
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)x509SubjectName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SubjectName];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDictionary *)x509IssuerName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1IssuerName];
|
||||
}];
|
||||
}
|
||||
|
||||
/// Retrieve the value with the specified label from the X509 dictionary provided
|
||||
/// @param desiredLabel The label you want, e.g: kSecOIDOrganizationName.
|
||||
/// @param dict The dictionary to look in (Subject or Issuer)
|
||||
/// @returns An @c NSString, the value for the specified label.
|
||||
- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict {
|
||||
@try {
|
||||
NSArray *valArray = dict[(__bridge NSString *)kSecPropertyKeyValue];
|
||||
|
||||
for (NSDictionary *curCertVal in valArray) {
|
||||
NSString *valueLabel = curCertVal[(__bridge NSString *)kSecPropertyKeyLabel];
|
||||
if ([valueLabel isEqual:desiredLabel]) {
|
||||
return curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the specified date from the certificate's values and convert from a reference date
|
||||
/// to an NSDate object.
|
||||
/// @param key The identifier for the date: @c kSecOIDX509V1ValiditityNot{Before,After}
|
||||
/// @return An @c NSDate representing the date and time the certificate is valid from or expires.
|
||||
- (NSDate *)dateForX509Key:(NSString *)key {
|
||||
NSDictionary *curCertVal = [self allCertificateValues][key];
|
||||
NSNumber *value = curCertVal[(__bridge NSString *)kSecPropertyKeyValue];
|
||||
|
||||
NSTimeInterval interval = [value doubleValue];
|
||||
if (interval) {
|
||||
return [NSDate dateWithTimeIntervalSinceReferenceDate:interval];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark Public Accessors
|
||||
|
||||
- (NSString *)SHA1 {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
NSMutableData *SHA1Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH];
|
||||
|
||||
CC_SHA1([self.certData bytes], (CC_LONG)[self.certData length], [SHA1Buffer mutableBytes]);
|
||||
|
||||
const unsigned char *bytes = (const unsigned char *)[SHA1Buffer bytes];
|
||||
return [NSString stringWithFormat:
|
||||
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8],
|
||||
bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16],
|
||||
bytes[17], bytes[18], bytes[19]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSData *)certData {
|
||||
return CFBridgingRelease(SecCertificateCopyData(self.certRef));
|
||||
}
|
||||
|
||||
- (NSString *)commonName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
CFStringRef commonName = NULL;
|
||||
SecCertificateCopyCommonName(self.certRef, &commonName);
|
||||
return CFBridgingRelease(commonName);
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)countryName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
|
||||
fromDictionary:[self x509SubjectName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)orgName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
|
||||
fromDictionary:[self x509SubjectName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)orgUnit {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
|
||||
fromDictionary:[self x509SubjectName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDate *)validFrom {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotBefore];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSDate *)validUntil {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotAfter];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)issuerCommonName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCommonName
|
||||
fromDictionary:[self x509IssuerName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)issuerCountryName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName
|
||||
fromDictionary:[self x509IssuerName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)issuerOrgName {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName
|
||||
fromDictionary:[self x509IssuerName]];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)issuerOrgUnit {
|
||||
return [self memoizedSelector:_cmd forBlock:^id{
|
||||
return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName
|
||||
fromDictionary:[self x509IssuerName]];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
55
Source/common/SNTCodesignChecker.h
Normal file
55
Source/common/SNTCodesignChecker.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCertificate;
|
||||
|
||||
/// SNTCodesignChecker validates a binary (either on-disk or in memory) has been signed
|
||||
/// and if so allows for pulling out the certificates that were used to sign it.
|
||||
@interface SNTCodesignChecker : NSObject
|
||||
|
||||
/// The SecStaticCodeRef that this SNTCodesignChecker is working around
|
||||
@property(readonly) SecStaticCodeRef codeRef;
|
||||
|
||||
/// Returns a dictionary of raw signing information
|
||||
@property(readonly) NSDictionary *signingInformation;
|
||||
|
||||
/// Returns an array of @c SNTCertificate objects representing the chain that signed this binary.
|
||||
@property(readonly) NSArray *certificates;
|
||||
|
||||
/// Returns the leaf certificate that this binary was signed with
|
||||
@property(readonly) SNTCertificate *leafCertificate;
|
||||
|
||||
/// Returns the on-disk path of this binary.
|
||||
@property(readonly) NSString *binaryPath;
|
||||
|
||||
/// Initialize an @c SNTCodesignChecker with a SecStaticCodeRef
|
||||
/// Designated initializer.
|
||||
/// Takes ownership of @c codeRef.
|
||||
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef;
|
||||
|
||||
/// Initialize an @c SNTCodesignChecker with a binary on disk.
|
||||
/// Returns nil if @c binaryPath does not exist, is not a binary or is not codesigned.
|
||||
- (instancetype)initWithBinaryPath:(NSString *)binaryPath;
|
||||
|
||||
/// Initialize an @c SNTCodesignChecker with the PID of a running process.
|
||||
- (instancetype)initWithPID:(pid_t)PID;
|
||||
|
||||
/// Initialize an @c SNTCodesignChecker for the currently-running process.
|
||||
- (instancetype)initWithSelf;
|
||||
|
||||
/// Returns true if the binary represented by @c otherChecker has signing information that matches
|
||||
/// this binary.
|
||||
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker;
|
||||
|
||||
@end
|
||||
190
Source/common/SNTCodesignChecker.m
Normal file
190
Source/common/SNTCodesignChecker.m
Normal file
@@ -0,0 +1,190 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCodesignChecker.h"
|
||||
|
||||
#import <Security/Security.h>
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
|
||||
|
||||
// kStaticSigningFlags are the flags used when validating signatures on disk.
|
||||
//
|
||||
// Don't validate resources but do validate nested code. Ignoring resources _dramatically_ speeds
|
||||
// up validation (see below) but does mean images, plists, etc will not be checked and modifying
|
||||
// these will not be considered invalid. To ensure any code inside the binary is still checked,
|
||||
// we check nested code.
|
||||
//
|
||||
// Timings with different flags:
|
||||
// Checking Xcode 5.1.1 bundle:
|
||||
// kSecCSDefaultFlags: 3.895s
|
||||
// kSecCSDoNotValidateResources: 0.013s
|
||||
// kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.013s
|
||||
//
|
||||
// Checking Google Chrome 36.0.1985.143 bundle:
|
||||
// kSecCSDefaultFlags: 0.529s
|
||||
// kSecCSDoNotValidateResources: 0.032s
|
||||
// kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.033s
|
||||
//
|
||||
const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
|
||||
|
||||
// kSigningFlags are the flags used when validating signatures for running binaries.
|
||||
//
|
||||
// No special flags needed currently.
|
||||
const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
|
||||
|
||||
|
||||
@implementation SNTCodesignChecker {
|
||||
/// Array of @c SNTCertificate's representing the chain of certs this executable was signed with.
|
||||
NSMutableArray *_certificates;
|
||||
}
|
||||
|
||||
#pragma mark Init/dealloc
|
||||
|
||||
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
// First check the signing is valid
|
||||
if (CFGetTypeID(codeRef) == SecStaticCodeGetTypeID()) {
|
||||
if (SecStaticCodeCheckValidity(codeRef, kStaticSigningFlags, NULL) != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
} else if (CFGetTypeID(codeRef) == SecCodeGetTypeID()) {
|
||||
if (SecCodeCheckValidity((SecCodeRef)codeRef, kSigningFlags, NULL) != errSecSuccess) {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Get CFDictionary of signing information for binary
|
||||
OSStatus status = errSecSuccess;
|
||||
CFDictionaryRef signingDict = NULL;
|
||||
status = SecCodeCopySigningInformation(codeRef, kSecCSSigningInformation, &signingDict);
|
||||
_signingInformation = CFBridgingRelease(signingDict);
|
||||
if (status != errSecSuccess) return nil;
|
||||
|
||||
// Get array of certificates.
|
||||
NSArray *certs = _signingInformation[(id)kSecCodeInfoCertificates];
|
||||
if (!certs) return nil;
|
||||
|
||||
// Wrap SecCertificateRef objects in SNTCertificate and put in a new NSArray
|
||||
_certificates = [[NSMutableArray alloc] initWithCapacity:certs.count];
|
||||
for (int i = 0; i < certs.count; ++i) {
|
||||
SecCertificateRef certRef = (__bridge SecCertificateRef)certs[i];
|
||||
SNTCertificate *newCert = [[SNTCertificate alloc] initWithSecCertificateRef:certRef];
|
||||
[_certificates addObject:newCert];
|
||||
}
|
||||
|
||||
_codeRef = codeRef;
|
||||
CFRetain(_codeRef);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBinaryPath:(NSString *)binaryPath {
|
||||
SecStaticCodeRef codeRef = NULL;
|
||||
|
||||
// Get SecStaticCodeRef for binary
|
||||
if (SecStaticCodeCreateWithPath((__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath
|
||||
isDirectory:NO],
|
||||
kSecCSDefaultFlags,
|
||||
&codeRef) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeRef];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeRef) CFRelease(codeRef);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPID:(pid_t)PID {
|
||||
SecCodeRef codeRef = NULL;
|
||||
NSDictionary *attributes = @{(__bridge NSString *)kSecGuestAttributePid: @(PID)};
|
||||
|
||||
if (SecCodeCopyGuestWithAttributes(NULL,
|
||||
(__bridge CFDictionaryRef)attributes,
|
||||
kSecCSDefaultFlags,
|
||||
&codeRef) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeRef];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeRef) CFRelease(codeRef);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSelf {
|
||||
SecCodeRef codeSelf = NULL;
|
||||
if (SecCodeCopySelf(kSecCSDefaultFlags, &codeSelf) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeSelf];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeSelf) CFRelease(codeSelf);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_codeRef) {
|
||||
CFRelease(_codeRef);
|
||||
_codeRef = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Description
|
||||
|
||||
- (NSString *)description {
|
||||
NSString *binarySource;
|
||||
if (CFGetTypeID(self.codeRef) == SecStaticCodeGetTypeID()) {
|
||||
binarySource = @"On-disk";
|
||||
} else {
|
||||
binarySource = @"In-memory";
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"%@ binary, signed by %@, located at: %@",
|
||||
binarySource,
|
||||
self.leafCertificate.orgName,
|
||||
self.binaryPath];
|
||||
}
|
||||
|
||||
#pragma mark Public accessors
|
||||
|
||||
- (SNTCertificate *)leafCertificate {
|
||||
return [self.certificates firstObject];
|
||||
}
|
||||
|
||||
- (NSString *)binaryPath {
|
||||
CFURLRef path;
|
||||
OSStatus status = SecCodeCopyPath(_codeRef, kSecCSDefaultFlags, &path);
|
||||
NSURL *pathURL = CFBridgingRelease(path);
|
||||
if (status != errSecSuccess) return nil;
|
||||
return [pathURL path];
|
||||
}
|
||||
|
||||
- (BOOL)signingInformationMatches:(SNTCodesignChecker *)otherChecker {
|
||||
return [self.certificates isEqual:otherChecker.certificates];
|
||||
}
|
||||
|
||||
@end
|
||||
213
Source/common/SNTCodesignChecker.m.old
Normal file
213
Source/common/SNTCodesignChecker.m.old
Normal file
@@ -0,0 +1,213 @@
|
||||
#import "SNTCodesignChecker.h"
|
||||
|
||||
#import <Security/Security.h>
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
|
||||
|
||||
// kStaticSigningFlags are the flags used when validating signatures on disk.
|
||||
//
|
||||
// Don't validate resources but do validate nested code. Ignoring resources _dramatically_ speeds
|
||||
// up validation (see below) but does mean images, plists, etc will not be checked and modifying
|
||||
// these will not be considered invalid. To ensure any code inside the binary is still checked,
|
||||
// we check nested code.
|
||||
//
|
||||
// Timings with different flags:
|
||||
// Checking Xcode 5.1.1 bundle:
|
||||
// kSecCSDefaultFlags: 3.895s
|
||||
// kSecCSDoNotValidateResources: 0.013s
|
||||
// kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.013s
|
||||
//
|
||||
// Checking Google Chrome 36.0.1985.143 bundle:
|
||||
// kSecCSDefaultFlags: 0.529s
|
||||
// kSecCSDoNotValidateResources: 0.032s
|
||||
// kSecCSDoNotValidateResources | kSecCSCheckNestedCode: 0.033s
|
||||
//
|
||||
const SecCSFlags kStaticSigningFlags = kSecCSDoNotValidateResources | kSecCSCheckNestedCode;
|
||||
|
||||
// kSigningFlags are the flags used when validating signatures for running binaries.
|
||||
//
|
||||
// No special flags needed currently.
|
||||
const SecCSFlags kSigningFlags = kSecCSDefaultFlags;
|
||||
|
||||
|
||||
@implementation SNTCodesignChecker
|
||||
|
||||
#pragma mark Init/dealloc
|
||||
|
||||
- (instancetype)initWithSecStaticCodeRef:(SecStaticCodeRef)codeRef {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
_codeRef = codeRef;
|
||||
CFRetain(_codeRef);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBinaryPath:(NSString *)binaryPath {
|
||||
SecStaticCodeRef codeRef = NULL;
|
||||
|
||||
// Get SecStaticCodeRef for binary
|
||||
if (SecStaticCodeCreateWithPath((__bridge CFURLRef)[NSURL fileURLWithPath:binaryPath
|
||||
isDirectory:NO],
|
||||
kSecCSDefaultFlags,
|
||||
&codeRef) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeRef];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeRef) CFRelease(codeRef);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPID:(pid_t)PID {
|
||||
SecCodeRef codeRef = NULL;
|
||||
NSDictionary *attributes = @{(__bridge NSString *)kSecGuestAttributePid: @(PID)};
|
||||
|
||||
if (SecCodeCopyGuestWithAttributes(NULL,
|
||||
(__bridge CFDictionaryRef)attributes,
|
||||
kSecCSDefaultFlags,
|
||||
&codeRef) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeRef];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeRef) CFRelease(codeRef);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithSelf {
|
||||
SecCodeRef codeSelf = NULL;
|
||||
if (SecCodeCopySelf(kSecCSDefaultFlags, &codeSelf) == errSecSuccess) {
|
||||
self = [self initWithSecStaticCodeRef:codeSelf];
|
||||
} else {
|
||||
self = nil;
|
||||
}
|
||||
|
||||
if (codeSelf) CFRelease(codeSelf);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (_codeRef) CFRelease(_codeRef);
|
||||
}
|
||||
|
||||
#pragma mark Validate
|
||||
|
||||
- (OSStatus)validate {
|
||||
return [self validateWithRequirement:NULL];
|
||||
}
|
||||
|
||||
- (OSStatus)validateAppleAnchor {
|
||||
SecRequirementRef req = NULL;
|
||||
SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &req);
|
||||
return [self validateWithRequirement:req];
|
||||
}
|
||||
|
||||
- (OSStatus)validateAppleAnchorGeneric {
|
||||
SecRequirementRef req = NULL;
|
||||
SecRequirementCreateWithString(CFSTR("anchor apple generic"), kSecCSDefaultFlags, &req);
|
||||
return [self validateWithRequirement:req];
|
||||
}
|
||||
|
||||
- (OSStatus)validateWithRequirement:(SecRequirementRef)requirement {
|
||||
// Validate the binary and save the return code.
|
||||
if (CFGetTypeID(self.codeRef) == SecStaticCodeGetTypeID()) {
|
||||
return SecStaticCodeCheckValidity(self.codeRef, kStaticSigningFlags, requirement);
|
||||
} else if (CFGetTypeID(self.codeRef) == SecCodeGetTypeID()) {
|
||||
return SecCodeCheckValidity((SecCodeRef)self.codeRef, kSigningFlags, requirement);
|
||||
} else {
|
||||
return errSecCSSignatureNotVerifiable;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Description
|
||||
|
||||
- (NSString *)description {
|
||||
NSString *retStr;
|
||||
if (CFGetTypeID(self.codeRef) == SecStaticCodeGetTypeID()) {
|
||||
retStr = @"On-disk binary, ";
|
||||
} else {
|
||||
retStr = @"In-memory binary, ";
|
||||
}
|
||||
|
||||
if ([self validate] == errSecSuccess) {
|
||||
[retStr appendFormat:@"signed by %@, ", self.leafCertificate.orgName];
|
||||
} else {
|
||||
[retStr appendFormat:@"unsigned, "];
|
||||
}
|
||||
|
||||
[retStr appendFormat:@"located at: %@", self.binaryPath];
|
||||
|
||||
return retStr;
|
||||
}
|
||||
|
||||
#pragma mark Public accessors
|
||||
|
||||
- (NSDictionary *)signingInformation {
|
||||
static NSDictionary *signingInformation = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Get dictionary of signing information for binary
|
||||
CFDictionaryRef signingDict = NULL;
|
||||
SecCodeCopySigningInformation(self.codeRef, kSecCSSigningInformation, &signingDict);
|
||||
signingInformation = CFBridgingRelease(signingDict);
|
||||
});
|
||||
return signingInformation;
|
||||
}
|
||||
|
||||
- (NSArray *)certificates {
|
||||
static NSArray *certificates = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
// Get array of certificates, wrap each one in a SNTCertificate and store in a new array.
|
||||
NSArray *certs = self.signingInformation[(__bridge NSString *)kSecCodeInfoCertificates];
|
||||
|
||||
NSMutableArray *tempCerts = [[NSMutableArray alloc] initWithCapacity:certs.count];
|
||||
for (id cert in certs) {
|
||||
SNTCertificate *newCert =
|
||||
[[SNTCertificate alloc] initWithSecCertificateRef:(SecCertificateRef)cert];
|
||||
if (newCert) [tempCerts addObject:newCert];
|
||||
}
|
||||
certificates = [tempCerts copy];
|
||||
});
|
||||
return certificates;
|
||||
}
|
||||
|
||||
- (SNTCertificate *)leafCertificate {
|
||||
return [self.certificates firstObject];
|
||||
}
|
||||
|
||||
- (NSString *)binaryPath {
|
||||
CFURLRef path;
|
||||
OSStatus status = SecCodeCopyPath(_codeRef, kSecCSDefaultFlags, &path);
|
||||
NSURL *pathURL = CFBridgingRelease(path);
|
||||
if (status != errSecSuccess) return nil;
|
||||
return [pathURL path];
|
||||
}
|
||||
|
||||
#pragma mark Comparisons
|
||||
|
||||
- (BOOL)signingChainMatches:(SNTCodesignChecker *)otherChecker {
|
||||
return [self.certificates isEqual:otherChecker.certificates];
|
||||
}
|
||||
|
||||
- (BOOL)teamSigningMatches:(SNTCodesignChecker *)otherChecker {
|
||||
SNTCertificate *myLeaf = [self.certificates firstObject];
|
||||
SNTCertificate *otherLeaf = [otherChecker.certificates firstObject];
|
||||
|
||||
return ([myLeaf.orgUnit isEqual:otherLeaf.orgUnit] &&
|
||||
[self validateAppleAnchorGeneric] &&
|
||||
[otherChecker validateAppleAnchorGeneric]);
|
||||
}
|
||||
|
||||
@end
|
||||
66
Source/common/SNTCommonEnums.h
Normal file
66
Source/common/SNTCommonEnums.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__COMMON__COMMONENUMS_H
|
||||
#define SANTA__COMMON__COMMONENUMS_H
|
||||
|
||||
// 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.
|
||||
|
||||
// Each enum contains an _UNKNOWN and a _MAX value, which the valid values must be between so that
|
||||
// the code can easily verify valid values.
|
||||
|
||||
typedef enum {
|
||||
RULETYPE_UNKNOWN,
|
||||
|
||||
RULETYPE_BINARY = 1,
|
||||
RULETYPE_CERT = 2,
|
||||
|
||||
RULETYPE_MAX
|
||||
} santa_ruletype_t;
|
||||
|
||||
typedef enum {
|
||||
RULESTATE_UNKNOWN,
|
||||
|
||||
RULESTATE_WHITELIST = 1,
|
||||
RULESTATE_BLACKLIST = 2,
|
||||
RULESTATE_SILENT_BLACKLIST = 3,
|
||||
RULESTATE_REMOVE = 4,
|
||||
|
||||
RULESTATE_MAX
|
||||
} santa_rulestate_t;
|
||||
|
||||
typedef enum {
|
||||
CLIENTMODE_UNKNOWN,
|
||||
|
||||
CLIENTMODE_MONITOR = 1,
|
||||
CLIENTMODE_LOCKDOWN = 2,
|
||||
|
||||
CLIENTMODE_MAX
|
||||
} santa_clientmode_t;
|
||||
|
||||
typedef enum {
|
||||
EVENTSTATE_UNKNOWN,
|
||||
|
||||
EVENTSTATE_ALLOW_UNKNOWN = 1,
|
||||
EVENTSTATE_ALLOW_BINARY = 2,
|
||||
EVENTSTATE_ALLOW_CERTIFICATE = 3,
|
||||
EVENTSTATE_BLOCK_UNKNOWN = 4,
|
||||
EVENTSTATE_BLOCK_BINARY = 5,
|
||||
EVENTSTATE_BLOCK_CERTIFICATE = 6,
|
||||
|
||||
EVENTSTATE_MAX
|
||||
} santa_eventstate_t;
|
||||
|
||||
#endif // SANTA__COMMON__COMMONENUMS_H
|
||||
61
Source/common/SNTConfigurator.h
Normal file
61
Source/common/SNTConfigurator.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
/// Singleton that provides an interface for managing configuration values on disk
|
||||
/// n.b: This class is designed as a singleton but is not enforced.
|
||||
@interface SNTConfigurator : NSObject
|
||||
|
||||
/// The operating mode
|
||||
@property santa_clientmode_t clientMode;
|
||||
|
||||
/// If YES, debug logging is enabled
|
||||
@property(readonly) BOOL debugLogging;
|
||||
|
||||
# pragma mark - Sync Settings
|
||||
|
||||
/// The base URL of the sync server
|
||||
@property(readonly) NSURL *syncBaseURL;
|
||||
|
||||
/// The machine owner
|
||||
@property(readonly) NSString *machineOwner;
|
||||
|
||||
/// If set, this over-rides the default machine ID used for syncing
|
||||
@property(readonly) NSString *machineIDOverride;
|
||||
|
||||
# pragma mark Server Auth Settings
|
||||
|
||||
/// If set, this is valid PEM containing one or more certificates to be used to evaluate the
|
||||
/// server's SSL chain, overriding the list of trusted CAs distributed with the OS.
|
||||
@property(readonly) NSData *syncServerAuthRootsData;
|
||||
|
||||
/// This property is the same as the above but is a file on disk containing the PEM data.
|
||||
@property(readonly) NSString *syncServerAuthRootsFile;
|
||||
|
||||
# pragma mark Client Auth Settings
|
||||
|
||||
/// If set, this is the Common Name of a certificate in the System keychain to be used for
|
||||
/// sync authentication. The corresponding private key must also be in the keychain.
|
||||
@property(readonly) NSString *syncClientAuthCertificateCn;
|
||||
|
||||
/// If set, this is the Issuer Name of a certificate in the System keychain to be used for
|
||||
/// sync authentication. The corresponding private key must also be in the keychain.
|
||||
@property(readonly) NSString *syncClientAuthCertificateIssuer;
|
||||
|
||||
|
||||
/// Retrieve the initialized singleton configurator object
|
||||
+ (instancetype)configurator;
|
||||
|
||||
@end
|
||||
151
Source/common/SNTConfigurator.m
Normal file
151
Source/common/SNTConfigurator.m
Normal file
@@ -0,0 +1,151 @@
|
||||
/// Copyright 2014 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 "SNTConfigurator.h"
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@interface SNTConfigurator ()
|
||||
@property NSMutableDictionary *configData;
|
||||
@end
|
||||
|
||||
@implementation SNTConfigurator
|
||||
|
||||
/// The hard-coded path to the config file
|
||||
static NSString * const kConfigFilePath = @"/var/db/santa/config.plist";
|
||||
|
||||
/// The keys in the config file
|
||||
static NSString * const kSyncBaseURLKey = @"SyncBaseURL";
|
||||
static NSString * const kClientAuthCertificateCNKey = @"ClientAuthCertificateCN";
|
||||
static NSString * const kClientAuthCertificateIssuerKey = @"ClientAuthCertificateIssuerCN";
|
||||
static NSString * const kServerAuthRootsDataKey = @"ServerAuthRootsData";
|
||||
static NSString * const kServerAuthRootsFileKey = @"ServerAuthRootsFile";
|
||||
static NSString * const kDebugLoggingKey = @"DebugLogging";
|
||||
static NSString * const kClientModeKey = @"ClientMode";
|
||||
|
||||
static NSString * const kMachineOwnerKey = @"MachineOwner";
|
||||
static NSString * const kMachineIDKey = @"MachineID";
|
||||
|
||||
static NSString * const kMachineOwnerPlistFileKey = @"MachineOwnerPlist";
|
||||
static NSString * const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
|
||||
|
||||
static NSString * const kMachineIDPlistFileKey = @"MachineIDPlist";
|
||||
static NSString * const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self reloadConfigData];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
# pragma mark Singleton retriever
|
||||
|
||||
+ (instancetype)configurator {
|
||||
static SNTConfigurator *sharedConfigurator = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedConfigurator = [[SNTConfigurator alloc] init];
|
||||
});
|
||||
return sharedConfigurator;
|
||||
}
|
||||
|
||||
# pragma mark Public Interface
|
||||
|
||||
- (NSURL *)syncBaseURL {
|
||||
return [NSURL URLWithString:self.configData[kSyncBaseURLKey]];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificateCn {
|
||||
return self.configData[kClientAuthCertificateCNKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificateIssuer {
|
||||
return self.configData[kClientAuthCertificateIssuerKey];
|
||||
}
|
||||
|
||||
- (NSData *)syncServerAuthRootsData {
|
||||
return self.configData[kServerAuthRootsDataKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncServerAuthRootsFile {
|
||||
return self.configData[kServerAuthRootsFileKey];
|
||||
}
|
||||
|
||||
- (NSString *)machineOwner {
|
||||
if (self.configData[kMachineOwnerPlistFileKey] && self.configData[kMachineOwnerPlistKeyKey]) {
|
||||
NSDictionary *plist =
|
||||
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineOwnerPlistFileKey]];
|
||||
return plist[kMachineOwnerPlistKeyKey];
|
||||
}
|
||||
|
||||
if (self.configData[kMachineOwnerKey]) {
|
||||
return self.configData[kMachineOwnerKey];
|
||||
}
|
||||
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSString *)machineIDOverride {
|
||||
if (self.configData[kMachineIDPlistFileKey] && self.configData[kMachineIDPlistKeyKey]) {
|
||||
NSDictionary *plist =
|
||||
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineIDPlistFileKey]];
|
||||
return plist[kMachineIDPlistKeyKey];
|
||||
}
|
||||
|
||||
if (self.configData[kMachineIDKey]) {
|
||||
return self.configData[kMachineIDKey];
|
||||
}
|
||||
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (BOOL)debugLogging {
|
||||
return [self.configData[kDebugLoggingKey] boolValue];
|
||||
}
|
||||
|
||||
- (santa_clientmode_t)clientMode {
|
||||
int cm = [self.configData[kClientModeKey] intValue];
|
||||
if (cm > CLIENTMODE_UNKNOWN && cm < CLIENTMODE_MAX) {
|
||||
return cm;
|
||||
} else {
|
||||
self.configData[kClientModeKey] = @(CLIENTMODE_MONITOR);
|
||||
return CLIENTMODE_MONITOR;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setClientMode:(santa_clientmode_t)newMode {
|
||||
if (newMode > CLIENTMODE_UNKNOWN && newMode < CLIENTMODE_MAX) {
|
||||
[self reloadConfigData];
|
||||
self.configData[kClientModeKey] = @(newMode);
|
||||
[self saveConfigToDisk];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)saveConfigToDisk {
|
||||
[self.configData writeToFile:kConfigFilePath atomically:YES];
|
||||
}
|
||||
|
||||
- (void)reloadConfigData {
|
||||
_configData = [[NSDictionary dictionaryWithContentsOfFile:kConfigFilePath] mutableCopy];
|
||||
|
||||
if (!_configData) {
|
||||
_configData = [NSMutableDictionary dictionary];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
17
Source/common/SNTDropRootPrivs.h
Normal file
17
Source/common/SNTDropRootPrivs.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Simple function to check and drop root privileges.
|
||||
/// @return True if dropping was successful or unnecessary.
|
||||
BOOL DropRootPrivileges();
|
||||
30
Source/common/SNTDropRootPrivs.m
Normal file
30
Source/common/SNTDropRootPrivs.m
Normal file
@@ -0,0 +1,30 @@
|
||||
/// Copyright 2014 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 "SNTDropRootPrivs.h"
|
||||
|
||||
BOOL DropRootPrivileges() {
|
||||
if (getuid() == 0 || geteuid() == 0 || getgid() == 0 || getegid() == 0) {
|
||||
if (setgid(-2) != 0 || setgroups(0, NULL) != 0 || setegid(-2) != 0 ||
|
||||
setuid(-2) != 0 || seteuid(-2) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getuid() != geteuid() || getgid() != getegid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
71
Source/common/SNTKernelCommon.h
Normal file
71
Source/common/SNTKernelCommon.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Common defines between kernel <-> userspace
|
||||
|
||||
#ifndef SANTA__COMMON__KERNELCOMMON_H
|
||||
#define SANTA__COMMON__KERNELCOMMON_H
|
||||
|
||||
// Defines the lengths of paths and SHA-1's passed around.
|
||||
#define MAX_PATH_LEN 1024
|
||||
#define MAX_SHA1_LEN 20
|
||||
#define MAX_SHA1_STRING 41
|
||||
#define MAX_VNODE_ID_STR 21
|
||||
|
||||
// Defines the name of the userclient class and the driver bundle ID.
|
||||
#define USERCLIENT_CLASS "com_google_SantaDriver"
|
||||
#define USERCLIENT_ID "com.google.santa-driver"
|
||||
|
||||
// List of methods supported by the driver.
|
||||
enum SantaDriverMethods {
|
||||
kSantaUserClientOpen,
|
||||
kSantaUserClientClose,
|
||||
kSantaUserClientAllowBinary,
|
||||
kSantaUserClientDenyBinary,
|
||||
kSantaUserClientClearCache,
|
||||
kSantaUserClientCacheCount,
|
||||
|
||||
// Any methods supported by the driver should be added above this line to
|
||||
// ensure this remains the count of methods.
|
||||
kSantaUserClientNMethods,
|
||||
};
|
||||
|
||||
// Enum defining actions that can be passed down the IODataQueue and in
|
||||
// response methods.
|
||||
typedef enum {
|
||||
ACTION_UNSET = 0,
|
||||
|
||||
// CHECKBW
|
||||
ACTION_REQUEST_CHECKBW = 10,
|
||||
ACTION_RESPOND_CHECKBW_ALLOW = 11,
|
||||
ACTION_RESPOND_CHECKBW_DENY = 12,
|
||||
|
||||
// SHUTDOWN
|
||||
ACTION_REQUEST_SHUTDOWN = 60,
|
||||
|
||||
// ERROR
|
||||
ACTION_ERROR = 99,
|
||||
} santa_action_t;
|
||||
|
||||
// Message struct that is sent down the IODataQueue.
|
||||
typedef struct {
|
||||
santa_action_t action;
|
||||
uid_t userId;
|
||||
pid_t pid;
|
||||
char sha1[MAX_SHA1_STRING];
|
||||
char path[MAX_PATH_LEN];
|
||||
uint64_t vnode_id;
|
||||
} santa_message_t;
|
||||
|
||||
#endif // SANTA__COMMON__KERNELCOMMON_H
|
||||
53
Source/common/SNTLogging.h
Normal file
53
Source/common/SNTLogging.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Logging definitions, for both kernel and user space.
|
||||
|
||||
#ifndef SANTA__COMMON__LOGGING_H
|
||||
#define SANTA__COMMON__LOGGING_H
|
||||
|
||||
#ifdef KERNEL
|
||||
|
||||
#ifdef DEBUG
|
||||
#define LOGD(...) IOLog("D santa-driver: " __VA_ARGS__); IOLog("\n");
|
||||
#else // DEBUG
|
||||
#define LOGD(...)
|
||||
#endif // DEBUG
|
||||
#define LOGI(...) IOLog("I santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGW(...) IOLog("W santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
#define LOGE(...) IOLog("E santa-driver: " __VA_ARGS__); IOLog("\n")
|
||||
|
||||
#else // KERNEL
|
||||
|
||||
#define LOG_LEVEL_ERROR 1
|
||||
#define LOG_LEVEL_WARN 2
|
||||
#define LOG_LEVEL_INFO 3
|
||||
#define LOG_LEVEL_DEBUG 4
|
||||
|
||||
/// Logging function.
|
||||
/// level is one of the levels defined above
|
||||
/// error is the destination a FILE, generally should be stdout or stderr
|
||||
/// format is the printf style format string
|
||||
/// ... is the arguments to format.
|
||||
void logMessage(int level, FILE *destination, NSString *format, ...);
|
||||
|
||||
/// Simple logging macros
|
||||
#define LOGD(logFormat, ...) logMessage(LOG_LEVEL_DEBUG, stdout, logFormat, ##__VA_ARGS__);
|
||||
#define LOGI(logFormat, ...) logMessage(LOG_LEVEL_INFO, stdout, logFormat, ##__VA_ARGS__);
|
||||
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__);
|
||||
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__);
|
||||
|
||||
#endif // KERNEL
|
||||
|
||||
#endif // SANTA__COMMON__LOGGING_H
|
||||
65
Source/common/SNTLogging.m
Normal file
65
Source/common/SNTLogging.m
Normal file
@@ -0,0 +1,65 @@
|
||||
/// Copyright 2014 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 "SNTLogging.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
static int logLevel = LOG_LEVEL_DEBUG; // default to info
|
||||
#else
|
||||
static int logLevel = LOG_LEVEL_INFO;
|
||||
#endif
|
||||
|
||||
void logMessage(int level, FILE *destination, NSString *format, ...) {
|
||||
static NSDateFormatter *dateFormatter;
|
||||
static NSString *binaryName;
|
||||
static dispatch_once_t pred;
|
||||
|
||||
dispatch_once(&pred, ^{
|
||||
dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
|
||||
[dateFormatter setDateFormat:@"YYYY-MM-dd HH:mm:ss.SSS'Z"];
|
||||
|
||||
binaryName = [[NSProcessInfo processInfo] processName];
|
||||
|
||||
// If debug logging is enabled, the process must be restarted.
|
||||
if ([[SNTConfigurator configurator] debugLogging]) {
|
||||
logLevel = LOG_LEVEL_DEBUG;
|
||||
}
|
||||
});
|
||||
|
||||
if (logLevel < level) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *s = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
// Only prepend timestamp, severity and binary name if stdout is not a TTY
|
||||
if (isatty(fileno(destination))) {
|
||||
fprintf(destination, "%s\n", [s UTF8String]);
|
||||
} else {
|
||||
NSString *levelName;
|
||||
switch (level) {
|
||||
case LOG_LEVEL_ERROR: levelName = @"E"; break;
|
||||
case LOG_LEVEL_WARN: levelName = @"W"; break;
|
||||
case LOG_LEVEL_INFO: levelName = @"I"; break;
|
||||
case LOG_LEVEL_DEBUG: levelName = @"D"; break;
|
||||
}
|
||||
|
||||
fprintf(destination, "%s\n", [[NSString stringWithFormat:@"[%@] %@ %@: %@",
|
||||
[dateFormatter stringFromDate:[NSDate date]], levelName, binaryName, s] UTF8String]);
|
||||
}
|
||||
}
|
||||
37
Source/common/SNTNotificationMessage.h
Normal file
37
Source/common/SNTNotificationMessage.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCertificate;
|
||||
|
||||
/// An SNTEvent is created when Santa is making a decision about an execution request.
|
||||
/// All of the information required to make that decision, log it, notify the user etc. must be
|
||||
/// encapsulated within this class.
|
||||
@interface SNTNotificationMessage : NSObject<NSSecureCoding>
|
||||
|
||||
/// The path of the binary that was blocked.
|
||||
@property(copy) NSString *path;
|
||||
|
||||
/// The SHA-1 of the binary that was blocked.
|
||||
@property(copy) NSString *SHA1;
|
||||
|
||||
/// An array of @c SNTCertificate objects representing the certificate chain the binary was signed with.
|
||||
@property(copy) NSArray *certificates;
|
||||
|
||||
/// A custom message to display to the user when blocking this binary, if any.
|
||||
@property(copy) NSString *customMessage;
|
||||
|
||||
// A convenience accessor to the first certificate in @c certificates.
|
||||
@property(readonly) SNTCertificate *leafCertificate;
|
||||
|
||||
@end
|
||||
55
Source/common/SNTNotificationMessage.m
Normal file
55
Source/common/SNTNotificationMessage.m
Normal file
@@ -0,0 +1,55 @@
|
||||
/// Copyright 2014 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 "SNTNotificationMessage.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
|
||||
@implementation SNTNotificationMessage
|
||||
|
||||
static NSString *const kPathKey = @"path";
|
||||
static NSString *const kSHA1Key = @"sha1";
|
||||
static NSString *const kCertificatesKey = @"certificates";
|
||||
static NSString *const kCustomMessageKey = @"custommessage";
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeObject:self.path forKey:kPathKey];
|
||||
[coder encodeObject:self.SHA1 forKey:kSHA1Key];
|
||||
[coder encodeObject:self.customMessage forKey:kCustomMessageKey];
|
||||
[coder encodeObject:self.certificates forKey:kCertificatesKey];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
_path = [decoder decodeObjectOfClass:[NSString class] forKey:kPathKey];
|
||||
_SHA1 = [decoder decodeObjectOfClass:[NSString class] forKey:kSHA1Key];
|
||||
_customMessage = [decoder decodeObjectOfClass:[NSString class] forKey:kCustomMessageKey];
|
||||
|
||||
NSSet *certClasses = [NSSet setWithObjects:[NSArray class], [SNTCertificate class], nil];
|
||||
_certificates = [decoder decodeObjectOfClasses:certClasses forKey:kCertificatesKey];
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Calculated Properties
|
||||
|
||||
- (SNTCertificate *)leafCertificate {
|
||||
return [self.certificates firstObject];
|
||||
}
|
||||
|
||||
@end
|
||||
38
Source/common/SNTRule.h
Normal file
38
Source/common/SNTRule.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
/// Represents a Rule.
|
||||
@interface SNTRule : NSObject<NSSecureCoding>
|
||||
|
||||
/// The SHA-1 hash of the object this rule is for
|
||||
@property NSString *SHA1;
|
||||
|
||||
/// The state of this rule
|
||||
@property santa_rulestate_t state;
|
||||
|
||||
/// The type of object this rule is for (binary, certificate)
|
||||
@property santa_ruletype_t type;
|
||||
|
||||
/// A custom message that will be displayed if this rule blocks a binary from executing
|
||||
@property NSString *customMsg;
|
||||
|
||||
/// Designated initializer.
|
||||
- (instancetype)initWithSHA1:(NSString *)SHA1
|
||||
state:(santa_rulestate_t)state
|
||||
type:(santa_ruletype_t)type
|
||||
customMsg:(NSString *)customMsg;
|
||||
|
||||
@end
|
||||
59
Source/common/SNTRule.m
Normal file
59
Source/common/SNTRule.m
Normal file
@@ -0,0 +1,59 @@
|
||||
/// Copyright 2014 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 "SNTRule.h"
|
||||
|
||||
@implementation SNTRule
|
||||
|
||||
static NSString *const kSHA1Key = @"sha1";
|
||||
static NSString *const kStateKey = @"state";
|
||||
static NSString *const kTypeKey = @"type";
|
||||
static NSString *const kCustomMessageKey = @"custommsg";
|
||||
|
||||
- (instancetype)initWithSHA1:(NSString *)SHA1
|
||||
state:(santa_rulestate_t)state
|
||||
type:(santa_ruletype_t)type
|
||||
customMsg:(NSString *)customMsg {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_SHA1 = SHA1;
|
||||
_state = state;
|
||||
_type = type;
|
||||
_customMsg = customMsg;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSSecureCoding
|
||||
|
||||
+ (BOOL)supportsSecureCoding { return YES; }
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeObject:self.SHA1 forKey:kSHA1Key];
|
||||
[coder encodeInt:self.state forKey:kStateKey];
|
||||
[coder encodeInt:self.type forKey:kTypeKey];
|
||||
[coder encodeObject:self.customMsg forKey:kCustomMessageKey];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
NSSet *stringPlusNull = [NSSet setWithObjects:[NSString class], [NSNull class], nil];
|
||||
|
||||
_SHA1 = [decoder decodeObjectOfClass:[NSString class] forKey:kSHA1Key];
|
||||
_state = [decoder decodeIntForKey:kStateKey];
|
||||
_type = [decoder decodeIntForKey:kTypeKey];
|
||||
_customMsg = [decoder decodeObjectOfClasses:stringPlusNull forKey:kCustomMessageKey];
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
40
Source/common/SNTStoredEvent.h
Normal file
40
Source/common/SNTStoredEvent.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
/// Represents an event stored in the database.
|
||||
@interface SNTStoredEvent : NSObject<NSSecureCoding>
|
||||
|
||||
@property NSNumber *idx;
|
||||
@property NSString *fileSHA1;
|
||||
@property NSString *filePath;
|
||||
@property NSString *fileBundleName;
|
||||
@property NSString *fileBundleID;
|
||||
@property NSString *fileBundleVersion;
|
||||
@property NSString *fileBundleVersionString;
|
||||
@property NSString *certSHA1;
|
||||
@property NSString *certCN;
|
||||
@property NSString *certOrg;
|
||||
@property NSString *certOU;
|
||||
@property NSDate *certValidFromDate;
|
||||
@property NSDate *certValidUntilDate;
|
||||
@property NSString *executingUser;
|
||||
@property NSDate *occurrenceDate;
|
||||
@property santa_eventstate_t decision;
|
||||
@property NSArray *loggedInUsers;
|
||||
@property NSArray *currentSessions;
|
||||
@property NSNumber *pid;
|
||||
|
||||
@end
|
||||
102
Source/common/SNTStoredEvent.m
Normal file
102
Source/common/SNTStoredEvent.m
Normal file
@@ -0,0 +1,102 @@
|
||||
/// Copyright 2014 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 "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTStoredEvent
|
||||
|
||||
+ (BOOL)supportsSecureCoding { return YES; }
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder {
|
||||
[coder encodeObject:self.idx forKey:@"idx"];
|
||||
[coder encodeObject:self.fileSHA1 forKey:@"fileSHA1"];
|
||||
[coder encodeObject:self.filePath forKey:@"filePath"];
|
||||
|
||||
if (self.fileBundleName) [coder encodeObject:self.fileBundleName forKey:@"fileBundleName"];
|
||||
if (self.fileBundleID) [coder encodeObject:self.fileBundleID forKey:@"fileBundleID"];
|
||||
if (self.fileBundleVersion) {
|
||||
[coder encodeObject:self.fileBundleVersion forKey:@"fileBundleVersion"];
|
||||
}
|
||||
if (self.fileBundleVersionString) {
|
||||
[coder encodeObject:self.fileBundleVersionString forKey:@"fileBundleVersionString"];
|
||||
}
|
||||
|
||||
if (self.certSHA1) [coder encodeObject:self.certSHA1 forKey:@"certSHA1"];
|
||||
if (self.certCN) [coder encodeObject:self.certCN forKey:@"certCN"];
|
||||
if (self.certOrg) [coder encodeObject:self.certOrg forKey:@"certOrg"];
|
||||
if (self.certOU) [coder encodeObject:self.certOU forKey:@"certOU"];
|
||||
if (self.certValidFromDate) {
|
||||
[coder encodeObject:self.certValidFromDate forKey:@"certValidFromDate"];
|
||||
}
|
||||
if (self.certValidUntilDate) {
|
||||
[coder encodeObject:self.certValidUntilDate forKey:@"certValidUntilDate"];
|
||||
}
|
||||
|
||||
[coder encodeObject:self.executingUser forKey:@"executingUser"];
|
||||
[coder encodeObject:self.occurrenceDate forKey:@"occurrenceDate"];
|
||||
[coder encodeInt:self.decision forKey:@"decision"];
|
||||
|
||||
if (self.loggedInUsers) [coder encodeObject:self.loggedInUsers forKey:@"loggedInUsers"];
|
||||
if (self.currentSessions) [coder encodeObject:self.currentSessions forKey:@"currentSessions"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder {
|
||||
_idx = [decoder decodeObjectOfClass:[NSNumber class] forKey:@"idx"];
|
||||
_fileSHA1 = [decoder decodeObjectOfClass:[NSString class] forKey:@"fileSHA1"];
|
||||
_filePath = [decoder decodeObjectOfClass:[NSString class] forKey:@"filePath"];
|
||||
_fileBundleName = [decoder decodeObjectOfClass:[NSString class] forKey:@"fileBundleName"];
|
||||
|
||||
_fileBundleID = [decoder decodeObjectOfClass:[NSString class] forKey:@"fileBundleID"];
|
||||
_fileBundleVersion = [decoder decodeObjectOfClass:[NSString class] forKey:@"fileBundleVersion"];
|
||||
_fileBundleVersionString =
|
||||
[decoder decodeObjectOfClass:[NSString class] forKey:@"fileBundleVersionString"];
|
||||
_certSHA1 = [decoder decodeObjectOfClass:[NSString class] forKey:@"certSHA1"];
|
||||
_certCN = [decoder decodeObjectOfClass:[NSString class] forKey:@"certCN"];
|
||||
_certOrg = [decoder decodeObjectOfClass:[NSString class] forKey:@"certOrg"];
|
||||
_certOU = [decoder decodeObjectOfClass:[NSString class] forKey:@"certOU"];
|
||||
_certValidFromDate = [decoder decodeObjectOfClass:[NSDate class] forKey:@"certValidFromDate"];
|
||||
_certValidUntilDate = [decoder decodeObjectOfClass:[NSDate class] forKey:@"certValidUntilDate"];
|
||||
_executingUser = [decoder decodeObjectOfClass:[NSString class] forKey:@"executingUser"];
|
||||
_occurrenceDate = [decoder decodeObjectOfClass:[NSDate class] forKey:@"occurrenceDate"];
|
||||
_decision = [decoder decodeIntForKey:@"decision"];
|
||||
|
||||
NSSet *stringAndArrayClasses = [NSSet setWithObjects:[NSArray class], [NSString class], nil];
|
||||
_loggedInUsers = [decoder decodeObjectOfClasses:stringAndArrayClasses forKey:@"loggedInUsers"];
|
||||
_currentSessions = [decoder decodeObjectOfClasses:stringAndArrayClasses
|
||||
forKey:@"currentSessions"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(SNTStoredEvent *)other {
|
||||
if (other == self) return YES;
|
||||
if (![other isKindOfClass:[SNTStoredEvent class]]) return NO;
|
||||
return ([self.fileSHA1 isEqual:other.fileSHA1] &&
|
||||
[self.idx isEqual:other.idx]);
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
NSUInteger prime = 31;
|
||||
NSUInteger result = 1;
|
||||
result = prime * result + [self.idx hash];
|
||||
result = prime * result + [self.fileSHA1 hash];
|
||||
result = prime * result + [self.filePath hash];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"SNTStoredEvent[%@] with SHA-1: %@", self.idx, self.fileSHA1];
|
||||
}
|
||||
|
||||
@end
|
||||
28
Source/common/SNTSystemInfo.h
Normal file
28
Source/common/SNTSystemInfo.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Simple class for fetching system information
|
||||
@interface SNTSystemInfo : NSObject
|
||||
|
||||
+ (NSString *)serialNumber;
|
||||
|
||||
+ (NSString *)hardwareUUID;
|
||||
|
||||
+ (NSString *)osVersion;
|
||||
+ (NSString *)osBuild;
|
||||
|
||||
+ (NSString *)shortHostname;
|
||||
+ (NSString *)longHostname;
|
||||
|
||||
@end
|
||||
73
Source/common/SNTSystemInfo.m
Normal file
73
Source/common/SNTSystemInfo.m
Normal file
@@ -0,0 +1,73 @@
|
||||
/// Copyright 2014 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 "SNTSystemInfo.h"
|
||||
|
||||
@implementation SNTSystemInfo
|
||||
|
||||
+ (NSString *)serialNumber {
|
||||
io_service_t platformExpert = IOServiceGetMatchingService(
|
||||
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
|
||||
if (!platformExpert) return nil;
|
||||
|
||||
NSString *serial = CFBridgingRelease(
|
||||
IORegistryEntryCreateCFProperty(platformExpert,
|
||||
CFSTR(kIOPlatformSerialNumberKey),
|
||||
kCFAllocatorDefault,
|
||||
0));
|
||||
|
||||
IOObjectRelease(platformExpert);
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
+ (NSString *)hardwareUUID {
|
||||
io_service_t platformExpert = IOServiceGetMatchingService(
|
||||
kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
|
||||
if (!platformExpert) return nil;
|
||||
|
||||
NSString *uuid = CFBridgingRelease(
|
||||
IORegistryEntryCreateCFProperty(platformExpert,
|
||||
CFSTR(kIOPlatformUUIDKey),
|
||||
kCFAllocatorDefault, 0));
|
||||
|
||||
IOObjectRelease(platformExpert);
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
+ (NSString *)osVersion {
|
||||
return [SNTSystemInfo _systemVersionDictionary][@"ProductVersion"];
|
||||
}
|
||||
|
||||
+ (NSString *)osBuild {
|
||||
return [SNTSystemInfo _systemVersionDictionary][@"ProductBuildVersion"];
|
||||
}
|
||||
|
||||
+ (NSString *)shortHostname {
|
||||
return [[[SNTSystemInfo longHostname] componentsSeparatedByString:@"."] firstObject];
|
||||
}
|
||||
|
||||
+ (NSString *)longHostname {
|
||||
return [[NSHost currentHost] name];
|
||||
}
|
||||
|
||||
# pragma mark - Internal
|
||||
|
||||
+ (NSDictionary *)_systemVersionDictionary {
|
||||
return [NSDictionary dictionaryWithContentsOfFile:
|
||||
@"/System/Library/CoreServices/SystemVersion.plist"];
|
||||
}
|
||||
|
||||
@end
|
||||
87
Source/common/SNTXPCConnection.h
Normal file
87
Source/common/SNTXPCConnection.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/**
|
||||
* A validating XPC connection/listener which uses codesigning to validate that both ends of the
|
||||
* connection were signed by the same certificate chain.
|
||||
*
|
||||
* Example server started by @c launchd where the @c launchd job has a @c MachServices key:
|
||||
*
|
||||
*@code
|
||||
* SNTXPCConnection *conn = [[SNTXPCConnection alloc] initServerWithName:@"MyServer"];
|
||||
* conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
|
||||
* conn.exportedObject = myObject;
|
||||
* conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyClientProtocol)];
|
||||
* [conn resume];
|
||||
*@endcode
|
||||
*
|
||||
* Example client, connecting to above server:
|
||||
*
|
||||
*@code
|
||||
* SNTXPCConnection *conn = [[SNTXPCConnection alloc] initClientWithName:"MyServer" withOptions:0];
|
||||
* conn.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyClientProtocol)];
|
||||
* conn.exportedObject = myObject;
|
||||
* conn.remoteInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyServerProtocol)];
|
||||
* conn.invalidationHandler = ^{ NSLog(@"Connection invalidated") };
|
||||
* [conn resume];
|
||||
*@endcode
|
||||
*
|
||||
* Either side can then send a message to the other with:
|
||||
*
|
||||
*@code
|
||||
* [conn.remoteObjectProxy selectorInRemoteInterface];
|
||||
*@endcode
|
||||
*
|
||||
* Note: messages are always delivered on a background thread!
|
||||
**/
|
||||
@interface SNTXPCConnection : NSObject<NSXPCListenerDelegate>
|
||||
|
||||
typedef void (^SNTXPCInvalidationBlock)(void);
|
||||
typedef void (^SNTXPCAcceptedBlock)(void);
|
||||
typedef void (^SNTXPCRejectedBlock)(void);
|
||||
|
||||
/// The interface the remote object should conform to.
|
||||
@property(retain) NSXPCInterface *remoteInterface;
|
||||
|
||||
/// A proxy to the object at the other end of the connection.
|
||||
/// *Warning*: Do not send a message to this object if you didn't set @c remoteInterface above
|
||||
/// before calling the @c resume method, doing so will throw an exception.
|
||||
@property(readonly) id remoteObjectProxy;
|
||||
|
||||
/// The interface this object conforms to.
|
||||
@property(retain) NSXPCInterface *exportedInterface;
|
||||
|
||||
/// The object that responds to messages from the other end.
|
||||
@property(retain) id exportedObject;
|
||||
|
||||
/// A block to run when the connection is invalidated.
|
||||
@property(copy) SNTXPCInvalidationBlock invalidationHandler;
|
||||
|
||||
/// A block to run when the connection has been accepted.
|
||||
@property(copy) SNTXPCAcceptedBlock acceptedHandler;
|
||||
|
||||
/// A block to run when the connection has been rejected.
|
||||
@property(copy) SNTXPCRejectedBlock rejectedHandler;
|
||||
|
||||
/// Initializer for the 'server' side of the connection, the binary that was started by launchd.
|
||||
- (instancetype)initServerWithName:(NSString *)name;
|
||||
|
||||
/// Initializer for the 'client' side of the connection. If the 'server' was started as a
|
||||
/// LaunchDaemon (running as root), pass |NSXPCConnectionPrivileged| for |options|, otherwise use 0.
|
||||
- (instancetype)initClientWithName:(NSString *)name options:(NSXPCConnectionOptions)options;
|
||||
|
||||
/// Call when the properties of the object have been set-up and you're ready for connections.
|
||||
- (void)resume;
|
||||
|
||||
@end
|
||||
197
Source/common/SNTXPCConnection.m
Normal file
197
Source/common/SNTXPCConnection.m
Normal file
@@ -0,0 +1,197 @@
|
||||
/// Copyright 2014 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 "SNTXPCConnection.h"
|
||||
|
||||
#import "SNTCodesignChecker.h"
|
||||
|
||||
@protocol XPCConnectionValidityRequest
|
||||
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block;
|
||||
@end
|
||||
|
||||
@interface SNTXPCConnection ()
|
||||
/// The XPC listener (used on server-side only).
|
||||
@property NSXPCListener *listenerObject;
|
||||
|
||||
/// The current connection object.
|
||||
@property NSXPCConnection *currentConnection;
|
||||
|
||||
/// The remote interface to use while the connection hasn't been validated.
|
||||
@property NSXPCInterface *validatorInterface;
|
||||
@end
|
||||
|
||||
@implementation SNTXPCConnection
|
||||
|
||||
#pragma mark Initializers
|
||||
- (instancetype)initServerWithName:(NSString *)name {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
|
||||
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
|
||||
_listenerObject = [[NSXPCListener alloc] initWithMachServiceName:name];
|
||||
|
||||
if (!_validatorInterface || !_listenerObject) return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initClientWithName:(NSString *)name options:(NSXPCConnectionOptions)options {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
Protocol *validatorProtocol = @protocol(XPCConnectionValidityRequest);
|
||||
_validatorInterface = [NSXPCInterface interfaceWithProtocol:validatorProtocol];
|
||||
_currentConnection = [[NSXPCConnection alloc] initWithMachServiceName:name
|
||||
options:options];
|
||||
|
||||
if (!_validatorInterface || !_currentConnection) return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark Connection set-up
|
||||
|
||||
- (void)resume {
|
||||
if (_listenerObject) {
|
||||
// A new listener doesn't do anything until a client connects.
|
||||
self.listenerObject.delegate = self;
|
||||
[self.listenerObject resume];
|
||||
} else {
|
||||
// A new client begins the validation process.
|
||||
NSXPCConnection *connection = _currentConnection;
|
||||
|
||||
connection.remoteObjectInterface = _validatorInterface;
|
||||
|
||||
connection.invalidationHandler = ^{
|
||||
[self invokeInvalidationHandler];
|
||||
self.currentConnection = nil;
|
||||
};
|
||||
|
||||
connection.interruptionHandler = ^{
|
||||
[self.currentConnection invalidate];
|
||||
};
|
||||
|
||||
[connection resume];
|
||||
|
||||
[[connection remoteObjectProxy] isConnectionValidWithBlock:^void(BOOL response) {
|
||||
pid_t pid = self.currentConnection.processIdentifier;
|
||||
|
||||
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
|
||||
SNTCodesignChecker *otherCS = [[SNTCodesignChecker alloc] initWithPID:pid];
|
||||
|
||||
if (response && [otherCS signingInformationMatches:selfCS]) {
|
||||
[self.currentConnection suspend];
|
||||
self.currentConnection.remoteObjectInterface = self.remoteInterface;
|
||||
self.currentConnection.exportedInterface = self.exportedInterface;
|
||||
self.currentConnection.exportedObject = self.exportedObject;
|
||||
[self invokeAcceptedHandler];
|
||||
[self.currentConnection resume];
|
||||
} else {
|
||||
[self invokeRejectedHandler];
|
||||
[self.currentConnection invalidate];
|
||||
self.currentConnection = nil;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)connection {
|
||||
// Reject connection if a connection already exists. As the invalidation/interruption handlers
|
||||
// both cause the currentConnection to be nil'd out, this should be OK.
|
||||
if (self.currentConnection) return NO;
|
||||
|
||||
connection.exportedObject = self;
|
||||
connection.exportedInterface = _validatorInterface;
|
||||
|
||||
connection.invalidationHandler = ^{
|
||||
[self invokeInvalidationHandler];
|
||||
self.currentConnection = nil;
|
||||
};
|
||||
|
||||
connection.interruptionHandler = ^{
|
||||
// Invalidate the connection, causing the handler above to run
|
||||
[self.currentConnection invalidate];
|
||||
};
|
||||
|
||||
// At this point the client is connected and can send messages but the only message it can send
|
||||
// is isConnectionValidWithBlock: and we won't send anything to it until it has.
|
||||
self.currentConnection = connection;
|
||||
|
||||
[connection resume];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)isConnectionValidWithBlock:(void (^)(BOOL))block {
|
||||
pid_t pid = self.currentConnection.processIdentifier;
|
||||
|
||||
SNTCodesignChecker *selfCS = [[SNTCodesignChecker alloc] initWithSelf];
|
||||
SNTCodesignChecker *otherCS = [[SNTCodesignChecker alloc] initWithPID:pid];
|
||||
|
||||
if ([otherCS signingInformationMatches:selfCS]) {
|
||||
[self.currentConnection suspend];
|
||||
self.currentConnection.remoteObjectInterface = self.remoteInterface;
|
||||
self.currentConnection.exportedInterface = self.exportedInterface;
|
||||
self.currentConnection.exportedObject = self.exportedObject;
|
||||
[self.currentConnection resume];
|
||||
|
||||
[self invokeAcceptedHandler];
|
||||
|
||||
// Let remote end know that we accepted. Note: in acception this must come last otherwise
|
||||
// the remote end might start sending messages before the interface is fully set-up.
|
||||
block(YES);
|
||||
} else {
|
||||
// Let remote end know that we rejected. Note: in rejection this must come first otherwise
|
||||
// the connection is invalidated before the client ever realizes.
|
||||
block(NO);
|
||||
|
||||
[self invokeRejectedHandler];
|
||||
|
||||
[self.currentConnection invalidate];
|
||||
self.currentConnection = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (id)remoteObjectProxy {
|
||||
if (self.currentConnection && self.currentConnection.remoteObjectInterface) {
|
||||
return [self.currentConnection remoteObjectProxyWithErrorHandler:^(NSError *error) {
|
||||
[self.currentConnection invalidate];
|
||||
}];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)invokeAcceptedHandler {
|
||||
if (self.acceptedHandler) {
|
||||
self.acceptedHandler();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invokeRejectedHandler {
|
||||
if (self.rejectedHandler) {
|
||||
self.rejectedHandler();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)invokeInvalidationHandler {
|
||||
if (self.invalidationHandler) {
|
||||
self.invalidationHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
52
Source/common/SNTXPCControlInterface.h
Normal file
52
Source/common/SNTXPCControlInterface.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
@class SNTRule;
|
||||
@class SNTStoredEvent;
|
||||
|
||||
/// Protocol implemented by santad and utilized by santactl
|
||||
@protocol SNTDaemonControlXPC
|
||||
|
||||
/// Kernel ops
|
||||
- (void)cacheCount:(void (^)(uint64_t))reply;
|
||||
- (void)flushCache:(void (^)(BOOL))reply;
|
||||
|
||||
/// Database ops
|
||||
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply;
|
||||
- (void)databaseRuleAddRule:(SNTRule *)rule withReply:(void (^)())reply;
|
||||
- (void)databaseRuleAddRules:(NSArray *)rules withReply:(void (^)())reply;
|
||||
|
||||
- (void)databaseEventCount:(void (^)(uint64_t count))reply;
|
||||
- (void)databaseEventForSHA1:(NSString *)sha1 withReply:(void (^)(SNTStoredEvent *))reply;
|
||||
- (void)databaseEventsPending:(void (^)(NSArray *events))reply;
|
||||
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids;
|
||||
|
||||
/// Misc ops
|
||||
- (void)clientMode:(void (^)(santa_clientmode_t))reply;
|
||||
- (void)setClientMode:(santa_clientmode_t)mode withReply:(void (^)())reply;
|
||||
|
||||
@end
|
||||
|
||||
@interface SNTXPCControlInterface : NSObject
|
||||
|
||||
/// Returns the MachService ID for this service.
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
/// Returns an initialized NSXPCInterface for the SNTDaemonControlXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning
|
||||
+ (NSXPCInterface *)controlInterface;
|
||||
|
||||
@end
|
||||
42
Source/common/SNTXPCControlInterface.m
Normal file
42
Source/common/SNTXPCControlInterface.m
Normal file
@@ -0,0 +1,42 @@
|
||||
/// Copyright 2014 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 "SNTXPCControlInterface.h"
|
||||
|
||||
#import "SNTRule.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTXPCControlInterface
|
||||
|
||||
+ (NSString *)serviceId {
|
||||
return @"SantaXPCControl";
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)controlInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTDaemonControlXPC)];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTStoredEvent class], nil]
|
||||
forSelector:@selector(databaseEventsPending:)
|
||||
argumentIndex:0
|
||||
ofReply:YES];
|
||||
|
||||
[r setClasses:[NSSet setWithObjects:[NSArray class], [SNTRule class], nil]
|
||||
forSelector:@selector(databaseRuleAddRules:withReply:)
|
||||
argumentIndex:0
|
||||
ofReply:NO];
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@end
|
||||
30
Source/common/SNTXPCNotifierInterface.h
Normal file
30
Source/common/SNTXPCNotifierInterface.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// Protocol implemented by SantaNotifier and utilized by santad
|
||||
@class SNTNotificationMessage;
|
||||
@protocol SNTNotifierXPC
|
||||
- (void)postBlockNotification:(SNTNotificationMessage *)event;
|
||||
@end
|
||||
|
||||
@interface SNTXPCNotifierInterface : NSObject
|
||||
|
||||
/// Returns the MachService ID for this service.
|
||||
+ (NSString *)serviceId;
|
||||
|
||||
/// Returns an initialized NSXPCInterface for the SNTNotifierXPC protocol.
|
||||
/// Ensures any methods that accept custom classes as arguments are set-up before returning
|
||||
+ (NSXPCInterface *)notifierInterface;
|
||||
|
||||
@end
|
||||
28
Source/common/SNTXPCNotifierInterface.m
Normal file
28
Source/common/SNTXPCNotifierInterface.m
Normal file
@@ -0,0 +1,28 @@
|
||||
/// Copyright 2014 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 "SNTXPCNotifierInterface.h"
|
||||
|
||||
@implementation SNTXPCNotifierInterface
|
||||
|
||||
+ (NSString *)serviceId {
|
||||
return @"SantaXPCNotifications";
|
||||
}
|
||||
|
||||
+ (NSXPCInterface *)notifierInterface {
|
||||
NSXPCInterface *r = [NSXPCInterface interfaceWithProtocol:@protocol(SNTNotifierXPC)];
|
||||
return r;
|
||||
}
|
||||
|
||||
@end
|
||||
53
Source/santa-driver/Resources/santa-driver-Info.plist
Normal file
53
Source/santa-driver/Resources/santa-driver-Info.plist
Normal file
@@ -0,0 +1,53 @@
|
||||
<?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>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.santa-driver</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>KEXT</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<key>IOKitPersonalities</key>
|
||||
<dict>
|
||||
<key>SantaDriver</key>
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.google.santa-driver</string>
|
||||
<key>IOClass</key>
|
||||
<string>com_google_SantaDriver</string>
|
||||
<key>IOProviderClass</key>
|
||||
<string>IOResources</string>
|
||||
<key>IOResourceMatch</key>
|
||||
<string>IOKit</string>
|
||||
<key>IOUserClientClass</key>
|
||||
<string>com_google_SantaDriverClient</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OSBundleLibraries</key>
|
||||
<dict>
|
||||
<key>com.apple.kpi.bsd</key>
|
||||
<string>9.0.0</string>
|
||||
<key>com.apple.kpi.iokit</key>
|
||||
<string>9.0.0</string>
|
||||
<key>com.apple.kpi.libkern</key>
|
||||
<string>9.0.0</string>
|
||||
<key>com.apple.kpi.mach</key>
|
||||
<string>9.0.0</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
505
Source/santa-driver/SantaDecisionManager.cc
Normal file
505
Source/santa-driver/SantaDecisionManager.cc
Normal file
@@ -0,0 +1,505 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDecisionManager.h"
|
||||
|
||||
#define super OSObject
|
||||
OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);
|
||||
|
||||
#pragma mark Object Lifecycle
|
||||
|
||||
SantaDecisionManager *SantaDecisionManager::WithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid) {
|
||||
SantaDecisionManager *me = new SantaDecisionManager;
|
||||
|
||||
if (me && !me->InitWithQueueAndPID(queue, pid)) {
|
||||
me->free();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::InitWithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid) {
|
||||
if (!super::init()) return false;
|
||||
|
||||
if (!pid) return false;
|
||||
if (!queue) return false;
|
||||
|
||||
listener_invocations_ = 0;
|
||||
dataqueue_ = queue;
|
||||
owning_pid_ = pid;
|
||||
owning_proc_ = proc_find(pid);
|
||||
|
||||
if (!(dataqueue_lock_ = IORWLockAlloc())) return FALSE;
|
||||
if (!(cached_decisions_lock_ = IORWLockAlloc())) return FALSE;
|
||||
if (!(cached_decisions_ = OSDictionary::withCapacity(1000))) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::free() {
|
||||
proc_rele(owning_proc_);
|
||||
|
||||
if (cached_decisions_) {
|
||||
cached_decisions_->release();
|
||||
cached_decisions_ = NULL;
|
||||
}
|
||||
|
||||
if (cached_decisions_lock_) {
|
||||
IORWLockFree(cached_decisions_lock_);
|
||||
cached_decisions_lock_ = NULL;
|
||||
}
|
||||
|
||||
if (dataqueue_lock_) {
|
||||
IORWLockFree(dataqueue_lock_);
|
||||
dataqueue_lock_ = NULL;
|
||||
}
|
||||
|
||||
super::free();
|
||||
}
|
||||
|
||||
# pragma mark Cache Management
|
||||
|
||||
bool SantaDecisionManager::AddToCache(
|
||||
const char *identifier, santa_action_t decision, uint64_t microsecs) {
|
||||
IORWLockWrite(cached_decisions_lock_);
|
||||
|
||||
if (cached_decisions_->getCount() > kMaxCacheSize) {
|
||||
// This could be made a _lot_ smarter, say only removing entries older
|
||||
// than a certain time period. However, with a kMaxCacheSize set
|
||||
// sufficiently large and a kMaxAllowCacheTimeMilliseconds set
|
||||
// sufficiently low, this should only ever occur if someone is purposefully
|
||||
// trying to make the cache grow.
|
||||
LOGD("Cache too large, flushing.");
|
||||
cached_decisions_->flushCollection();
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (decision == ACTION_REQUEST_CHECKBW) {
|
||||
SantaMessage *pending = new SantaMessage();
|
||||
pending->setAction(ACTION_REQUEST_CHECKBW, 0);
|
||||
result = cached_decisions_->setObject(identifier, pending);
|
||||
pending->release(); // it was retained when added to the dictionary
|
||||
} else {
|
||||
SantaMessage *pending = OSDynamicCast(
|
||||
SantaMessage, cached_decisions_->getObject(identifier));
|
||||
if (pending) {
|
||||
pending->setAction(decision, microsecs);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::CacheCheck(const char *identifier) {
|
||||
IORWLockRead(cached_decisions_lock_);
|
||||
bool shouldInvalidate = (cached_decisions_->getObject(identifier) != NULL);
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
if (shouldInvalidate) {
|
||||
IORWLockWrite(cached_decisions_lock_);
|
||||
cached_decisions_->removeObject(identifier);
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::CacheCount() {
|
||||
return cached_decisions_->getCount();
|
||||
}
|
||||
|
||||
void SantaDecisionManager::ClearCache() {
|
||||
IORWLockWrite(cached_decisions_lock_);
|
||||
cached_decisions_->flushCollection();
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
}
|
||||
|
||||
santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {
|
||||
santa_action_t result = ACTION_UNSET;
|
||||
uint64_t decision_time = 0;
|
||||
|
||||
IORWLockRead(cached_decisions_lock_);
|
||||
SantaMessage *cached_decision = OSDynamicCast(
|
||||
SantaMessage, cached_decisions_->getObject(identifier));
|
||||
if (cached_decision) {
|
||||
result = cached_decision->getAction();
|
||||
decision_time = cached_decision->getMicrosecs();
|
||||
}
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
|
||||
if (result == ACTION_REQUEST_CHECKBW) {
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
|
||||
if (result == ACTION_RESPOND_CHECKBW_ALLOW ||
|
||||
result == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
uint64_t diff_time = GetCurrentUptime();
|
||||
|
||||
if (result == ACTION_RESPOND_CHECKBW_ALLOW) {
|
||||
if ((kMaxAllowCacheTimeMilliseconds * 1000) > diff_time) {
|
||||
diff_time = 0;
|
||||
} else {
|
||||
diff_time -= (kMaxAllowCacheTimeMilliseconds * 1000);
|
||||
}
|
||||
} else if (result == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
if ((kMaxDenyCacheTimeMilliseconds * 1000) > diff_time) {
|
||||
diff_time = 0;
|
||||
} else {
|
||||
diff_time -= (kMaxDenyCacheTimeMilliseconds * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (decision_time < diff_time) {
|
||||
IORWLockWrite(cached_decisions_lock_);
|
||||
cached_decisions_->removeObject(identifier);
|
||||
IORWLockUnlock(cached_decisions_lock_);
|
||||
return ACTION_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
# pragma mark Queue Management
|
||||
|
||||
bool SantaDecisionManager::PostToQueue(santa_message_t message) {
|
||||
IORWLockWrite(dataqueue_lock_);
|
||||
bool kr = dataqueue_->enqueue(&message, sizeof(message));
|
||||
IORWLockUnlock(dataqueue_lock_);
|
||||
return kr;
|
||||
}
|
||||
|
||||
santa_action_t SantaDecisionManager::FetchDecision(
|
||||
const kauth_cred_t credential,
|
||||
const vfs_context_t vfs_context,
|
||||
const vnode_t vnode) {
|
||||
santa_action_t return_action = ACTION_UNSET;
|
||||
|
||||
// Fetch Vnode ID & string
|
||||
uint64_t vnode_id = GetVnodeIDForVnode(vfs_context, vnode);
|
||||
char vnode_id_str[MAX_VNODE_ID_STR];
|
||||
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu", vnode_id);
|
||||
|
||||
// Check to see if item is in cache
|
||||
return_action = GetFromCache(vnode_id_str);
|
||||
|
||||
// If item wasn't in cache, fetch decision from daemon.
|
||||
if (return_action == ACTION_UNSET) {
|
||||
// Add pending request to cache
|
||||
AddToCache(vnode_id_str, ACTION_REQUEST_CHECKBW, 0);
|
||||
|
||||
// Get SHA-1
|
||||
// TODO(rah): Investigate possible race condition where file is modified
|
||||
// in between SHA-1 being calculated and response for said file being
|
||||
// received.
|
||||
char sha[MAX_SHA1_STRING];
|
||||
if (!CalculateSHA1ForVnode(credential, vfs_context, vnode, sha)) {
|
||||
LOGD("Unable to get SHA-1 for file, denying execution");
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
}
|
||||
|
||||
// Get path
|
||||
char path[MAX_PATH_LEN];
|
||||
int name_len = MAX_PATH_LEN;
|
||||
if (vn_getpath(vnode, path, &name_len) != 0) {
|
||||
path[0] = '\0';
|
||||
}
|
||||
|
||||
// Prepare to send message to daemon
|
||||
santa_message_t message;
|
||||
strncpy(message.sha1, sha, MAX_SHA1_STRING);
|
||||
strncpy(message.path, path, MAX_PATH_LEN);
|
||||
message.userId = kauth_cred_getuid(credential);
|
||||
message.pid = proc_selfpid();
|
||||
message.action = ACTION_REQUEST_CHECKBW;
|
||||
message.vnode_id = vnode_id;
|
||||
|
||||
// Wait for the daemon to respond or die.
|
||||
do {
|
||||
// Send request to daemon...
|
||||
if (!PostToQueue(message)) {
|
||||
LOGE("Failed to queue request for %s.", path);
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
|
||||
// ... and wait for it to respond. If after kRequestLoopSleepMilliseconds
|
||||
// * kMaxRequestLoops it still hasn't responded, send request again.
|
||||
for (int i = 0; i < kMaxRequestLoops; ++i) {
|
||||
IOSleep(kRequestLoopSleepMilliseconds);
|
||||
return_action = GetFromCache(vnode_id_str);
|
||||
if (return_action != ACTION_UNSET) break;
|
||||
}
|
||||
} while (return_action == ACTION_UNSET && proc_exiting(owning_proc_) == 0);
|
||||
|
||||
if (return_action == ACTION_UNSET || return_action == ACTION_ERROR) {
|
||||
LOGE("Daemon process did not respond correctly. Allowing executions "
|
||||
"until it comes back.");
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return return_action;
|
||||
}
|
||||
|
||||
# pragma mark Misc
|
||||
|
||||
bool SantaDecisionManager::CalculateSHA1ForVnode(const kauth_cred_t credential,
|
||||
const vfs_context_t context,
|
||||
const vnode_t vp,
|
||||
char *out) {
|
||||
out[0] = '\0';
|
||||
|
||||
// Get binary size
|
||||
uint64_t binary_size;
|
||||
struct vnode_attr vap;
|
||||
VATTR_INIT(&vap);
|
||||
VATTR_WANTED(&vap, va_data_size);
|
||||
vnode_getattr(vp, &vap, context);
|
||||
binary_size = vap.va_data_size;
|
||||
|
||||
// Initialize the SHA1 context
|
||||
SHA1_CTX sha1_ctx;
|
||||
SHA1Init(&sha1_ctx);
|
||||
|
||||
// |chunkSize| should equal one page so that where possible
|
||||
// the kernel can offload the calculation to dedicated hardware.
|
||||
int chunkSize = PAGE_SIZE_64;
|
||||
void *readChunk = IOMalloc(chunkSize);
|
||||
|
||||
// Credentials needed for vn_rdwr
|
||||
kauth_cred_t kerncred = vfs_context_ucred(context);
|
||||
proc_t p = vfs_context_proc(context);
|
||||
|
||||
// Read the file in chunks, updating the SHA as we go
|
||||
for (uint64_t offset = 0; offset < binary_size; offset += chunkSize) {
|
||||
int readSize;
|
||||
if (offset + chunkSize > binary_size) {
|
||||
readSize = (int)(binary_size - offset);
|
||||
} else {
|
||||
readSize = chunkSize;
|
||||
}
|
||||
|
||||
int resid; // unused
|
||||
if (vn_rdwr(UIO_READ, vp, (caddr_t)readChunk, readSize, offset,
|
||||
UIO_SYSSPACE, IO_NOAUTH, kerncred, &resid, p) != 0) {
|
||||
IOFree(readChunk, chunkSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
SHA1Update(&sha1_ctx, readChunk, readSize);
|
||||
}
|
||||
|
||||
// Free |readChunk|
|
||||
IOFree(readChunk, chunkSize);
|
||||
|
||||
// Finalize the SHA-1 into |buf|
|
||||
char buf[MAX_SHA1_LEN];
|
||||
SHA1Final(buf, &sha1_ctx);
|
||||
|
||||
// Convert the binary SHA into a hex digest string
|
||||
for (int i = 0; i < MAX_SHA1_LEN; i++) {
|
||||
snprintf(out + (2*i), 3, "%02x", (unsigned char)buf[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::GetVnodeIDForVnode(const vfs_context_t context,
|
||||
const vnode_t vp) {
|
||||
struct vnode_attr vap;
|
||||
VATTR_INIT(&vap);
|
||||
VATTR_WANTED(&vap, va_fileid);
|
||||
vnode_getattr(vp, &vap, context);
|
||||
return vap.va_fileid;
|
||||
}
|
||||
|
||||
uint64_t SantaDecisionManager::GetCurrentUptime() {
|
||||
clock_sec_t sec;
|
||||
clock_usec_t usec;
|
||||
clock_get_system_microtime(&sec, &usec);
|
||||
return (uint64_t)((sec * 1000000) + usec);
|
||||
}
|
||||
|
||||
# pragma mark Invocation Tracking & PID comparison
|
||||
|
||||
SInt32 SantaDecisionManager::GetListenerInvocations() {
|
||||
return listener_invocations_;
|
||||
}
|
||||
|
||||
void SantaDecisionManager::IncrementListenerInvocations() {
|
||||
OSIncrementAtomic(&listener_invocations_);
|
||||
}
|
||||
|
||||
void SantaDecisionManager::DecrementListenerInvocations() {
|
||||
OSDecrementAtomic(&listener_invocations_);
|
||||
}
|
||||
|
||||
bool SantaDecisionManager::MatchesOwningPID(const pid_t other_pid) {
|
||||
return (owning_pid_ == other_pid);
|
||||
}
|
||||
|
||||
# pragma mark Listener Control
|
||||
|
||||
kern_return_t SantaDecisionManager::StartListener() {
|
||||
process_listener_ = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
|
||||
process_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (!process_listener_) return kIOReturnInternalError;
|
||||
LOGD("Process listener started.");
|
||||
|
||||
vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
|
||||
vnode_scope_callback,
|
||||
reinterpret_cast<void *>(this));
|
||||
if (!vnode_listener_) return kIOReturnInternalError;
|
||||
|
||||
LOGD("Vnode listener started.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
kern_return_t SantaDecisionManager::StopListener() {
|
||||
kauth_unlisten_scope(vnode_listener_);
|
||||
vnode_listener_ = NULL;
|
||||
|
||||
kauth_unlisten_scope(process_listener_);
|
||||
process_listener_ = NULL;
|
||||
|
||||
// Wait for any active invocations to finish before returning
|
||||
do {
|
||||
IOSleep(5);
|
||||
} while (GetListenerInvocations());
|
||||
|
||||
// Delete any cached decisions
|
||||
ClearCache();
|
||||
|
||||
LOGD("Vnode listener stopped.");
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
#undef super
|
||||
|
||||
#pragma mark Kauth Callbacks
|
||||
|
||||
extern int process_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3) {
|
||||
if (idata == NULL) {
|
||||
LOGE("Process callback established without valid decision manager.");
|
||||
return KAUTH_RESULT_ALLOW;
|
||||
}
|
||||
SantaDecisionManager *sdm = OSDynamicCast(
|
||||
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
|
||||
|
||||
// Note: this prevents a debugger from attaching to an existing santad
|
||||
// process but doesn't prevent starting santad under a debugger. This check
|
||||
// is only here to try and prevent the user from deadlocking their machine
|
||||
// by attaching a debugger, so if they work around it and end up deadlocking,
|
||||
// that's their problem.
|
||||
if (action == KAUTH_PROCESS_CANTRACE &&
|
||||
sdm->MatchesOwningPID(proc_pid((proc_t)arg0))) {
|
||||
*(reinterpret_cast<int *>(arg1)) = EPERM;
|
||||
LOGD("Denied debugger access");
|
||||
return KAUTH_RESULT_DENY;
|
||||
}
|
||||
|
||||
return KAUTH_RESULT_ALLOW;
|
||||
}
|
||||
|
||||
|
||||
extern int vnode_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3) {
|
||||
// The default action is to defer
|
||||
int returnResult = KAUTH_RESULT_DEFER;
|
||||
|
||||
// Cast arguments to correct types
|
||||
if (idata == NULL) {
|
||||
LOGE("Vnode callback established without valid decision manager.");
|
||||
return returnResult;
|
||||
}
|
||||
SantaDecisionManager *sdm = OSDynamicCast(
|
||||
SantaDecisionManager, reinterpret_cast<OSObject *>(idata));
|
||||
vfs_context_t vfs_context = reinterpret_cast<vfs_context_t>(arg0);
|
||||
vnode_t vnode = reinterpret_cast<vnode_t>(arg1);
|
||||
|
||||
// Only operate on regular files (not directories, symlinks, etc.)
|
||||
vtype vt = vnode_vtype(vnode);
|
||||
if (vt != VREG) return returnResult;
|
||||
|
||||
if (action & KAUTH_VNODE_ACCESS) return returnResult;
|
||||
|
||||
// Filter for only WRITE_DATA actions
|
||||
if (action & KAUTH_VNODE_WRITE_DATA || action & KAUTH_VNODE_APPEND_DATA) {
|
||||
char vnode_id_str[MAX_VNODE_ID_STR];
|
||||
snprintf(vnode_id_str, MAX_VNODE_ID_STR, "%llu",
|
||||
sdm->GetVnodeIDForVnode(vfs_context, vnode));
|
||||
|
||||
sdm->CacheCheck(vnode_id_str);
|
||||
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
// Filter for only EXECUTE actions
|
||||
if (action & KAUTH_VNODE_EXECUTE) {
|
||||
sdm->IncrementListenerInvocations();
|
||||
|
||||
// Fetch decision
|
||||
santa_action_t returnedAction = sdm->FetchDecision(
|
||||
credential, vfs_context, vnode);
|
||||
|
||||
switch (returnedAction) {
|
||||
case ACTION_RESPOND_CHECKBW_ALLOW:
|
||||
returnResult = KAUTH_RESULT_ALLOW;
|
||||
break;
|
||||
case ACTION_RESPOND_CHECKBW_DENY:
|
||||
*(reinterpret_cast<int *>(arg3)) = EACCES;
|
||||
returnResult = KAUTH_RESULT_DENY;
|
||||
break;
|
||||
default:
|
||||
// NOTE: Any unknown response or error condition causes us to fail open.
|
||||
// Whilst from a security perspective this is bad, it's important that
|
||||
// we don't break user's machines. Every fallen open response will come
|
||||
// through this code path and cause this log entry to be created, so we
|
||||
// can investigate each case and try to fix the root cause.
|
||||
char path[MAX_PATH_LEN];
|
||||
int name_len = MAX_PATH_LEN;
|
||||
if (vn_getpath(vnode, path, &name_len) != 0) {
|
||||
path[0] = '\0';
|
||||
}
|
||||
LOGW("Didn't receive a valid response for %s. Received: %d.",
|
||||
path,
|
||||
returnedAction);
|
||||
break;
|
||||
}
|
||||
|
||||
sdm->DecrementListenerInvocations();
|
||||
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
return returnResult;
|
||||
}
|
||||
156
Source/santa-driver/SantaDecisionManager.h
Normal file
156
Source/santa-driver/SantaDecisionManager.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
|
||||
#define SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
|
||||
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IOSharedDataQueue.h>
|
||||
#include <libkern/c++/OSDictionary.h>
|
||||
#include <libkern/crypto/sha1.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include "SantaMessage.h"
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
/// The maximum number of milliseconds a cached deny message should be
|
||||
/// considered valid.
|
||||
const uint64_t kMaxDenyCacheTimeMilliseconds = 500;
|
||||
|
||||
/// The maximum number of milliseconds a cached allow message should be
|
||||
/// considered valid.
|
||||
const uint64_t kMaxAllowCacheTimeMilliseconds = 1000 * 60 * 60 * 24;
|
||||
|
||||
/// While waiting for a response from the daemon, this is the number of
|
||||
/// milliseconds to sleep for before checking the cache for a response.
|
||||
const int kRequestLoopSleepMilliseconds = 10;
|
||||
|
||||
/// While waiting for a response from the daemon, this is the maximum number
|
||||
/// of loops to wait before sending the request again.
|
||||
const int kMaxRequestLoops = 50;
|
||||
|
||||
/// Maximum number of entries in the in-kernel cache.
|
||||
const int kMaxCacheSize = 10000;
|
||||
|
||||
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
|
||||
/// and responding to the request appropriately.
|
||||
///
|
||||
/// Documentation on the Kauth parts can be found here:
|
||||
/// https://developer.apple.com/library/mac/technotes/tn2127/_index.html
|
||||
class SantaDecisionManager : public OSObject {
|
||||
OSDeclareDefaultStructors(SantaDecisionManager);
|
||||
|
||||
public:
|
||||
// Convenience constructor
|
||||
// Queue remains owned by caller but must exist for lifetime of
|
||||
// SantaDecisionManager instance.
|
||||
static SantaDecisionManager *WithQueueAndPID(
|
||||
IOSharedDataQueue *queue, pid_t pid);
|
||||
|
||||
bool InitWithQueueAndPID(IOSharedDataQueue *queue, pid_t pid);
|
||||
void free();
|
||||
|
||||
// Decision Fetching / Daemon Communication
|
||||
bool PostToQueue(santa_message_t);
|
||||
santa_action_t FetchDecision(const kauth_cred_t credential,
|
||||
const vfs_context_t vfs_context,
|
||||
const vnode_t vnode);
|
||||
|
||||
// Hash calculation
|
||||
bool CalculateSHA1ForVnode(const kauth_cred_t credential,
|
||||
const vfs_context_t context,
|
||||
const vnode_t vnode,
|
||||
char *out);
|
||||
|
||||
// Vnode ID string
|
||||
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
|
||||
|
||||
// Cache management
|
||||
bool AddToCache(const char *identifier,
|
||||
const santa_action_t decision,
|
||||
const uint64_t microsecs);
|
||||
void CacheCheck(const char *identifier);
|
||||
uint64_t CacheCount();
|
||||
void ClearCache();
|
||||
santa_action_t GetFromCache(const char *identifier);
|
||||
|
||||
// Listener invocation management
|
||||
SInt32 GetListenerInvocations();
|
||||
void IncrementListenerInvocations();
|
||||
void DecrementListenerInvocations();
|
||||
|
||||
// Owning PID comparison
|
||||
bool MatchesOwningPID(const pid_t other_pid);
|
||||
|
||||
// Returns the current system uptime in microseconds
|
||||
uint64_t GetCurrentUptime();
|
||||
|
||||
// Starting and stopping the listener
|
||||
kern_return_t StartListener();
|
||||
kern_return_t StopListener();
|
||||
|
||||
private:
|
||||
OSDictionary *cached_decisions_;
|
||||
IORWLock *cached_decisions_lock_;
|
||||
|
||||
IOSharedDataQueue *dataqueue_;
|
||||
IORWLock *dataqueue_lock_;
|
||||
|
||||
SInt32 listener_invocations_;
|
||||
|
||||
pid_t owning_pid_;
|
||||
proc_t owning_proc_;
|
||||
|
||||
kauth_listener_t vnode_listener_;
|
||||
kauth_listener_t process_listener_;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
/// The callback function for the Vnode scope
|
||||
/// @param actor's credentials
|
||||
/// @param data that was passed when the listener was registered
|
||||
/// @param action that was requested
|
||||
/// @param VFS context
|
||||
/// @param Vnode being operated on
|
||||
/// @param Parent Vnode. May be NULL.
|
||||
/// @param Pointer to an errno-style error.
|
||||
extern int vnode_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3);
|
||||
|
||||
/// The callback function for the Process scope
|
||||
/// @param actor's credentials
|
||||
/// @param data that was passed when the listener was registered
|
||||
/// @param action that was requested (KAUTH_PROCESS_{CANTRACE,CANSIGNAL})
|
||||
/// @param target process
|
||||
/// @param Pointer to an errno-style error.
|
||||
/// @param unused
|
||||
/// @param unused
|
||||
extern int process_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3);
|
||||
} // extern C
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTADECISIONMANAGER_H
|
||||
37
Source/santa-driver/SantaDriver.cc
Normal file
37
Source/santa-driver/SantaDriver.cc
Normal file
@@ -0,0 +1,37 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDriver.h"
|
||||
|
||||
#define super IOService
|
||||
#define SantaDriver com_google_SantaDriver
|
||||
|
||||
// The defines above can'be used in this function, we must use the full names.
|
||||
OSDefineMetaClassAndStructors(com_google_SantaDriver, IOService);
|
||||
|
||||
bool SantaDriver::start(IOService *provider) {
|
||||
if (!super::start(provider)) return false;
|
||||
|
||||
registerService();
|
||||
|
||||
LOGI("Loaded, version %s.", OSKextGetCurrentVersionString());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDriver::stop(IOService *provider) {
|
||||
LOGI("Unloaded.");
|
||||
}
|
||||
|
||||
#undef super
|
||||
33
Source/santa-driver/SantaDriver.h
Normal file
33
Source/santa-driver/SantaDriver.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__SANTA_DRIVER__SANTADRIVER_H
|
||||
#define SANTA__SANTA_DRIVER__SANTADRIVER_H
|
||||
|
||||
#include <IOKit/IOService.h>
|
||||
#include <libkern/OSKextLib.h>
|
||||
|
||||
#include "SantaDecisionManager.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
/// The driver class, which provides just the start/stop functions.
|
||||
class com_google_SantaDriver : public IOService {
|
||||
OSDeclareDefaultStructors(com_google_SantaDriver);
|
||||
|
||||
public:
|
||||
bool start(IOService *provider);
|
||||
void stop(IOService *provider);
|
||||
};
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTADRIVER_H
|
||||
308
Source/santa-driver/SantaDriverClient.cc
Normal file
308
Source/santa-driver/SantaDriverClient.cc
Normal file
@@ -0,0 +1,308 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaDriverClient.h"
|
||||
|
||||
#define super IOUserClient
|
||||
#define SantaDriverClient com_google_SantaDriverClient
|
||||
|
||||
// The defines above can'be used in this function, must use the full names.
|
||||
OSDefineMetaClassAndStructors(com_google_SantaDriverClient, IOUserClient);
|
||||
|
||||
# pragma mark Driver Management
|
||||
|
||||
bool SantaDriverClient::initWithTask(
|
||||
task_t owningTask, void *securityID, UInt32 type) {
|
||||
if (clientHasPrivilege(
|
||||
owningTask, kIOClientPrivilegeAdministrator) != KERN_SUCCESS) {
|
||||
LOGW("Unprivileged client attempted to connect.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!super::initWithTask(owningTask, securityID, type)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SantaDriverClient::start(IOService *provider) {
|
||||
fProvider = OSDynamicCast(com_google_SantaDriver, provider);
|
||||
|
||||
if (!fProvider) return false;
|
||||
if (!super::start(provider)) return false;
|
||||
|
||||
fSDMLock = IOLockAlloc();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SantaDriverClient::stop(IOService *provider) {
|
||||
super::stop(provider);
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientClose() {
|
||||
close();
|
||||
terminate(kIOServiceSynchronous);
|
||||
|
||||
fProvider = NULL;
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
bool SantaDriverClient::terminate(IOOptionBits options) {
|
||||
// We have to lock before this check in case the client exits and the kext
|
||||
// is unloaded very shortly afterwards.
|
||||
IOLockLock(fSDMLock);
|
||||
if (fSDM) {
|
||||
fSDM->StopListener();
|
||||
|
||||
// Ask santad to shutdown
|
||||
santa_message_t message;
|
||||
message.action = ACTION_REQUEST_SHUTDOWN;
|
||||
message.userId = 0;
|
||||
message.pid = 0;
|
||||
message.vnode_id = 0;
|
||||
fSDM->PostToQueue(message);
|
||||
|
||||
LOGI("Client disconnected.");
|
||||
|
||||
fSDM->release();
|
||||
fSDM = NULL;
|
||||
}
|
||||
IOLockUnlock(fSDMLock);
|
||||
|
||||
if (fProvider && fProvider->isOpen(this)) fProvider->close(this);
|
||||
|
||||
return super::terminate(options);
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::registerNotificationPort(mach_port_t port,
|
||||
UInt32 type,
|
||||
UInt32 ref) {
|
||||
if ((!fDataQueue) || (port == MACH_PORT_NULL)) return kIOReturnError;
|
||||
|
||||
fDataQueue->setNotificationPort(port);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clientMemoryForType(UInt32 type,
|
||||
IOOptionBits *options,
|
||||
IOMemoryDescriptor **memory) {
|
||||
*memory = NULL;
|
||||
*options = 0;
|
||||
|
||||
if (type == kIODefaultMemoryType) {
|
||||
if (!fSharedMemory) return kIOReturnNoMemory;
|
||||
fSharedMemory->retain(); // client will decrement this ref
|
||||
*memory = fSharedMemory;
|
||||
|
||||
return fSDM->StartListener();
|
||||
}
|
||||
|
||||
return kIOReturnNoMemory;
|
||||
}
|
||||
|
||||
#pragma mark Callable Methods
|
||||
|
||||
IOReturn SantaDriverClient::open() {
|
||||
if (isInactive()) return kIOReturnNotAttached;
|
||||
|
||||
if (!fProvider->open(this)) {
|
||||
LOGW("A second client tried to connect.");
|
||||
return kIOReturnExclusiveAccess;
|
||||
}
|
||||
|
||||
fDataQueue = IOSharedDataQueue::withCapacity((sizeof(santa_message_t) +
|
||||
DATA_QUEUE_ENTRY_HEADER_SIZE)
|
||||
* kMaxQueueEvents);
|
||||
if (!fDataQueue) return kIOReturnNoMemory;
|
||||
|
||||
fSharedMemory = fDataQueue->getMemoryDescriptor();
|
||||
if (!fSharedMemory) {
|
||||
fDataQueue->release();
|
||||
fDataQueue = NULL;
|
||||
return kIOReturnVMError;
|
||||
}
|
||||
|
||||
IOLockLock(fSDMLock);
|
||||
fSDM = SantaDecisionManager::WithQueueAndPID(fDataQueue, proc_selfpid());
|
||||
IOLockUnlock(fSDMLock);
|
||||
|
||||
LOGI("Client connected, PID: %d.", proc_selfpid());
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_open(
|
||||
SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->open();
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::close() {
|
||||
if (!fProvider) return kIOReturnNotAttached;
|
||||
if (fProvider->isOpen(this)) fProvider->close(this);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_close(
|
||||
SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->close();
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::allow_binary(const uint64_t vnode_id) {
|
||||
char vnode_id_str[21];
|
||||
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
|
||||
fSDM->AddToCache(vnode_id_str,
|
||||
ACTION_RESPOND_CHECKBW_ALLOW,
|
||||
fSDM->GetCurrentUptime());
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_allow_binary(
|
||||
SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->allow_binary(
|
||||
*(static_cast<const uint64_t *>(arguments->scalarInput)));
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::deny_binary(const uint64_t vnode_id) {
|
||||
char vnode_id_str[21];
|
||||
snprintf(vnode_id_str, sizeof(vnode_id_str), "%llu", vnode_id);
|
||||
fSDM->AddToCache(vnode_id_str,
|
||||
ACTION_RESPOND_CHECKBW_DENY,
|
||||
fSDM->GetCurrentUptime());
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_deny_binary(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->deny_binary(
|
||||
*(static_cast<const uint64_t *>(arguments->scalarInput)));
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clear_cache() {
|
||||
fSDM->ClearCache();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_clear_cache(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->clear_cache();
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::cache_count(uint64_t *output) {
|
||||
*output = fSDM->CacheCount();
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::static_cache_count(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments) {
|
||||
if (!target) return kIOReturnBadArgument;
|
||||
return target->cache_count(&(arguments->scalarOutput[0]));
|
||||
}
|
||||
|
||||
#pragma mark Method Resolution
|
||||
|
||||
IOReturn SantaDriverClient::externalMethod(
|
||||
UInt32 selector,
|
||||
IOExternalMethodArguments *arguments,
|
||||
IOExternalMethodDispatch *dispatch,
|
||||
OSObject *target,
|
||||
void *reference) {
|
||||
// Array of methods callable by clients. The order of these must match the
|
||||
// order of the items in |SantaDriverMethods| in SNTKernelCommon.h
|
||||
IOExternalMethodDispatch sMethods[kSantaUserClientNMethods] = {
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(&SantaDriverClient::static_open),
|
||||
0, // input scalar
|
||||
0, // input struct
|
||||
0, // output scalar
|
||||
0 // output struct
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_close),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_allow_binary),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_deny_binary),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_clear_cache),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
},
|
||||
{
|
||||
reinterpret_cast<IOExternalMethodAction>(
|
||||
&SantaDriverClient::static_cache_count),
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
if (selector < static_cast<UInt32>(kSantaUserClientNMethods)) {
|
||||
dispatch = &(sMethods[selector]);
|
||||
if (!target) target = this;
|
||||
} else {
|
||||
return kIOReturnBadArgument;
|
||||
}
|
||||
|
||||
return super::externalMethod(selector,
|
||||
arguments,
|
||||
dispatch,
|
||||
target,
|
||||
reference);
|
||||
}
|
||||
|
||||
#undef super
|
||||
112
Source/santa-driver/SantaDriverClient.h
Normal file
112
Source/santa-driver/SantaDriverClient.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H
|
||||
#define SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H
|
||||
|
||||
#include <IOKit/IOUserClient.h>
|
||||
#include <IOKit/IOSharedDataQueue.h>
|
||||
#include <IOKit/IOLib.h>
|
||||
#include <IOKit/IODataQueueShared.h>
|
||||
#include <libkern/crypto/sha1.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/proc.h>
|
||||
|
||||
#include "SantaDecisionManager.h"
|
||||
#include "SantaDriver.h"
|
||||
#include "SantaMessage.h"
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
// The maximum number of messages can be kept in the IODataQueue at any time.
|
||||
const int kMaxQueueEvents = 64;
|
||||
|
||||
/// This class is instantiated by IOKit when a new client process attempts to
|
||||
/// connect to the driver. Starting, stopping, handling connections, allocating
|
||||
/// shared memory and establishing a data queue is handled here.
|
||||
///
|
||||
/// Documentation on how the IOUserClient parts of this code work can be found
|
||||
/// here:
|
||||
/// @link https://developer.apple.com/library/mac/samplecode/SimpleUserClient/Listings/User_Client_Info_txt.html
|
||||
class com_google_SantaDriverClient : public IOUserClient {
|
||||
OSDeclareDefaultStructors(com_google_SantaDriverClient);
|
||||
|
||||
private:
|
||||
IOSharedDataQueue *fDataQueue;
|
||||
IOMemoryDescriptor *fSharedMemory;
|
||||
com_google_SantaDriver *fProvider;
|
||||
SantaDecisionManager *fSDM;
|
||||
IOLock *fSDMLock;
|
||||
|
||||
public:
|
||||
bool start(IOService *provider);
|
||||
void stop(IOService *provider);
|
||||
IOReturn clientClose();
|
||||
bool terminate(IOOptionBits options);
|
||||
bool initWithTask(task_t owningTask, void *securityID, UInt32 type);
|
||||
|
||||
IOReturn registerNotificationPort(
|
||||
mach_port_t port, UInt32 type, UInt32 refCon);
|
||||
|
||||
IOReturn clientMemoryForType(
|
||||
UInt32 type, IOOptionBits *options, IOMemoryDescriptor **memory);
|
||||
|
||||
IOReturn externalMethod(
|
||||
UInt32 selector,
|
||||
IOExternalMethodArguments *arguments,
|
||||
IOExternalMethodDispatch *dispatch,
|
||||
OSObject *target, void *reference);
|
||||
|
||||
IOReturn open();
|
||||
static IOReturn static_open(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
IOReturn close();
|
||||
static IOReturn static_close(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to allow a binary.
|
||||
IOReturn allow_binary(uint64_t vnode_id);
|
||||
static IOReturn static_allow_binary(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to deny a binary.
|
||||
IOReturn deny_binary(uint64_t vnode_id);
|
||||
static IOReturn static_deny_binary(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to empty the cache.
|
||||
IOReturn clear_cache();
|
||||
static IOReturn static_clear_cache(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to find out how many items are in the cache
|
||||
IOReturn cache_count(uint64_t *output);
|
||||
static IOReturn static_cache_count(
|
||||
com_google_SantaDriverClient *target,
|
||||
void *reference,
|
||||
IOExternalMethodArguments *arguments);
|
||||
};
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTADRIVERUSERCLIENT_H
|
||||
31
Source/santa-driver/SantaMessage.cc
Normal file
31
Source/santa-driver/SantaMessage.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SantaMessage.h"
|
||||
|
||||
OSDefineMetaClassAndStructors(SantaMessage, OSObject);
|
||||
|
||||
uint64_t SantaMessage::getMicrosecs() const {
|
||||
return microsecs_;
|
||||
}
|
||||
|
||||
santa_action_t SantaMessage::getAction() const {
|
||||
return action_;
|
||||
}
|
||||
|
||||
void SantaMessage::setAction(const santa_action_t action,
|
||||
const uint64_t microsecs) {
|
||||
action_ = action;
|
||||
microsecs_ = microsecs;
|
||||
}
|
||||
42
Source/santa-driver/SantaMessage.h
Normal file
42
Source/santa-driver/SantaMessage.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__SANTA_DRIVER__SANTAMESSAGE_H
|
||||
#define SANTA__SANTA_DRIVER__SANTAMESSAGE_H
|
||||
|
||||
#include <libkern/c++/OSObject.h>
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
/// An OSObject wrapper around a @c santa_action_t and a time.
|
||||
/// Only OSObject subclasses can be inserted into an OSDictionary.
|
||||
class SantaMessage : public OSObject {
|
||||
OSDeclareDefaultStructors(SantaMessage)
|
||||
|
||||
private:
|
||||
santa_action_t action_;
|
||||
uint64_t microsecs_;
|
||||
|
||||
public:
|
||||
// Returns the time the action was last set.
|
||||
uint64_t getMicrosecs() const;
|
||||
|
||||
// Returns the set action.
|
||||
santa_action_t getAction() const;
|
||||
|
||||
// Sets the acion and receive time.
|
||||
void setAction(const santa_action_t action, const uint64_t microsecs);
|
||||
};
|
||||
|
||||
#endif // SANTA__SANTA_DRIVER__SANTAMESSAGE_H
|
||||
20
Source/santactl/Resources/santactl-Info.plist
Normal file
20
Source/santactl/Resources/santactl-Info.plist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?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>CFBundleIdentifier</key>
|
||||
<string>com.google.santactl</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
<key>CSFlags</key>
|
||||
<string>kill</string>
|
||||
</dict>
|
||||
</plist>
|
||||
3
Source/santactl/Resources/santactl-Prefix.pch
Normal file
3
Source/santactl/Resources/santactl-Prefix.pch
Normal file
@@ -0,0 +1,3 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
76
Source/santactl/SNTCommandController.h
Normal file
76
Source/santactl/SNTCommandController.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTXPCConnection;
|
||||
|
||||
/// Protocol that each command must adhere to.
|
||||
@protocol SNTCommand <NSObject>
|
||||
/// Return YES if command requires root.
|
||||
+ (BOOL)requiresRoot;
|
||||
|
||||
/// A small summary of the command, to be printed with the list of available commands
|
||||
+ (NSString *)shortHelpText;
|
||||
|
||||
/// A longer description of the command when the user runs "santactl help x"
|
||||
+ (NSString *)longHelpText;
|
||||
|
||||
@optional
|
||||
|
||||
/// Either of the following two methods needs to be implemented
|
||||
|
||||
/// Called when the user is running the command
|
||||
/// @param arguments an array of arguments passed in
|
||||
/// @note This method (or one of the methods it calls) is responsible for calling exit().
|
||||
+ (void)runWithArguments:(NSArray *)arguments;
|
||||
|
||||
/// Called when the user is running the command
|
||||
/// @param arguments an array of arguments passed in
|
||||
/// @param connection to santad. Will be nil if connection failed.
|
||||
/// @note This method (or one of the methods it calls) is responsible for calling exit().
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn;
|
||||
@end
|
||||
|
||||
/// Responsible for maintaining the list of available commands by name, printing their help text
|
||||
/// when requested and launching them when requested. All of the methods in this class are
|
||||
/// class methods because the @c registerCommand:named: method is called by the @c +load method
|
||||
/// of each command class and so we cannot rely on its instantiation.
|
||||
@interface SNTCommandController : NSObject
|
||||
|
||||
/// Register a new command with the specified name. Do not use this directly, use the
|
||||
/// @c REGISTER_COMMAND_NAME macro instead.
|
||||
+ (void)registerCommand:(Class<SNTCommand>)command named:(NSString *)name;
|
||||
|
||||
/// Returns a usage string listing all of the available commands
|
||||
+ (NSString *)usage;
|
||||
|
||||
/// Returns the descriptive text for the given command, if it exists
|
||||
+ (NSString *)helpForCommandWithName:(NSString *)command;
|
||||
|
||||
|
||||
/// Returns YES if @c commandName exists.
|
||||
+ (BOOL)hasCommandWithName:(NSString *)commandName;
|
||||
|
||||
/// Runs the given command with the given arguments.
|
||||
/// @c commandName the name of a previously-registered command
|
||||
/// @c arguments an array of arguments to pass to the command
|
||||
/// @return an integer return code to exit with.
|
||||
+ (int)runCommandWithName:(NSString *)commandName arguments:(NSArray *)arguments;
|
||||
|
||||
@end
|
||||
|
||||
/// This macro registers a given class as a command with the name passed in @c a (which must be an
|
||||
/// NSString). Must be placed just inside the implementation of the class, ideally at the top.
|
||||
/// The class that uses this macro must implement the SNTCommand protcol.
|
||||
#define REGISTER_COMMAND_NAME(a) \
|
||||
+ (void)load { [SNTCommandController registerCommand:[self class] named:a]; }
|
||||
132
Source/santactl/SNTCommandController.m
Normal file
132
Source/santactl/SNTCommandController.m
Normal file
@@ -0,0 +1,132 @@
|
||||
/// Copyright 2014 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 "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@implementation SNTCommandController
|
||||
|
||||
/// A dictionary to hold all of the available commands.
|
||||
/// Key is the name of the command
|
||||
/// Value is the Class
|
||||
static NSMutableDictionary *registeredCommands;
|
||||
|
||||
+ (void)registerCommand:(Class<SNTCommand>)command named:(NSString *)name {
|
||||
if (!registeredCommands) {
|
||||
registeredCommands = [NSMutableDictionary dictionary];
|
||||
}
|
||||
registeredCommands[name] = command;
|
||||
}
|
||||
|
||||
+ (NSString *)usage {
|
||||
NSMutableString *helpText = [[NSMutableString alloc] init];
|
||||
|
||||
int longestCommandName = 0;
|
||||
for (NSString *cmdName in registeredCommands) {
|
||||
if ([cmdName length] > longestCommandName) {
|
||||
longestCommandName = (int)[cmdName length];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString *cmdName in
|
||||
[[registeredCommands allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) {
|
||||
Class<SNTCommand> cmd = registeredCommands[cmdName];
|
||||
[helpText appendFormat:@"\t%*s - %@\n", longestCommandName,
|
||||
[cmdName UTF8String], [cmd shortHelpText]];
|
||||
}
|
||||
|
||||
return helpText;
|
||||
}
|
||||
|
||||
+ (NSString *)helpForCommandWithName:(NSString *)commandName {
|
||||
Class<SNTCommand> command = registeredCommands[commandName];
|
||||
if (command) {
|
||||
NSMutableString *helpText = [[NSMutableString alloc] init];
|
||||
[helpText appendFormat:@"Help for '%@':\n", commandName];
|
||||
[helpText appendString:[command longHelpText]];
|
||||
return helpText;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (SNTXPCConnection *)connectToDaemon {
|
||||
// TODO(rah): Re-factor this so that successfully establishing the connection runs the command,
|
||||
// instead of having to sleep until the connection is made.
|
||||
|
||||
SNTXPCConnection *daemonConn =
|
||||
[[SNTXPCConnection alloc] initClientWithName:[SNTXPCControlInterface serviceId]
|
||||
options:NSXPCConnectionPrivileged];
|
||||
daemonConn.remoteInterface = [SNTXPCControlInterface controlInterface];
|
||||
|
||||
__block int connected = -1;
|
||||
daemonConn.acceptedHandler = ^{
|
||||
connected = 1;
|
||||
};
|
||||
|
||||
daemonConn.rejectedHandler = ^{
|
||||
connected = 0;
|
||||
printf("The daemon rejected the connection\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
daemonConn.invalidationHandler = ^{
|
||||
connected = 0;
|
||||
printf("An error occurred communicating with the daemon\n");
|
||||
exit(1);
|
||||
};
|
||||
|
||||
[daemonConn resume];
|
||||
|
||||
int idx = 10;
|
||||
do {
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||
--idx;
|
||||
} while (connected == -1 && idx > 0);
|
||||
|
||||
if (connected > 0) {
|
||||
return daemonConn;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)hasCommandWithName:(NSString *)commandName {
|
||||
return ([registeredCommands objectForKey:commandName] != nil);
|
||||
}
|
||||
|
||||
+ (int)runCommandWithName:(NSString *)commandName arguments:(NSArray *)arguments {
|
||||
Class<SNTCommand> command = registeredCommands[commandName];
|
||||
if (command) {
|
||||
if ([command requiresRoot] && getuid() != 0) {
|
||||
printf("The command '%s' requires root privileges.\n", [commandName UTF8String]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ([(id)command respondsToSelector:@selector(runWithArguments:daemonConnection:)]) {
|
||||
[command runWithArguments:arguments daemonConnection:[self connectToDaemon]];
|
||||
} else if ([(id)command respondsToSelector:@selector(runWithArguments:)]) {
|
||||
[command runWithArguments:arguments];
|
||||
} else {
|
||||
printf("The command '%s' has not been implemented correctly.\n", [commandName UTF8String]);
|
||||
}
|
||||
|
||||
// The command is responsible for quitting.
|
||||
[[NSRunLoop mainRunLoop] run];
|
||||
}
|
||||
return 128;
|
||||
}
|
||||
|
||||
@end
|
||||
107
Source/santactl/binaryinfo/SNTCommandBinaryInfo.m
Normal file
107
Source/santactl/binaryinfo/SNTCommandBinaryInfo.m
Normal file
@@ -0,0 +1,107 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTBinaryInfo.h"
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
|
||||
@interface SNTCommandBinaryInfo : NSObject<SNTCommand>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandBinaryInfo
|
||||
|
||||
REGISTER_COMMAND_NAME(@"binaryinfo");
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Prints information about the given binary.";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return (@"The details provided will be the same ones Santa uses to make a decision about binaries"
|
||||
@"This includes SHA-1, code signing information and the type of binary");
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments {
|
||||
NSString *filePath = [arguments firstObject];
|
||||
|
||||
if (!filePath) {
|
||||
LOGI(@"Missing file path");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
BOOL directory;
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&directory]) {
|
||||
LOGI(@"File does not exist");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (directory) {
|
||||
LOGI(@"Not a regular file");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Convert to absolute, standardized path
|
||||
filePath = [filePath stringByStandardizingPath];
|
||||
if (![filePath isAbsolutePath]) {
|
||||
NSString *cwd = [[NSFileManager defaultManager] currentDirectoryPath];
|
||||
filePath = [cwd stringByAppendingPathComponent:filePath];
|
||||
}
|
||||
|
||||
LOGI(@"Info for file: %@", filePath);
|
||||
LOGI(@"-----------------------------------------------------------");
|
||||
|
||||
SNTBinaryInfo *ftd = [[SNTBinaryInfo alloc] initWithPath:filePath];
|
||||
|
||||
LOGI(@"%-20s: %@", "SHA-1", [ftd SHA1]);
|
||||
|
||||
NSArray *archs = [ftd architectures];
|
||||
if (archs) {
|
||||
LOGI(@"%-20s: %@ (%@)", "Type", [ftd machoType], [archs componentsJoinedByString:@", "]);
|
||||
} else {
|
||||
LOGI(@"%-20s: %@", "Type", [ftd machoType]);
|
||||
}
|
||||
|
||||
SNTCodesignChecker *csc = [[SNTCodesignChecker alloc] initWithBinaryPath:filePath];
|
||||
|
||||
LOGI(@"%-20s: %s", "Code-signed", (csc) ? "Yes" : "No");
|
||||
|
||||
if (csc) {
|
||||
LOGI(@"Signing chain\n");
|
||||
|
||||
[csc.certificates enumerateObjectsUsingBlock:^(SNTCertificate *c,
|
||||
unsigned long idx,
|
||||
BOOL *stop) {
|
||||
idx++; // index from 1
|
||||
LOGI(@" %2lu. %-20s: %@", idx, "SHA-1", c.SHA1);
|
||||
LOGI(@" %-20s: %@", "Common Name", c.commonName);
|
||||
LOGI(@" %-20s: %@", "Organization", c.orgName);
|
||||
LOGI(@" %-20s: %@", "Organizational Unit", c.orgUnit);
|
||||
LOGI(@" %-20s: %@", "Valid From", c.validFrom);
|
||||
LOGI(@" %-20s: %@", "Valid Until", c.validUntil);
|
||||
LOGI(@"");
|
||||
}];
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@end
|
||||
53
Source/santactl/flushcache/SNTCommandFlushCache.m
Normal file
53
Source/santactl/flushcache/SNTCommandFlushCache.m
Normal file
@@ -0,0 +1,53 @@
|
||||
/// Copyright 2014 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 "SNTLogging.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandFlushCache : NSObject<SNTCommand>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandFlushCache
|
||||
|
||||
REGISTER_COMMAND_NAME(@"flushcache");
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Flush the kernel cache";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return @"Flushes the in-kernel cache of whitelisted binaries.\n\n"
|
||||
@"Returns 0 if successful, 1 otherwise";
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
[[daemonConn remoteObjectProxy] flushCache:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Cache flush requested");
|
||||
exit(0);
|
||||
} else {
|
||||
LOGE(@"Cache flush failed");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
80
Source/santactl/main.m
Normal file
80
Source/santactl/main.m
Normal file
@@ -0,0 +1,80 @@
|
||||
/// Copyright 2014 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"
|
||||
|
||||
/**
|
||||
* santactl is a command-line utility for managing Santa.
|
||||
* As it can be used for a number of distinct operations, its operation is split into different
|
||||
* 'commands' which are expected to be the first argument to the binary. The main function below
|
||||
* is simply responsible for either passing control to the specified command or printing a useful
|
||||
* usage string.
|
||||
**/
|
||||
|
||||
void print_usage() {
|
||||
printf("Usage: santactl:\n%s\n", [[SNTCommandController usage] UTF8String]);
|
||||
}
|
||||
|
||||
void print_unknown_command(NSString *commandName) {
|
||||
printf("Unknown command: %s\n", [commandName UTF8String]);
|
||||
}
|
||||
|
||||
void print_string(NSString *string) {
|
||||
printf("%s\n", [string UTF8String]);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// Do not buffer stdout
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
@autoreleasepool {
|
||||
NSMutableArray *arguments = [[[NSProcessInfo processInfo] arguments] mutableCopy];
|
||||
[arguments removeObjectAtIndex:0];
|
||||
|
||||
NSString *commandName = [arguments firstObject];
|
||||
if (!commandName ||
|
||||
[commandName isEqualToString:@"usage"] ||
|
||||
[commandName isEqualToString:@"commands"]) {
|
||||
print_usage();
|
||||
return 1;;
|
||||
}
|
||||
[arguments removeObjectAtIndex:0];
|
||||
|
||||
if ([commandName isEqualToString:@"help"]) {
|
||||
if ([arguments count]) {
|
||||
// User wants help for specific command
|
||||
commandName = [arguments firstObject];
|
||||
if (![SNTCommandController hasCommandWithName:commandName]) {
|
||||
print_unknown_command(commandName);
|
||||
return 1;
|
||||
} else {
|
||||
print_string([SNTCommandController helpForCommandWithName:commandName]);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// User generally wants help
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// User knows what command they want, does it exist?
|
||||
if (![SNTCommandController hasCommandWithName:commandName]) {
|
||||
print_unknown_command(commandName);
|
||||
return 128;
|
||||
}
|
||||
|
||||
return [SNTCommandController runCommandWithName:commandName arguments:arguments];
|
||||
}
|
||||
}
|
||||
114
Source/santactl/status/SNTCommandStatus.m
Normal file
114
Source/santactl/status/SNTCommandStatus.m
Normal file
@@ -0,0 +1,114 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#import "SNTCommandController.h"
|
||||
|
||||
#include <IOKit/kext/KextManager.h>
|
||||
|
||||
#import "SNTBinaryInfo.h"
|
||||
#import "SNTKernelCommon.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandStatus : NSObject<SNTCommand>
|
||||
@end
|
||||
|
||||
@implementation SNTCommandStatus
|
||||
|
||||
REGISTER_COMMAND_NAME(@"status");
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Get status about Santa";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return @"Returns status information about Santa.";
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
|
||||
// Version information
|
||||
LOGI(@">>> Versions");
|
||||
LOGI(@"%-30s | %@", "santa-driver version", [self kextVersion]);
|
||||
LOGI(@"%-30s | %@", "santad version", [self daemonVersion]);
|
||||
LOGI(@"%-30s | %@",
|
||||
"santactl version",
|
||||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]);
|
||||
LOGI(@"%-30s | %@", "SantaGUI version", [self guiVersion]);
|
||||
LOGI(@"");
|
||||
|
||||
// Kext status
|
||||
__block uint64_t cacheCount = -1;
|
||||
[[daemonConn remoteObjectProxy] cacheCount:^(uint64_t count) {
|
||||
cacheCount = count;
|
||||
}];
|
||||
do { usleep(5000); } while (cacheCount == -1);
|
||||
LOGI(@">>> Kernel Info");
|
||||
LOGI(@"%-30s | %d", "Kernel cache count", cacheCount);
|
||||
LOGI(@"");
|
||||
|
||||
// Database counts
|
||||
__block uint64_t eventCount = 1, binaryRuleCount = -1, certRuleCount = -1;
|
||||
[[daemonConn remoteObjectProxy] databaseRuleCounts:^(uint64_t binary, uint64_t certificate) {
|
||||
binaryRuleCount = binary;
|
||||
certRuleCount = certificate;
|
||||
}];
|
||||
[[daemonConn remoteObjectProxy] databaseEventCount:^(uint64_t count) {
|
||||
eventCount = count;
|
||||
}];
|
||||
do { usleep(5000); } while (eventCount == -1 || binaryRuleCount == -1 || certRuleCount == -1);
|
||||
LOGI(@">>> Database Info");
|
||||
LOGI(@"%-30s | %d", "Binary Rules", binaryRuleCount);
|
||||
LOGI(@"%-30s | %d", "Certificate Rules", certRuleCount);
|
||||
LOGI(@"%-30s | %d", "Events Pending Upload", eventCount);
|
||||
LOGI(@"");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+ (NSString *)kextVersion {
|
||||
NSDictionary *loadedKexts = CFBridgingRelease(
|
||||
KextManagerCopyLoadedKextInfo((__bridge CFArrayRef)@[ @(USERCLIENT_ID) ],
|
||||
(__bridge CFArrayRef)@[ @"CFBundleVersion" ]));
|
||||
|
||||
if (loadedKexts[@(USERCLIENT_ID)] && loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"]) {
|
||||
return loadedKexts[@(USERCLIENT_ID)][@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
SNTBinaryInfo *driverInfo =
|
||||
[[SNTBinaryInfo alloc] initWithPath:@"/System/Library/Extensions/santa-driver.kext"];
|
||||
if (driverInfo) {
|
||||
return [driverInfo.bundleVersion stringByAppendingString:@" (unloaded)"];
|
||||
}
|
||||
|
||||
return @"not found";
|
||||
}
|
||||
|
||||
+ (NSString *)daemonVersion {
|
||||
SNTBinaryInfo *daemonInfo = [[SNTBinaryInfo alloc] initWithPath:@"/usr/libexec/santad"];
|
||||
return daemonInfo.bundleVersion;
|
||||
}
|
||||
|
||||
+ (NSString *)guiVersion {
|
||||
SNTBinaryInfo *guiInfo =
|
||||
[[SNTBinaryInfo alloc] initWithPath:@"/Applications/Santa.app/Contents/MacOS/Santa"];
|
||||
return guiInfo.bundleVersion;
|
||||
}
|
||||
|
||||
@end
|
||||
54
Source/santactl/sync/SNTAuthenticatingURLSession.h
Normal file
54
Source/santactl/sync/SNTAuthenticatingURLSession.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// An authenticating NSURLSession, which can do both pinned verification of the SSL server
|
||||
/// and handle client certificate authentication from the keychain.
|
||||
@interface SNTAuthenticatingURLSession : NSObject<NSURLSessionDelegate>
|
||||
|
||||
/// The underlying session. Pass this session to NSURLRequest methods.
|
||||
@property(readonly) NSURLSession *session;
|
||||
|
||||
/// If set, this is the user-agent to send with requests, otherwise remains the default
|
||||
/// CFNetwork-based name.
|
||||
@property(nonatomic) NSString *userAgent;
|
||||
|
||||
/// If set, the server that we connect to _must_ match this string. Redirects to other
|
||||
/// hosts will not be allowed.
|
||||
@property(nonatomic) NSString *serverHostname;
|
||||
|
||||
/// This should be PEM data containing one or more certificates to use to verify the server's
|
||||
/// certificate chain. This will override the trusted roots in the System Roots.
|
||||
@property(nonatomic) NSData *serverRootsPemData;
|
||||
|
||||
/// If set and client certificate authentication is needed, will search the keychain for a
|
||||
/// certificate matching this common name and use that for authentication
|
||||
/// @note: Not case sensitive
|
||||
/// @note: If multiple matching certificates are found, the first one is used.
|
||||
/// @note: If this property is not set and neither is |clientCertIssuerCn|, the allowed issuers
|
||||
/// provided by the server will be used to find a matching certificate.
|
||||
@property(nonatomic) NSString *clientCertCommonName;
|
||||
|
||||
/// If set and client certificate authentication is needed, will search the keychain for a
|
||||
/// certificate issued by an issuer with this name and use that for authentication.
|
||||
///
|
||||
/// @note: Not case sensitive
|
||||
/// @note: If multiple matching certificates are found, the first one is used.
|
||||
/// @note: If this property is not set and neither is |clientCertCommonName|, the allowed issuers
|
||||
/// provided by the server will be used to find a matching certificate.
|
||||
@property(nonatomic) NSString *clientCertIssuerCn;
|
||||
|
||||
/// Designated initializer
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
|
||||
|
||||
@end
|
||||
261
Source/santactl/sync/SNTAuthenticatingURLSession.m
Normal file
261
Source/santactl/sync/SNTAuthenticatingURLSession.m
Normal file
@@ -0,0 +1,261 @@
|
||||
/// Copyright 2014 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 "SNTAuthenticatingURLSession.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDERDecoder.h"
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@implementation SNTAuthenticatingURLSession
|
||||
|
||||
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_session = [NSURLSession sessionWithConfiguration:configuration
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
[config setTLSMinimumSupportedProtocol:kTLSProtocol12];
|
||||
[config setHTTPShouldUsePipelining:YES];
|
||||
return [self initWithSessionConfiguration:config];
|
||||
}
|
||||
|
||||
#pragma mark User Agent property
|
||||
|
||||
- (NSString *)userAgent {
|
||||
return _session.configuration.HTTPAdditionalHeaders[@"User-Agent"];
|
||||
}
|
||||
|
||||
- (void)setUserAgent:(NSString *)userAgent {
|
||||
NSMutableDictionary *addlHeaders = [_session.configuration.HTTPAdditionalHeaders mutableCopy];
|
||||
addlHeaders[@"User-Agent"] = userAgent;
|
||||
_session.configuration.HTTPAdditionalHeaders = addlHeaders;
|
||||
}
|
||||
|
||||
#pragma mark NSURLSessionDelegate methods
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *credential))completionHandler {
|
||||
NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
|
||||
|
||||
if (challenge.previousFailureCount > 0) {
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.serverHostname && ![self.serverHostname isEqual:protectionSpace.host]) {
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![protectionSpace.protocol isEqual:NSURLProtectionSpaceHTTPS]) {
|
||||
LOGD(@"Protection Space: %@ is not a secure protocol", protectionSpace.protocol);
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protectionSpace.receivesCredentialSecurely) {
|
||||
LOGD(@"Protection Space: secure authentication or protocol cannot be established");
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *authMethod = [protectionSpace authenticationMethod];
|
||||
|
||||
if (authMethod == NSURLAuthenticationMethodClientCertificate && NO) {
|
||||
NSURLCredential *cred = [self clientCredentialForProtectionSpace:protectionSpace];
|
||||
if (cred) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
|
||||
return;
|
||||
} else {
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
} else if (authMethod == NSURLAuthenticationMethodServerTrust) {
|
||||
NSURLCredential *cred = [self serverCredentialForProtectionSpace:protectionSpace];
|
||||
if (cred) {
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, cred);
|
||||
return;
|
||||
} else {
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
|
||||
#pragma mark Private Helpers for URLSession:didReceiveChallenge:completionHandler:
|
||||
|
||||
/// Handles the process of locating a valid client certificate for authentication.
|
||||
/// Operates in one of three modes, depending on the configuration in config.plist
|
||||
///
|
||||
/// Mode 1: if syncClientAuthCertificateCn is set, look for an identity in the keychain with a
|
||||
/// matching common name and return it.
|
||||
/// Mode 2: if syncClientAuthCertificateIssuer is set, look for an identity in the keychain with a
|
||||
/// matching issuer common name and return it.
|
||||
/// Mode 3: use the list of issuer details sent down by the server to find an identity in the
|
||||
/// keychain.
|
||||
///
|
||||
/// If a valid identity cannot be found, returns nil.
|
||||
- (NSURLCredential *)clientCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
|
||||
__block OSStatus err = errSecSuccess;
|
||||
CFArrayRef cfIdentities = NULL;
|
||||
err = SecItemCopyMatching((__bridge CFDictionaryRef)@{
|
||||
(id)kSecClass : (id)kSecClassIdentity,
|
||||
(id)kSecReturnRef : @YES,
|
||||
(id)kSecMatchLimit : (id)kSecMatchLimitAll }, (CFTypeRef *)&cfIdentities);
|
||||
|
||||
if (err != noErr) {
|
||||
LOGD(@"Client Trust: Failed to load client identities, SecItemCopyMatching returned: %d",
|
||||
(int)err);
|
||||
return nil;
|
||||
}
|
||||
NSArray *identities = CFBridgingRelease(cfIdentities);
|
||||
|
||||
__block SecIdentityRef _foundIdentity;
|
||||
|
||||
// Manually iterate through available identities to find one with an allowed issuer.
|
||||
[identities enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
|
||||
SecIdentityRef identityRef = (__bridge SecIdentityRef)obj;
|
||||
|
||||
SecCertificateRef certificate = NULL;
|
||||
err = SecIdentityCopyCertificate(identityRef, &certificate);
|
||||
if (err != errSecSuccess) {
|
||||
LOGD(@"Client Trust: Failed to read certificate data: %d. Skipping identity", (int)err);
|
||||
return;
|
||||
}
|
||||
|
||||
SNTCertificate *clientCert = [[SNTCertificate alloc] initWithSecCertificateRef:certificate];
|
||||
CFRelease(certificate);
|
||||
|
||||
// Switch identity finding method depending on config
|
||||
if (self.clientCertCommonName) {
|
||||
if ([clientCert.commonName compare:self.clientCertCommonName
|
||||
options:NSCaseInsensitiveSearch]) {
|
||||
LOGD(@"Client Trust: Valid client identity %@", clientCert);
|
||||
_foundIdentity = identityRef;
|
||||
CFRetain(_foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
} else if (self.clientCertIssuerCn) {
|
||||
if ([clientCert.issuerCommonName compare:self.clientCertIssuerCn
|
||||
options:NSCaseInsensitiveSearch]) {
|
||||
LOGD(@"Client Trust: Valid client identity %@", clientCert);
|
||||
_foundIdentity = identityRef;
|
||||
CFRetain(_foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
} else {
|
||||
for (NSData *allowedIssuer in protectionSpace.distinguishedNames) {
|
||||
SNTDERDecoder *decoder = [[SNTDERDecoder alloc] initWithData:allowedIssuer];
|
||||
if (!decoder) continue;
|
||||
if ([clientCert.issuerCommonName isEqual:decoder.commonName] &&
|
||||
[clientCert.issuerCountryName isEqual:decoder.countryName] &&
|
||||
[clientCert.issuerOrgName isEqual:decoder.organizationName] &&
|
||||
[clientCert.issuerOrgUnit isEqual:decoder.organizationalUnit]) {
|
||||
LOGD(@"Client Trust: Valid client identity %@", clientCert);
|
||||
_foundIdentity = identityRef;
|
||||
CFRetain(_foundIdentity);
|
||||
*stop = YES;
|
||||
return; // return from enumeration block
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
if (_foundIdentity == NULL) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [NSURLCredential credentialWithIdentity:_foundIdentity
|
||||
certificates:nil
|
||||
persistence:NSURLCredentialPersistenceForSession];
|
||||
}
|
||||
|
||||
/// Handles the process of evaluating the server's certificate chain.
|
||||
/// Operates in one of three modes, depending on the configuration in config.plist
|
||||
///
|
||||
/// Mode 1: if syncServerAuthRootsData is set, evaluates the server's certificate chain contains
|
||||
/// one of the certificates in the PEM data in the config plist.
|
||||
/// Mode 2: if syncServerAuthRootsFile is set, evaluates the server's certificate chain contains
|
||||
/// one of the certificates in the PEM data in the file specified.
|
||||
/// Mode 3: evaluates the server's certificate chain is trusted by the keychain.
|
||||
///
|
||||
/// If the server's certificate chain does not evaluate for any reason, returns nil.
|
||||
- (NSURLCredential *)serverCredentialForProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
|
||||
SecTrustRef serverTrust = protectionSpace.serverTrust;
|
||||
if (serverTrust == NULL) {
|
||||
LOGD(@"Server Trust: No server trust information available");
|
||||
return nil;
|
||||
}
|
||||
|
||||
OSStatus err = errSecSuccess;
|
||||
|
||||
if (self.serverRootsPemData) {
|
||||
NSString *pemStrings = [[NSString alloc] initWithData:self.serverRootsPemData
|
||||
encoding:NSASCIIStringEncoding];
|
||||
NSArray *certs = [SNTCertificate certificatesFromPEM:pemStrings];
|
||||
|
||||
// Make a new array of the SecCertificateRef's from the SNTCertificate's.
|
||||
NSMutableArray *certRefs = [[NSMutableArray alloc] initWithCapacity:certs.count];
|
||||
for (SNTCertificate *cert in certs) {
|
||||
[certRefs addObject:(id)cert.certRef];
|
||||
}
|
||||
|
||||
// Set this array of certs as the anchors to trust.
|
||||
err = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)certRefs);
|
||||
if (err != errSecSuccess) {
|
||||
LOGE(@"Server Trust: Could not set anchor certificates");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate the server's cert chain.
|
||||
SecTrustResultType result = kSecTrustResultInvalid;
|
||||
err = SecTrustEvaluate(serverTrust, &result);
|
||||
if (err != errSecSuccess) {
|
||||
LOGE(@"Server Trust: Unable to evaluate certificate chain for server");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Print details about the server's leaf certificate.
|
||||
SecCertificateRef firstCert = SecTrustGetCertificateAtIndex(protectionSpace.serverTrust, 0);
|
||||
if (firstCert) {
|
||||
SNTCertificate *cert = [[SNTCertificate alloc] initWithSecCertificateRef:firstCert];
|
||||
LOGD(@"Server Trust: Server leaf cert: %@", cert);
|
||||
}
|
||||
|
||||
// Having a trust level "unspecified" by the user is the usual result, described at
|
||||
// https://developer.apple.com/library/mac/qa/qa1360
|
||||
if (result != kSecTrustResultProceed && result != kSecTrustResultUnspecified) {
|
||||
LOGE(@"Server Trust: Server isn't trusted. SecTrustResultType: %d", result);
|
||||
return nil;
|
||||
}
|
||||
|
||||
return [NSURLCredential credentialForTrust:serverTrust];
|
||||
}
|
||||
|
||||
@end
|
||||
219
Source/santactl/sync/SNTCommandSync.m
Normal file
219
Source/santactl/sync/SNTCommandSync.m
Normal file
@@ -0,0 +1,219 @@
|
||||
/// Copyright 2014 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 "SNTAuthenticatingURLSession.h"
|
||||
#import "SNTCommandSyncEventUpload.h"
|
||||
#import "SNTCommandSyncLogUpload.h"
|
||||
#import "SNTCommandSyncPostflight.h"
|
||||
#import "SNTCommandSyncPreflight.h"
|
||||
#import "SNTCommandSyncRuleDownload.h"
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDropRootPrivs.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTSystemInfo.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@interface SNTCommandSync : NSObject<SNTCommand>
|
||||
@property NSURLSession *session;
|
||||
@property SNTXPCConnection *daemonConn;
|
||||
@property SNTCommandSyncStatus *progress;
|
||||
@end
|
||||
|
||||
@implementation SNTCommandSync
|
||||
|
||||
REGISTER_COMMAND_NAME(@"sync");
|
||||
|
||||
+ (BOOL)requiresRoot {
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSString *)shortHelpText {
|
||||
return @"Synchronizes Santa with the server";
|
||||
}
|
||||
|
||||
+ (NSString *)longHelpText {
|
||||
return @"";
|
||||
}
|
||||
|
||||
+ (void)runWithArguments:(NSArray *)arguments daemonConnection:(SNTXPCConnection *)daemonConn {
|
||||
SNTConfigurator *config = [SNTConfigurator configurator];
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
LOGE(@"Failed to drop root privileges. Exiting.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Dropping root privileges to the 'nobody' user causes the default NSURLCache to throw
|
||||
// sandbox errors, which are benign but annoying. This line disables the cache entirely.
|
||||
[NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:0
|
||||
diskCapacity:0
|
||||
diskPath:nil]];
|
||||
|
||||
SNTCommandSync *s = [[self alloc] init];
|
||||
|
||||
SNTAuthenticatingURLSession *authURLSession = [[SNTAuthenticatingURLSession alloc] init];
|
||||
|
||||
authURLSession.userAgent = @"santactl-sync/";
|
||||
NSString *santactlVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
if (santactlVersion) {
|
||||
authURLSession.userAgent = [authURLSession.userAgent stringByAppendingString:santactlVersion];
|
||||
}
|
||||
|
||||
// Configure server auth
|
||||
if ([config syncServerAuthRootsFile]) {
|
||||
NSData *rootsData = [NSData dataWithContentsOfFile:[config syncServerAuthRootsFile]];
|
||||
authURLSession.serverRootsPemData = rootsData;
|
||||
} else if ([config syncServerAuthRootsData]) {
|
||||
authURLSession.serverRootsPemData = [config syncServerAuthRootsData];
|
||||
}
|
||||
|
||||
// Configure client auth
|
||||
if ([config syncClientAuthCertificateCn]) {
|
||||
authURLSession.clientCertCommonName = [config syncClientAuthCertificateCn];
|
||||
} else if ([config syncClientAuthCertificateIssuer]) {
|
||||
authURLSession.clientCertIssuerCn = [config syncClientAuthCertificateIssuer];
|
||||
}
|
||||
|
||||
s.session = [authURLSession session];
|
||||
s.daemonConn = daemonConn;
|
||||
|
||||
// Gather some data needed during some sync stages
|
||||
s.progress = [[SNTCommandSyncStatus alloc] init];
|
||||
|
||||
s.progress.syncBaseURL = config.syncBaseURL;
|
||||
if (!s.progress.syncBaseURL) {
|
||||
LOGE(@"Missing SyncBaseURL. Can't sync without it.");
|
||||
exit(1);
|
||||
}
|
||||
authURLSession.serverHostname = s.progress.syncBaseURL.host;
|
||||
|
||||
s.progress.machineID = config.machineIDOverride;
|
||||
if (!s.progress.machineID || [s.progress.machineID isEqual:@""]) {
|
||||
s.progress.machineID = [SNTSystemInfo hardwareUUID];
|
||||
}
|
||||
if (!s.progress.machineID || [s.progress.machineID isEqual:@""]) {
|
||||
LOGE(@"Missing Machine ID. Can't sync without it.");
|
||||
exit(1);
|
||||
}
|
||||
s.progress.machineOwner = config.machineOwner;
|
||||
|
||||
if (arguments.count == 2 && [[arguments firstObject] isEqual:@"singleevent"]) {
|
||||
[s eventUploadSingleEvent:arguments[1]];
|
||||
} else {
|
||||
[s preflight];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)preflight {
|
||||
[SNTCommandSyncPreflight performSyncInSession:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Preflight complete");
|
||||
if (self.progress.uploadLogURL) {
|
||||
[self logUpload];
|
||||
} else {
|
||||
[self eventUpload];
|
||||
}
|
||||
} else {
|
||||
LOGE(@"Preflight failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)logUpload {
|
||||
[SNTCommandSyncLogUpload performSyncInSession:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Log upload complete");
|
||||
[self eventUpload];
|
||||
} else {
|
||||
LOGE(@"Log upload failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)eventUpload {
|
||||
[SNTCommandSyncEventUpload performSyncInSession:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Event upload complete");
|
||||
[self ruleDownload];
|
||||
} else {
|
||||
LOGE(@"Event upload failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)eventUploadSingleEvent:(NSString *)sha1 {
|
||||
[SNTCommandSyncEventUpload uploadSingleEventWithSHA1:sha1
|
||||
session:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Event upload complete");
|
||||
exit(0);
|
||||
} else {
|
||||
LOGW(@"Event upload failed");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)ruleDownload {
|
||||
[SNTCommandSyncRuleDownload performSyncInSession:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Rule download complete");
|
||||
[self postflight];
|
||||
} else {
|
||||
LOGE(@"Rule download failed, aborting run");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)postflight {
|
||||
[SNTCommandSyncPostflight performSyncInSession:self.session
|
||||
progress:self.progress
|
||||
daemonConn:self.daemonConn
|
||||
completionHandler:^(BOOL success) {
|
||||
if (success) {
|
||||
LOGI(@"Postflight complete");
|
||||
LOGI(@"Sync completed successfully");
|
||||
exit(0);
|
||||
} else {
|
||||
LOGE(@"Postflight failed");
|
||||
exit(1);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
31
Source/santactl/sync/SNTCommandSyncEventUpload.h
Normal file
31
Source/santactl/sync/SNTCommandSyncEventUpload.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCommandSyncStatus;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@interface SNTCommandSyncEventUpload : NSObject
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
+ (void)uploadSingleEventWithSHA1:(NSString *)SHA1
|
||||
session:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
@end
|
||||
156
Source/santactl/sync/SNTCommandSyncEventUpload.m
Normal file
156
Source/santactl/sync/SNTCommandSyncEventUpload.m
Normal file
@@ -0,0 +1,156 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncEventUpload.h"
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@implementation SNTCommandSyncEventUpload
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = [NSURL URLWithString:[@"eventupload/" stringByAppendingString:progress.machineID]
|
||||
relativeToURL:progress.syncBaseURL];
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseEventsPending:^(NSArray *events) {
|
||||
if ([events count] == 0) {
|
||||
handler(YES);
|
||||
} else {
|
||||
[self uploadEventsFromArray:events
|
||||
toURL:url
|
||||
inSession:session
|
||||
batchSize:progress.eventBatchSize
|
||||
daemonConn:daemonConn
|
||||
completionHandler:handler];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)uploadSingleEventWithSHA1:(NSString *)SHA1
|
||||
session:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = [NSURL URLWithString:[@"eventupload/" stringByAppendingString:progress.machineID]
|
||||
relativeToURL:progress.syncBaseURL];
|
||||
[[daemonConn remoteObjectProxy] databaseEventForSHA1:SHA1 withReply:^(SNTStoredEvent *event) {
|
||||
if (!event) {
|
||||
handler(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
[self uploadEventsFromArray:@[ event ]
|
||||
toURL:url
|
||||
inSession:session
|
||||
batchSize:1
|
||||
daemonConn:daemonConn
|
||||
completionHandler:handler];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)uploadEventsFromArray:(NSArray *)events
|
||||
toURL:(NSURL *)url
|
||||
inSession:(NSURLSession *)session
|
||||
batchSize:(int32_t)batchSize
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSMutableArray *uploadEvents = [[NSMutableArray alloc] init];
|
||||
|
||||
NSMutableArray *eventIds = [NSMutableArray arrayWithCapacity:events.count];
|
||||
for (SNTStoredEvent *event in events) {
|
||||
NSMutableDictionary *newEvent = [@{
|
||||
@"file_sha1": event.fileSHA1,
|
||||
@"file_path": [event.filePath stringByDeletingLastPathComponent],
|
||||
@"file_name": [event.filePath lastPathComponent],
|
||||
@"executing_user": event.executingUser,
|
||||
@"execution_time": @([event.occurrenceDate timeIntervalSince1970]),
|
||||
@"decision": @(event.decision),
|
||||
@"logged_in_users": event.loggedInUsers,
|
||||
@"current_sessions": event.currentSessions} mutableCopy];
|
||||
|
||||
|
||||
if (event.fileBundleID) newEvent[@"file_bundle_id"] = event.fileBundleID;
|
||||
if (event.fileBundleName) newEvent[@"file_bundle_name"] = event.fileBundleName;
|
||||
if (event.fileBundleVersion) newEvent[@"file_bundle_version"] = event.fileBundleVersion;
|
||||
if (event.fileBundleVersionString) {
|
||||
newEvent[@"file_bundle_version_string"] = event.fileBundleVersionString;
|
||||
}
|
||||
|
||||
if (event.certSHA1) newEvent[@"cert_sha1"] = event.certSHA1;
|
||||
if (event.certCN) newEvent[@"cert_cn"] = event.certCN;
|
||||
if (event.certOrg) newEvent[@"cert_org"] = event.certOrg;
|
||||
if (event.certOU) newEvent[@"cert_ou"] = event.certOU;
|
||||
if (event.certValidFromDate) {
|
||||
newEvent[@"cert_valid_from"] = @([event.certValidFromDate timeIntervalSince1970]);
|
||||
}
|
||||
if (event.certValidUntilDate) {
|
||||
newEvent[@"cert_valid_until"] = @([event.certValidUntilDate timeIntervalSince1970]);
|
||||
}
|
||||
|
||||
[uploadEvents addObject:newEvent];
|
||||
|
||||
[eventIds addObject:event.idx];
|
||||
|
||||
if (eventIds.count >= batchSize) break;
|
||||
}
|
||||
|
||||
NSDictionary *uploadReq = @{@"events": uploadEvents};
|
||||
|
||||
NSData *requestBody;
|
||||
@try {
|
||||
requestBody = [NSJSONSerialization dataWithJSONObject:uploadReq options:0 error:nil];
|
||||
} @catch (NSException *exception) {
|
||||
LOGE(@"Failed to parse event into JSON");
|
||||
}
|
||||
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error) {
|
||||
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
|
||||
handler(NO);
|
||||
} else {
|
||||
LOGI(@"Uploaded %d events", eventIds.count);
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRemoveEventsWithIDs:eventIds];
|
||||
|
||||
NSArray *nextEvents = [events subarrayWithRange:NSMakeRange(eventIds.count,
|
||||
events.count - eventIds.count)];
|
||||
|
||||
if (nextEvents.count == 0) {
|
||||
handler(YES);
|
||||
} else {
|
||||
[self uploadEventsFromArray:nextEvents
|
||||
toURL:url
|
||||
inSession:session
|
||||
batchSize:batchSize
|
||||
daemonConn:daemonConn
|
||||
completionHandler:handler];
|
||||
}
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@end
|
||||
25
Source/santactl/sync/SNTCommandSyncLogUpload.h
Normal file
25
Source/santactl/sync/SNTCommandSyncLogUpload.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCommandSyncStatus;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@interface SNTCommandSyncLogUpload : NSObject
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
@end
|
||||
88
Source/santactl/sync/SNTCommandSyncLogUpload.m
Normal file
88
Source/santactl/sync/SNTCommandSyncLogUpload.m
Normal file
@@ -0,0 +1,88 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncLogUpload.h"
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
|
||||
|
||||
@implementation SNTCommandSyncLogUpload
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = progress.uploadLogURL;
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
NSString *boundary = @"santa-sync-upload-boundary";
|
||||
|
||||
NSString *contentType =
|
||||
[NSString stringWithFormat:@"multipart/form-data; charset=UTF-8; boundary=%@", boundary];
|
||||
[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
// General logs
|
||||
NSMutableArray *logsToUpload = [@[ @"/var/log/santa.log",
|
||||
@"/var/log/system.log" ] mutableCopy];
|
||||
|
||||
// Kernel Panics, santad & santactl crashes
|
||||
NSString *diagsDir = @"/Library/Logs/DiagnosticReports/";
|
||||
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:diagsDir];
|
||||
NSString *file;
|
||||
while (file = [dirEnum nextObject]) {
|
||||
if ([[file pathExtension] isEqualToString: @"panic"] ||
|
||||
[file hasPrefix:@"santad"] ||
|
||||
[file hasPrefix:@"santactl"]) {
|
||||
[logsToUpload addObject:[diagsDir stringByAppendingString:file]];
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the body of the request, encoded as a multipart/form-data.
|
||||
// Along the way, gzip the individual log files (they'll be stored in blobstore gzipped, which is
|
||||
// what we want) and append .gz to their filenames.
|
||||
NSMutableData *reqBody = [[NSMutableData alloc] init];
|
||||
for (NSString *log in logsToUpload) {
|
||||
[reqBody appendData:
|
||||
[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[reqBody appendData:
|
||||
[[NSString stringWithFormat:@"Content-Disposition: multipart/form-data; "
|
||||
@"name=\"files\"; "
|
||||
@"filename=\"%@.gz\"\r\n", [log lastPathComponent]]
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[reqBody appendData:
|
||||
[@"Content-Type: application/x-gzip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[reqBody appendData:[NSData dataWithContentsOfFile:log]];
|
||||
[reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
[reqBody appendData:
|
||||
[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
// Upload the logs
|
||||
[[session uploadTaskWithRequest:req
|
||||
fromData:reqBody
|
||||
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
|
||||
handler(NO);
|
||||
} else {
|
||||
LOGI(@"Uploaded %d logs", [logsToUpload count]);
|
||||
handler(YES);
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@end
|
||||
25
Source/santactl/sync/SNTCommandSyncPostflight.h
Normal file
25
Source/santactl/sync/SNTCommandSyncPostflight.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCommandSyncStatus;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@interface SNTCommandSyncPostflight : NSObject
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
@end
|
||||
45
Source/santactl/sync/SNTCommandSyncPostflight.m
Normal file
45
Source/santactl/sync/SNTCommandSyncPostflight.m
Normal file
@@ -0,0 +1,45 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncPostflight.h"
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
|
||||
@implementation SNTCommandSyncPostflight
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = [NSURL URLWithString:[@"postflight/" stringByAppendingString:progress.machineID]
|
||||
relativeToURL:progress.syncBaseURL];
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error) {
|
||||
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
|
||||
handler(NO);
|
||||
} else {
|
||||
handler(YES);
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
25
Source/santactl/sync/SNTCommandSyncPreflight.h
Normal file
25
Source/santactl/sync/SNTCommandSyncPreflight.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCommandSyncStatus;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@interface SNTCommandSyncPreflight : NSObject
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
@end
|
||||
72
Source/santactl/sync/SNTCommandSyncPreflight.m
Normal file
72
Source/santactl/sync/SNTCommandSyncPreflight.m
Normal file
@@ -0,0 +1,72 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncPreflight.h"
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
#import "SNTSystemInfo.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
@implementation SNTCommandSyncPreflight
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = [NSURL URLWithString:[@"preflight/" stringByAppendingString:progress.machineID]
|
||||
relativeToURL:progress.syncBaseURL];
|
||||
|
||||
NSMutableDictionary *requestDict = [NSMutableDictionary dictionary];
|
||||
requestDict[@"serial_no"] = [SNTSystemInfo serialNumber];
|
||||
requestDict[@"hostname"] = [SNTSystemInfo shortHostname];
|
||||
requestDict[@"santa_version"] = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
requestDict[@"os_version"] = [SNTSystemInfo osVersion];
|
||||
requestDict[@"os_build"] = [SNTSystemInfo osBuild];
|
||||
requestDict[@"primary_user"] = progress.machineOwner;
|
||||
|
||||
NSData *requestBody = [NSJSONSerialization dataWithJSONObject:requestDict
|
||||
options:0
|
||||
error:nil];
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
[req setHTTPBody:requestBody];
|
||||
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error) {
|
||||
long statusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
if (statusCode != 200) {
|
||||
LOGD(@"HTTP Response: %@",
|
||||
[[NSHTTPURLResponse localizedStringForStatusCode:statusCode] capitalizedString]);
|
||||
handler(NO);
|
||||
} else {
|
||||
NSDictionary *r = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
progress.eventBatchSize = [r[@"batch_size"] intValue];
|
||||
progress.uploadLogURL = [NSURL URLWithString:r[@"upload_logs_url"]];
|
||||
|
||||
if (r[@"client_mode"]) {
|
||||
[[daemonConn remoteObjectProxy] setClientMode:[r[@"client_mode"] intValue] withReply:^{}];
|
||||
}
|
||||
|
||||
handler(YES);
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@end
|
||||
25
Source/santactl/sync/SNTCommandSyncRuleDownload.h
Normal file
25
Source/santactl/sync/SNTCommandSyncRuleDownload.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
@class SNTCommandSyncStatus;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
@interface SNTCommandSyncRuleDownload : NSObject
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler;
|
||||
|
||||
@end
|
||||
113
Source/santactl/sync/SNTCommandSyncRuleDownload.m
Normal file
113
Source/santactl/sync/SNTCommandSyncRuleDownload.m
Normal file
@@ -0,0 +1,113 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncRuleDownload.h"
|
||||
|
||||
#import "SNTCommandSyncStatus.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
@implementation SNTCommandSyncRuleDownload
|
||||
|
||||
+ (void)performSyncInSession:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
NSURL *url = [NSURL URLWithString:[@"ruledownload/" stringByAppendingString:progress.machineID]
|
||||
relativeToURL:progress.syncBaseURL];
|
||||
[self ruleDownloadWithCursor:nil
|
||||
url:url
|
||||
session:session
|
||||
progress:progress
|
||||
daemonConn:daemonConn
|
||||
completionHandler:handler];
|
||||
}
|
||||
|
||||
+ (void)ruleDownloadWithCursor:(NSString *)cursor
|
||||
url:(NSURL *)url
|
||||
session:(NSURLSession *)session
|
||||
progress:(SNTCommandSyncStatus *)progress
|
||||
daemonConn:(SNTXPCConnection *)daemonConn
|
||||
completionHandler:(void (^)(BOOL success))handler {
|
||||
|
||||
NSDictionary *requestDict;
|
||||
if (cursor) {
|
||||
requestDict = @{@"cursor": cursor};
|
||||
} else {
|
||||
requestDict = @{};
|
||||
}
|
||||
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:url];
|
||||
[req setHTTPBody:[NSJSONSerialization dataWithJSONObject:requestDict
|
||||
options:0
|
||||
error:nil]];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
[[session dataTaskWithRequest:req completionHandler:^(NSData *data,
|
||||
NSURLResponse *response,
|
||||
NSError *error) {
|
||||
if ([(NSHTTPURLResponse *)response statusCode] != 200) {
|
||||
LOGD(@"HTTP Response Code: %d", [(NSHTTPURLResponse *)response statusCode]);
|
||||
handler(NO);
|
||||
} else {
|
||||
NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
NSArray *receivedRules = resp[@"rules"];
|
||||
|
||||
if (receivedRules.count == 0) {
|
||||
handler(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray *rules = [[NSMutableArray alloc] initWithCapacity:receivedRules.count];
|
||||
|
||||
for (NSDictionary *rule in receivedRules) {
|
||||
SNTRule *newRule = [[SNTRule alloc] init];
|
||||
newRule.SHA1 = rule[@"sha1"];
|
||||
|
||||
newRule.state = [rule[@"state"] intValue];
|
||||
if (newRule.state <= RULESTATE_UNKNOWN || newRule.state >= RULESTATE_MAX) continue;
|
||||
|
||||
newRule.type = [rule[@"type"] intValue];
|
||||
if (newRule.type <= RULETYPE_UNKNOWN || newRule.type >= RULETYPE_MAX) continue;
|
||||
|
||||
NSString *customMsg = rule[@"custom_msg"];
|
||||
if (customMsg) {
|
||||
newRule.customMsg = customMsg;
|
||||
}
|
||||
|
||||
[rules addObject:newRule];
|
||||
}
|
||||
|
||||
[[daemonConn remoteObjectProxy] databaseRuleAddRules:rules withReply:^{
|
||||
LOGI(@"Downloaded %d rule(s)", rules.count);
|
||||
|
||||
if (resp[@"cursor"]) {
|
||||
[self ruleDownloadWithCursor:resp[@"cursor"]
|
||||
url:url
|
||||
session:session
|
||||
progress:progress
|
||||
daemonConn:daemonConn
|
||||
completionHandler:handler];
|
||||
} else {
|
||||
handler(YES);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}] resume];
|
||||
}
|
||||
|
||||
@end
|
||||
32
Source/santactl/sync/SNTCommandSyncStatus.h
Normal file
32
Source/santactl/sync/SNTCommandSyncStatus.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// 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 SNTCommandSyncStatus : NSObject
|
||||
|
||||
/// The base API URL
|
||||
@property NSURL *syncBaseURL;
|
||||
|
||||
/// Machine identifier and owner
|
||||
@property NSString *machineID;
|
||||
@property NSString *machineOwner;
|
||||
|
||||
/// Batch size for uploading events, sent from server
|
||||
@property int32_t eventBatchSize;
|
||||
|
||||
/// Log upload URL sent from server
|
||||
@property NSURL *uploadLogURL;
|
||||
|
||||
@end
|
||||
18
Source/santactl/sync/SNTCommandSyncStatus.m
Normal file
18
Source/santactl/sync/SNTCommandSyncStatus.m
Normal file
@@ -0,0 +1,18 @@
|
||||
/// Copyright 2014 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 "SNTCommandSyncStatus.h"
|
||||
|
||||
@implementation SNTCommandSyncStatus
|
||||
@end
|
||||
15
Source/santactl/sync/SNTDERDecoder.h
Normal file
15
Source/santactl/sync/SNTDERDecoder.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/// This is a simple ASN.1 decoder that utilizes Apple's SecAsn1Decode
|
||||
/// to parse the @c distinguishedNames property of NSURLProtectionSpace.
|
||||
@interface SNTDERDecoder : NSObject
|
||||
|
||||
@property(readonly) NSString *commonName;
|
||||
@property(readonly) NSString *organizationName;
|
||||
@property(readonly) NSString *organizationalUnit;
|
||||
@property(readonly) NSString *countryName;
|
||||
|
||||
/// Designated initializer. Pass in one of the NSData objects in the
|
||||
/// NSURLProtectionSpace.distinguishedNames array
|
||||
/// Returns nil if decoding fails to find any expected objects
|
||||
- (instancetype)initWithData:(NSData *)data;
|
||||
|
||||
@end
|
||||
201
Source/santactl/sync/SNTDERDecoder.m
Normal file
201
Source/santactl/sync/SNTDERDecoder.m
Normal file
@@ -0,0 +1,201 @@
|
||||
#import "SNTDERDecoder.h"
|
||||
|
||||
#import <Security/SecAsn1Coder.h>
|
||||
#import <Security/SecAsn1Templates.h>
|
||||
|
||||
@interface SNTDERDecoder ()
|
||||
@property NSDictionary *decodedObjects;
|
||||
@end
|
||||
|
||||
@implementation SNTDERDecoder
|
||||
|
||||
#pragma mark Init
|
||||
|
||||
- (instancetype)initWithData:(NSData *)data {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if (!data) return nil;
|
||||
|
||||
_decodedObjects = [self decodeData:data];
|
||||
if (!_decodedObjects || [_decodedObjects count] == 0) return nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"/C=%@/O=%@/OU=%@/CN=%@",
|
||||
self.countryName,
|
||||
self.organizationName,
|
||||
self.organizationalUnit,
|
||||
self.commonName];
|
||||
}
|
||||
|
||||
# pragma mark Accessors
|
||||
|
||||
- (NSString *)commonName {
|
||||
return self.decodedObjects[(__bridge id)kSecOIDCommonName];
|
||||
}
|
||||
|
||||
- (NSString *)organizationName {
|
||||
return self.decodedObjects[(__bridge id)kSecOIDOrganizationName];
|
||||
}
|
||||
|
||||
- (NSString *)organizationalUnit {
|
||||
return self.decodedObjects[(__bridge id)kSecOIDOrganizationalUnitName];
|
||||
}
|
||||
|
||||
- (NSString *)countryName {
|
||||
return self.decodedObjects[(__bridge id)kSecOIDCountryName];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
/**
|
||||
* The DER data provided by NSURLProtectionSpace.distinguishedNames looks like
|
||||
* this:
|
||||
*
|
||||
* SEQUENCE {
|
||||
* SET {
|
||||
* SEQUENCE {
|
||||
* OBJECT IDENTIFIER (2 5 4 6)
|
||||
* PrintableString 'US'
|
||||
* }
|
||||
* }
|
||||
* SET {
|
||||
* SEQUENCE {
|
||||
* OBJECT IDENTIFIER (2 5 4 10)
|
||||
* PrintableString 'Megaco Inc'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* This method assumes the passed in data will be in that format. If it isn't,
|
||||
* the DER decoding will fail and this method will return nil.
|
||||
**/
|
||||
- (NSDictionary *)decodeData:(NSData *)data {
|
||||
typedef struct {
|
||||
SecAsn1Oid oid;
|
||||
SecAsn1Item value;
|
||||
} OIDKeyValue;
|
||||
|
||||
static const SecAsn1Template kOIDValueTemplate[] = {
|
||||
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(OIDKeyValue) },
|
||||
{ SEC_ASN1_OBJECT_ID, offsetof(OIDKeyValue, oid), NULL, 0 },
|
||||
{ SEC_ASN1_ANY_CONTENTS, offsetof(OIDKeyValue, value), NULL, 0 },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
OIDKeyValue **vals;
|
||||
} OIDKeyValueList;
|
||||
|
||||
static const SecAsn1Template kSetOfOIDValueTemplate[] = {
|
||||
{ SEC_ASN1_SET_OF, 0, kOIDValueTemplate, sizeof(OIDKeyValueList) },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
OIDKeyValueList **lists;
|
||||
} OIDKeyValueListSeq;
|
||||
|
||||
static const SecAsn1Template kSequenceOfSetOfOIDValueTemplate[] = {
|
||||
{ SEC_ASN1_SEQUENCE_OF, 0, kSetOfOIDValueTemplate, sizeof(OIDKeyValueListSeq) },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
OSStatus err = errSecSuccess;
|
||||
SecAsn1CoderRef coder;
|
||||
|
||||
err = SecAsn1CoderCreate(&coder);
|
||||
if (err != errSecSuccess) return nil;
|
||||
|
||||
OIDKeyValueListSeq a;
|
||||
err = SecAsn1Decode(coder,
|
||||
data.bytes,
|
||||
data.length,
|
||||
kSequenceOfSetOfOIDValueTemplate,
|
||||
&a);
|
||||
SecAsn1CoderRelease(coder);
|
||||
if (err != errSecSuccess) return nil;
|
||||
|
||||
// The data is decoded but now it's in a number of embedded structs.
|
||||
// Massage that into a nice dictionary of OID->String pairs.
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
OIDKeyValueList *anAttr;
|
||||
for (NSUInteger i = 0; (anAttr = a.lists[i]); i++) {
|
||||
OIDKeyValue *keyValue = anAttr->vals[0];
|
||||
|
||||
// Sanity check
|
||||
if (keyValue->value.Length > data.length) return nil;
|
||||
|
||||
// Get the string value. First try creating as a UTF-8 string. If that fails,
|
||||
// fallback to trying as an ASCII string. If it still doesn't work, continue on
|
||||
// to the next value.
|
||||
NSString *valueString;
|
||||
valueString = [[NSString alloc] initWithBytes:keyValue->value.Data
|
||||
length:keyValue->value.Length
|
||||
encoding:NSUTF8StringEncoding];
|
||||
if (!valueString) {
|
||||
valueString = [[NSString alloc] initWithBytes:keyValue->value.Data
|
||||
length:keyValue->value.Length
|
||||
encoding:NSASCIIStringEncoding];
|
||||
}
|
||||
if (!valueString) continue;
|
||||
|
||||
// The OID is still encoded, so we need to decode it.
|
||||
NSString *objectId = [SNTDERDecoder decodeOIDWithBytes:keyValue->oid.Data
|
||||
length:keyValue->oid.Length];
|
||||
|
||||
// Add to the dictionary
|
||||
dict[objectId] = valueString;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes an ASN.1 Object Identifier into a string separated by periods.
|
||||
* See http://msdn.microsoft.com/en-us/library/bb540809(v=vs.85).aspx for
|
||||
* details of the encoding.
|
||||
**/
|
||||
+ (NSString *)decodeOIDWithBytes:(unsigned char *)bytes length:(NSUInteger)length {
|
||||
NSMutableArray *objectId = [NSMutableArray array];
|
||||
BOOL inVariableLengthByte = NO;
|
||||
NSUInteger variableLength = 0;
|
||||
for (NSUInteger i = 0; i < length; ++i) {
|
||||
if (i == 0) {
|
||||
// The first byte is actually two values, the top 4 bits are the first value * 40
|
||||
// and the bottom 4 bits are the second value.
|
||||
[objectId addObject:@((NSUInteger)bytes[i] / 40)];
|
||||
[objectId addObject:@((NSUInteger)bytes[i] % 40)];
|
||||
} else {
|
||||
// The remaining bytes are encoded with Variable Length Quantity.
|
||||
unsigned char byte = bytes[i];
|
||||
if (byte & 0x80) {
|
||||
inVariableLengthByte = YES;
|
||||
|
||||
NSUInteger a = (NSUInteger) (byte & ~0x80);
|
||||
variableLength = variableLength << 7;
|
||||
variableLength += a;
|
||||
} else if (inVariableLengthByte) {
|
||||
NSUInteger a = (NSUInteger) (byte & ~0x80);
|
||||
variableLength = variableLength << 7;
|
||||
variableLength += a;
|
||||
inVariableLengthByte = NO;
|
||||
[objectId addObject:@(variableLength)];
|
||||
variableLength = 0;
|
||||
} else {
|
||||
[objectId addObject:@((NSUInteger)byte)];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [objectId componentsJoinedByString:@"."];
|
||||
}
|
||||
|
||||
@end
|
||||
18
Source/santad/Resources/santad-Info.plist
Normal file
18
Source/santad/Resources/santad-Info.plist
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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>CFBundleIdentifier</key>
|
||||
<string>com.google.santad</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.7</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>0.7</string>
|
||||
</dict>
|
||||
</plist>
|
||||
3
Source/santad/Resources/santad-Prefix.pch
Normal file
3
Source/santad/Resources/santad-Prefix.pch
Normal file
@@ -0,0 +1,3 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#endif
|
||||
21
Source/santad/SNTApplication.h
Normal file
21
Source/santad/SNTApplication.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
/// The main controller class for santad
|
||||
@interface SNTApplication : NSObject
|
||||
|
||||
/// Begins fielding requests from the driver
|
||||
- (int)run;
|
||||
|
||||
@end
|
||||
158
Source/santad/SNTApplication.m
Normal file
158
Source/santad/SNTApplication.m
Normal file
@@ -0,0 +1,158 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#import "SNTApplication.h"
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDaemonControlController.h"
|
||||
#import "SNTDatabaseController.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTExecutionController.h"
|
||||
#import "SNTRuleTable.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
@interface SNTApplication ()
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTEventTable *eventTable;
|
||||
@property SNTExecutionController *execController;
|
||||
@property SNTRuleTable *ruleTable;
|
||||
@property SNTXPCConnection *controlConnection;
|
||||
@property SNTXPCConnection *notifierConnection;
|
||||
@end
|
||||
|
||||
@implementation SNTApplication
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
// Locate and connect to driver
|
||||
_driverManager = [[SNTDriverManager alloc] init];
|
||||
|
||||
if (!_driverManager) {
|
||||
LOGE(@"Failed to connect to driver, exiting.");
|
||||
|
||||
// TODO(rah): Consider trying to load the extension from within santad.
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Initialize tables
|
||||
_ruleTable = [SNTDatabaseController ruleTable];
|
||||
if (! _ruleTable) {
|
||||
LOGE(@"Failed to initialize rule table.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
_eventTable = [SNTDatabaseController eventTable];
|
||||
if (! _eventTable) {
|
||||
LOGE(@"Failed to initialize event table.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Establish XPC listener for GUI agent connections
|
||||
_notifierConnection =
|
||||
[[SNTXPCConnection alloc] initServerWithName:[SNTXPCNotifierInterface serviceId]];
|
||||
_notifierConnection.remoteInterface = [SNTXPCNotifierInterface notifierInterface];
|
||||
[_notifierConnection resume];
|
||||
|
||||
// Establish XPC listener for santactl connections
|
||||
_controlConnection =
|
||||
[[SNTXPCConnection alloc] initServerWithName:[SNTXPCControlInterface serviceId]];
|
||||
_controlConnection.exportedInterface = [SNTXPCControlInterface controlInterface];
|
||||
_controlConnection.exportedObject =
|
||||
[[SNTDaemonControlController alloc] initWithDriverManager:_driverManager];
|
||||
[_controlConnection resume];
|
||||
|
||||
// Get client mode and begin observing for updates
|
||||
SNTConfigurator *configurator = [SNTConfigurator configurator];
|
||||
santa_clientmode_t clientMode = [configurator clientMode];
|
||||
[configurator addObserver:self
|
||||
forKeyPath:@"clientMode"
|
||||
options:NSKeyValueObservingOptionNew
|
||||
context:NULL];
|
||||
|
||||
// Initialize the binary checker object
|
||||
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
|
||||
ruleTable:_ruleTable
|
||||
eventTable:_eventTable
|
||||
operatingMode:clientMode
|
||||
notifierConnection:_notifierConnection];
|
||||
if (!_execController) return nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context {
|
||||
if ([keyPath isEqual:@"clientMode"]) {
|
||||
self.execController.operatingMode = [change[NSKeyValueChangeNewKey] intValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (int)run {
|
||||
LOGI(@"Connected to driver, activating.");
|
||||
|
||||
dispatch_queue_t q = dispatch_queue_create("com.google.santad.driver_queue",
|
||||
DISPATCH_QUEUE_CONCURRENT);
|
||||
|
||||
[self.driverManager listenWithBlock:^BOOL(santa_message_t message) {
|
||||
@autoreleasepool {
|
||||
switch (message.action) {
|
||||
case ACTION_REQUEST_SHUTDOWN: {
|
||||
LOGI(@"Driver requested a shutdown");
|
||||
// Sleep before exiting to give driver chance to ready itself
|
||||
sleep(10);
|
||||
return NO;
|
||||
}
|
||||
case ACTION_REQUEST_CHECKBW: {
|
||||
// Validate the binary aynchronously on a concurrent queue so we don't
|
||||
// hold up other execution requests in the background.
|
||||
dispatch_async(q, ^{
|
||||
struct passwd *user = getpwuid(message.userId);
|
||||
NSString *userName;
|
||||
if (user) {
|
||||
userName = @(user->pw_name);
|
||||
}
|
||||
|
||||
[self.execController validateBinaryWithSHA1:@(message.sha1)
|
||||
path:@(message.path)
|
||||
userName:userName
|
||||
pid:@(message.pid)
|
||||
vnodeId:message.vnode_id];
|
||||
});
|
||||
return YES;
|
||||
}
|
||||
default: {
|
||||
LOGE(@"Received request without an action");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
26
Source/santad/SNTDaemonControlController.h
Normal file
26
Source/santad/SNTDaemonControlController.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/// Copyright 2014 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 "SNTXPCControlInterface.h"
|
||||
|
||||
@class SNTDriverManager;
|
||||
|
||||
/// SNTDaemonControlController handles all of the RPCs from santactl
|
||||
@interface SNTDaemonControlController : NSObject<SNTDaemonControlXPC>
|
||||
|
||||
@property SNTDriverManager *driverManager;
|
||||
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager;
|
||||
|
||||
@end
|
||||
90
Source/santad/SNTDaemonControlController.m
Normal file
90
Source/santad/SNTDaemonControlController.m
Normal file
@@ -0,0 +1,90 @@
|
||||
/// Copyright 2014 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 "SNTDaemonControlController.h"
|
||||
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDatabaseController.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
@implementation SNTDaemonControlController
|
||||
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_driverManager = driverManager;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Kernel ops
|
||||
|
||||
- (void)cacheCount:(void (^)(uint64_t))reply; {
|
||||
uint64_t count = [self.driverManager cacheCount];
|
||||
reply(count);
|
||||
}
|
||||
|
||||
- (void)flushCache:(void (^)(BOOL))reply {
|
||||
reply([self.driverManager flushCache]);
|
||||
}
|
||||
|
||||
#pragma mark Database ops
|
||||
|
||||
- (void)databaseRuleCounts:(void (^)(uint64_t binary, uint64_t certificate))reply {
|
||||
SNTRuleTable *rdb = [SNTDatabaseController ruleTable];
|
||||
reply([rdb binaryRuleCount], [rdb certificateRuleCount]);
|
||||
}
|
||||
|
||||
- (void)databaseRuleAddRule:(SNTRule *)rule withReply:(void (^)())reply {
|
||||
[[SNTDatabaseController ruleTable] addRule:rule];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)databaseRuleAddRules:(NSArray *)rules withReply:(void (^)())reply {
|
||||
[[SNTDatabaseController ruleTable] addRules:rules];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)databaseEventCount:(void (^)(uint64_t count))reply {
|
||||
reply([[SNTDatabaseController eventTable] eventsPendingCount]);
|
||||
}
|
||||
|
||||
- (void)databaseEventForSHA1:(NSString *)sha1 withReply:(void (^)(SNTStoredEvent *))reply {
|
||||
reply([[SNTDatabaseController eventTable] latestEventForSHA1:sha1]);
|
||||
}
|
||||
|
||||
- (void)databaseEventsPending:(void (^)(NSArray *events))reply {
|
||||
reply([[SNTDatabaseController eventTable] pendingEvents]);
|
||||
}
|
||||
|
||||
- (void)databaseRemoveEventsWithIDs:(NSArray *)ids {
|
||||
[[SNTDatabaseController eventTable] deleteEventsWithIndexes:ids];
|
||||
}
|
||||
|
||||
#pragma mark Misc
|
||||
|
||||
- (void)clientMode:(void (^)(santa_clientmode_t))reply {
|
||||
reply([[SNTConfigurator configurator] clientMode]);
|
||||
}
|
||||
|
||||
- (void)setClientMode:(santa_clientmode_t)mode withReply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setClientMode:mode];
|
||||
reply();
|
||||
}
|
||||
|
||||
@end
|
||||
32
Source/santad/SNTDatabaseController.h
Normal file
32
Source/santad/SNTDatabaseController.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
// These imports are in the header rather than implementation to keep them in one place, saving
|
||||
// classes that use this one from also having to import FMDB stuff.
|
||||
#import <FMDB/FMDB.h>
|
||||
|
||||
@class SNTConfigTable;
|
||||
@class SNTEventTable;
|
||||
@class SNTRuleTable;
|
||||
|
||||
/// Provides methods to get an instance of one of the database table controllers with a
|
||||
/// pre-configured database queue.
|
||||
@interface SNTDatabaseController : NSObject
|
||||
|
||||
/// Returns an instance of the respective table class initialized with an appropriate database queue
|
||||
/// Will initialize only once, regardless of calling thread.
|
||||
+ (SNTEventTable *)eventTable;
|
||||
+ (SNTRuleTable *)ruleTable;
|
||||
|
||||
@end
|
||||
82
Source/santad/SNTDatabaseController.m
Normal file
82
Source/santad/SNTDatabaseController.m
Normal file
@@ -0,0 +1,82 @@
|
||||
/// Copyright 2014 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 "SNTDatabaseController.h"
|
||||
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTRuleTable.h"
|
||||
|
||||
@implementation SNTDatabaseController
|
||||
|
||||
static NSString * const kDatabasePath = @"/var/db/santa";
|
||||
static NSString * const kRulesDatabaseName = @"rules.db";
|
||||
static NSString * const kEventsDatabaseName = @"events.db";
|
||||
|
||||
+ (SNTEventTable *)eventTable {
|
||||
static FMDatabaseQueue *eventDatabaseQueue = nil;
|
||||
static dispatch_once_t eventDatabaseToken;
|
||||
dispatch_once(&eventDatabaseToken, ^{
|
||||
[self createDatabasePath];
|
||||
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kEventsDatabaseName];
|
||||
eventDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
|
||||
|
||||
#ifndef DEBUG
|
||||
[eventDatabaseQueue inDatabase:^(FMDatabase *db) {
|
||||
db.logsErrors = NO;
|
||||
}];
|
||||
#endif
|
||||
});
|
||||
|
||||
return [[SNTEventTable alloc] initWithDatabaseQueue:eventDatabaseQueue];
|
||||
}
|
||||
|
||||
+ (SNTRuleTable *)ruleTable {
|
||||
static FMDatabaseQueue *ruleDatabaseQueue = nil;
|
||||
static dispatch_once_t ruleDatabaseToken;
|
||||
dispatch_once(&ruleDatabaseToken, ^{
|
||||
[self createDatabasePath];
|
||||
NSString *fullPath = [kDatabasePath stringByAppendingPathComponent:kRulesDatabaseName];
|
||||
ruleDatabaseQueue = [[FMDatabaseQueue alloc] initWithPath:fullPath];
|
||||
|
||||
#ifndef DEBUG
|
||||
[ruleDatabaseQueue inDatabase:^(FMDatabase *db) {
|
||||
db.logsErrors = NO;
|
||||
}];
|
||||
#endif
|
||||
});
|
||||
return [[SNTRuleTable alloc] initWithDatabaseQueue:ruleDatabaseQueue];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
/// Create the folder that contains the databases
|
||||
+ (void)createDatabasePath {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
NSDictionary *attrs = @{ NSFileOwnerAccountName: @"root",
|
||||
NSFileGroupOwnerAccountName: @"wheel",
|
||||
NSFilePosixPermissions: @0755 };
|
||||
|
||||
if (![fm fileExistsAtPath:kDatabasePath]) {
|
||||
[fm createDirectoryAtPath:kDatabasePath
|
||||
withIntermediateDirectories:YES
|
||||
attributes:attrs
|
||||
error:nil];
|
||||
} else {
|
||||
[fm setAttributes:attrs ofItemAtPath:kDatabasePath error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
35
Source/santad/SNTDatabaseTable.h
Normal file
35
Source/santad/SNTDatabaseTable.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/// Copyright 2014 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.
|
||||
|
||||
// These imports are in the header rather than implementation to keep them in one place, saving
|
||||
// classes that use this one from also having to import FMDB stuff.
|
||||
#import <FMDB/FMDB.h>
|
||||
|
||||
@interface SNTDatabaseTable : NSObject
|
||||
|
||||
/// Designated initializer.
|
||||
- (instancetype)initWithDatabaseQueue:(FMDatabaseQueue *)db;
|
||||
|
||||
/// Subclasses should override this method to apply schema updates. The passed in version nubmer
|
||||
/// is the current version of the table. The return value is the new version of the table. If
|
||||
/// updating the table failed, return a negative number. If there was no update to apply, return 0.
|
||||
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version;
|
||||
|
||||
/// Wrappers around the respective FMDatabaseQueue methods. If the object we initialized with was
|
||||
/// a database queue, these just pass through. If the object we initialized with was an FMDatabase
|
||||
/// we just call the block with the database, potentially wrapping in a transaction.
|
||||
- (void)inDatabase:(void (^)(FMDatabase *db))block;
|
||||
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block;
|
||||
|
||||
@end
|
||||
69
Source/santad/SNTDatabaseTable.m
Normal file
69
Source/santad/SNTDatabaseTable.m
Normal file
@@ -0,0 +1,69 @@
|
||||
/// Copyright 2014 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 "SNTDatabaseTable.h"
|
||||
|
||||
#import "SNTLogging.h"
|
||||
|
||||
@interface SNTDatabaseTable ()
|
||||
@property FMDatabaseQueue *dbQ;
|
||||
@end
|
||||
|
||||
@implementation SNTDatabaseTable
|
||||
|
||||
- (instancetype)initWithDatabaseQueue:(FMDatabaseQueue *)db {
|
||||
if (!db) return nil;
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_dbQ = db;
|
||||
[self updateTableSchema];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
|
||||
[self doesNotRecognizeSelector:_cmd];
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Called at the end of initialization to ensure the table in the
|
||||
/// database exists and uses the latest schema.
|
||||
- (void)updateTableSchema {
|
||||
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
||||
|
||||
int currentVersion = [db userVersion];
|
||||
int newVersion = [self initializeDatabase:db fromVersion:currentVersion];
|
||||
if (newVersion < 1) return;
|
||||
|
||||
LOGD(@"Updated %@ from version %d to %d", [self className], currentVersion, newVersion);
|
||||
|
||||
[db setUserVersion:newVersion];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)inDatabase:(void (^)(FMDatabase *db))block {
|
||||
[self.dbQ inDatabase:block];
|
||||
}
|
||||
|
||||
- (void)inTransaction:(void (^)(FMDatabase *db, BOOL *rollback))block {
|
||||
[self.dbQ inTransaction:block];
|
||||
}
|
||||
|
||||
@end
|
||||
36
Source/santad/SNTDriverManager.h
Normal file
36
Source/santad/SNTDriverManager.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTKernelCommon.h"
|
||||
|
||||
@class SNTNotificationMessage;
|
||||
|
||||
/// Manages the connection between daemon and kernel.
|
||||
@interface SNTDriverManager : NSObject
|
||||
|
||||
/// Handles requests from the kernel using the given block.
|
||||
/// @note Loops indefinitely until either the callback block returns @c NO or there is an error
|
||||
/// trying to read data from the data queue.
|
||||
- (void)listenWithBlock:(BOOL (^)(santa_message_t message))callback;
|
||||
|
||||
/// Sends a response to a query back to the kernel.
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeId;
|
||||
|
||||
/// Get the number of binaries in the kernel's cache.
|
||||
- (uint64_t)cacheCount;
|
||||
|
||||
/// Flush the kernel's binary cache.
|
||||
- (BOOL)flushCache;
|
||||
|
||||
@end
|
||||
188
Source/santad/SNTDriverManager.m
Normal file
188
Source/santad/SNTDriverManager.m
Normal file
@@ -0,0 +1,188 @@
|
||||
/// Copyright 2014 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 "SNTDriverManager.h"
|
||||
|
||||
#include <IOKit/IODataQueueClient.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTNotificationMessage.h"
|
||||
|
||||
@interface SNTDriverManager ()
|
||||
@property IODataQueueMemory *queueMemory;
|
||||
@property io_connect_t connection;
|
||||
@property mach_port_t receivePort;
|
||||
@end
|
||||
|
||||
@implementation SNTDriverManager
|
||||
|
||||
#pragma mark init/dealloc
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
kern_return_t kr;
|
||||
io_service_t serviceObject;
|
||||
CFDictionaryRef classToMatch;
|
||||
|
||||
if (!(classToMatch = IOServiceMatching(USERCLIENT_CLASS))) {
|
||||
LOGD(@"Failed to create matching dictionary");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Locate driver. Wait for it if necessary.
|
||||
do {
|
||||
CFRetain(classToMatch); // this ref is released by IOServiceGetMatchingService
|
||||
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, classToMatch);
|
||||
|
||||
if (!serviceObject) {
|
||||
sleep(10);
|
||||
}
|
||||
} while (!serviceObject);
|
||||
CFRelease(classToMatch);
|
||||
|
||||
// This calls |initWithTask|, |attach| and |start| in |SantaDriverClient|
|
||||
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &_connection);
|
||||
IOObjectRelease(serviceObject);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to open Santa driver service");
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Call |open| in |SantaDriverClient|
|
||||
kr = IOConnectCallMethod(_connection, kSantaUserClientOpen, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
if (kr == kIOReturnExclusiveAccess) {
|
||||
LOGD(@"A client is already connected");
|
||||
return nil;
|
||||
} else if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"An error occurred while opening the connection");
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
IOServiceClose(_connection);
|
||||
}
|
||||
|
||||
# pragma mark Incoming messages
|
||||
|
||||
- (void)listenWithBlock:(BOOL (^)(santa_message_t message))callback {
|
||||
kern_return_t kr;
|
||||
santa_message_t vdata;
|
||||
UInt32 dataSize;
|
||||
|
||||
mach_vm_address_t address = 0;
|
||||
mach_vm_size_t size = 0;
|
||||
unsigned int msgType = 1;
|
||||
|
||||
// Allocate a mach port to receive notifactions from the IODataQueue
|
||||
if (!(self.receivePort = IODataQueueAllocateNotificationPort())) {
|
||||
LOGD(@"Failed to allocate notification port");
|
||||
return;
|
||||
}
|
||||
|
||||
// This will call registerNotificationPort() inside our user client class
|
||||
kr = IOConnectSetNotificationPort(self.connection, msgType, self.receivePort, 0);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to register notification port: %d", kr);
|
||||
mach_port_destroy(mach_task_self(), self.receivePort);
|
||||
return;
|
||||
}
|
||||
|
||||
// This will call clientMemoryForType() inside our user client class,
|
||||
// which activates the Kauth listeners.
|
||||
kr = IOConnectMapMemory(self.connection, kIODefaultMemoryType, mach_task_self(),
|
||||
&address, &size, kIOMapAnywhere);
|
||||
if (kr != kIOReturnSuccess) {
|
||||
LOGD(@"Failed to map memory: %d", kr);
|
||||
mach_port_destroy(mach_task_self(), self.receivePort);
|
||||
return;
|
||||
}
|
||||
|
||||
self.queueMemory = (IODataQueueMemory *)address;
|
||||
BOOL breakOut = NO;
|
||||
|
||||
while (IODataQueueWaitForAvailableData(self.queueMemory,
|
||||
self.receivePort) == kIOReturnSuccess) {
|
||||
while (IODataQueueDataAvailable(self.queueMemory)) {
|
||||
dataSize = sizeof(vdata);
|
||||
kr = IODataQueueDequeue(self.queueMemory, &vdata, &dataSize);
|
||||
if (kr == kIOReturnSuccess) {
|
||||
if (! callback(vdata)) {
|
||||
breakOut = YES;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOGD(@"Error receiving data: %d", kr);
|
||||
breakOut = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (breakOut) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IOConnectUnmapMemory(self.connection, kIODefaultMemoryType, mach_task_self(), address);
|
||||
mach_port_destroy(mach_task_self(), self.receivePort);
|
||||
}
|
||||
|
||||
#pragma mark Outgoing messages
|
||||
|
||||
- (kern_return_t)postToKernelAction:(santa_action_t)action forVnodeID:(uint64_t)vnodeId {
|
||||
switch (action) {
|
||||
case ACTION_RESPOND_CHECKBW_ALLOW:
|
||||
return IOConnectCallScalarMethod(self.connection,
|
||||
kSantaUserClientAllowBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
0,
|
||||
0);
|
||||
case ACTION_RESPOND_CHECKBW_DENY:
|
||||
return IOConnectCallScalarMethod(self.connection,
|
||||
kSantaUserClientDenyBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
0,
|
||||
0);
|
||||
default:
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
- (uint64_t)cacheCount {
|
||||
uint32_t input_count = 1;
|
||||
uint64_t cache_count = 0;
|
||||
|
||||
IOConnectCallScalarMethod(self.connection,
|
||||
kSantaUserClientCacheCount,
|
||||
0,
|
||||
0,
|
||||
&cache_count,
|
||||
&input_count);
|
||||
return cache_count;
|
||||
}
|
||||
|
||||
- (BOOL)flushCache {
|
||||
return IOConnectCallScalarMethod(
|
||||
self.connection, kSantaUserClientClearCache, 0, 0, 0, 0) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
@end
|
||||
43
Source/santad/SNTEventTable.h
Normal file
43
Source/santad/SNTEventTable.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/// Copyright 2014 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 "SNTDatabaseTable.h"
|
||||
|
||||
@class SNTNotificationMessage;
|
||||
@class SNTStoredEvent;
|
||||
|
||||
/// Responsible for managing the event table in the Santa database.
|
||||
@interface SNTEventTable : SNTDatabaseTable
|
||||
|
||||
/// Add event to the database
|
||||
- (void)addStoredEvent:(SNTStoredEvent *)event;
|
||||
|
||||
/// Number of events in database.
|
||||
- (int)eventsPendingCount;
|
||||
|
||||
/// Retrieves all events in the database
|
||||
/// @return NSArray of SNTStoredEvent
|
||||
- (NSArray *)pendingEvents;
|
||||
|
||||
/// Retrieve an event from the database.
|
||||
/// @return a single SNTStoredEvent
|
||||
- (SNTStoredEvent *)latestEventForSHA1:(NSString *)sha1;
|
||||
|
||||
/// Delete a single event from the database using its index.
|
||||
- (void)deleteEventWithIndex:(NSNumber *)index;
|
||||
|
||||
/// Delete multiple events from the database with an array of indexes.
|
||||
- (void)deleteEventsWithIndexes:(NSArray *)indexes;
|
||||
|
||||
@end
|
||||
213
Source/santad/SNTEventTable.m
Normal file
213
Source/santad/SNTEventTable.m
Normal file
@@ -0,0 +1,213 @@
|
||||
/// Copyright 2014 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 "SNTEventTable.h"
|
||||
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTNotificationMessage.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
|
||||
@implementation SNTEventTable
|
||||
|
||||
- (int)initializeDatabase:(FMDatabase *)db fromVersion:(int)version {
|
||||
int newVersion = 0;
|
||||
|
||||
if (version < 1) {
|
||||
[db executeUpdate:@"CREATE TABLE 'events' ("
|
||||
"'idx' INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||
"'fileSHA1' TEXT NOT NULL,"
|
||||
"'filePath' TEXT NOT NULL,"
|
||||
"'fileBundleID' TEXT,"
|
||||
"'fileBundleVersion' TEXT,"
|
||||
"'fileBundleVersionString' TEXT,"
|
||||
"'fileBundleName' TEXT,"
|
||||
"'certSHA1' TEXT,"
|
||||
"'certCN' TEXT,"
|
||||
"'certOrg' TEXT,"
|
||||
"'certOU' TEXT,"
|
||||
"'certValidFromDate' REAL,"
|
||||
"'certValidUntilDate' REAL,"
|
||||
"'occurrenceDate' REAL,"
|
||||
"'executingUser' TEXT,"
|
||||
"'decision' INT,"
|
||||
"'loggedInUsers' BLOB,"
|
||||
"'currentSessions' BLOB"
|
||||
@");"];
|
||||
[db executeUpdate:@"CREATE INDEX event_filesha1 ON events (fileSHA1);"];
|
||||
|
||||
newVersion = 1;
|
||||
}
|
||||
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
#pragma mark Loading / Storing
|
||||
|
||||
- (void)addStoredEvent:(SNTStoredEvent *)event {
|
||||
if (!event.fileSHA1 ||
|
||||
!event.filePath ||
|
||||
!event.occurrenceDate ||
|
||||
!event.executingUser ||
|
||||
!event.decision) return;
|
||||
|
||||
NSMutableDictionary *parameters = [@{@"fileSHA1": event.fileSHA1,
|
||||
@"filePath": event.filePath,
|
||||
@"occurrenceDate": event.occurrenceDate,
|
||||
@"executingUser": event.executingUser,
|
||||
@"decision": @(event.decision)} mutableCopy];
|
||||
|
||||
if (event.certSHA1) parameters[@"certSHA1"] = event.certSHA1;
|
||||
if (event.certCN) parameters[@"certCN"] = event.certCN;
|
||||
if (event.certOrg) parameters[@"certOrg"] = event.certOrg;
|
||||
if (event.certOU) parameters[@"certOU"] = event.certOU;
|
||||
if (event.certValidFromDate) parameters[@"certValidFromDate"] = event.certValidFromDate;
|
||||
if (event.certValidUntilDate) parameters[@"certValidUntilDate"] = event.certValidUntilDate;
|
||||
|
||||
if (event.fileBundleID) parameters[@"fileBundleID"] = event.fileBundleID;
|
||||
if (event.fileBundleName) parameters[@"fileBundleName"] = event.fileBundleName;
|
||||
if (event.fileBundleVersion) parameters[@"fileBundleVersion"] = event.fileBundleVersion;
|
||||
if (event.fileBundleVersionString) {
|
||||
parameters[@"fileBundleVersionString"] = event.fileBundleVersionString;
|
||||
}
|
||||
|
||||
if (event.loggedInUsers) {
|
||||
NSData *usersData = [NSKeyedArchiver archivedDataWithRootObject:event.loggedInUsers];
|
||||
parameters[@"loggedInUsers"] = usersData;
|
||||
}
|
||||
|
||||
if (event.currentSessions ) {
|
||||
NSData *sessionsData = [NSKeyedArchiver archivedDataWithRootObject:event.currentSessions];
|
||||
parameters[@"currentSessions"] = sessionsData;
|
||||
}
|
||||
|
||||
NSString *paramString = [[parameters allKeys] componentsJoinedByString:@","];
|
||||
NSString *paramStringColon = [paramString stringByReplacingOccurrencesOfString:@","
|
||||
withString:@",:"];
|
||||
paramStringColon = [@":" stringByAppendingString:paramStringColon];
|
||||
|
||||
NSString *sql = [NSString stringWithFormat:@"INSERT INTO 'events' (%@) VALUES (%@)",
|
||||
paramString,
|
||||
paramStringColon];
|
||||
|
||||
[self inTransaction:^(FMDatabase *db, BOOL *rollback) {
|
||||
if (![db executeUpdate:sql withParameterDictionary:parameters]) {
|
||||
LOGD(@"Failed to save event");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (SNTStoredEvent *)eventFromResultSet:(FMResultSet *)rs {
|
||||
SNTStoredEvent *event = [[SNTStoredEvent alloc] init];
|
||||
|
||||
event.idx = @([rs intForColumn:@"idx"]);
|
||||
event.fileSHA1 = [rs stringForColumn:@"fileSHA1"];
|
||||
event.filePath = [rs stringForColumn:@"filePath"];
|
||||
event.occurrenceDate = [rs dateForColumn:@"occurrenceDate"];
|
||||
event.executingUser = [rs stringForColumn:@"executingUser"];
|
||||
event.decision = [rs intForColumn:@"decision"];
|
||||
|
||||
event.certSHA1 = [rs stringForColumn:@"certSHA1"];
|
||||
event.certCN = [rs stringForColumn:@"certCN"];
|
||||
event.certOrg = [rs stringForColumn:@"certOrg"];
|
||||
event.certOU = [rs stringForColumn:@"certOU"];
|
||||
event.certValidFromDate = [rs dateForColumn:@"certValidFromDate"];
|
||||
event.certValidUntilDate = [rs dateForColumn:@"certValidUntilDate"];
|
||||
|
||||
event.fileBundleID = [rs stringForColumn:@"fileBundleID"];
|
||||
event.fileBundleName = [rs stringForColumn:@"fileBundleName"];
|
||||
event.fileBundleVersion = [rs stringForColumn:@"fileBundleVersion"];
|
||||
event.fileBundleVersionString = [rs stringForColumn:@"fileBundleVersionString"];
|
||||
|
||||
NSData *currentSessions = [rs dataForColumn:@"currentSessions"];
|
||||
NSData *loggedInUsers = [rs dataForColumn:@"loggedInUsers"];
|
||||
|
||||
if (currentSessions) {
|
||||
event.currentSessions = [NSKeyedUnarchiver unarchiveObjectWithData:currentSessions];
|
||||
}
|
||||
|
||||
if (loggedInUsers) {
|
||||
event.loggedInUsers = [NSKeyedUnarchiver unarchiveObjectWithData:loggedInUsers];
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
#pragma mark Querying/Retreiving
|
||||
|
||||
- (int)eventsPendingCount {
|
||||
__block int eventsPending = 0;
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
eventsPending = [db intForQuery:@"SELECT COUNT(*) FROM events"];
|
||||
}];
|
||||
return eventsPending;
|
||||
}
|
||||
|
||||
- (BOOL)existingEventForBinary:(SNTNotificationMessage *)event {
|
||||
__block BOOL result = NO;
|
||||
NSString *qry = @"SELECT fileSHA1 FROM events WHERE fileSHA1=? LIMIT 1";
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
result = [db boolForQuery:qry, event.SHA1];
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (SNTStoredEvent *)latestEventForSHA1:(NSString *)sha1 {
|
||||
__block SNTStoredEvent *storedEvent;
|
||||
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events WHERE fileSHA1=? "
|
||||
@"ORDER BY occurrenceDate DESC LIMIT 1;", sha1];
|
||||
|
||||
if ([rs next]) {
|
||||
storedEvent = [self eventFromResultSet:rs];
|
||||
}
|
||||
|
||||
[rs close];
|
||||
}];
|
||||
|
||||
return storedEvent;
|
||||
}
|
||||
|
||||
- (NSArray *)pendingEvents {
|
||||
NSMutableArray *pendingEvents = [[NSMutableArray alloc] init];
|
||||
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
FMResultSet *rs = [db executeQuery:@"SELECT * FROM events"];
|
||||
|
||||
while ([rs next]) {
|
||||
[pendingEvents addObject:[self eventFromResultSet:rs]];
|
||||
}
|
||||
|
||||
[rs close];
|
||||
}];
|
||||
|
||||
return pendingEvents;
|
||||
}
|
||||
|
||||
#pragma mark Deleting
|
||||
|
||||
- (void)deleteEventWithIndex:(NSNumber *)index {
|
||||
[self inDatabase:^(FMDatabase *db) {
|
||||
[db executeUpdate:@"DELETE FROM events WHERE idx=?", index];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteEventsWithIndexes:(NSArray *)indexes {
|
||||
for (NSNumber *index in indexes) {
|
||||
[self deleteEventWithIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
57
Source/santad/SNTExecutionController.h
Normal file
57
Source/santad/SNTExecutionController.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/// Copyright 2014 Google Inc. All rights reserved.
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#include "SNTCommonEnums.h"
|
||||
|
||||
@class SNTCodesignChecker;
|
||||
@class SNTDriverManager;
|
||||
@class SNTEventTable;
|
||||
@class SNTRuleTable;
|
||||
@class SNTXPCConnection;
|
||||
|
||||
/// SNTExecutionController is responsible for everything that happens when a request to execute
|
||||
/// a binary occurs:
|
||||
/// + Making a decision about whether to allow or deny this binary based on any existing rules for
|
||||
/// that specific binary, its signing certificate and the operating mode of santad.
|
||||
/// + Sending the decision to the kernel as soon as possible
|
||||
/// + (If denied or unknown) Storing details about the execution event to the database
|
||||
/// for upload and spwaning santactl to quickly try and send that to the server.
|
||||
/// + (If denied) Potentially sending a message to SantaGUI to notify the user
|
||||
/// + Logging the event to the log file
|
||||
///
|
||||
@interface SNTExecutionController : NSObject
|
||||
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTRuleTable *ruleTable;
|
||||
@property SNTEventTable *eventTable;
|
||||
@property SNTXPCConnection *notifierConnection;
|
||||
@property santa_clientmode_t operatingMode;
|
||||
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
|
||||
ruleTable:(SNTRuleTable *)ruleTable
|
||||
eventTable:(SNTEventTable *)eventTable
|
||||
operatingMode:(santa_clientmode_t)operating_mode
|
||||
notifierConnection:(SNTXPCConnection *)notifierConn;
|
||||
|
||||
// Handles the logic of deciding whether to allow the binary to run or not, sends the response to
|
||||
// the kernel, logs the event to the log and if necessary stores the event in the database and
|
||||
// sends a notification to the GUI agent.
|
||||
- (void)validateBinaryWithSHA1:(NSString *)sha1
|
||||
path:(NSString *)path
|
||||
userName:(NSString *)userName
|
||||
pid:(NSNumber *)pid
|
||||
vnodeId:(uint64_t)vnodeId;
|
||||
|
||||
|
||||
@end
|
||||
325
Source/santad/SNTExecutionController.m
Normal file
325
Source/santad/SNTExecutionController.m
Normal file
@@ -0,0 +1,325 @@
|
||||
/// Copyright 2014 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 "SNTExecutionController.h"
|
||||
|
||||
#include <utmpx.h>
|
||||
|
||||
#include "SNTLogging.h"
|
||||
|
||||
#import "SNTBinaryInfo.h"
|
||||
#import "SNTCertificate.h"
|
||||
#import "SNTCodesignChecker.h"
|
||||
#import "SNTDriverManager.h"
|
||||
#import "SNTDropRootPrivs.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTNotificationMessage.h"
|
||||
#import "SNTRule.h"
|
||||
#import "SNTRuleTable.h"
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
@implementation SNTExecutionController
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
- (instancetype)initWithDriverManager:(SNTDriverManager *)driverManager
|
||||
ruleTable:(SNTRuleTable *)ruleTable
|
||||
eventTable:(SNTEventTable *)eventTable
|
||||
operatingMode:(santa_clientmode_t)operatingMode
|
||||
notifierConnection:(SNTXPCConnection *)notifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_driverManager = driverManager;
|
||||
_ruleTable = ruleTable;
|
||||
_eventTable = eventTable;
|
||||
_operatingMode = operatingMode;
|
||||
_notifierConnection = notifier;
|
||||
LOGI(@"Log format: Decision (A|D), Reason (B|C), SHA-1, Path, Cert SHA-1, Cert CN");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Binary Validation
|
||||
|
||||
- (void)validateBinaryWithSHA1:(NSString *)sha1
|
||||
path:(NSString *)path
|
||||
userName:(NSString *)userName
|
||||
pid:(NSNumber *)pid
|
||||
vnodeId:(uint64_t)vnodeId {
|
||||
// Step 1 - in scope?
|
||||
if (![self fileIsInScope:path]) {
|
||||
[self.driverManager postToKernelAction:ACTION_RESPOND_CHECKBW_ALLOW forVnodeID:vnodeId];
|
||||
LOGD(@"File out of scope: %@", path);
|
||||
return;
|
||||
}
|
||||
|
||||
// These will be filled in either in step 2, 3 or 4.
|
||||
santa_action_t respondedAction = ACTION_UNSET;
|
||||
SNTRule *rule;
|
||||
|
||||
// Step 2 - binary rule?
|
||||
rule = [self.ruleTable binaryRuleForSHA1:sha1];
|
||||
if (rule) {
|
||||
respondedAction = [self actionForRuleState:rule.state];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
}
|
||||
|
||||
SNTBinaryInfo *binInfo = [[SNTBinaryInfo alloc] initWithPath:path];
|
||||
SNTCodesignChecker *csInfo = [[SNTCodesignChecker alloc] initWithBinaryPath:path];
|
||||
|
||||
// Step 3 - cert rule?
|
||||
if (!rule) {
|
||||
rule = [self.ruleTable certificateRuleForSHA1:csInfo.leafCertificate.SHA1];
|
||||
if (rule) {
|
||||
respondedAction = [self actionForRuleState:rule.state];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4 - default rule :-(
|
||||
if (!rule) {
|
||||
respondedAction = [self defaultDecision];
|
||||
[self.driverManager postToKernelAction:respondedAction forVnodeID:vnodeId];
|
||||
}
|
||||
|
||||
// Step 5 - log to database
|
||||
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY || !rule) {
|
||||
SNTStoredEvent *se = [[SNTStoredEvent alloc] init];
|
||||
se.fileSHA1 = sha1;
|
||||
se.filePath = path;
|
||||
se.fileBundleID = [binInfo bundleIdentifier];
|
||||
se.fileBundleName = [binInfo bundleName];
|
||||
|
||||
if ([binInfo bundleShortVersionString]) {
|
||||
se.fileBundleVersionString = [binInfo bundleShortVersionString];
|
||||
}
|
||||
|
||||
if ([binInfo bundleVersion]) {
|
||||
se.fileBundleVersion = [binInfo bundleVersion];
|
||||
}
|
||||
|
||||
se.certSHA1 = csInfo.leafCertificate.SHA1;
|
||||
se.certCN = csInfo.leafCertificate.commonName;
|
||||
se.certOrg = csInfo.leafCertificate.orgName;
|
||||
se.certOU = csInfo.leafCertificate.orgUnit;
|
||||
se.certValidFromDate = csInfo.leafCertificate.validFrom;
|
||||
se.certValidUntilDate = csInfo.leafCertificate.validUntil;
|
||||
se.executingUser = userName;
|
||||
se.occurrenceDate = [[NSDate alloc] init];
|
||||
se.decision = [self eventStateForDecision:respondedAction type:rule.type];
|
||||
se.pid = pid;
|
||||
|
||||
NSArray *loggedInUsers, *currentSessions;
|
||||
[self loggedInUsers:&loggedInUsers sessions:¤tSessions];
|
||||
se.currentSessions = currentSessions;
|
||||
se.loggedInUsers = loggedInUsers;
|
||||
|
||||
[self.eventTable addStoredEvent:se];
|
||||
}
|
||||
|
||||
// Step 6 - log to log file
|
||||
[self logDecisionForEventState:[self eventStateForDecision:respondedAction type:rule.type]
|
||||
sha1:sha1
|
||||
path:path
|
||||
leafCert:csInfo.leafCertificate];
|
||||
|
||||
// Step 7 - alert user
|
||||
if (respondedAction == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
// So the server has something to show the user straight away, initiate an event
|
||||
// upload for the blocked binary rather than waiting for the next sync.
|
||||
// The event upload is skipped if the full path is equal to that of /usr/sbin/santactl so that
|
||||
/// on the off chance that santactl is not whitelisted, we don't get into an infinite loop.
|
||||
if (![path isEqual:@"/usr/sbin/santactl"]) {
|
||||
[self initiateEventUploadForSHA1:sha1];
|
||||
}
|
||||
|
||||
SNTNotificationMessage *notMsg = [[SNTNotificationMessage alloc] init];
|
||||
notMsg.path = path;
|
||||
notMsg.SHA1 = sha1;
|
||||
notMsg.customMessage = rule.customMsg;
|
||||
notMsg.certificates = csInfo.certificates;
|
||||
[[self.notifierConnection remoteObjectProxy] postBlockNotification:notMsg];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Checks whether the file at @c path is in-scope for checking with Santa.
|
||||
|
||||
Files that are out of scope:
|
||||
+ Non Mach-O files
|
||||
+ Files in whitelisted directories.
|
||||
|
||||
@return @c YES if file is in scope, @c NO otherwise.
|
||||
**/
|
||||
- (BOOL)fileIsInScope:(NSString *)path {
|
||||
// Determine if file is within a whitelisted directory.
|
||||
if ([self pathIsInWhitelistedDir:path]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// If file is not a Mach-O file, we're not interested.
|
||||
// TODO(rah): Consider adding an option to check scripts
|
||||
SNTBinaryInfo *binInfo = [[SNTBinaryInfo alloc] initWithPath:path];
|
||||
if (![binInfo isMachO]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)pathIsInWhitelistedDir:(NSString *)path {
|
||||
// TODO(rah): Implement this.
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (santa_eventstate_t)eventStateForDecision:(santa_action_t)decision type:(santa_ruletype_t)type {
|
||||
if (decision == ACTION_RESPOND_CHECKBW_ALLOW) {
|
||||
if (type == RULETYPE_BINARY) {
|
||||
return EVENTSTATE_ALLOW_BINARY;
|
||||
} else if (type == RULETYPE_CERT) {
|
||||
return EVENTSTATE_ALLOW_CERTIFICATE;
|
||||
} else {
|
||||
return EVENTSTATE_ALLOW_UNKNOWN;
|
||||
}
|
||||
} else if (decision == ACTION_RESPOND_CHECKBW_DENY) {
|
||||
if (type == RULETYPE_BINARY) {
|
||||
return EVENTSTATE_BLOCK_BINARY;
|
||||
} else if (decision == RULETYPE_CERT) {
|
||||
return EVENTSTATE_BLOCK_CERTIFICATE;
|
||||
} else {
|
||||
return EVENTSTATE_BLOCK_UNKNOWN;
|
||||
}
|
||||
} else {
|
||||
return EVENTSTATE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)logDecisionForEventState:(santa_eventstate_t)eventState
|
||||
sha1:(NSString *)sha1
|
||||
path:(NSString *)path
|
||||
leafCert:(SNTCertificate *)cert {
|
||||
NSString *d, *r, *outLog;
|
||||
|
||||
switch (eventState) {
|
||||
case EVENTSTATE_ALLOW_BINARY:
|
||||
d = @"A"; r = @"B"; break;
|
||||
case EVENTSTATE_ALLOW_CERTIFICATE:
|
||||
d = @"A"; r = @"C"; break;
|
||||
case EVENTSTATE_ALLOW_UNKNOWN:
|
||||
d = @"A"; r = @"?"; break;
|
||||
case EVENTSTATE_BLOCK_BINARY:
|
||||
d = @"D"; r = @"B"; break;
|
||||
case EVENTSTATE_BLOCK_CERTIFICATE:
|
||||
d = @"D"; r = @"C"; break;
|
||||
case EVENTSTATE_BLOCK_UNKNOWN:
|
||||
d = @"D"; r = @"?"; break;
|
||||
default:
|
||||
d = @"?"; r = @"?"; break;
|
||||
}
|
||||
|
||||
// Ensure there are no commas in the path name (as this will be confusing in the log)
|
||||
NSString *printPath = [path stringByReplacingOccurrencesOfString:@"," withString:@"<comma>"];
|
||||
|
||||
if (cert && cert.SHA1 && cert.commonName) {
|
||||
// Also ensure there are no commas in the cert's common name.
|
||||
NSString *printCommonName = [cert.commonName stringByReplacingOccurrencesOfString:@","
|
||||
withString:@"<comma>"];
|
||||
outLog = [NSString stringWithFormat:@"%@,%@,%@,%@,%@,%@", d, r, sha1, printPath,
|
||||
cert.SHA1, printCommonName];
|
||||
} else {
|
||||
outLog = [NSString stringWithFormat:@"%@,%@,%@,%@", d, r, sha1, printPath];
|
||||
}
|
||||
|
||||
// Now make sure none of the log line has a newline in it.
|
||||
LOGI(@"%@", [[outLog componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]
|
||||
componentsJoinedByString:@" "]);
|
||||
}
|
||||
|
||||
- (void)initiateEventUploadForSHA1:(NSString *)sha1 {
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
pid_t child = fork();
|
||||
if (child == 0) {
|
||||
fclose(stdout);
|
||||
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(execl("/usr/sbin/santactl", "/usr/sbin/santactl", "sync",
|
||||
"singleevent", [sha1 UTF8String], NULL));
|
||||
}
|
||||
}
|
||||
|
||||
- (santa_action_t)defaultDecision {
|
||||
switch (self.operatingMode) {
|
||||
case CLIENTMODE_MONITOR:
|
||||
return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
case CLIENTMODE_LOCKDOWN:
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
default:
|
||||
// This should never happen, panic and lockdown.
|
||||
LOGE(@"Client mode is unset while enforcement is in effect. Blocking.");
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
}
|
||||
}
|
||||
|
||||
- (santa_action_t)actionForRuleState:(santa_rulestate_t)state {
|
||||
switch (state) {
|
||||
case RULESTATE_WHITELIST:
|
||||
return ACTION_RESPOND_CHECKBW_ALLOW;
|
||||
case RULESTATE_BLACKLIST:
|
||||
case RULESTATE_SILENT_BLACKLIST:
|
||||
return ACTION_RESPOND_CHECKBW_DENY;
|
||||
default:
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loggedInUsers:(NSArray **)users sessions:(NSArray **)sessions {
|
||||
struct utmpx *nxt;
|
||||
|
||||
NSMutableDictionary *loggedInUsers = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *loggedInHosts = [[NSMutableDictionary alloc] init];
|
||||
|
||||
while ((nxt = getutxent())) {
|
||||
if (nxt->ut_type != USER_PROCESS) continue;
|
||||
|
||||
NSString *userName = [NSString stringWithUTF8String:nxt->ut_user];
|
||||
|
||||
NSString *sessionName;
|
||||
if (strnlen(nxt->ut_host, 1) > 0) {
|
||||
sessionName = [NSString stringWithFormat:@"%s@%s", nxt->ut_user, nxt->ut_host];
|
||||
} else {
|
||||
sessionName = [NSString stringWithFormat:@"%s@%s", nxt->ut_user, nxt->ut_line];
|
||||
}
|
||||
|
||||
if (userName && ![userName isEqual:@""]) {
|
||||
loggedInUsers[userName] = @"";
|
||||
}
|
||||
|
||||
if (sessionName && ![sessionName isEqual:@":"]) {
|
||||
loggedInHosts[sessionName] = @"";
|
||||
}
|
||||
}
|
||||
|
||||
endutxent();
|
||||
|
||||
*users = [loggedInUsers allKeys];
|
||||
*sessions = [loggedInHosts allKeys];
|
||||
}
|
||||
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user