mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc87cde668 | ||
|
|
400c413029 | ||
|
|
0e6eb45732 | ||
|
|
7ca2028c19 | ||
|
|
08144b54a7 |
83
Docs/deployment/com.google.santa.example.mobileconfig
Normal file
83
Docs/deployment/com.google.santa.example.mobileconfig
Normal file
@@ -0,0 +1,83 @@
|
||||
<?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>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>PayloadContent</key>
|
||||
<dict>
|
||||
<key>com.google.santa</key>
|
||||
<dict>
|
||||
<key>Forced</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_preference_settings</key>
|
||||
<dict>
|
||||
<key>BannedBlockMessage</key>
|
||||
<string>This application has been banned</string>
|
||||
<key>ClientMode</key>
|
||||
<integer>1</integer>
|
||||
<key>EnablePageZeroProtection</key>
|
||||
<false/>
|
||||
<key>EventDetailText</key>
|
||||
<string>Open sync server</string>
|
||||
<key>EventDetailURL</key>
|
||||
<string>https://sync-server-hostname/blockables/%file_sha%</string>
|
||||
<key>FileChangesRegex</key>
|
||||
<string>^/(?!(?:private/tmp|Library/(?:Caches|Managed Installs/Logs|(?:Managed )?Preferences))/)</string>
|
||||
<key>MachineIDKey</key>
|
||||
<string>MachineUUID</string>
|
||||
<key>MachineIDPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>MachineOwnerKey</key>
|
||||
<string>Owner</string>
|
||||
<key>MachineOwnerPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>ModeNotificationLockdown</key>
|
||||
<string>Entering Lockdown mode</string>
|
||||
<key>ModeNotificationMonitor</key>
|
||||
<string>Entering Monitor mode<br/>Please be careful!</string>
|
||||
<key>MoreInfoURL</key>
|
||||
<string>https://sync-server-hostname/moreinfo</string>
|
||||
<key>SyncBaseURL</key>
|
||||
<string>https://sync-server-hostname/api/santa/</string>
|
||||
<key>UnknownBlockMessage</key>
|
||||
<string>This application has been blocked from executing.</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>0342c558-a101-4a08-a0b9-40cc00039ea5</string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.ManagedClient.preferences</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>0342c558-a101-4a08-a0b9-40cc00039ea5</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDescription</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<true/>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>9020fb2d-cab3-420f-9268-acca4868bdd0</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,8 +1,12 @@
|
||||
# Important
|
||||
|
||||
Santa v0.9.21 has moved to using an Apple [Configuration Profile](https://developer.apple.com/library/content/featuredarticles/iPhoneConfigurationProfileRef/Introduction/Introduction.html) to manage the local configuration. The old config file (`/var/db/santa/config.plist`) is no longer used.
|
||||
|
||||
# Configuration
|
||||
|
||||
Two configuration methods can be used to control Santa: local configuration and a sync server controlled configuration. There are certain options that can only be controlled with a local configuration and others that can only be controlled with a sync server controlled configuration. Additionally, there are options that can be controlled by both.
|
||||
Two configuration methods can be used to control Santa: a local configuration profile and a sync server controlled configuration. There are certain options that can only be controlled with a local configuration profile and others that can only be controlled with a sync server controlled configuration. Additionally, there are options that can be controlled by both.
|
||||
|
||||
## Local Configuration
|
||||
## Local Configuration Profile
|
||||
|
||||
| Key | Value Type | Description |
|
||||
| ----------------------------- | ---------- | ---------------------------------------- |
|
||||
@@ -32,7 +36,7 @@ Two configuration methods can be used to control Santa: local configuration and
|
||||
| MachineIDPlist | String | The path to a plist that contains the MachineOwnerKey / value pair. |
|
||||
| MachineIDKey | String | The key to use on MachineIDPlist. |
|
||||
|
||||
*protected keys: If a sync server is configured, this setting cannot be changed while santad is running as it is assumed the setting will be provided by the sync server.
|
||||
*overridable by the sync server: run `santactl status` to check the current running config
|
||||
|
||||
##### EventDetailURL
|
||||
|
||||
@@ -50,49 +54,108 @@ This property contains a kind of format string to be turned into the URL to send
|
||||
|
||||
For example: `https://sync-server-hostname/%machine_id%/%file_sha%`
|
||||
|
||||
##### Example Config
|
||||
##### Example Configuration Profile
|
||||
|
||||
Here is an example of a configuration that could be set.
|
||||
Here is an example of a configuration profile that could be set. It was generated with Tim Sutton's great [mcxToProfile](https://github.com/timsutton/mcxToProfile) tool. A copy is also available [here](com.google.santa.example.mobileconfig).
|
||||
|
||||
A few key points to when creating your configuration profile:
|
||||
|
||||
* `com.google.santa` needs to be the key inside `PayloadContent`
|
||||
* The `PayloadScope` needs to be `System`
|
||||
|
||||
```xml
|
||||
<?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>BannedBlockMessage</key>
|
||||
<string>This application has been banned</string>
|
||||
<key>ClientMode</key>
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>PayloadContent</key>
|
||||
<dict>
|
||||
<key>com.google.santa</key>
|
||||
<dict>
|
||||
<key>Forced</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>mcx_preference_settings</key>
|
||||
<dict>
|
||||
<key>BannedBlockMessage</key>
|
||||
<string>This application has been banned</string>
|
||||
<key>ClientMode</key>
|
||||
<integer>1</integer>
|
||||
<key>EnablePageZeroProtection</key>
|
||||
<false/>
|
||||
<key>EventDetailText</key>
|
||||
<string>Open sync server</string>
|
||||
<key>EventDetailURL</key>
|
||||
<string>https://sync-server-hostname/blockables/%file_sha%</string>
|
||||
<key>FileChangesRegex</key>
|
||||
<string>^/(?!(?:private/tmp|Library/(?:Caches|Managed Installs/Logs|(?:Managed )?Preferences))/)</string>
|
||||
<key>MachineIDKey</key>
|
||||
<string>MachineUUID</string>
|
||||
<key>MachineIDPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>MachineOwnerKey</key>
|
||||
<string>Owner</string>
|
||||
<key>MachineOwnerPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>ModeNotificationLockdown</key>
|
||||
<string>Entering Lockdown mode</string>
|
||||
<key>ModeNotificationMonitor</key>
|
||||
<string>Entering Monitor mode<br/>Please be careful!</string>
|
||||
<key>MoreInfoURL</key>
|
||||
<string>https://sync-server-hostname/moreinfo</string>
|
||||
<key>SyncBaseURL</key>
|
||||
<string>https://sync-server-hostname/api/santa/</string>
|
||||
<key>UnknownBlockMessage</key>
|
||||
<string>This application has been blocked from executing.</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>PayloadEnabled</key>
|
||||
<true/>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>0342c558-a101-4a08-a0b9-40cc00039ea5</string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.ManagedClient.preferences</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>0342c558-a101-4a08-a0b9-40cc00039ea5</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDescription</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.google.santa</string>
|
||||
<key>PayloadOrganization</key>
|
||||
<string></string>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<true/>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>9020fb2d-cab3-420f-9268-acca4868bdd0</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>EnablePageZeroProtection</key>
|
||||
<false/>
|
||||
<key>EventDetailText</key>
|
||||
<string>Open sync server</string>
|
||||
<key>EventDetailURL</key>
|
||||
<string>https://sync-server-hostname/blockables/%file_sha%</string>
|
||||
<key>FileChangesRegex</key>
|
||||
<string>^/(?!(?:private/tmp|Library/(?:Caches|Managed Installs/Logs|(?:Managed )?Preferences))/)</string>
|
||||
<key>MachineIDKey</key>
|
||||
<string>MachineUUID</string>
|
||||
<key>MachineIDPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>MachineOwnerKey</key>
|
||||
<string>Owner</string>
|
||||
<key>MachineOwnerPlist</key>
|
||||
<string>/Library/Preferences/com.company.machine-mapping.plist</string>
|
||||
<key>ModeNotificationLockdown</key>
|
||||
<string>Entering Lockdown mode</string>
|
||||
<key>ModeNotificationMonitor</key>
|
||||
<string>Entering Monitor mode<br/>Please be careful!</string>
|
||||
<key>MoreInfoURL</key>
|
||||
<string>https://sync-server-hostname/moreinfo</string>
|
||||
<key>SyncBaseURL</key>
|
||||
<string>https://sync-server-hostname/api/santa/</string>
|
||||
<key>UnknownBlockMessage</key>
|
||||
<string>This application has been blocked from executing.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
```
|
||||
|
||||
Configuration profiles have a `.mobileconfig` file extension. There are many ways to install configuration profiles:
|
||||
|
||||
* Double click them in Finder
|
||||
* Use the `/usr/bin/profiles` tool
|
||||
* Use an MDM
|
||||
|
||||
## Sync server Provided Configuration
|
||||
|
||||
| Key | Value Type | Description |
|
||||
|
||||
@@ -35,28 +35,19 @@ Running Santa in Lockdown Mode will stop all blacklisted binaries and additional
|
||||
|
||||
##### Changing Modes
|
||||
|
||||
There are two ways to change the running mode: changing the config.plist and with a sync server configuration.
|
||||
There are two ways to change the running mode: changing the configuration profile and with a sync server configuration.
|
||||
|
||||
###### Change modes with the config.plist
|
||||
###### Change modes with the configuration profile
|
||||
|
||||
The `ClientMode` config key is protected while santad is running and will revert any attempt to change it.
|
||||
Set the `ClientMode` in your configuration profile to the integer value `1` for MONITOR or `2` for LOCKDOWN.
|
||||
|
||||
Change to Monitor mode:
|
||||
|
||||
```sh
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist
|
||||
sudo defaults write /var/db/santa/config.plist ClientMode -int 1
|
||||
sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
```xml
|
||||
<key>ClientMode</key>
|
||||
<integer>1</integer>
|
||||
```
|
||||
|
||||
Change to Lockdown mode:
|
||||
Install your new configuration profile, it will overwrite any old `com.google.santa` profiles you may have already install. See the [configuration](../deployment/configuration.md) document for more details.
|
||||
|
||||
```sh
|
||||
sudo launchctl unload /Library/LaunchDaemons/com.google.santad.plist
|
||||
sudo defaults write /var/db/santa/config.plist ClientMode -int 2
|
||||
sudo launchctl load /Library/LaunchDaemons/com.google.santad.plist
|
||||
```
|
||||
|
||||
######Change modes with a sync server
|
||||
###### Change modes with a sync server
|
||||
|
||||
The mode is set in the preflight sync stage. Use the key `client_mode` and a value of `MONITOR` or `LOCKDOWN`.
|
||||
|
||||
@@ -29,4 +29,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: acd378b3727c923d912e09812da344f7375c14fe
|
||||
|
||||
COCOAPODS: 1.2.1
|
||||
COCOAPODS: 1.3.1
|
||||
|
||||
@@ -149,7 +149,6 @@
|
||||
0DEA5F7D1CF64EB600704398 /* SNTCommandSyncRuleDownload.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D0A1EC2191998C900B8450F /* SNTCommandSyncRuleDownload.m */; };
|
||||
0DEFB7C01ACB28B000B92AAE /* SNTCommandSyncConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7BF1ACB28B000B92AAE /* SNTCommandSyncConstants.m */; };
|
||||
0DEFB7C41ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
0DEFB7C61ACDE5F600B92AAE /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
0DEFB7C81ACF0BFE00B92AAE /* SNTFileWatcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C71ACF0BFE00B92AAE /* SNTFileWatcherTest.m */; };
|
||||
0DF395641AB76A7900CBC520 /* NSData+Zlib.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DF395631AB76A7900CBC520 /* NSData+Zlib.m */; };
|
||||
@@ -172,6 +171,8 @@
|
||||
C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */ = {isa = PBXBuildFile; fileRef = C76614EB1D142D3C00D150C1 /* SNTCommandCheckCache.m */; };
|
||||
C776A1071DEE160500A56616 /* SNTCommandSyncManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C776A1061DEE160500A56616 /* SNTCommandSyncManager.m */; };
|
||||
C78227631E1C3C7D006EB2D6 /* santabs.xpc in CopyFiles */ = {isa = PBXBuildFile; fileRef = C78227541E1C3C58006EB2D6 /* santabs.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
C7943FE92028B855008D4F76 /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
C7943FEA2028C4F7008D4F76 /* SNTFileWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DEFB7C31ACDD80100B92AAE /* SNTFileWatcher.m */; };
|
||||
C795ED901D80A5BE007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; };
|
||||
C795ED911D80B66B007CFF42 /* SNTPolicyProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = C795ED8F1D80A5BE007CFF42 /* SNTPolicyProcessor.m */; };
|
||||
C79A23581E23F7E80037AFA8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C79A23561E23F7E80037AFA8 /* main.m */; };
|
||||
@@ -1221,13 +1222,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-santad-santabs-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2146C1274A8C11B3755A8A67 /* [CP] Check Pods Manifest.lock */ = {
|
||||
@@ -1236,13 +1240,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-santad-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
23869BA352E2C86DEFE62819 /* [CP] Copy Pods Resources */ = {
|
||||
@@ -1296,13 +1303,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-LogicTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
B193461D3612BCB47C16E407 /* [CP] Check Pods Manifest.lock */ = {
|
||||
@@ -1311,13 +1321,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Santa-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
BA20035148DDEF5808B2C7EF /* [CP] Embed Pods Frameworks */ = {
|
||||
@@ -1358,13 +1371,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-santactl-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
D49A3AB950AFD99741E9AF89 /* [CP] Embed Pods Frameworks */ = {
|
||||
@@ -1500,6 +1516,7 @@
|
||||
C76614EC1D142D3C00D150C1 /* SNTCommandCheckCache.m in Sources */,
|
||||
0D416401191974F1006A356A /* SNTCommandSyncState.m in Sources */,
|
||||
0DC5D871192160180078A5C0 /* SNTCommandSyncLogUpload.m in Sources */,
|
||||
C7943FE92028B855008D4F76 /* SNTFileWatcher.m in Sources */,
|
||||
0DB77FDA1CD14092004DF060 /* SNTBlockMessage.m in Sources */,
|
||||
0D35BDA218FD71CE00921A21 /* main.m in Sources */,
|
||||
0DCD6043190ACCB8006B445C /* SNTFileInfo.m in Sources */,
|
||||
@@ -1518,6 +1535,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C7943FEA2028C4F7008D4F76 /* SNTFileWatcher.m in Sources */,
|
||||
0DCA552718C95928002A7DAE /* SNTXPCConnection.m in Sources */,
|
||||
0D385DF1180DE51600418BC6 /* SNTAppDelegate.m in Sources */,
|
||||
0D88680A1AC48A1200B86659 /* SNTSystemInfo.m in Sources */,
|
||||
@@ -1532,7 +1550,6 @@
|
||||
0D1B477019A53419008CADD3 /* SNTAboutWindowController.m in Sources */,
|
||||
0D668E8118D1121700E29A8B /* SNTMessageWindow.m in Sources */,
|
||||
0DA73CA11934F8100056D7C4 /* SNTLogging.m in Sources */,
|
||||
0DEFB7C51ACDD80100B92AAE /* SNTFileWatcher.m in Sources */,
|
||||
C7479F051E53704E0054C1CF /* SNTXPCBundleServiceInterface.m in Sources */,
|
||||
0D20710E1A7C4A86008B0A9A /* SNTStoredEvent.m in Sources */,
|
||||
0DE2CE561CA05561002B649A /* SNTAccessibleTextField.m in Sources */,
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#import "SNTAboutWindowController.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTFileWatcher.h"
|
||||
#import "SNTNotificationManager.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
@@ -24,7 +23,6 @@
|
||||
|
||||
@interface SNTAppDelegate ()
|
||||
@property SNTAboutWindowController *aboutWindowController;
|
||||
@property SNTFileWatcher *configFileWatcher;
|
||||
@property SNTNotificationManager *notificationManager;
|
||||
@property SNTXPCConnection *daemonListener;
|
||||
@property SNTXPCConnection *bundleListener;
|
||||
@@ -36,12 +34,6 @@
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
|
||||
[self setupMenu];
|
||||
|
||||
self.configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kMobileConfigFilePath
|
||||
handler:^(unsigned long data) {
|
||||
if (!(data & DISPATCH_VNODE_ATTRIB)) [[SNTConfigurator configurator] reloadConfigData];
|
||||
}];
|
||||
|
||||
self.notificationManager = [[SNTNotificationManager alloc] init];
|
||||
|
||||
NSNotificationCenter *workspaceNotifications = [[NSWorkspace sharedWorkspace] notificationCenter];
|
||||
|
||||
@@ -102,14 +102,16 @@ static NSString * const silencedNotificationsKey = @"SilencedNotifications";
|
||||
case SNTClientModeMonitor:
|
||||
un.informativeText = @"Switching into Monitor mode";
|
||||
customMsg = [[SNTConfigurator configurator] modeNotificationMonitor];
|
||||
customMsg = [SNTBlockMessage stringFromHTML:customMsg];
|
||||
if (customMsg.length) un.informativeText = customMsg;
|
||||
if (!customMsg) break;
|
||||
if (!customMsg.length) return;
|
||||
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
|
||||
break;
|
||||
case SNTClientModeLockdown:
|
||||
un.informativeText = @"Switching into Lockdown mode";
|
||||
customMsg = [[SNTConfigurator configurator] modeNotificationLockdown];
|
||||
customMsg = [SNTBlockMessage stringFromHTML:customMsg];
|
||||
if (customMsg.length) un.informativeText = customMsg;
|
||||
if (!customMsg) break;
|
||||
if (!customMsg.length) return;
|
||||
un.informativeText = [SNTBlockMessage stringFromHTML:customMsg];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
@@ -16,12 +16,10 @@
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
|
||||
extern NSString *const kSyncStateFilePath;
|
||||
extern NSString *const kMobileConfigFilePath;
|
||||
|
||||
///
|
||||
/// Singleton that provides an interface for managing configuration values on disk
|
||||
/// @note This class is designed as a singleton but that is not strictly enforced.
|
||||
/// @note All properties are KVO compliant.
|
||||
///
|
||||
@interface SNTConfigurator : NSObject
|
||||
|
||||
@@ -30,16 +28,12 @@ extern NSString *const kMobileConfigFilePath;
|
||||
///
|
||||
/// The operating mode.
|
||||
///
|
||||
@property(nonatomic) SNTClientMode clientMode;
|
||||
@property(readonly, nonatomic) SNTClientMode clientMode;
|
||||
|
||||
///
|
||||
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
|
||||
/// Set the operating mode as received from a sync server.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotalL) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(nonatomic) NSRegularExpression *fileChangesRegex;
|
||||
- (void)setSyncServerClientMode:(SNTClientMode)newMode;
|
||||
|
||||
///
|
||||
/// The regex of whitelisted paths. Regexes are specified in ICU format.
|
||||
@@ -48,7 +42,12 @@ extern NSString *const kMobileConfigFilePath;
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(nonatomic) NSRegularExpression *whitelistPathRegex;
|
||||
@property(readonly, nonatomic) NSRegularExpression *whitelistPathRegex;
|
||||
|
||||
///
|
||||
/// Set the regex of whitelisted paths as received from a sync server.
|
||||
///
|
||||
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re;
|
||||
|
||||
///
|
||||
/// The regex of blacklisted paths. Regexes are specified in ICU format.
|
||||
@@ -57,7 +56,21 @@ extern NSString *const kMobileConfigFilePath;
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(nonatomic) NSRegularExpression *blacklistPathRegex;
|
||||
@property(readonly, nonatomic) NSRegularExpression *blacklistPathRegex;
|
||||
|
||||
///
|
||||
/// Set the regex of blacklisted paths as received from a sync server.
|
||||
///
|
||||
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re;
|
||||
|
||||
///
|
||||
/// The regex of paths to log file changes for. Regexes are specified in ICU format.
|
||||
///
|
||||
/// The regex flags IXSM can be used, though the s (dotalL) and m (multiline) flags are
|
||||
/// pointless as a path only ever has a single line.
|
||||
/// If the regex doesn't begin with ^ to match from the beginning of the line, it will be added.
|
||||
///
|
||||
@property(readonly, nonatomic) NSRegularExpression *fileChangesRegex;
|
||||
|
||||
///
|
||||
/// Enable __PAGEZERO protection, defaults to YES
|
||||
@@ -202,13 +215,8 @@ extern NSString *const kMobileConfigFilePath;
|
||||
+ (instancetype)configurator;
|
||||
|
||||
///
|
||||
/// Re-read config data from disk.
|
||||
/// Clear the sync server configuration from the effective configuration.
|
||||
///
|
||||
- (void)reloadConfigData;
|
||||
|
||||
///
|
||||
/// Notify the receiver that the sync state file has changed.
|
||||
///
|
||||
- (void)syncStateFileChanged:(unsigned long)data;
|
||||
- (void)clearSyncState;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,23 +16,25 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#import "SNTFileWatcher.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTStrengthify.h"
|
||||
#import "SNTSystemInfo.h"
|
||||
|
||||
@interface SNTConfigurator ()
|
||||
@property NSMutableDictionary *configData;
|
||||
|
||||
/// Creating NSRegularExpression objects is not fast, so cache them.
|
||||
@property NSRegularExpression *cachedFileChangesRegex;
|
||||
@property NSRegularExpression *cachedWhitelistDirRegex;
|
||||
@property NSRegularExpression *cachedBlacklistDirRegex;
|
||||
|
||||
/// A NSUserDefaults object set to use the com.google.santa suite.
|
||||
@property(readonly, nonatomic) NSUserDefaults *defaults;
|
||||
|
||||
/// Keys used by a mobileconfig or sync server
|
||||
@property(readonly, nonatomic) NSArray *syncServerKeys;
|
||||
@property(readonly, nonatomic) NSArray *mobileConfigKeys;
|
||||
// Keys and expected value types.
|
||||
@property(readonly, nonatomic) NSDictionary *syncServerKeyTypes;
|
||||
@property(readonly, nonatomic) NSDictionary *forcedConfigKeyTypes;
|
||||
|
||||
/// Holds the configurations from a sync server and mobileconfig.
|
||||
@property NSMutableDictionary *syncState;
|
||||
@property NSMutableDictionary *configState;
|
||||
|
||||
/// Watcher for the sync-state.plist.
|
||||
@property(nonatomic) SNTFileWatcher *syncStateWatcher;
|
||||
@end
|
||||
|
||||
@implementation SNTConfigurator
|
||||
@@ -43,9 +45,6 @@ NSString *const kSyncStateFilePath = @"/var/db/santa/sync-state.plist";
|
||||
/// The domain used by mobileconfig.
|
||||
static NSString *const kMobileConfigDomain = @"com.google.santa";
|
||||
|
||||
/// The hard-coded path to the mobileconfig file.
|
||||
NSString *const kMobileConfigFilePath = @"/Library/Managed Preferences/com.google.santa.plist";
|
||||
|
||||
/// The keys managed by a mobileconfig.
|
||||
static NSString *const kSyncBaseURLKey = @"SyncBaseURL";
|
||||
static NSString *const kClientAuthCertificateFileKey = @"ClientAuthCertificateFile";
|
||||
@@ -72,11 +71,12 @@ static NSString *const kModeNotificationLockdown = @"ModeNotificationLockdown";
|
||||
|
||||
static NSString *const kEnablePageZeroProtectionKey = @"EnablePageZeroProtection";
|
||||
|
||||
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
|
||||
|
||||
// The keys managed by a sync server or mobileconfig.
|
||||
static NSString *const kClientModeKey = @"ClientMode";
|
||||
static NSString *const kWhitelistRegexKey = @"WhitelistRegex";
|
||||
static NSString *const kBlacklistRegexKey = @"BlacklistRegex";
|
||||
static NSString *const kFileChangesRegexKey = @"FileChangesRegex";
|
||||
|
||||
// The keys managed by a sync server.
|
||||
static NSString *const kFullSyncLastSuccess = @"FullSyncLastSuccess";
|
||||
@@ -86,22 +86,46 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_defaults = [[NSUserDefaults alloc] initWithSuiteName:kMobileConfigDomain];
|
||||
_syncServerKeys = @[
|
||||
kClientModeKey, kWhitelistRegexKey, kBlacklistRegexKey, kFileChangesRegexKey,
|
||||
kFullSyncLastSuccess, kRuleSyncLastSuccess, kSyncCleanRequired
|
||||
];
|
||||
_mobileConfigKeys = @[
|
||||
kClientModeKey, kFileChangesRegexKey, kWhitelistRegexKey, kBlacklistRegexKey,
|
||||
kEnablePageZeroProtectionKey, kMoreInfoURLKey, kEventDetailURLKey, kEventDetailTextKey,
|
||||
kUnknownBlockMessage, kBannedBlockMessage, kModeNotificationMonitor,
|
||||
kModeNotificationLockdown, kSyncBaseURLKey, kClientAuthCertificateFileKey,
|
||||
kClientAuthCertificatePasswordKey, kClientAuthCertificateCNKey,
|
||||
kClientAuthCertificateIssuerKey, kServerAuthRootsDataKey, kServerAuthRootsFileKey,
|
||||
kMachineOwnerKey, kMachineIDKey, kMachineOwnerPlistFileKey, kMachineOwnerPlistKeyKey,
|
||||
kMachineIDPlistFileKey, kMachineIDPlistKeyKey
|
||||
];
|
||||
[self reloadConfigData];
|
||||
_syncServerKeyTypes = @{
|
||||
kClientModeKey : [NSNumber class],
|
||||
kWhitelistRegexKey : [NSRegularExpression class],
|
||||
kBlacklistRegexKey : [NSRegularExpression class],
|
||||
kFullSyncLastSuccess : [NSDate class],
|
||||
kRuleSyncLastSuccess : [NSDate class],
|
||||
kSyncCleanRequired : [NSNumber class]
|
||||
};
|
||||
_forcedConfigKeyTypes = @{
|
||||
kClientModeKey : [NSNumber class],
|
||||
kFileChangesRegexKey : [NSRegularExpression class],
|
||||
kWhitelistRegexKey : [NSRegularExpression class],
|
||||
kBlacklistRegexKey : [NSRegularExpression class],
|
||||
kEnablePageZeroProtectionKey : [NSNumber class],
|
||||
kMoreInfoURLKey : [NSString class],
|
||||
kEventDetailURLKey : [NSString class],
|
||||
kEventDetailTextKey : [NSString class],
|
||||
kUnknownBlockMessage : [NSString class],
|
||||
kBannedBlockMessage : [NSString class],
|
||||
kModeNotificationMonitor : [NSString class],
|
||||
kModeNotificationLockdown : [NSString class],
|
||||
kSyncBaseURLKey : [NSString class],
|
||||
kClientAuthCertificateFileKey : [NSString class],
|
||||
kClientAuthCertificatePasswordKey : [NSString class],
|
||||
kClientAuthCertificateCNKey : [NSString class],
|
||||
kClientAuthCertificateIssuerKey : [NSString class],
|
||||
kServerAuthRootsDataKey : [NSData class],
|
||||
kServerAuthRootsFileKey : [NSString class],
|
||||
kMachineOwnerKey : [NSString class],
|
||||
kMachineIDKey : [NSString class],
|
||||
kMachineOwnerPlistFileKey : [NSString class],
|
||||
kMachineOwnerPlistKeyKey : [NSString class],
|
||||
kMachineIDPlistFileKey : [NSString class],
|
||||
kMachineIDPlistKeyKey : [NSString class],
|
||||
};
|
||||
_defaults = [NSUserDefaults standardUserDefaults];
|
||||
[_defaults addSuiteNamed:@"com.google.santa"];
|
||||
_configState = [self readForcedConfig];
|
||||
_syncState = [self readSyncStateFromDisk] ?: [NSMutableDictionary dictionary];
|
||||
[self startWatchingDefaults];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -117,277 +141,301 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return sharedConfigurator;
|
||||
}
|
||||
|
||||
#pragma mark KVO Dependencies
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingClientMode {
|
||||
return [NSSet setWithObjects:@"syncState", @"configState", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingWhitelistPathRegex {
|
||||
return [NSSet setWithObjects:@"syncState", @"configState", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingBlacklistPathRegex {
|
||||
return [NSSet setWithObjects:@"syncState", @"configState", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFileChangesRegex {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnablePageZeroProtection {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingMoreInfoURL {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEventDetailURL {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEventDetailText {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingUnknownBlockMessage {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingBannedBlockMessage {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingModeNotificationMonitor {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingModeNotificationLockdown {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateFile {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificatePassword {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateCn {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncClientAuthCertificateIssuer {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncServerAuthRootsData {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncServerAuthRootsFile {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFullSyncLastSuccess {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingMachineOwner {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingMachineID {
|
||||
return [NSSet setWithObject:@"configState"];
|
||||
}
|
||||
|
||||
#pragma mark Public Interface
|
||||
|
||||
- (SNTClientMode)clientMode {
|
||||
NSInteger cm = SNTClientModeUnknown;
|
||||
|
||||
id mode = self.configData[kClientModeKey];
|
||||
if ([mode respondsToSelector:@selector(longLongValue)]) {
|
||||
cm = (NSInteger)[mode longLongValue];
|
||||
}
|
||||
|
||||
SNTClientMode cm = [self.syncState[kClientModeKey] longLongValue];
|
||||
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown) {
|
||||
return (SNTClientMode)cm;
|
||||
} else {
|
||||
LOGE(@"Client mode was set to bad value: %ld. Resetting to MONITOR.", cm);
|
||||
self.clientMode = SNTClientModeMonitor;
|
||||
return SNTClientModeMonitor;
|
||||
return cm;
|
||||
}
|
||||
|
||||
cm = [self.configState[kClientModeKey] longLongValue];
|
||||
if (cm == SNTClientModeMonitor || cm == SNTClientModeLockdown) {
|
||||
return cm;
|
||||
}
|
||||
|
||||
return SNTClientModeMonitor;
|
||||
}
|
||||
|
||||
- (void)setClientMode:(SNTClientMode)newMode {
|
||||
- (void)setSyncServerClientMode:(SNTClientMode)newMode {
|
||||
if (newMode == SNTClientModeMonitor || newMode == SNTClientModeLockdown) {
|
||||
self.configData[kClientModeKey] = @(newMode);
|
||||
[self saveSyncStateToDisk];
|
||||
[self updateSyncStateForKey:kClientModeKey value:@(newMode)];
|
||||
} else {
|
||||
LOGW(@"Ignoring request to change client mode to %ld", newMode);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)whitelistPathRegex {
|
||||
if (!self.cachedWhitelistDirRegex && self.configData[kWhitelistRegexKey]) {
|
||||
NSString *re = self.configData[kWhitelistRegexKey];
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedWhitelistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedWhitelistDirRegex;
|
||||
return self.syncState[kWhitelistRegexKey] ?: self.configState[kWhitelistRegexKey];
|
||||
}
|
||||
|
||||
- (void)setWhitelistPathRegex:(NSRegularExpression *)re {
|
||||
if (!re) {
|
||||
[self.configData removeObjectForKey:kWhitelistRegexKey];
|
||||
} else {
|
||||
self.configData[kWhitelistRegexKey] = [re pattern];
|
||||
}
|
||||
self.cachedWhitelistDirRegex = nil;
|
||||
[self saveSyncStateToDisk];
|
||||
- (void)setSyncServerWhitelistPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kWhitelistRegexKey value:re];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)blacklistPathRegex {
|
||||
if (!self.cachedBlacklistDirRegex && self.configData[kBlacklistRegexKey]) {
|
||||
NSString *re = self.configData[kBlacklistRegexKey];
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedBlacklistDirRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedBlacklistDirRegex;
|
||||
return self.syncState[kBlacklistRegexKey] ?: self.configState[kBlacklistRegexKey];
|
||||
}
|
||||
|
||||
- (void)setBlacklistPathRegex:(NSRegularExpression *)re {
|
||||
if (!re) {
|
||||
[self.configData removeObjectForKey:kBlacklistRegexKey];
|
||||
} else {
|
||||
self.configData[kBlacklistRegexKey] = [re pattern];
|
||||
}
|
||||
self.cachedBlacklistDirRegex = nil;
|
||||
[self saveSyncStateToDisk];
|
||||
- (void)setSyncServerBlacklistPathRegex:(NSRegularExpression *)re {
|
||||
[self updateSyncStateForKey:kBlacklistRegexKey value:re];
|
||||
}
|
||||
|
||||
- (NSRegularExpression *)fileChangesRegex {
|
||||
if (!self.cachedFileChangesRegex && self.configData[kFileChangesRegexKey]) {
|
||||
NSString *re = self.configData[kFileChangesRegexKey];
|
||||
if (![re hasPrefix:@"^"]) re = [@"^" stringByAppendingString:re];
|
||||
self.cachedFileChangesRegex = [NSRegularExpression regularExpressionWithPattern:re
|
||||
options:0
|
||||
error:NULL];
|
||||
}
|
||||
return self.cachedFileChangesRegex;
|
||||
}
|
||||
|
||||
- (void)setFileChangesRegex:(NSRegularExpression *)re {
|
||||
if (!re) {
|
||||
[self.configData removeObjectForKey:kFileChangesRegexKey];
|
||||
} else {
|
||||
self.configData[kFileChangesRegexKey] = [re pattern];
|
||||
}
|
||||
self.cachedFileChangesRegex = nil;
|
||||
[self saveSyncStateToDisk];
|
||||
}
|
||||
|
||||
- (BOOL)enablePageZeroProtection {
|
||||
NSNumber *keyValue = self.configData[kEnablePageZeroProtectionKey];
|
||||
return keyValue ? [keyValue boolValue] : YES;
|
||||
}
|
||||
|
||||
- (NSURL *)moreInfoURL {
|
||||
return [NSURL URLWithString:self.configData[kMoreInfoURLKey]];
|
||||
}
|
||||
|
||||
- (NSString *)eventDetailURL {
|
||||
return self.configData[kEventDetailURLKey];
|
||||
}
|
||||
|
||||
- (NSString *)eventDetailText {
|
||||
return self.configData[kEventDetailTextKey];
|
||||
}
|
||||
|
||||
- (NSString *)unknownBlockMessage {
|
||||
return self.configData[kUnknownBlockMessage];
|
||||
}
|
||||
|
||||
- (NSString *)bannedBlockMessage {
|
||||
return self.configData[kBannedBlockMessage];
|
||||
}
|
||||
|
||||
- (NSString *)modeNotificationMonitor {
|
||||
return self.configData[kModeNotificationMonitor];
|
||||
}
|
||||
|
||||
- (NSString *)modeNotificationLockdown {
|
||||
return self.configData[kModeNotificationLockdown];
|
||||
return self.configState[kFileChangesRegexKey];
|
||||
}
|
||||
|
||||
- (NSURL *)syncBaseURL {
|
||||
NSString *urlStr = self.configData[kSyncBaseURLKey];
|
||||
if (urlStr) {
|
||||
NSURL *url = [NSURL URLWithString:urlStr];
|
||||
if (!url) LOGW(@"SyncBaseURL is not a valid URL!");
|
||||
return url;
|
||||
}
|
||||
return nil;
|
||||
NSString *urlString = self.configState[kSyncBaseURLKey];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
if (urlString && !url) LOGW(@"SyncBaseURL is not a valid URL!");
|
||||
return url;
|
||||
}
|
||||
|
||||
- (BOOL)enablePageZeroProtection {
|
||||
NSNumber *number = self.configState[kEnablePageZeroProtectionKey];
|
||||
return number ? [number boolValue] : YES;
|
||||
}
|
||||
|
||||
- (NSURL *)moreInfoURL {
|
||||
return [NSURL URLWithString:self.configState[kMoreInfoURLKey]];
|
||||
}
|
||||
|
||||
- (NSString *)eventDetailURL {
|
||||
return self.configState[kEventDetailURLKey];
|
||||
}
|
||||
|
||||
- (NSString *)eventDetailText {
|
||||
return self.configState[kEventDetailTextKey];
|
||||
}
|
||||
|
||||
- (NSString *)unknownBlockMessage {
|
||||
return self.configState[kUnknownBlockMessage];
|
||||
}
|
||||
|
||||
- (NSString *)bannedBlockMessage {
|
||||
return self.configState[kBannedBlockMessage];
|
||||
}
|
||||
|
||||
- (NSString *)modeNotificationMonitor {
|
||||
return self.configState[kModeNotificationMonitor];
|
||||
}
|
||||
|
||||
- (NSString *)modeNotificationLockdown {
|
||||
return self.configState[kModeNotificationLockdown];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificateFile {
|
||||
return self.configData[kClientAuthCertificateFileKey];
|
||||
return self.configState[kClientAuthCertificateFileKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificatePassword {
|
||||
return self.configData[kClientAuthCertificatePasswordKey];
|
||||
return self.configState[kClientAuthCertificatePasswordKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificateCn {
|
||||
return self.configData[kClientAuthCertificateCNKey];
|
||||
return self.configState[kClientAuthCertificateCNKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncClientAuthCertificateIssuer {
|
||||
return self.configData[kClientAuthCertificateIssuerKey];
|
||||
return self.configState[kClientAuthCertificateIssuerKey];
|
||||
}
|
||||
|
||||
- (NSData *)syncServerAuthRootsData {
|
||||
return self.configData[kServerAuthRootsDataKey];
|
||||
return self.configState[kServerAuthRootsDataKey];
|
||||
}
|
||||
|
||||
- (NSString *)syncServerAuthRootsFile {
|
||||
return self.configData[kServerAuthRootsFileKey];
|
||||
return self.configState[kServerAuthRootsFileKey];
|
||||
}
|
||||
|
||||
- (NSDate *)fullSyncLastSuccess {
|
||||
return self.configData[kFullSyncLastSuccess];
|
||||
return self.syncState[kFullSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)fullSyncLastSuccess {
|
||||
self.configData[kFullSyncLastSuccess] = fullSyncLastSuccess;
|
||||
[self saveSyncStateToDisk];
|
||||
self.syncState[kFullSyncLastSuccess] = fullSyncLastSuccess;
|
||||
self.ruleSyncLastSuccess = fullSyncLastSuccess;
|
||||
}
|
||||
|
||||
- (NSDate *)ruleSyncLastSuccess {
|
||||
return self.configData[kRuleSyncLastSuccess];
|
||||
return self.syncState[kRuleSyncLastSuccess];
|
||||
}
|
||||
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)ruleSyncLastSuccess {
|
||||
self.configData[kRuleSyncLastSuccess] = ruleSyncLastSuccess;
|
||||
self.syncState[kRuleSyncLastSuccess] = ruleSyncLastSuccess;
|
||||
[self saveSyncStateToDisk];
|
||||
}
|
||||
|
||||
- (BOOL)syncCleanRequired {
|
||||
return [self.configData[kSyncCleanRequired] boolValue];
|
||||
return [self.syncState[kSyncCleanRequired] boolValue];
|
||||
}
|
||||
|
||||
- (void)setSyncCleanRequired:(BOOL)syncCleanRequired {
|
||||
self.configData[kSyncCleanRequired] = @(syncCleanRequired);
|
||||
self.syncState[kSyncCleanRequired] = @(syncCleanRequired);
|
||||
[self saveSyncStateToDisk];
|
||||
}
|
||||
|
||||
- (NSString *)machineOwner {
|
||||
NSString *machineOwner;
|
||||
NSString *machineOwner = self.configState[kMachineOwnerKey];
|
||||
if (machineOwner) return machineOwner;
|
||||
|
||||
if (self.configData[kMachineOwnerPlistFileKey] && self.configData[kMachineOwnerPlistKeyKey]) {
|
||||
NSDictionary *plist =
|
||||
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineOwnerPlistFileKey]];
|
||||
machineOwner = plist[self.configData[kMachineOwnerPlistKeyKey]];
|
||||
NSString *plistPath = self.configState[kMachineOwnerPlistFileKey];
|
||||
NSString *plistKey = self.configState[kMachineOwnerPlistKeyKey];
|
||||
if (plistPath && plistKey) {
|
||||
NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||||
machineOwner = [plist[plistKey] isKindOfClass:[NSString class]] ? plist[plistKey] : nil;
|
||||
}
|
||||
|
||||
if (self.configData[kMachineOwnerKey]) {
|
||||
machineOwner = self.configData[kMachineOwnerKey];
|
||||
}
|
||||
|
||||
if (!machineOwner) machineOwner = @"";
|
||||
|
||||
return machineOwner;
|
||||
return machineOwner ?: @"";
|
||||
}
|
||||
|
||||
- (NSString *)machineID {
|
||||
NSString *machineId;
|
||||
NSString *machineId = self.configState[kMachineIDKey];
|
||||
if (machineId) return machineId;
|
||||
|
||||
if (self.configData[kMachineIDPlistFileKey] && self.configData[kMachineIDPlistKeyKey]) {
|
||||
NSDictionary *plist =
|
||||
[NSDictionary dictionaryWithContentsOfFile:self.configData[kMachineIDPlistFileKey]];
|
||||
machineId = plist[self.configData[kMachineIDPlistKeyKey]];
|
||||
NSString *plistPath = self.configState[kMachineIDPlistFileKey];
|
||||
NSString *plistKey = self.configState[kMachineIDPlistKeyKey];
|
||||
|
||||
if (plistPath && plistKey) {
|
||||
NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistPath];
|
||||
machineId = [plist[plistKey] isKindOfClass:[NSString class]] ? plist[plistKey] : nil;
|
||||
}
|
||||
|
||||
if (self.configData[kMachineIDKey]) {
|
||||
machineId = self.configData[kMachineIDKey];
|
||||
}
|
||||
|
||||
if ([machineId length] == 0) {
|
||||
machineId = [SNTSystemInfo hardwareUUID];
|
||||
}
|
||||
|
||||
return machineId;
|
||||
}
|
||||
|
||||
- (void)reloadConfigData {
|
||||
// Load the mobileconfig
|
||||
self.configData = [self mobileConfig];
|
||||
if (!self.configData[kClientModeKey]) {
|
||||
// Default to Monitor if the config is missing or invalid
|
||||
self.configData[kClientModeKey] = @(SNTClientModeMonitor);
|
||||
}
|
||||
|
||||
// Nothing else to do if a sync server is not involved
|
||||
if (!self.configData[kSyncBaseURLKey]) return;
|
||||
|
||||
// Load the last known sync state
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:kSyncStateFilePath]) return;
|
||||
NSMutableDictionary *syncState = [self syncState];
|
||||
if (!self) return;
|
||||
|
||||
// Overwrite or add the sync state to the running config
|
||||
for (NSString *key in [self syncServerKeys]) {
|
||||
self.configData[key] = syncState[key];
|
||||
}
|
||||
[self saveSyncStateToDisk];
|
||||
return machineId.length ? machineId : [SNTSystemInfo hardwareUUID];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (NSMutableDictionary *)syncState {
|
||||
NSError *error;
|
||||
NSData *readData = [NSData dataWithContentsOfFile:kSyncStateFilePath
|
||||
options:NSDataReadingMappedIfSafe
|
||||
error:&error];
|
||||
if (!readData) {
|
||||
LOGE(@"Could not read sync state file: %@, replacing.", [error localizedDescription]);
|
||||
[self saveSyncStateToDisk];
|
||||
return nil;
|
||||
}
|
||||
///
|
||||
/// Update the syncState. Triggers a KVO event for all dependents.
|
||||
///
|
||||
- (BOOL)updateSyncStateForKey:(NSString *)key value:(id)value {
|
||||
NSMutableDictionary *syncState = self.syncState.mutableCopy;
|
||||
syncState[key] = value;
|
||||
self.syncState = syncState;
|
||||
[self saveSyncStateToDisk];
|
||||
return YES;
|
||||
}
|
||||
|
||||
///
|
||||
/// Read the saved syncState.
|
||||
///
|
||||
- (NSMutableDictionary *)readSyncStateFromDisk {
|
||||
// Only read the sync state if a sync server is configured.
|
||||
if (!self.syncBaseURL) return nil;
|
||||
// Only santad should read this file.
|
||||
if (geteuid() != 0) return nil;
|
||||
NSMutableDictionary *syncState =
|
||||
[NSPropertyListSerialization propertyListWithData:readData
|
||||
options:NSPropertyListMutableContainers
|
||||
format:NULL
|
||||
error:&error];
|
||||
if (!syncState) {
|
||||
LOGE(@"Could not parse sync state file: %@, replacing.", [error localizedDescription]);
|
||||
[self saveSyncStateToDisk];
|
||||
return nil;
|
||||
[NSMutableDictionary dictionaryWithContentsOfFile:kSyncStateFilePath];
|
||||
for (NSString *key in syncState.allKeys) {
|
||||
if (self.syncServerKeyTypes[key] == [NSRegularExpression class]) {
|
||||
NSString *pattern = [syncState[key] isKindOfClass:[NSString class]] ? syncState[key] : nil;
|
||||
syncState[key] = [self expressionForPattern:pattern];
|
||||
} else if (![syncState[key] isKindOfClass:self.syncServerKeyTypes[key]]) {
|
||||
syncState[key] = nil;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
WEAKIFY(self);
|
||||
self.syncStateWatcher = [[SNTFileWatcher alloc] initWithFilePath:kSyncStateFilePath
|
||||
handler:^(unsigned long data) {
|
||||
STRONGIFY(self);
|
||||
[self syncStateFileChanged:data];
|
||||
}];
|
||||
});
|
||||
return syncState;
|
||||
}
|
||||
|
||||
@@ -395,13 +443,14 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
/// Saves the current effective syncState to disk.
|
||||
///
|
||||
- (void)saveSyncStateToDisk {
|
||||
// Only save the sync state if a sync server is configured.
|
||||
if (!self.syncBaseURL) return;
|
||||
// Only santad should write to this file.
|
||||
if (geteuid() != 0) return;
|
||||
NSMutableDictionary *syncState =
|
||||
[NSMutableDictionary dictionaryWithCapacity:[self syncServerKeys].count];
|
||||
for (NSString *key in [self syncServerKeys]) {
|
||||
syncState[key] = self.configData[key];
|
||||
}
|
||||
// Either remove
|
||||
NSMutableDictionary *syncState = self.syncState.mutableCopy;
|
||||
syncState[kWhitelistRegexKey] = [syncState[kWhitelistRegexKey] pattern];
|
||||
syncState[kBlacklistRegexKey] = [syncState[kBlacklistRegexKey] pattern];
|
||||
[syncState writeToFile:kSyncStateFilePath atomically:YES];
|
||||
[[NSFileManager defaultManager] setAttributes:@{ NSFilePosixPermissions : @0644 }
|
||||
ofItemAtPath:kSyncStateFilePath error:NULL];
|
||||
@@ -419,19 +468,18 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
int mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
int desired = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
if (fileStat.st_uid != 0 || fileStat.st_gid != 0 || (fileStat.st_mode & mask) != desired) {
|
||||
LOGD(@"Sync state file permissions changed, fixing.");
|
||||
LOGI(@"Sync state file permissions changed, fixing.");
|
||||
chown(cPath, 0, 0);
|
||||
chmod(cPath, desired);
|
||||
}
|
||||
} else {
|
||||
NSDictionary *syncState = [self syncState];
|
||||
for (NSString *key in self.syncServerKeys) {
|
||||
if (((self.configData[key] && !syncState[key]) ||
|
||||
(!self.configData[key] && syncState[key]) ||
|
||||
(self.configData[key] && ![self.configData[key] isEqualTo:syncState[key]]))) {
|
||||
NSDictionary *newSyncState = [self readSyncStateFromDisk];
|
||||
for (NSString *key in self.syncState) {
|
||||
if (((self.syncState[key] && !newSyncState[key]) ||
|
||||
(!self.syncState[key] && newSyncState[key]) ||
|
||||
(self.syncState[key] && ![self.syncState[key] isEqualTo:newSyncState[key]]))) {
|
||||
// Ignore sync url and dates
|
||||
if ([key isEqualToString:kSyncBaseURLKey] ||
|
||||
[key isEqualToString:kRuleSyncLastSuccess] ||
|
||||
if ([key isEqualToString:kRuleSyncLastSuccess] ||
|
||||
[key isEqualToString:kFullSyncLastSuccess]) continue;
|
||||
LOGE(@"Sync state file changed, replacing");
|
||||
[self saveSyncStateToDisk];
|
||||
@@ -441,25 +489,57 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a config provided by a com.google.santa mobileconfig or an empty
|
||||
/// NSMutableDictionary object if no config applies.
|
||||
///
|
||||
- (NSMutableDictionary *)mobileConfig {
|
||||
NSMutableDictionary *config =
|
||||
[NSMutableDictionary dictionaryWithCapacity:[self mobileConfigKeys].count];
|
||||
for (NSString *key in [self mobileConfigKeys]) {
|
||||
if ([self.defaults objectIsForcedForKey:key]) {
|
||||
config[key] = [self.defaults objectForKey:key];
|
||||
- (void)clearSyncState {
|
||||
self.syncState = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
#pragma mark Private Defaults Methods
|
||||
|
||||
- (NSRegularExpression *)expressionForPattern:(NSString *)pattern {
|
||||
if (!pattern) return nil;
|
||||
if (![pattern hasPrefix:@"^"]) pattern = [@"^" stringByAppendingString:pattern];
|
||||
return [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:NULL];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)readForcedConfig {
|
||||
NSMutableDictionary *forcedConfig = [NSMutableDictionary dictionary];
|
||||
for (NSString *key in self.forcedConfigKeyTypes) {
|
||||
id obj = [self forcedConfigValueForKey:key];
|
||||
forcedConfig[key] = [obj isKindOfClass:self.forcedConfigKeyTypes[key]] ? obj : nil;
|
||||
// Create the regex objects now
|
||||
if (self.forcedConfigKeyTypes[key] == [NSRegularExpression class]) {
|
||||
NSString *pattern = [obj isKindOfClass:[NSString class]] ? obj : nil;
|
||||
forcedConfig[key] = [self expressionForPattern:pattern];
|
||||
}
|
||||
}
|
||||
if (config[kSyncBaseURLKey]) {
|
||||
for (NSString *key in [self syncServerKeys]) {
|
||||
if ([key isEqualToString:kSyncBaseURLKey]) continue;
|
||||
[config removeObjectForKey:key];
|
||||
}
|
||||
}
|
||||
return config;
|
||||
return forcedConfig;
|
||||
}
|
||||
|
||||
- (id)forcedConfigValueForKey:(NSString *)key {
|
||||
id obj = [self.defaults objectForKey:key];
|
||||
return [self.defaults objectIsForcedForKey:key inDomain:kMobileConfigDomain] ? obj : nil;
|
||||
}
|
||||
|
||||
- (void)startWatchingDefaults {
|
||||
// Only santad should listen.
|
||||
if (geteuid() != 0) return;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(defaultsChanged:)
|
||||
name:NSUserDefaultsDidChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)defaultsChanged:(void *)v {
|
||||
SEL handleChange = @selector(handleChange);
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:handleChange object:nil];
|
||||
[self performSelector:handleChange withObject:nil afterDelay:5.0f];
|
||||
}
|
||||
|
||||
///
|
||||
/// Update the configState. Triggers a KVO event for all dependents.
|
||||
///
|
||||
- (void)handleChange {
|
||||
self.configState = [self readForcedConfig];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -37,6 +37,7 @@ enum SantaDriverMethods {
|
||||
kSantaUserClientOpen,
|
||||
kSantaUserClientAllowBinary,
|
||||
kSantaUserClientDenyBinary,
|
||||
kSantaUserClientAcknowledgeBinary,
|
||||
kSantaUserClientClearCache,
|
||||
kSantaUserClientCacheCount,
|
||||
kSantaUserClientCheckCache,
|
||||
@@ -64,7 +65,8 @@ typedef enum {
|
||||
ACTION_RESPOND_ALLOW = 20,
|
||||
ACTION_RESPOND_DENY = 21,
|
||||
ACTION_RESPOND_TOOLONG = 22,
|
||||
|
||||
ACTION_RESPOND_ACK = 23,
|
||||
|
||||
// NOTIFY
|
||||
ACTION_NOTIFY_EXEC = 30,
|
||||
ACTION_NOTIFY_WRITE = 31,
|
||||
|
||||
@@ -75,8 +75,11 @@
|
||||
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply;
|
||||
- (void)xsrfToken:(void (^)(NSString *))reply;
|
||||
- (void)setXsrfToken:(NSString *)token reply:(void (^)())reply;
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply;
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply;
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply;
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply;
|
||||
- (void)setWhitelistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
- (void)setBlacklistPathRegex:(NSString *)pattern reply:(void (^)())reply;
|
||||
|
||||
@@ -233,10 +233,15 @@ void SantaDecisionManager::AddToCache(
|
||||
case ACTION_REQUEST_BINARY:
|
||||
decision_cache->set(identifier, val, 0);
|
||||
break;
|
||||
case ACTION_RESPOND_ACK:
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
break;
|
||||
case ACTION_RESPOND_ALLOW:
|
||||
case ACTION_RESPOND_DENY:
|
||||
decision_cache->set(
|
||||
identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56));
|
||||
// TODO(bur): Avoid calling set() twice, finding and locking buckets is fast, but not free.
|
||||
if (decision_cache->set(identifier, val, ((uint64_t)ACTION_REQUEST_BINARY << 56))) {
|
||||
decision_cache->set(identifier, val, ((uint64_t)ACTION_RESPOND_ACK << 56));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -316,10 +321,16 @@ santa_action_t SantaDecisionManager::GetFromDaemon(
|
||||
return ACTION_ERROR;
|
||||
}
|
||||
|
||||
// Check the cache every kRequestLoopSleepMilliseconds. Break this loop and send the request
|
||||
// again if kRequestCacheChecks is reached. Don't break the loop if the daemon is working on the
|
||||
// request, indicated with ACTION_RESPOND_ACK.
|
||||
auto cache_check_count = 0;
|
||||
do {
|
||||
msleep((void *)message->vnode_id, NULL, 0, "", &ts_);
|
||||
return_action = GetFromCache(identifier);
|
||||
} while (return_action == ACTION_REQUEST_BINARY && ClientConnected());
|
||||
} while (ClientConnected() &&
|
||||
((return_action == ACTION_REQUEST_BINARY && ++cache_check_count < kRequestCacheChecks)
|
||||
|| (return_action == ACTION_RESPOND_ACK)));
|
||||
} while (!RESPONSE_VALID(return_action) && ClientConnected());
|
||||
|
||||
// If response is still not valid, the daemon exited
|
||||
@@ -354,7 +365,7 @@ santa_action_t SantaDecisionManager::FetchDecision(
|
||||
// If item is not in cache, break out of loop to send request to daemon.
|
||||
if (RESPONSE_VALID(return_action)) {
|
||||
return return_action;
|
||||
} else if (return_action == ACTION_REQUEST_BINARY) {
|
||||
} else if (return_action == ACTION_REQUEST_BINARY || return_action == ACTION_RESPOND_ACK) {
|
||||
// This thread will now sleep for kRequestLoopSleepMilliseconds (1s) or
|
||||
// until AddToCache is called, indicating a response has arrived.
|
||||
msleep((void *)vnode_id, NULL, 0, "", &ts_);
|
||||
|
||||
@@ -144,6 +144,12 @@ class SantaDecisionManager : public OSObject {
|
||||
*/
|
||||
static const uint32_t kRequestLoopSleepMilliseconds = 1000;
|
||||
|
||||
/**
|
||||
While waiting for a response from the daemon, this is the maximum number cache checks before
|
||||
re-sending the request.
|
||||
*/
|
||||
static const uint32_t kRequestCacheChecks = 5;
|
||||
|
||||
/**
|
||||
The maximum number of milliseconds a cached deny message should be
|
||||
considered valid.
|
||||
|
||||
@@ -152,6 +152,18 @@ IOReturn SantaDriverClient::deny_binary(
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::acknowledge_binary(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
if (!me) return kIOReturnBadArgument;
|
||||
|
||||
const uint64_t vnode_id = static_cast<const uint64_t>(arguments->scalarInput[0]);
|
||||
if (!vnode_id) return kIOReturnInvalid;
|
||||
me->decisionManager->AddToCache(vnode_id, ACTION_RESPOND_ACK, 0);
|
||||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
IOReturn SantaDriverClient::clear_cache(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments) {
|
||||
SantaDriverClient *me = OSDynamicCast(SantaDriverClient, target);
|
||||
@@ -199,6 +211,7 @@ IOReturn SantaDriverClient::externalMethod(
|
||||
{ &SantaDriverClient::open, 0, 0, 0, 0 },
|
||||
{ &SantaDriverClient::allow_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::deny_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::acknowledge_binary, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::clear_cache, 1, 0, 0, 0 },
|
||||
{ &SantaDriverClient::cache_count, 0, 0, 2, 0 },
|
||||
{ &SantaDriverClient::check_cache, 1, 0, 1, 0 }
|
||||
|
||||
@@ -87,6 +87,11 @@ class com_google_SantaDriverClient : public IOUserClient {
|
||||
static IOReturn deny_binary(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to acknowledge a binary request. This is used for large binaries that
|
||||
/// may take a while to reach a decision.
|
||||
static IOReturn acknowledge_binary(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
/// The daemon calls this to empty the cache.
|
||||
static IOReturn clear_cache(
|
||||
OSObject *target, void *reference, IOExternalMethodArguments *arguments);
|
||||
|
||||
@@ -98,11 +98,20 @@
|
||||
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
||||
|
||||
dispatch_async(self.queue, ^{
|
||||
// Use the highest bundle we can find. Save and reuse the bundle infomation when creating
|
||||
// the related binary events.
|
||||
// Use the highest bundle we can find.
|
||||
SNTFileInfo *b = [[SNTFileInfo alloc] initWithPath:event.fileBundlePath];
|
||||
b.useAncestorBundle = YES;
|
||||
event.fileBundlePath = b.bundlePath;
|
||||
|
||||
// If path to the bundle is unavailable, stop. SantaGUI will revert to
|
||||
// using the offending blockable.
|
||||
if (!event.fileBundlePath) {
|
||||
reply(nil, nil, 0);
|
||||
dispatch_semaphore_signal(sema);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse the bundle infomation when creating the related binary events.
|
||||
event.fileBundleID = b.bundleIdentifier;
|
||||
event.fileBundleName = b.bundleName;
|
||||
event.fileBundleVersion = b.bundleVersion;
|
||||
|
||||
@@ -103,15 +103,26 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
}];
|
||||
|
||||
// Sync status
|
||||
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss Z";
|
||||
NSDate *lastSyncSuccess = [[SNTConfigurator configurator] fullSyncLastSuccess];
|
||||
NSString *lastSyncSuccessStr = [dateFormatter stringFromDate:lastSyncSuccess] ?: @"Never";
|
||||
NSDate *lastRuleSyncSuccess = [[SNTConfigurator configurator] ruleSyncLastSuccess];
|
||||
NSString *lastRuleSyncSuccessStr =
|
||||
[dateFormatter stringFromDate:lastRuleSyncSuccess] ?: lastSyncSuccessStr;
|
||||
BOOL syncCleanReqd = [[SNTConfigurator configurator] syncCleanRequired];
|
||||
__block NSDate *fullSyncLastSuccess;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] fullSyncLastSuccess:^(NSDate *date) {
|
||||
fullSyncLastSuccess = date;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block NSDate *ruleSyncLastSuccess;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] ruleSyncLastSuccess:^(NSDate *date) {
|
||||
ruleSyncLastSuccess = date;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block BOOL syncCleanReqd = NO;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] syncCleanRequired:^(BOOL clean) {
|
||||
syncCleanReqd = clean;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block BOOL pushNotifications = NO;
|
||||
if ([[SNTConfigurator configurator] syncBaseURL]) {
|
||||
@@ -136,6 +147,15 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
fprintf(stderr, "Failed to retrieve some stats from daemon\n\n");
|
||||
}
|
||||
|
||||
// Format dates
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
dateFormatter.dateFormat = @"yyyy/MM/dd HH:mm:ss Z";
|
||||
NSString *fullSyncLastSuccessStr = [dateFormatter stringFromDate:fullSyncLastSuccess] ?: @"Never";
|
||||
NSString *ruleSyncLastSuccessStr =
|
||||
[dateFormatter stringFromDate:ruleSyncLastSuccess] ?: fullSyncLastSuccessStr;
|
||||
|
||||
NSString *syncURLStr = [[[SNTConfigurator configurator] syncBaseURL] absoluteString];
|
||||
|
||||
if ([arguments containsObject:@"--json"]) {
|
||||
NSDictionary *stats = @{
|
||||
@"daemon" : @{
|
||||
@@ -158,8 +178,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
@"sync" : @{
|
||||
@"server" : syncURLStr ?: @"null",
|
||||
@"clean_required" : @(syncCleanReqd),
|
||||
@"last_successful_full" : lastSyncSuccessStr ?: @"null",
|
||||
@"last_successful_rule" : lastRuleSyncSuccessStr ?: @"null",
|
||||
@"last_successful_full" : fullSyncLastSuccessStr ?: @"null",
|
||||
@"last_successful_rule" : ruleSyncLastSuccessStr ?: @"null",
|
||||
@"push_notifications" : pushNotifications ? @"Connected" : @"Disconnected",
|
||||
@"bundle_scanning" : @(bundlesEnabled)
|
||||
},
|
||||
@@ -187,8 +207,8 @@ REGISTER_COMMAND_NAME(@"status")
|
||||
printf(">>> Sync Info\n");
|
||||
printf(" %-25s | %s\n", "Sync Server", [syncURLStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Clean Sync Required", (syncCleanReqd ? "Yes" : "No"));
|
||||
printf(" %-25s | %s\n", "Last Successful Full Sync", [lastSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Last Successful Rule Sync", [lastRuleSyncSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Last Successful Full Sync", [fullSyncLastSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Last Successful Rule Sync", [ruleSyncLastSuccessStr UTF8String]);
|
||||
printf(" %-25s | %s\n", "Push Notifications",
|
||||
(pushNotifications ? "Connected" : "Disconnected"));
|
||||
printf(" %-25s | %s\n", "Bundle Scanning", (bundlesEnabled ? "Yes" : "No"));
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
// Update last sync success
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] setSyncLastSuccess:[NSDate date] reply:replyBlock];
|
||||
[[self.daemonConn remoteObjectProxy] setFullSyncLastSuccess:[NSDate date] reply:replyBlock];
|
||||
|
||||
// Wait for dispatch group
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
|
||||
|
||||
@@ -60,11 +60,17 @@
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
__block BOOL syncClean = NO;
|
||||
dispatch_group_enter(group);
|
||||
[[self.daemonConn remoteObjectProxy] syncCleanRequired:^(BOOL clean) {
|
||||
syncClean = clean;
|
||||
dispatch_group_leave(group);
|
||||
}];
|
||||
|
||||
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC));
|
||||
|
||||
// If user requested it or we've never had a successful sync, try from a clean slate.
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"] ||
|
||||
[[SNTConfigurator configurator] syncCleanRequired]) {
|
||||
if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--clean"] || syncClean) {
|
||||
LOGD(@"Clean sync requested by user");
|
||||
requestDict[kRequestCleanSync] = @YES;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
@import DiskArbitration;
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#import "SNTCommonEnums.h"
|
||||
#import "SNTConfigurator.h"
|
||||
#import "SNTDaemonControlController.h"
|
||||
@@ -28,22 +25,22 @@
|
||||
#import "SNTEventLog.h"
|
||||
#import "SNTEventTable.h"
|
||||
#import "SNTExecutionController.h"
|
||||
#import "SNTFileWatcher.h"
|
||||
#import "SNTLogging.h"
|
||||
#import "SNTNotificationQueue.h"
|
||||
#import "SNTRuleTable.h"
|
||||
#import "SNTSyncdQueue.h"
|
||||
#import "SNTXPCConnection.h"
|
||||
#import "SNTXPCControlInterface.h"
|
||||
#import "SNTXPCNotifierInterface.h"
|
||||
|
||||
@interface SNTApplication ()
|
||||
@property DASessionRef diskArbSession;
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTEventLog *eventLog;
|
||||
@property SNTExecutionController *execController;
|
||||
@property SNTFileWatcher *configFileWatcher;
|
||||
@property SNTFileWatcher *syncStateWatcher;
|
||||
@property SNTXPCConnection *controlConnection;
|
||||
@property SNTNotificationQueue *notQueue;
|
||||
@property pid_t syncdPID;
|
||||
@end
|
||||
|
||||
@implementation SNTApplication
|
||||
@@ -73,18 +70,37 @@
|
||||
|
||||
_eventLog = [[SNTEventLog alloc] init];
|
||||
|
||||
SNTNotificationQueue *notQueue = [[SNTNotificationQueue alloc] init];
|
||||
self.notQueue = [[SNTNotificationQueue alloc] init];
|
||||
SNTSyncdQueue *syncdQueue = [[SNTSyncdQueue alloc] init];
|
||||
|
||||
// Restart santactl if it goes down
|
||||
syncdQueue.invalidationHandler = ^{
|
||||
[self startSyncd];
|
||||
};
|
||||
|
||||
|
||||
// Listen for actionable config changes.
|
||||
NSKeyValueObservingOptions bits = (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld);
|
||||
[[SNTConfigurator configurator] addObserver:self
|
||||
forKeyPath:@"clientMode"
|
||||
options:bits
|
||||
context:NULL];
|
||||
[[SNTConfigurator configurator] addObserver:self
|
||||
forKeyPath:@"syncBaseURL"
|
||||
options:bits
|
||||
context:NULL];
|
||||
[[SNTConfigurator configurator] addObserver:self
|
||||
forKeyPath:@"whitelistPathRegex"
|
||||
options:bits
|
||||
context:NULL];
|
||||
[[SNTConfigurator configurator] addObserver:self
|
||||
forKeyPath:@"blacklistPathRegex"
|
||||
options:bits
|
||||
context:NULL];
|
||||
|
||||
// Establish XPC listener for Santa and santactl connections
|
||||
SNTDaemonControlController *dc =
|
||||
[[SNTDaemonControlController alloc] initWithDriverManager:_driverManager
|
||||
notificationQueue:notQueue
|
||||
notificationQueue:self.notQueue
|
||||
syncdQueue:syncdQueue
|
||||
eventLog:_eventLog];
|
||||
|
||||
@@ -94,43 +110,11 @@
|
||||
_controlConnection.exportedObject = dc;
|
||||
[_controlConnection resume];
|
||||
|
||||
__block SNTClientMode origMode = [[SNTConfigurator configurator] clientMode];
|
||||
__block NSURL *origSyncURL = [[SNTConfigurator configurator] syncBaseURL];
|
||||
_configFileWatcher = [[SNTFileWatcher alloc] initWithFilePath:kMobileConfigFilePath
|
||||
handler:^(unsigned long data) {
|
||||
|
||||
LOGD(@"Mobileconfig changed, reloading.");
|
||||
[[SNTConfigurator configurator] reloadConfigData];
|
||||
|
||||
// Flush cache if client just went into lockdown.
|
||||
SNTClientMode newMode = [[SNTConfigurator configurator] clientMode];
|
||||
if (origMode != newMode) {
|
||||
origMode = newMode;
|
||||
if (newMode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
}
|
||||
|
||||
// Start santactl if the syncBaseURL changed from nil --> somthing
|
||||
NSURL *syncURL = [[SNTConfigurator configurator] syncBaseURL];
|
||||
if (!origSyncURL && syncURL) {
|
||||
origSyncURL = syncURL;
|
||||
LOGI(@"SyncBaseURL added, starting santactl.");
|
||||
[self startSyncd];
|
||||
}
|
||||
}];
|
||||
|
||||
_syncStateWatcher = [[SNTFileWatcher alloc] initWithFilePath:kSyncStateFilePath
|
||||
handler:^(unsigned long data) {
|
||||
[[SNTConfigurator configurator] syncStateFileChanged:data];
|
||||
}];
|
||||
|
||||
// Initialize the binary checker object
|
||||
_execController = [[SNTExecutionController alloc] initWithDriverManager:_driverManager
|
||||
ruleTable:ruleTable
|
||||
eventTable:eventTable
|
||||
notifierQueue:notQueue
|
||||
notifierQueue:self.notQueue
|
||||
syncdQueue:syncdQueue
|
||||
eventLog:_eventLog];
|
||||
// Start up santactl as a daemon if a sync server exists.
|
||||
@@ -142,18 +126,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startSyncd {
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) return;
|
||||
|
||||
if (fork() == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(EPERM);
|
||||
}
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--daemon", "--syslog", NULL));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
LOGI(@"Connected to driver, activating.");
|
||||
|
||||
@@ -270,4 +242,76 @@ void diskDisappearedCallback(DADiskRef disk, void *context) {
|
||||
[app.driverManager flushCacheNonRootOnly:YES];
|
||||
}
|
||||
|
||||
- (void)startSyncd {
|
||||
if (![[SNTConfigurator configurator] syncBaseURL]) return;
|
||||
[self stopSyncd];
|
||||
self.syncdPID = fork();
|
||||
if (self.syncdPID == -1) {
|
||||
LOGI(@"Failed to fork");
|
||||
self.syncdPID = 0;
|
||||
} else if (self.syncdPID == 0) {
|
||||
// Ensure we have no privileges
|
||||
if (!DropRootPrivileges()) {
|
||||
_exit(EPERM);
|
||||
}
|
||||
_exit(execl(kSantaCtlPath, kSantaCtlPath, "sync", "--daemon", "--syslog", NULL));
|
||||
}
|
||||
LOGI(@"santactl started with pid: %i", self.syncdPID);
|
||||
}
|
||||
|
||||
- (void)stopSyncd {
|
||||
if (!self.syncdPID) return;
|
||||
int ret = kill(self.syncdPID, SIGKILL);
|
||||
LOGD(@"kill(%i, 9) = %i", self.syncdPID, ret);
|
||||
self.syncdPID = 0;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary<NSString *,id> *)change
|
||||
context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"clientMode"]) {
|
||||
SNTClientMode new =
|
||||
[change[@"new"] isKindOfClass:[NSNumber class]] ? [change[@"new"] longLongValue] : 0;
|
||||
SNTClientMode old =
|
||||
[change[@"old"] isKindOfClass:[NSNumber class]] ? [change[@"old"] longLongValue] : 0;
|
||||
if (new != old) [self clientModeDidChange:new];
|
||||
} else if ([keyPath isEqualToString:@"syncBaseURL"]) {
|
||||
NSURL *new = [change[@"new"] isKindOfClass:[NSURL class]] ? change[@"new"] : nil;
|
||||
NSURL *old = [change[@"old"] isKindOfClass:[NSURL class]] ? change[@"old"] : nil;
|
||||
if (!new && !old) return;
|
||||
if (![new.absoluteString isEqualToString:old.absoluteString]) [self syncBaseURLDidChange:new];
|
||||
} else if ([keyPath isEqualToString:@"whitelistPathRegex"] ||
|
||||
[keyPath isEqualToString:@"blacklistPathRegex"]) {
|
||||
NSRegularExpression *new =
|
||||
[change[@"new"] isKindOfClass:[NSRegularExpression class]] ? change[@"new"] : nil;
|
||||
NSRegularExpression *old =
|
||||
[change[@"old"] isKindOfClass:[NSRegularExpression class]] ? change[@"old"] : nil;
|
||||
if (!new && !old) return;
|
||||
if (![new.pattern isEqualToString:old.pattern]) {
|
||||
LOGI(@"Changed [white|black]list regex, flushing cache");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clientModeDidChange:(SNTClientMode)clientMode {
|
||||
if (clientMode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
[[self.notQueue.notifierConnection remoteObjectProxy] postClientModeNotification:clientMode];
|
||||
}
|
||||
|
||||
- (void)syncBaseURLDidChange:(NSURL *)syncBaseURL {
|
||||
if (syncBaseURL) {
|
||||
LOGI(@"Starting santactl with new SyncBaseURL: %@", syncBaseURL);
|
||||
[self startSyncd];
|
||||
} else {
|
||||
LOGI(@"SyncBaseURL removed, killing santactl pid: %i", self.syncdPID);
|
||||
[self stopSyncd];
|
||||
[[SNTConfigurator configurator] clearSyncState];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -146,15 +146,7 @@ double watchdogRAMPeak = 0;
|
||||
}
|
||||
|
||||
- (void)setClientMode:(SNTClientMode)mode reply:(void (^)())reply {
|
||||
if ([[SNTConfigurator configurator] clientMode] != mode) {
|
||||
// Flush cache if client just went into lockdown.
|
||||
if (mode == SNTClientModeLockdown) {
|
||||
LOGI(@"Changed client mode, flushing cache.");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
}
|
||||
[[SNTConfigurator configurator] setClientMode:mode];
|
||||
[[self.notQueue.notifierConnection remoteObjectProxy] postClientModeNotification:mode];
|
||||
}
|
||||
[[SNTConfigurator configurator] setSyncServerClientMode:mode];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -167,16 +159,28 @@ double watchdogRAMPeak = 0;
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)setSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
- (void)fullSyncLastSuccess:(void (^)(NSDate *))reply {
|
||||
reply([[SNTConfigurator configurator] fullSyncLastSuccess]);
|
||||
}
|
||||
|
||||
- (void)setFullSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setFullSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)ruleSyncLastSuccess:(void (^)(NSDate *))reply {
|
||||
reply([[SNTConfigurator configurator] ruleSyncLastSuccess]);
|
||||
}
|
||||
|
||||
- (void)setRuleSyncLastSuccess:(NSDate *)date reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setRuleSyncLastSuccess:date];
|
||||
reply();
|
||||
}
|
||||
|
||||
- (void)syncCleanRequired:(void (^)(BOOL))reply {
|
||||
reply([[SNTConfigurator configurator] syncCleanRequired]);
|
||||
}
|
||||
|
||||
- (void)setSyncCleanRequired:(BOOL)cleanReqd reply:(void (^)())reply {
|
||||
[[SNTConfigurator configurator] setSyncCleanRequired:cleanReqd];
|
||||
reply();
|
||||
@@ -186,9 +190,7 @@ double watchdogRAMPeak = 0;
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setWhitelistPathRegex:re];
|
||||
LOGI(@"Received new whitelist regex, flushing cache");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
[[SNTConfigurator configurator] setSyncServerWhitelistPathRegex:re];
|
||||
reply();
|
||||
}
|
||||
|
||||
@@ -196,9 +198,7 @@ double watchdogRAMPeak = 0;
|
||||
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern
|
||||
options:0
|
||||
error:NULL];
|
||||
[[SNTConfigurator configurator] setBlacklistPathRegex:re];
|
||||
LOGI(@"Received new blacklist regex, flushing cache");
|
||||
[self.driverManager flushCacheNonRootOnly:NO];
|
||||
[[SNTConfigurator configurator] setSyncServerBlacklistPathRegex:re];
|
||||
reply();
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +162,13 @@ static const int MAX_DELAY = 15;
|
||||
1,
|
||||
0,
|
||||
0);
|
||||
case ACTION_RESPOND_ACK:
|
||||
return IOConnectCallScalarMethod(_connection,
|
||||
kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeId,
|
||||
1,
|
||||
0,
|
||||
0);
|
||||
default:
|
||||
return KERN_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
#import "SNTStoredEvent.h"
|
||||
#import "SNTSyncdQueue.h"
|
||||
|
||||
// A binary is considered large at ~30MB. Large binaries take longer to hash and consequently
|
||||
// longer to post a decision back to santa-driver. When a binary is considered large santad will
|
||||
// let santa-driver know it has received its request and is working on a decision. This allows
|
||||
// santa-driver to relax; it does not have to worry about resending the request due to a timeout.
|
||||
static size_t kLargeBinarySize = 30 * 1024 * 1024;
|
||||
|
||||
@interface SNTExecutionController ()
|
||||
@property SNTDriverManager *driverManager;
|
||||
@property SNTEventLog *eventLog;
|
||||
@@ -102,6 +108,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// If the binary is large let santa-driver know we received the request and we are working on it.
|
||||
if (binInfo.fileSize > kLargeBinarySize) {
|
||||
LOGD(@"%@ is larger than %zu. Letting santa-driver know we are working on it.",
|
||||
binInfo.path, kLargeBinarySize);
|
||||
[_driverManager postToKernelAction:ACTION_RESPOND_ACK forVnodeID:message.vnode_id];
|
||||
}
|
||||
|
||||
// Get codesigning info about the file.
|
||||
NSError *csError;
|
||||
MOLCodesignChecker *csInfo = [[MOLCodesignChecker alloc] initWithBinaryPath:binInfo.path
|
||||
|
||||
@@ -99,6 +99,9 @@
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientAllowBinary, &vnodeid, 1, 0, 0);
|
||||
} else if (action == ACTION_RESPOND_DENY) {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientDenyBinary, &vnodeid, 1, 0, 0);
|
||||
} else if (action == ACTION_RESPOND_ACK) {
|
||||
IOConnectCallScalarMethod(self.connection, kSantaUserClientAcknowledgeBinary,
|
||||
&vnodeid, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,6 +242,18 @@
|
||||
} else if (strncmp("/bin/cat", vdata.path, strlen("/bin/cat")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
self.timesSeenCat++;
|
||||
} else if (strncmp("/usr/bin/cal", vdata.path, strlen("/usr/bin/cal")) == 0) {
|
||||
static int count = 0;
|
||||
if (count++) TFAILINFO("Large binary should not re-request");
|
||||
[self postToKernelAction:ACTION_RESPOND_ACK forVnodeID:vdata.vnode_id];
|
||||
for (int i = 0; i < 15; ++i) {
|
||||
printf("\033[s"); // save cursor position
|
||||
printf("%i/15", i);
|
||||
sleep(1);
|
||||
printf("\033[u"); // restore cursor position
|
||||
}
|
||||
printf("\033[K\033[u"); // clear line, restore cursor position
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
} else if (strncmp("/bin/ln", vdata.path, strlen("/bin/ln")) == 0) {
|
||||
[self postToKernelAction:ACTION_RESPOND_ALLOW forVnodeID:vdata.vnode_id];
|
||||
|
||||
@@ -554,6 +569,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testLargeBinary {
|
||||
TSTART("Handles large binary");
|
||||
|
||||
@try {
|
||||
NSTask *testexec = [self taskWithPath:@"/usr/bin/cal"];
|
||||
[testexec launch];
|
||||
[testexec waitUntilExit];
|
||||
} @catch (NSException *e) {
|
||||
TFAILINFO("Failed to launch");
|
||||
}
|
||||
|
||||
TPASS();
|
||||
}
|
||||
|
||||
#pragma mark - Main
|
||||
|
||||
- (void)runTests {
|
||||
@@ -580,6 +609,7 @@
|
||||
|
||||
printf("\n-> Performance tests:\033[m\n");
|
||||
[self testCachePerformance];
|
||||
[self testLargeBinary];
|
||||
[self handlesLotsOfBinaries];
|
||||
|
||||
printf("\nAll tests passed.\n\n");
|
||||
|
||||
Reference in New Issue
Block a user