Compare commits

..

15 Commits

Author SHA1 Message Date
Pete Markowsky
fd23a5c3b7 Fix up endTimestamp to be Monarch compliant (#879)
Fix up endTimestamp field to be Monarch compliant.
2022-08-16 22:32:29 -04:00
Russell Hancox
ec203e8796 Project: Rename Source/santa -> Source/gui (#877) 2022-08-12 14:19:01 -04:00
Russell Hancox
57ff69208d GUI: Missed a required dependency (#876) 2022-08-12 14:02:22 -04:00
Russell Hancox
f00b7d2ded GUI: Expose SNTNotificationManager.h for the test. (#875) 2022-08-12 13:46:25 -04:00
Russell Hancox
9791fdd53c Project: Add a GH action to prevent trailing whitespace (#873) 2022-08-12 12:46:11 -04:00
Russell Hancox
26e2203f1e GUI: Improve signing chain key reporting in distributed notifications. (#874)
Also add a group for GUI unit_tests and include in the overall project tests group.
2022-08-12 11:03:21 -04:00
Russell Hancox
4a47195d12 Santa: Post distributed notification when showing block UI (#870)
Fixes #869
2022-08-11 12:34:35 -04:00
Russell Hancox
4436e221df GUI: Add silent mode configuration option. (#871)
When enabled, this option disables *all* GUI notifications from Santa. This is intended for kiosk-style machines where it is not expected for users to _ever_ execute unknown binaries.

Fixes #862
2022-08-11 09:17:07 -04:00
Russell Hancox
deccc8a148 GUI: For App Store published apps, include team ID. (#872)
With this change, the publisher field for an App Store published app will be  instead of

Fixes #758
2022-08-11 08:15:42 -04:00
Henry S
06da796a4d Docs: add link to GitHub (#868) 2022-08-08 16:38:34 -04:00
Russell Hancox
7b99a76d0d Docs: Add StaticRules to example mobileconfig (#866) 2022-08-03 10:59:18 -04:00
Pete Markowsky
c2d3e99446 Sync Protocol Docs (#860)
Initial commit of sync protocol docs.
2022-07-28 17:27:43 -04:00
Russell Hancox
6db7fea8ae syncservice: Add tests for NSData+Zlib and Postflight (#864) 2022-07-26 13:05:35 -04:00
Kathryn Hancox
6fcb4cfe63 Docs: Add recommended rollout doc (#861) 2022-07-22 13:50:25 -04:00
bfreezy
8b55ee4da5 santad: only allow root read+write permissions on sync-state.plist (#858) 2022-07-18 13:32:08 -04:00
61 changed files with 870 additions and 74 deletions

View File

@@ -1,13 +1,14 @@
name: Check Markdown links
name: Check Markdown
on:
on:
pull_request:
paths:
- "**.md"
jobs:
markdown-link-check:
markdown-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: gaurav-nelson/github-action-markdown-link-check@v1
- run: "! git grep -EIn $'[ \t]+$'"

View File

@@ -20,6 +20,7 @@ jobs:
- name: Run linters
run: ./Testing/lint.sh
build_userspace:
strategy:
fail-fast: false

7
BUILD
View File

@@ -74,14 +74,14 @@ launchctl load /Library/LaunchAgents/com.google.santa.plist
run_command(
name = "reload",
srcs = [
"//Source/santa:Santa",
"//Source/gui:Santa",
],
cmd = """
set -e
rm -rf /tmp/bazel_santa_reload
unzip -d /tmp/bazel_santa_reload \
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/santa/Santa.zip >/dev/null
$${BUILD_WORKSPACE_DIRECTORY}/bazel-out/*$(COMPILATION_MODE)*/bin/Source/gui/Santa.zip >/dev/null
echo "You may be asked for your password for sudo"
sudo BINARIES=/tmp/bazel_santa_reload CONF=$${BUILD_WORKSPACE_DIRECTORY}/Conf \
$${BUILD_WORKSPACE_DIRECTORY}/Conf/install.sh
@@ -96,7 +96,7 @@ echo "Time to stop being naughty"
genrule(
name = "release",
srcs = [
"//Source/santa:Santa",
"//Source/gui:Santa",
"Conf/install.sh",
"Conf/uninstall.sh",
"Conf/com.google.santa.bundleservice.plist",
@@ -191,6 +191,7 @@ test_suite(
name = "unit_tests",
tests = [
"//Source/common:unit_tests",
"//Source/gui:unit_tests",
"//Source/santactl:unit_tests",
"//Source/santad:unit_tests",
"//Source/santametricservice:unit_tests",

View File

@@ -4,10 +4,10 @@
<img src="https://raw.githubusercontent.com/google/santa/main/Source/santa/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
</p>
Santa is a binary authorization system for macOS. It consists of a system
extension that monitors for executions, a daemon that makes execution decisions
Santa is a binary authorization system for macOS. It consists of a system
extension that monitors for executions, a daemon that makes execution decisions
based on the contents of a local database, a GUI agent that notifies the user in
case of a block decision and a command-line utility for managing the system and
case of a block decision and a command-line utility for managing the system and
synchronizing the database with a server.
It is named Santa because it keeps track of binaries that are naughty or nice.

View File

@@ -1,7 +1,7 @@
# Reporting a Vulnerability
If you believe you have found a security vulnerability, we would appreciate private disclosure
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
so that we can work on a fix before disclosure. Any vulnerabilities reported to us will be
disclosed publicly either when a new version with fixes is released or 90 days has passed,
whichever comes first.

View File

@@ -255,6 +255,16 @@
#pragma mark - GUI Settings
///
/// When silent mode is enabled, Santa will never show notifications for
/// blocked processes.
///
/// This can be a very confusing experience for users, use with caution.
///
/// Defaults to NO.
///
@property(readonly, nonatomic) BOOL enableSilentMode;
///
/// The text to display when opening Santa.app.
/// If unset, the default text will be displayed.

View File

@@ -66,7 +66,8 @@ static NSString *const kMachineOwnerPlistKeyKey = @"MachineOwnerKey";
static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
static NSString *const kAboutText = @"AboutText";
static NSString *const kEnableSilentModeKey = @"EnableSilentMode";
static NSString *const kAboutTextKey = @"AboutText";
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
static NSString *const kEventDetailURLKey = @"EventDetailURL";
static NSString *const kEventDetailTextKey = @"EventDetailText";
@@ -172,7 +173,8 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
kRemountUSBModeKey : array,
kEnablePageZeroProtectionKey : number,
kEnableBadSignatureProtectionKey : number,
kAboutText : string,
kEnableSilentModeKey : string,
kAboutTextKey : string,
kMoreInfoURLKey : string,
kEventDetailURLKey : string,
kEventDetailTextKey : string,
@@ -303,6 +305,10 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingEnableSilentMode {
return [self configStateSet];
}
+ (NSSet *)keyPathsForValuesAffectingAboutText {
return [self configStateSet];
}
@@ -611,8 +617,13 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
return number ? [number boolValue] : NO;
}
- (BOOL)enableSilentMode {
NSNumber *number = self.configState[kEnableSilentModeKey];
return number ? [number boolValue] : NO;
}
- (NSString *)aboutText {
return self.configState[kAboutText];
return self.configState[kAboutTextKey];
}
- (NSURL *)moreInfoURL {
@@ -959,7 +970,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
syncState[kAllowedPathRegexKey] = [syncState[kAllowedPathRegexKey] pattern];
syncState[kBlockedPathRegexKey] = [syncState[kBlockedPathRegexKey] pattern];
[syncState writeToFile:kSyncStateFilePath atomically:YES];
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0644}
[[NSFileManager defaultManager] setAttributes:@{NSFilePosixPermissions : @0600}
ofItemAtPath:kSyncStateFilePath
error:NULL];
}

View File

@@ -1,4 +1,5 @@
load("@build_bazel_rules_apple//apple:macos.bzl", "macos_application")
load("//:helper.bzl", "santa_unit_test")
licenses(["notice"])
@@ -31,6 +32,9 @@ objc_library(
"SNTNotificationManager.m",
"main.m",
],
hdrs = [
"SNTNotificationManager.h",
],
data = [
"Resources/AboutWindow.xib",
"Resources/DeviceMessageWindow.xib",
@@ -49,6 +53,7 @@ objc_library(
"//Source/common:SNTLogging",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTStrengthify",
"//Source/common:SNTSyncConstants",
"//Source/common:SNTXPCControlInterface",
"//Source/common:SNTXPCNotifierInterface",
"@MOLCertificate",
@@ -89,3 +94,26 @@ macos_application(
visibility = ["//:santa_package_group"],
deps = [":SantaGUI_lib"],
)
santa_unit_test(
name = "SNTNotificationManagerTest",
srcs = [
"SNTNotificationManagerTest.m",
],
sdk_frameworks = [
"Cocoa",
],
deps = [
":SantaGUI_lib",
"//Source/common:SNTStoredEvent",
"@OCMock",
],
)
test_suite(
name = "unit_tests",
tests = [
":SNTNotificationManagerTest",
],
visibility = ["//:santa_package_group"],
)

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/gui/SNTAboutWindowController.h"
#import "Source/common/SNTConfigurator.h"

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAccessibleTextField.h"
#import "Source/gui/SNTAccessibleTextField.h"
@implementation SNTAccessibleTextField

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
#import <MOLXPCConnection/MOLXPCConnection.h>
@@ -20,8 +20,8 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAboutWindowController.h"
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTAboutWindowController.h"
#import "Source/gui/SNTNotificationManager.h"
@interface SNTAppDelegate ()
@property SNTAboutWindowController *aboutWindowController;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@class SNTStoredEvent;

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import <MOLCertificate/MOLCertificate.h>
#import <SecurityInterface/SFCertificatePanel.h>
@@ -20,7 +20,7 @@
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@interface SNTBinaryMessageWindowController ()
/// The custom message to display for this event
@@ -139,7 +139,9 @@
- (NSString *)publisherInfo {
MOLCertificate *leafCert = [self.event.signingChain firstObject];
if (leafCert.commonName && leafCert.orgName) {
if ([leafCert.commonName isEqualToString:@"Apple Mac OS Application Signing"]) {
return [NSString stringWithFormat:@"App Store (Team ID: %@)", self.event.teamID];
} else if (leafCert.commonName && leafCert.orgName) {
return [NSString stringWithFormat:@"%@ - %@", leafCert.orgName, leafCert.commonName];
} else if (leafCert.commonName) {
return leafCert.commonName;

View File

@@ -14,7 +14,7 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -12,12 +12,12 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/common/SNTBlockMessage.h"
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTDeviceEvent.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
NS_ASSUME_NONNULL_BEGIN

View File

@@ -12,7 +12,7 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@implementation SNTMessageWindow

View File

@@ -1,6 +1,6 @@
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
#import "Source/santa/SNTMessageWindow.h"
#import "Source/gui/SNTMessageWindow.h"
@implementation SNTMessageWindowController

View File

@@ -15,9 +15,9 @@
#import <Cocoa/Cocoa.h>
#import "Source/common/SNTXPCNotifierInterface.h"
#import "Source/santa/SNTBinaryMessageWindowController.h"
#import "Source/santa/SNTDeviceMessageWindowController.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTBinaryMessageWindowController.h"
#import "Source/gui/SNTDeviceMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
///
/// Keeps track of pending notifications and ensures only one is presented to the user at a time.

View File

@@ -12,8 +12,9 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import "Source/santa/SNTNotificationManager.h"
#import "Source/gui/SNTNotificationManager.h"
#import <MOLCertificate/MOLCertificate.h>
#import <MOLXPCConnection/MOLXPCConnection.h>
#import <UserNotifications/UserNotifications.h>
@@ -23,8 +24,9 @@
#import "Source/common/SNTLogging.h"
#import "Source/common/SNTStoredEvent.h"
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSyncConstants.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTMessageWindowController.h"
#import "Source/gui/SNTMessageWindowController.h"
@interface SNTNotificationManager ()
@@ -112,12 +114,59 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
pendingMsg.delegate = self;
[self.pendingNotifications addObject:pendingMsg];
[self postDistributedNotification:pendingMsg];
if (!self.currentWindowController) {
[self showQueuedWindow];
}
}
// For blocked execution notifications, post an NSDistributedNotificationCenter
// notification with the important details from the stored event. Distributed
// notifications are system-wide broadcasts that can be sent by apps and observed
// from separate processes. This allows users of Santa to write tools that
// perform actions when we block execution, such as trigger management tools or
// display an enterprise-specific UI (which is particularly useful when combined
// with the EnableSilentMode configuration option, to disable Santa's standard UI).
- (void)postDistributedNotification:(SNTMessageWindowController *)pendingMsg {
if (![pendingMsg isKindOfClass:[SNTBinaryMessageWindowController class]]) {
return;
}
SNTBinaryMessageWindowController *wc = (SNTBinaryMessageWindowController *)pendingMsg;
NSDistributedNotificationCenter *dc = [NSDistributedNotificationCenter defaultCenter];
NSMutableArray<NSDictionary *> *signingChain =
[NSMutableArray arrayWithCapacity:wc.event.signingChain.count];
for (MOLCertificate *cert in wc.event.signingChain) {
[signingChain addObject:@{
kCertSHA256 : cert.SHA256 ?: @"",
kCertCN : cert.commonName ?: @"",
kCertOrg : cert.orgName ?: @"",
kCertOU : cert.orgUnit ?: @"",
kCertValidFrom : @([cert.validFrom timeIntervalSince1970]) ?: @0,
kCertValidUntil : @([cert.validUntil timeIntervalSince1970]) ?: @0,
}];
}
NSDictionary *userInfo = @{
kFileSHA256 : wc.event.fileSHA256 ?: @"",
kFilePath : wc.event.filePath ?: @"",
kFileBundleName : wc.event.fileBundleName ?: @"",
kFileBundleID : wc.event.fileBundleID ?: @"",
kFileBundleVersion : wc.event.fileBundleVersion ?: @"",
kFileBundleShortVersionString : wc.event.fileBundleVersionString ?: @"",
kTeamID : wc.event.teamID ?: @"",
kExecutingUser : wc.event.executingUser ?: @"",
kExecutionTime : @([wc.event.occurrenceDate timeIntervalSince1970]) ?: @0,
kPID : wc.event.pid ?: @0,
kPPID : wc.event.ppid ?: @0,
kParentName : wc.event.parentName ?: @"",
kSigningChain : signingChain,
};
[dc postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:userInfo];
}
- (void)showQueuedWindow {
// Notifications arrive on a background thread but UI updates must happen on the main thread.
// This includes making windows.
@@ -208,6 +257,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
#pragma mark SNTNotifierXPC protocol methods
- (void)postClientModeNotification:(SNTClientMode)clientmode {
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
@@ -246,6 +297,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
}
- (void)postRuleSyncNotificationWithCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
UNUserNotificationCenter *un = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
@@ -262,6 +315,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
}
- (void)postBlockNotification:(SNTStoredEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;
@@ -274,6 +329,8 @@ static NSString *const silencedNotificationsKey = @"SilencedNotifications";
}
- (void)postUSBBlockNotification:(SNTDeviceEvent *)event withCustomMessage:(NSString *)message {
if ([SNTConfigurator configurator].enableSilentMode) return;
if (!event) {
LOGI(@"Error: Missing event object in message received from daemon!");
return;

View File

@@ -0,0 +1,76 @@
/// Copyright 2022 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 <MOLCertificate/MOLCertificate.h>
// #import <MOLCodesignChecker/MOLCodesignChecker.h>
#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "Source/gui/SNTNotificationManager.h"
#import "Source/common/SNTStoredEvent.h"
@class SNTBinaryMessageWindowController;
@interface SNTNotificationManager (Testing)
- (void)hashBundleBinariesForEvent:(SNTStoredEvent *)event
withController:(SNTBinaryMessageWindowController *)controller;
@end
@interface SNTNotificationManagerTest : XCTestCase
@end
@implementation SNTNotificationManagerTest
- (void)setUp {
[super setUp];
fclose(stdout);
}
- (void)testPostBlockNotificationSendsDistributedNotification {
SNTStoredEvent *ev = [[SNTStoredEvent alloc] init];
ev.fileSHA256 = @"the-sha256";
ev.filePath = @"/Applications/Safari.app/Contents/MacOS/Safari";
ev.fileBundleName = @"Safari";
ev.fileBundlePath = @"/Applications/Safari.app";
ev.fileBundleID = @"com.apple.Safari";
ev.fileBundleVersion = @"18614.1.14.1.15";
ev.fileBundleVersionString = @"16.0";
ev.executingUser = @"rah";
ev.occurrenceDate = [NSDate dateWithTimeIntervalSince1970:1660221048];
ev.decision = SNTEventStateBlockBinary;
ev.pid = @84156;
ev.ppid = @1;
ev.parentName = @"launchd";
SNTNotificationManager *sut = OCMPartialMock([[SNTNotificationManager alloc] init]);
OCMStub([sut hashBundleBinariesForEvent:OCMOCK_ANY withController:OCMOCK_ANY]).andDo(nil);
id dncMock = OCMClassMock([NSDistributedNotificationCenter class]);
OCMStub([dncMock defaultCenter]).andReturn(dncMock);
[sut postBlockNotification:ev withCustomMessage:@""];
OCMVerify([dncMock postNotificationName:@"com.google.santa.notification.blockedeexecution"
object:@"com.google.santa"
userInfo:[OCMArg checkWithBlock:^BOOL(NSDictionary *userInfo) {
XCTAssertEqualObjects(userInfo[@"file_sha256"], @"the-sha256");
XCTAssertEqualObjects(userInfo[@"pid"], @84156);
XCTAssertEqualObjects(userInfo[@"ppid"], @1);
XCTAssertEqualObjects(userInfo[@"execution_time"], @1660221048);
return YES;
}]]);
}
@end

View File

@@ -17,7 +17,7 @@
#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTXPCControlInterface.h"
#import "Source/santa/SNTAppDelegate.h"
#import "Source/gui/SNTAppDelegate.h"
@interface SNTSystemExtensionDelegate : NSObject <OSSystemExtensionRequestDelegate>
@end

View File

@@ -119,7 +119,7 @@ REGISTER_COMMAND_NAME(@"metrics")
printf(">>> Root Labels\n");
[self prettyPrintRootLabels:normalizedMetrics[@"root_labels"]];
printf("\n");
printf(">>> Metrics \n");
printf(">>> Metrics\n");
[self prettyPrintMetricValues:normalizedMetrics[@"metrics"]];
}

View File

@@ -7,7 +7,7 @@
hostname | testHost
username | testUser
>>> Metrics
>>> Metrics
Metric Name | /santa/rules
Description | Number of rules
Type | SNTMetricTypeGaugeInt64

View File

@@ -76,6 +76,7 @@ santa_unit_test(
deps = [
":SNTMetricFormatTestHelper",
":SNTMetricMonarchJSONFormat",
"@OCMock",
],
)

View File

@@ -100,7 +100,8 @@ const NSString *kKey = @"key";
}
}
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric {
- (NSArray<NSDictionary *> *)encodeDataForMetric:(NSDictionary *)metric
withEndTimestamp:(NSDate *)endTimestamp {
NSMutableArray<NSDictionary *> *monarchMetricData = [[NSMutableArray alloc] init];
for (NSString *fieldName in metric[@"fields"]) {
@@ -112,8 +113,8 @@ const NSString *kKey = @"key";
}
monarchDataEntry[kStartTimestamp] = [self->_dateFormatter stringFromDate:entry[@"created"]];
monarchDataEntry[kEndTimestamp] =
[self->_dateFormatter stringFromDate:entry[@"last_updated"]];
// Monarch wants all the end timestamp to be updated, even if the value does not change.
monarchDataEntry[kEndTimestamp] = [self->_dateFormatter stringFromDate:endTimestamp];
if (!metric[@"type"]) {
LOGE(@"metric type is nil");
@@ -157,10 +158,13 @@ const NSString *kKey = @"key";
}
/**
* formatMetric translates the SNTMetricSet metric entries into those consumable by Monarch.
* formatMetric translates the SNTMetricSet metric entries into those consumable
* by Monarch.
**/
- (NSDictionary *)formatMetric:(NSString *)name withMetric:(NSDictionary *)metric {
- (NSDictionary *)formatMetric:(NSString *)name
withValue:(NSDictionary *)metric
andEndtimestamp:(NSDate *)endTimestamp {
NSMutableDictionary *monarchMetric = [[NSMutableDictionary alloc] init];
monarchMetric[kMetricName] = name;
@@ -175,7 +179,7 @@ const NSString *kKey = @"key";
}
[self encodeValueAndStreamKindFor:name withMetric:metric into:monarchMetric];
monarchMetric[@"data"] = [self encodeDataForMetric:metric];
monarchMetric[@"data"] = [self encodeDataForMetric:metric withEndTimestamp:endTimestamp];
return monarchMetric;
}
@@ -185,10 +189,12 @@ const NSString *kKey = @"key";
**/
- (NSDictionary *)normalize:(NSDictionary *)metrics {
NSMutableArray<NSDictionary *> *monarchMetrics = [[NSMutableArray alloc] init];
NSDate *endTimestamp = [NSDate date];
for (NSString *metricName in metrics[@"metrics"]) {
[monarchMetrics addObject:[self formatMetric:metricName
withMetric:metrics[@"metrics"][metricName]]];
withValue:metrics[@"metrics"][metricName]
andEndtimestamp:endTimestamp]];
}
NSMutableArray<NSDictionary *> *rootLabels = [[NSMutableArray alloc] init];

View File

@@ -1,5 +1,7 @@
#import <XCTest/XCTest.h>
#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>
#import "Source/santametricservice/Formats/SNTMetricFormatTestHelper.h"
#import "Source/santametricservice/Formats/SNTMetricMonarchJSONFormat.h"
@@ -9,6 +11,10 @@
@implementation SNTMetricMonarchJSONFormatTest
- (void)testMetricsConversionToJSON {
id classMock = OCMClassMock([NSDate class]);
OCMStub([classMock date])
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
NSDictionary *validMetricsDict = [SNTMetricFormatTestHelper createValidMetricsDictionary];
SNTMetricMonarchJSONFormat *formatter = [[SNTMetricMonarchJSONFormat alloc] init];
NSError *err = nil;

View File

@@ -26,7 +26,7 @@
"data" : [
{
"int64Value" : 1,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -37,7 +37,7 @@
},
{
"int64Value" : 3,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -54,7 +54,7 @@
"data" : [
{
"int64Value" : 123456789,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
],
@@ -73,7 +73,7 @@
"data" : [
{
"int64Value" : 1,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -84,7 +84,7 @@
},
{
"int64Value" : 2,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"field" : [
{
"name" : "rule_type",
@@ -104,7 +104,7 @@
"data" : [
{
"boolValue" : true,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]
@@ -116,7 +116,7 @@
"data" : [
{
"int64Value" : 1250999830800,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]
@@ -127,7 +127,7 @@
"data" : [
{
"int64Value" : 987654321,
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
],
@@ -141,7 +141,7 @@
"data" : [
{
"stringValue" : "20210809.0.1",
"endTimestamp" : "2021-09-16T21:07:34.826Z",
"endTimestamp" : "2021-09-16T21:08:10.000Z",
"startTimestamp" : "2021-09-16T21:07:34.826Z"
}
]

View File

@@ -164,6 +164,10 @@ NSDictionary *validMetricsDict = nil;
}
- (void)testWritingMonarchJSONToAFile {
id classMock = OCMClassMock([NSDate class]);
OCMStub([classMock date])
.andReturn([NSDate dateWithTimeIntervalSince1970:1631826490]); // 2021-09-16 21:08:10Z
OCMStub([self.mockConfigurator exportMetrics]).andReturn(YES);
OCMStub([self.mockConfigurator metricFormat]).andReturn(SNTMetricFormatTypeMonarchJSON);
OCMStub([self.mockConfigurator metricURL]).andReturn(self.jsonURL);

View File

@@ -120,6 +120,17 @@ santa_unit_test(
],
)
santa_unit_test(
name = "NSDataZlibTest",
srcs = [
"NSData+Zlib.h",
"NSData+Zlib.m",
"NSDataZlibTest.m",
],
resources = glob(["testdata/sync_preflight_basic.*"]),
sdk_dylibs = ["libz"],
)
objc_library(
name = "broadcaster_lib",
srcs = ["SNTSyncBroadcaster.m"],
@@ -171,6 +182,7 @@ macos_command_line_application(
test_suite(
name = "unit_tests",
tests = [
":NSDataZlibTest",
":SNTSyncTest",
],
visibility = ["//:santa_package_group"],

View File

@@ -0,0 +1,54 @@
/// Copyright 2022 Google Inc. All rights reserved.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
#import <XCTest/XCTest.h>
#import "Source/santasyncservice/NSData+Zlib.h"
@interface NSDataZlibTest : XCTestCase
@end
@implementation NSDataZlibTest
- (void)setUp {
[super setUp];
}
- (NSData *)dataFromFixture:(NSString *)file {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:file ofType:nil];
XCTAssertNotNil(path, @"failed to load testdata: %@", file);
return [NSData dataWithContentsOfFile:path];
}
- (void)testZlibCompressed {
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
NSData *want = [self dataFromFixture:@"sync_preflight_basic.z"];
XCTAssertEqualObjects([sut zlibCompressed], want);
}
- (void)testGzipCompressed {
NSData *sut = [self dataFromFixture:@"sync_preflight_basic.json"];
NSData *want = [self dataFromFixture:@"sync_preflight_basic.gz"];
XCTAssertEqualObjects([sut gzipCompressed], want);
}
- (void)testCompressEmpty {
NSData *sut = [NSData data];
XCTAssertNil([sut zlibCompressed]);
XCTAssertNil([sut gzipCompressed]);
};
@end

View File

@@ -468,4 +468,36 @@
OCMVerify([self.daemonConnRop databaseRuleAddRules:rules cleanSlate:NO reply:OCMOCK_ANY]);
}
#pragma mark - SNTSyncPostflight Tests
- (void)testPostflightBasicResponse {
[self setupDefaultDaemonConnResponses];
SNTSyncPostflight *sut = [[SNTSyncPostflight alloc] initWithState:self.syncState];
[self stubRequestBody:nil response:nil error:nil validateBlock:nil];
XCTAssertTrue([sut sync]);
OCMVerify([self.daemonConnRop setFullSyncLastSuccess:OCMOCK_ANY reply:OCMOCK_ANY]);
self.syncState.clientMode = SNTClientModeMonitor;
XCTAssertTrue([sut sync]);
OCMVerify([self.daemonConnRop setClientMode:SNTClientModeMonitor reply:OCMOCK_ANY]);
self.syncState.cleanSync = YES;
XCTAssertTrue([sut sync]);
OCMVerify([self.daemonConnRop setSyncCleanRequired:NO reply:OCMOCK_ANY]);
self.syncState.allowlistRegex = @"^horse$";
self.syncState.blocklistRegex = @"^donkey$";
XCTAssertTrue([sut sync]);
OCMVerify([self.daemonConnRop setAllowedPathRegex:@"^horse$" reply:OCMOCK_ANY]);
OCMVerify([self.daemonConnRop setBlockedPathRegex:@"^donkey$" reply:OCMOCK_ANY]);
self.syncState.blockUSBMount = YES;
self.syncState.remountUSBMode = @[ @"readonly" ];
XCTAssertTrue([sut sync]);
OCMVerify([self.daemonConnRop setBlockUSBMount:YES reply:OCMOCK_ANY]);
OCMVerify([self.daemonConnRop setRemountUSBMode:@[ @"readonly" ] reply:OCMOCK_ANY]);
}
@end

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,10 @@ function main() {
find $GIT_ROOT \( -name "*.m" -o -name "*.h" -name "*.mm" \) -exec clang-format --Werror --dry-run {} \+
err="$(( $err | $? ))"
go get github.com/bazelbuild/buildtools/buildifier
! git grep -EIn $'[ \t]+$'
err="$(( $err | $? ))"
go install github.com/bazelbuild/buildtools/buildifier@latest
~/go/bin/buildifier --lint=warn -r $GIT_ROOT
err="$(( $err | $? ))"
return $err

View File

@@ -5,7 +5,7 @@ parent: Concepts
# Rules
Rules provide the primary evaluation mechanism for allowing and blocking
binaries with Santa on macOS. There are three types of rules: binary,
binaries with Santa on macOS. There are three types of rules: binary,
certificate, and TeamID.
##### Binary Rules
@@ -67,8 +67,8 @@ SHA-256 hash.
##### Apple Developer Team ID Rules
The Apple Developer Program Team ID is a 10-character identifier issued by Apple
and tied to developer accounts/organizations. This is distinct from Certificates,
as a single developer account can and frequently will request/rotate between
multiple different signing certificates and entitlements. This is an even more
as a single developer account can and frequently will request/rotate between
multiple different signing certificates and entitlements. This is an even more
powerful rule with broader reach than individual certificate rules.
##### Rule Evaluation

View File

@@ -20,6 +20,8 @@
<integer>1</integer>
<key>EnablePageZeroProtection</key>
<false/>
<key>EnableSilentMode</key>
<false/>
<key>EventDetailText</key>
<string>Open sync server</string>
<key>EventDetailURL</key>
@@ -40,6 +42,36 @@
<string>Entering Monitor mode&lt;br/&gt;Please be careful!</string>
<key>MoreInfoURL</key>
<string>https://sync-server-hostname/moreinfo</string>
<key>StaticRules</key>
<array>
<dict>
<!-- Always allow files signed by Google LLC -->
<key>identifier</key>
<string>EQHXZ8M8AV</string>
<key>policy</key>
<string>ALLOWLIST</string>
<key>rule_type</key>
<string>TEAMID</string>
</dict>
<dict>
<!-- Always allow files signed by "Internal Tools Certificate" -->
<key>identifier</key>
<string>b2617611fb6c008bfe9e05b7a633d4f21c403a0a1a88b514a04c3e5e111be025</string>
<key>policy</key>
<string>ALLOWLIST</string>
<key>rule_type</key>
<string>CERTIFICATE</string>
</dict>
<dict>
<!-- Always BLOCK the BundleExample.app binary in Santa's testdata files, for testing -->
<key>identifier</key>
<string>b7c1e3fd640c5f211c89b02c2c6122f78ce322aa5c56eb0bb54bc422a8f8b670</string>
<key>policy</key>
<string>BLOCKLIST</string>
<key>rule_type</key>
<string>BINARY</string>
</dict>
</array>
<key>SyncBaseURL</key>
<string>https://sync-server-hostname/api/santa/</string>
<key>UnknownBlockMessage</key>

View File

@@ -1,7 +1,7 @@
---
title: Configuration
parent: Deployment
nav_order: 2
nav_order: 3
---
# Configuration
@@ -30,6 +30,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
| EnableBadSignatureProtection | Bool | Enable bad signature protection, defaults to NO. If this flag is set to YES, binaries with a bad signing chain will be blocked even in MONITOR mode, **unless** the binary is allowed by an explicit rule. |
| EnablePageZeroProtection | Bool | Enable `__PAGEZERO` protection, defaults to YES. If this flag is set to YES, 32-bit binaries that are missing the `__PAGEZERO` segment will be blocked even in MONITOR mode, **unless** the binary is allowed by an explicit rule. |
| EnableSysxCache | Bool | Enables a secondary cache that ensures better performance when multiple EndpointSecurity system extensions are installed. Defaults to YES in 2021.8, defaults to NO in earlier versions. |
| EnableSilentMode | Bool | If true, Santa will not post any GUI notifications. This can be a very confusing experience for users, use with caution. Defaults to NO. |
| AboutText | String | The text to display when the user opens Santa.app. If unset, the default text will be displayed. |
| MoreInfoURL | String | The URL to open when the user clicks "More Info..." when opening Santa.app. If unset, the button will not be displayed. |
| EventDetailURL | String | See the [EventDetailURL](#eventdetailurl) section below. |
@@ -215,7 +216,7 @@ ways to install configuration profiles:
| enable\_bundles\* | Bool | If set to `True` the bundle scanning feature is enabled. Defaults to `False`. |
| enable\_transitive\_rules | Bool | If set to `True` the transitive rule feature is enabled. Defaults to `False`. |
| enable\_all\_event\_upload | Bool | If set to `True` the client will upload events for all executions, including those that are explicitly allowed. |
| block\_usb\_mass\_storage | Bool | If set to 'True' blocking USB Mass storage feature is enabled. Defaults to `False`. |
| block\_usb\_mass\_storage | Bool | If set to 'True' blocking USB Mass storage feature is enabled. Defaults to `False`. |
| remount\_usb\_mode | Array | Array of strings for arguments to pass to mount -o (any of "rdonly", "noexec", "nosuid", "nobrowse", "noowners", "nodev", "async", "-j"). when forcibly remounting devices. No default. |

View File

@@ -6,7 +6,7 @@ nav_order: 1
# Getting Started
This page shows you the process to get started with your deployment of Santa.
This page shows you the process to get started with your deployment of Santa.
**Note:** You can combine each of the profiles listed in the following steps into a single profile containing the different payloads: configuration, TCC, system extension, and notifications.
@@ -14,13 +14,13 @@ This page shows you the process to get started with your deployment of Santa.
1. Create and install your Santa configuration profile to customize your deployment of Santa. See [Configuration](configuration.md) for a reference list of the available options and an [example profile](https://github.com/google/santa/blob/main/docs/deployment/com.google.santa.example.mobileconfig).
1. Install the TCC and system extension configuration profiles:
1. Install the TCC and system extension configuration profiles:
- The TCC profile provides Santa the access it requires to read files anywhere on disk. See an [example TCC profile](https://github.com/google/santa/blob/main/docs/deployment/tcc.configuration-profile-policy.santa.example.mobileconfig).
- The system extension profile allows Santa to run without approval from the user. See an [example system extension profile](https://github.com/google/santa/blob/main/docs/deployment/system-extension-policy.santa.example.mobileconfig).
1. (Optional) Customize and install the notification settings profile. This allows you to set up notifications to alert when Santa is switching [modes](../concepts/mode.md). See an [example notification settings profile](https://github.com/google/santa/blob/main/docs/deployment/notificationsettings.santa.example.mobileconfig).
1. (Optional) Customize and install the notification settings profile. This allows you to set up notifications to alert when Santa is switching [modes](../concepts/mode.md). See an [example notification settings profile](https://github.com/google/santa/blob/main/docs/deployment/notificationsettings.santa.example.mobileconfig).
The notifications modified through this profile are different to the main Santa GUI pop-ups. To configure the [Santa GUI](../binaries/santa-gui.md) notifications, use the [configuration profile](configuration.md) (in step 2).
1. Install the latest Santa package from [GitHub](https://github.com/google/santa/releases) (where you can also find release notes). The package is distributed as a `PKG` wrapped inside a `DMG`, both of which are properly signed and can be validated.
1. Install the latest Santa package from [GitHub](https://github.com/google/santa/releases) (where you can also find release notes). The package is distributed as a `PKG` wrapped inside a `DMG`, both of which are properly signed and can be validated.

View File

@@ -0,0 +1,26 @@
---
title: Recommended Rollout Strategy
parent: Deployment
nav_order: 2
---
# Recommended Rollout Strategy
We recommend the following strategy to rollout Santa to an existing fleet of machines. This approach can help avoid too much disruption during the process.
As part of this strategy, we recommend using a sync server with Santa. For a list of open-source sync servers, see the [Sync Servers](sync-servers.md) page.
1. Configure the sync server to assign all clients to `MONITOR` mode and ensure that [event](../concepts/events.md) collection is working. See [Sync Server Provided Configuration](configuration.md#sync-server-provided-configuration) for a list of the configuration options.
1. Deploy to all hosts. Ideally, the deployment is a slow process over a reasonable period of time. That is, an incremental deployment to small groups of machines depending on your fleet size with enough time to monitor and manage the deployment. A slower deployment will allow you to catch incompatibilities early in the rollout before a full deployment is complete.
1. Leave the client in [`MONITOR` mode](../concepts/mode.md) for a defined period of time to allow event collection to take place.
1. Analyze the incoming events uploaded by the client in order to determine which applications you need to allow list.
1. Create allow rules as appropriate based on the previous analysis. See [Rules](../concepts/rules.md) for more explanation and examples of setting up rules.
1. Continue to analyze as you deploy the allow rules. The aim is to ensure that the number of incoming events is manageable.
1. Slowly move clients to `LOCKDOWN` mode. If possible, use the analysis to guide which hosts you move. For example, if a host is not uploading any block events, it's a good candidate for switching modes.

View File

@@ -1,12 +1,12 @@
---
title: Sync Servers
parent: Deployment
nav_order: 3
nav_order: 4
---
# Sync Servers
Santa's [SyncBaseURL](configuration.md#sync-base-url) configuration flag allows you to synchronize with a management server, which uploads events that have occurred on the machine and downloads new rules.
Santa's [SyncBaseURL](configuration.md#sync-base-url) configuration flag allows you to synchronize with a management server, which uploads events that have occurred on the machine and downloads new rules.
There are several open-source servers you can sync with:
@@ -17,4 +17,4 @@ There are several open-source servers you can sync with:
Alternatively, `santactl` can configure rules locally without a sync server.
See the [Syncing Overview](../introduction/syncing-overview.md) page for an explanation of how syncing works in Santa.
See the [Syncing Overview](../introduction/syncing-overview.md) page for an explanation of how syncing works in Santa.

View File

@@ -1,17 +1,17 @@
---
title: Troubleshooting
parent: Deployment
nav_order: 4
nav_order: 5
---
# Troubleshooting
As kernel extensions have been considered deprecated for several OS releases,
this page will cover troublshooting the system extension and related topics.
this page will cover troublshooting the system extension and related topics.
## Confirming Status
While there's an entire page on [santactl](../binaries/santactl.md), it's one of the best ways to start
While there's an entire page on [santactl](../binaries/santactl.md), it's one of the best ways to start
determining the cause of an issue:
```sh
@@ -58,4 +58,4 @@ question as described on the [santactl](../binaries/santactl.md) page.
Depending on the presence or implementation details of a sync server, there may
be queues and a process for allowing binaries or updated developer certificates.
Events may also be observable from the server
Events may also be observable from the server

View File

@@ -0,0 +1,430 @@
---
title: Sync Protocol
parent: Development
---
# Summary
This document describes the protocol between Santa and the sync server, also known as the sync protocol. Implementors should be able to use this to create their own sync servers.
## Background
Santa can be run and configured with a sync server. This allows an admin to
easily configure and sync rules across a fleet of macOS systems. In addition to
distributing rules, using a sync server enables an admin to override some local
configuration options e.g. `LOCKDOWN` mode on both a fleet-wide and
per-host basis.
# Protocol Overview
The sync protocol is an HTTP/JSON based protocol. As such it is
assumed that both the server and client add `Content-Type` headers are set to
`application/json`.
The sync protocol is client initiated and consists of 4 request-response
transactions called stages, `preflight`, `eventupload`, `ruledownload`, and `postflight`.
A sync may consist of all 4 stages, just the `eventupload` stage or just the `ruledownload` stage.
| Stage | What it Does |
|---|---|
| **Preflight** | Report current Santa settings and machine attributes to sync server & retrieve configuration settings |
| **Event Upload** | Report new blockable events to the sync server |
| **Rule Download** | Retrieves new rules |
| **Postflight** | Reports stats |
If the server returns an HTTP status other than `200` for any stage than the sync stops and the next stage is not performed.
At a high level this looks like the following sequence:
```mermaid
sequenceDiagram
client ->> server: POST /preflight/<machine_id>
server -->> client: preflight response (200)
loop until all events are uploaded
client ->> server: POST /eventupload/<machine_id>
server -->> client: eventupload response (200)
end
loop until all rules are downloaded
client ->> server: GET /ruledownload/<machine_id>
server --> client: ruledownload response (200)
end
client ->> server: POST /postflight/<machine_id> request
server -->> client: postflight response (200)
```
Where `<machine_id>` is a unique string identifier for the client. By default
Santa uses the hardware UUID. It may also be set using the [MachineID, MachineIDPlist, and MachineIDKey options](../deployment/configuration.md) in the
configuration.
## Authentication
The protocol expects the client to authenticate the server via SSL/TLS. Additionally, a sync server may support client certificates and use mutual TLS.
## Stages
All URLs are of the form `/<stage_name>/<machine_id>`, e.g. the preflight URL is `/preflight/<machine_id>`.
### Preflight
The preflight stage is used by the client to report host information to the sync
server and to retrieve a limited set of configuration settings from the server.
These configuration options override the initial values set from the application
configuration profile.
This follows the following transaction:
```mermaid
sequenceDiagram
client ->> server: POST /preflight/<machine_id>
server -->> client: preflight response
```
#### `preflight` Request
The request consists of the following JSON keys:
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| serial_num | YES | string | The macOS serial number from IOKit `kIOPlatformSerialNumberKey` | "XXXZ30URLVDQ" |
| hostname | YES | string | The FQDN hostname of the client | markowsky.example.com |
| os_version | YES | string | The OS version of the client from /System/Library/CoreServices/SystemVersion.plist | 12.4 |
| os_build | YES | string | The OS build from /System/Library/CoreServices/SystemVersion.plist | "21F5048e" |
| model_identifier | NO | string | The model of the macOS system | |
| santa_version | YES | string | 2022.3 |
| primary_user | YES | string | The username | markowsky |
| binary_rule_count | NO | int | Number of binary allow / deny rules the client has at time of sync| 1000 |
| certificate_rule_count | NO | int | Number of certificate allow / deny rules the client has at time of sync | 3400 |
| compiler_rule_count | NO | int | Number of compiler rules the client has time of sync |
| transitive_rule_count | NO | int | Number of transitive rules the client has at the time of sync |
| teamid_rule_count | NO | int | Number of TeamID rules the client has at the time of sync | 24 |
| client_mode | YES | string | the mode the client is operating in, either "LOCKDOWN" or "MONITOR" | LOCKDOWN |
| clean_sync | NO | bool | the client has requested that a clean sync of its rules from the server. | true |
### Example preflight request JSON Payload:
```json
{
"compiler_rule_count" : 14,
"client_mode" : "MONITOR",
"santa_version" : "2022.6",
"serial_num" : "XXXZ30URLVDQ",
"binary_rule_count" : 43676,
"hostname" : "markowsky.example.com",
"primary_user" : "markowsky",
"certificate_rule_count" : 2364,
"teamid_rule_count" : 0,
"os_build" : "21F5048e",
"transitive_rule_count" : 0,
"os_version" : "12.4",
"model_identifier" : "MacBookPro15,1",
"clean_sync": true,
}
```
#### `preflight` Response
If all of the data is well formed, the server responds with an HTTP status code of 200 and provides a JSON response.
The JSON object has the following keys:
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| enable_bundles | NO | boolean | Enabled bundle scanning | true |
| enable_transitive_rules | NO | boolean | Whether or not to enable transitive allowlisting | true |
| batch_size | YES | integer | Number of events to upload at a time | 128 |
| full_sync_interval | YES | integer | Number of seconds between full syncs | 600 |
| client_mode | YES | string | Operating mode to set for the client | either "MONITOR" or "LOCKDOWN" |
| allowed_path_regex | YES | list of strings | List of regular expressions to allow a binary to execute from a path | ["/Users/markowsk/foo/.*"] |
| blocked_path_regex | YES | list of strings | List of regular expressions to block a binary from executing by path | ["/tmp/"] |
| block_usb_mount | NO | boolean | Block USB mass storage devices | true |
| remount_usb_mode | NO | string | Force USB mass storage devices to be remounted with the following permissions (see [configuration](../deployment/configuration.md)) | |
| clean_sync | YES | boolean | Whether or not the rules should be dropped and synced entirely from the server | true |
#### Example Preflight Response Payload
```json
{
"batch_size": 100,
"client_mode": "MONITOR",
"allowed_path_regex": null,
"blocked_path_regex": null,
"clean_sync": false,
"bundles_enabled": true,
"enable_transitive_rules": false
}
```
### EventUpload
After the `preflight` stage has completed the client then initiates the
`eventupload` stage if it has any events to upload. If there aren't any events
this stage is skipped.
It consists of the following transaction, that may be repeated until all events are uploaded.
```mermaid
sequenceDiagram
client ->> server: POST /eventupload/<machine_id>
server -->> client: eventupload response
```
#### `eventupload` Request
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| events | YES | list of event objects | list of events to upload | see example payload |
##### Event Objects
:information_source: Events are explained in more depth in the [Events page](../concepts/events.md).
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| file_sha256 | YES | string | sha256 of the executable that was executed | "fc6679da622c3ff38933220b8e73c7322ecdc94b4570c50ecab0da311b292682" |
| file_path | YES | string | Absolute file path to the executable that was blocked | "/tmp/foo" |
| file_name | YES | string | Command portion of the path of the blocked executable | "foo" |
| executing_user | YES | string | Username that executed the binary | "markowsky" |
| execution_time | YES | int | Unix timestamp of when the execution occured | 23344234232 |
| loggedin_users | NO | List of strings | list of usernames logged in according to utmp | ["markowsky"] |
| current_sessions | YES | List of strings | list of user sessions | ["markowsky@console", "markowsky@ttys000"] |
| decision | YES | string | The decision Santa made for this binary, BUNDLE_BINARY is used to preemptively report binaries in a bundle. **Must be one of the examples**.| "ALLOW_BINARY", "ALLOW_CERTIFICATE", "ALLOW_SCOPE", "ALLOW_TEAMID", "ALLOW_UNKNOWN", "BLOCK_BINARY", "BLOCK_CERTIFICATE", "BLOCK_SCOPE", "BLOCK_TEAMID", "BLOCK_UNKNOWN", "BUNDLE_BINARY" |
| file_bundle_id | NO | string | The executable's containing bundle's identifier as specified in the Info.plist | "com.apple.safari" |
| file_bundle_path | NO | string | The path that the bundle resids in | /Applications/Santa.app |
| file_bundle_executable_rel_path | NO | string | The relative path of the binary within the Bundle | "Contents/MacOS/AppName" |
| file_bundle_name | NO | string | The bundle's display name. | "Google Chrome" |
| file_bundle_version | NO | string | The bundle version string | "9999.1.1" |
| file_bundle_version_string | NO | string | Bundle short version string | "2.3.4" |
| file_bundle_hash | NO | string | SHA256 hash of all executables in the bundle | "7466e3687f540bcb7792c6d14d5a186667dbe18a85021857b42effe9f0370805" |
| file_bundle_hash_millis | NO | int | The time in milliseconds it took to find all of the binaries, hash and produce the bundle_hash | 1234775 |
| pid | YES | int | Process id of the executable that was blocked | 1234 |
| ppid | YES | int | Parent process id of the executable that was blocked | 456 |
| parent_name | YES | Parent process short command name of the executable that was blocked | "bar" |
| quarantine_data_url | NO | string | The actual URL of the quarantined item from the quarantine database that this binary was downloaded from | https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg |
| quarantine_referer_url | NO | string | Referring URL that lead to the binary being downloaded if known. | https://www.google.com/chrome/downloads/ |
| quarantine_timestamp | NO | int | Unix Timestamp of when the binary was downloaded or 0 if not quarantined | 0 |
| quarantine_agent_bundle_id | NO | string | The bundle ID of the software that quarantined the binary | "com.apple.Safari" |
| signing_chain | NO | list of signing chain objects | Certs used to code sign the executable | See next section |
##### Signing Chain Objects
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| sha256 | YES | string | sha256 of the certificate used to sign | "7ae80b9ab38af0c63a9a81765f434d9a7cd8f720eb6037ef303de39d779bc258" |
| cn | YES | string | Common Name field of the certificate used to sign | "Apple Worldwide Developer Relations Certification Authority" |
| org | YES | string | Org field of the certificate used to sign | "Google LLC" |
| ou | YES | string | OU field of the certificate used to sign | "G3" |
| valid_from | YES | int | Unix timestamp of when the cert was issued | 1647447514 |
| valid_until | YES | int | Unix timestamp of when the cert expires | 1678983513 |
##### `eventupload` Request Example Payload
```json
{
"events": [{
"file_path": "\/Applications\/Santa.app\/Contents\/MacOS",
"file_bundle_version": "9999.1.1",
"parent_name": "launchd",
"logged_in_users": [
"markowsky"
],
"quarantine_timestamp": 0,
"signing_chain": [{
"cn": "Apple Development: Google Development (XXXXXXXXXX)",
"valid_until": 1678983513,
"org": "Google LLC",
"valid_from": 1647447514,
"ou": "XXXXXXXXXX",
"sha256": "7ae80b9ab38af0c63a9a81765f434d9a7cd8f720eb6037ef303de39d779bc258"
},
{
"cn": "Apple Worldwide Developer Relations Certification Authority",
"valid_until": 1897776000,
"org": "Apple Inc.",
"valid_from": 1582136027,
"ou": "G3",
"sha256": "dcf21878c77f4198e4b4614f03d696d89c66c66008d4244e1b99161aac91601f"
},
{
"cn": "Apple Root CA",
"valid_until": 2054670036,
"org": "Apple Inc.",
"valid_from": 1146001236,
"ou": "Apple Certification Authority",
"sha256": "b0b1730ecbc7ff4505142c49f1295e6eda6bcaed7e2c68c5be91b5a11001f024"
}
],
"file_bundle_name": "santasyncservice",
"executing_user": "root",
"ppid": 1,
"file_bundle_path": "/Applications/Santa.app",
"file_name": "santasyncservice",
"execution_time": 1657764366.475035,
"file_sha256": "8621d92262aef379d3cfe9e099f287be5b996a281995b5cc64932f7d62f3dc85",
"decision": "ALLOW_BINARY",
"file_bundle_id": "com.google.santa.syncservice",
"file_bundle_version_string": "9999.1.1",
"pid": 2595,
"current_sessions": [
"markowsky@console",
"markowsky@ttys000",
"markowsky@ttys001",
"markowsky@ttys003"
],
"team_id": "XXXXXXXXXX"
}]
}
```
#### `eventupload` Response
The server should reply with an HTTP 200 if the request was successfully received and processed.
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| event_upload_bundle_binaries | NO | list of strings | An array of bundle hashes that the sync server needs to be uploaded | ["8621d92262aef379d3cfe9e099f287be5b996a281995b5cc64932f7d62f3dc85"] |
##### `eventupload` Response Example Payload
```json
{
"event_upload_bundle_binaries": ["8621d92262aef379d3cfe9e099f287be5b996a281995b5cc64932f7d62f3dc85"]
}
```
### Rule Download
After events have been uploaded to the sync server, the `ruledownload` stage begins in a full sync.
Like the previous stages this is a simple HTTP request response cycle like so:
```mermaid
sequenceDiagram
client ->> server: POST /ruledownload/<machine_id>
server -->> client: ruledownload response
```
If either the client or server requested a clean sync in the `preflight` stage, the client is expected to purge its existing rules and download new rules from the sync server.
If a clean sync was not requested by either the client or the sync service, then the sync service should only send new rules seen since the last time the client synced.
Santa applies rules idempoently and is designed to receive rules multiple times without issue.
#### `ruledownload` Request
This stage is initiated via an HTTP POST request to the URL `/ruledownload/<machine_id>`
| Key | Required | Type | Meaning |
|---|---|---|---|
| cursor | NO | string | a field used by the sync server to indicate where the next batch of rules should start |
##### `ruledownload` Request Example Payload
On the first request the payload is an empty dictionary
```json
{}
```
In the `ruledownload` response a special field called `cursor` will exist if there are more rules to download from server. The value and form of this field is left to the sync server implementor. It is expected to be used to track where the next batch of rules should start.
On subsequent requests to the server the `cursor` field is sent with the value from the previous response e.g.
```json
{"cursor":"CpgBChcKCnVwZGF0ZWRfZHQSCQjh94a58uLlAhJ5ahVzfmdvb2dsZS5jb206YXBwbm90aHJyYAsSCUJsb2NrYWJsZSJAMTczOThkYWQzZDAxZGRmYzllMmEwYjBiMWQxYzQyMjY1OWM2ZjA3YmU1MmY3ZjQ1OTVmNDNlZjRhZWI5MGI4YQwLEgRSdWxlGICA8MvA0tIJDBgAIAA="}
```
#### `ruledownload` Response
When a `ruledownload` request is received, the sync server responds with a JSON object
containing a list of rule objects and a cursor so the client can resume
downloading if the rules need to be downloaded in multiple batches.
| Key | Required | Type | Meaning |
|---|---|---|---|
| cursor | NO | string | Used to continue a rule download in a future request |
| rules | YES | list of Rule objects | List of rule objects (see next section). |
##### Rules Objects
| Key | Required | Type | Meaning | Example Value |
|---|---|---|---|---|
| identifier | YES | string | The attribute of the binary the rule should match on e.g. the team ID of a binary or sha256 hash value | "ff2a7daa4c25cbd5b057e4471c6a22aba7d154dadfb5cce139c37cf795f41c9c" |
| policy | YES | string | identifies the action to perform in response to the rule matching must be one of the examples. | "ALLOWLIST","ALLOWLIST_COMPILER", "BLOCKLIST", "REMOVE", "SILENT_BLOCKLIST" |
| rule_type | YES | string | identifies the type of rule must be one of he examples | "BINARY", "CERTIFICATE", "TEAMID" |
| custom_msg | NO | string | A custom message to display when the rule matches | "Hello" |
| creation_time | NO | float64 | time the rule was created | 1573543803.349378 |
| file_bundle_binary_count | NO | integer | The number of binaries in a bundle | 13 |
| file_bundle_hash | NO | string | The SHA256 of all binaries in a bundle. | "7466e3687f540bcb7792c6d14d5a186667dbe18a85021857b42effe9f0370805" |
##### Example `ruledownload` Response Payload
```json
{
"rules": [{
"identifier": "ff2a7daa4c25cbd5b057e4471c6a22aba7d154dadfb5cce139c37cf795f41c9c",
"rule_type": "CERTIFICATE",
"policy": "BLOCKLIST",
"custom_msg": "",
"creation_time": 1573543803.349378
}, {
"identifier": "233e741538e1cdf4835b3f2662e372cf0c2694b7e20b4e4663559c7fb0a9f234",
"rule_type": "BINARY",
"policy": "ALLOWLIST",
"custom_msg": "",
"creation_time": 1573572118.380034,
"file_bundle_binary_count": 13,
"file_bundle_hash": "7466e3687f540bcb7792c6d14d5a186667dbe18a85021857b42effe9f0370805"
},
{
"identifier": "EQHXZ8M8AV",
"rule_type": "TEAMID",
"policy": "ALLOWLIST",
"custom_msg": "Allow Software Google's Team ID",
"creation_time": 1576623399.151607,
"file_bundle_binary_count": 7,
"file_bundle_hash": "e4736dd3a731f5f71850984175c0ec54dcde06021af18f476eb480c707fbecda"
}],
"cursor": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXzfmdvb2dsZS5jb206YXBwbm90aHJyYAsSCUJsb2NrYWJsZSJANGYyYTA2MjY1ZjRiODQ2M2Y2YjI0MmNiZTMwMTNkMGZhNjlkNDUxNmI4OTU3Y2I3ZDAxZDcyMTJkM2NhZmZiNAwLEgRSdWxlGICA8Kehk9MKDBgAIAA="
}
```
### Postflight
The postflight stage is used for the client to inform the sync server that it has successfully finished syncing. After sending the request, the client is expected to update its internal state applying any configuration changes sent by the sync server during the preflight step.
This stage uses an HTTP POST request to the url `/postflight/<machine_id>`
```mermaid
sequenceDiagram
client ->> server: POST /postflight/<machine_id>
server -->> client: postflight response
```
#### `postflight` Request
The request is empty and should not be parsed by the sync server.
#### `postflight` Response
The server should reply with an HTTP 200 if the request was successfully received and processed. Any message body is ignored by the client.
<div id="mermaidjs-code" style="visibility: hidden">
<script src="https://unpkg.com/mermaid@9.1.3/dist/mermaid.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
mermaid.initialize({
startOnLoad:true,
theme: "forest",
});
window.mermaid.init(undefined, document.querySelectorAll('.language-mermaid'));
});
</script>
</div>

View File

@@ -9,7 +9,9 @@ Santa is a binary authorization system for macOS. It consists of a system extens
It is named Santa because it keeps track of binaries that are naughty or nice.
## Features
The project and the latest release is available on [**GitHub**](https://github.com/google/santa).
## Features
* [**Multiple modes:**](concepts/mode.md) In the default `MONITOR` mode, all binaries except those marked as blocked will be allowed to run, whilst being logged and recorded in the events database. In `LOCKDOWN` mode, only listed binaries are allowed to run.
* [**Event logging:**](concepts/events.md) All binary launches are logged. When in either mode, all unknown or denied binaries are stored in the database to enable later aggregation.
@@ -59,4 +61,4 @@ The following pages describe the main components that make up Santa:
### Development
* [Building Santa](development/building.md): How to build and load Santa for testing on a development machine.
* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa.
* [Contributing](development/contributing.md): How to contribute a bug fix or new feature to Santa.

View File

@@ -7,7 +7,7 @@ redirect_from:
# Binary Authorization Overview
NOTE: This doc is out-dated and will be updated soon. We don't rely on a Kernel
NOTE: This doc is out-dated and will be updated soon. We don't rely on a Kernel
Extension anymore.
#### Background
@@ -49,7 +49,7 @@ documentation. This flow does not cover the logging component of Santa, see the
`execve()` the same `vnode_id`, santa-driver will have that thread wait
for the in-flight decision from santad. All subsequent `execve()`s for
the same `vnode_id` will use the decision in the cache as explained
in #2, until the cache is invalidated.
in #2, until the cache is invalidated.
* If the executing file is written to while any of the threads are waiting
for a response the `ACTION_REQUEST_BINARY` entry is removed, forcing the
decision-making process to be restarted.