mirror of
https://github.com/google/santa.git
synced 2026-01-15 09:17:59 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
261425aa64 | ||
|
|
c17c890356 | ||
|
|
e4e1704495 | ||
|
|
737525b746 | ||
|
|
8199348091 | ||
|
|
9f41fbb124 | ||
|
|
ff0efe952b | ||
|
|
c711129ac9 | ||
|
|
a56f6c5447 | ||
|
|
fadc9b505b | ||
|
|
c7766d5993 | ||
|
|
341abf044b | ||
|
|
b1cf83a7e3 | ||
|
|
013b0b40af | ||
|
|
6093118ba1 | ||
|
|
6719d4c32a | ||
|
|
1ce4756771 | ||
|
|
9a7dcefb92 |
@@ -4,7 +4,7 @@ bazel_dep(name = "apple_support", version = "1.15.1", repo_name = "build_bazel_a
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.2", repo_name = "com_google_absl")
|
||||
bazel_dep(name = "rules_python", version = "0.33.2")
|
||||
bazel_dep(name = "rules_cc", version = "0.0.9")
|
||||
bazel_dep(name = "rules_apple", version = "3.6.0", repo_name = "build_bazel_rules_apple")
|
||||
bazel_dep(name = "rules_apple", version = "3.8.0", repo_name = "build_bazel_rules_apple")
|
||||
bazel_dep(name = "rules_swift", version = "2.0.0-rc1", repo_name = "build_bazel_rules_swift")
|
||||
bazel_dep(name = "rules_fuzzing", version = "0.5.2")
|
||||
bazel_dep(name = "protobuf", version = "27.2", repo_name = "com_google_protobuf")
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Santa
|
||||
|
||||
> [!NOTE]
|
||||
> **As of 2025, Santa is no longer maintained by Google.** We encourage
|
||||
> existing users to migrate to an actively maintained fork of Santa, such as
|
||||
> https://github.com/northpolesec/santa.
|
||||
|
||||
---
|
||||
|
||||
[](https://github.com/google/santa/blob/main/LICENSE)
|
||||
[](https://github.com/google/santa/actions/workflows/ci.yml)
|
||||
[](https://github.com/google/santa/releases/latest)
|
||||
|
||||
@@ -368,7 +368,12 @@ static NSString *const kSyncTypeRequired = @"SyncTypeRequired";
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingStaticRules {
|
||||
return [self configStateSet];
|
||||
static NSSet *set;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
set = [NSSet setWithObject:NSStringFromSelector(@selector(cachedStaticRules))];
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSyncBaseURL {
|
||||
|
||||
@@ -423,8 +423,14 @@ extern NSString *const NSURLQuarantinePropertiesKey WEAK_IMPORT_ATTRIBUTE;
|
||||
return self.infoDict;
|
||||
}
|
||||
|
||||
d = self.bundle.infoDictionary;
|
||||
if (d) {
|
||||
// `-[NSBundle infoDictionary]` is heavily cached, changes to the Info.plist are not realized.
|
||||
// Use `CFBundleCopyInfoDictionaryInDirectory` instead, which does not appear to cache.
|
||||
NSString *bundlePath = [self bundlePath];
|
||||
if (bundlePath.length) {
|
||||
d = CFBridgingRelease(CFBundleCopyInfoDictionaryInDirectory(
|
||||
(__bridge CFURLRef)[NSURL fileURLWithPath:bundlePath]));
|
||||
}
|
||||
if (d.count) {
|
||||
self.infoDict = d;
|
||||
return self.infoDict;
|
||||
}
|
||||
|
||||
@@ -605,10 +605,15 @@ NSString *SNTMetricMakeStringFromMetricType(SNTMetricType metricType) {
|
||||
|
||||
/** Export current state of the SNTMetricSet as an NSDictionary. */
|
||||
- (NSDictionary *)export {
|
||||
NSDictionary *exported = nil;
|
||||
NSDictionary *exported;
|
||||
|
||||
NSArray *callbacks;
|
||||
@synchronized(self) {
|
||||
callbacks = [_callbacks mutableCopy];
|
||||
}
|
||||
|
||||
// Invoke callbacks to ensure metrics are up to date.
|
||||
for (void (^cb)(void) in _callbacks) {
|
||||
for (void (^cb)(void) in callbacks) {
|
||||
cb();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,4 +54,19 @@
|
||||
///
|
||||
+ (NSString *)modelIdentifier;
|
||||
|
||||
///
|
||||
/// @return The Santa product version, e.g. 2024.6
|
||||
///
|
||||
+ (NSString *)santaProductVersion;
|
||||
|
||||
///
|
||||
/// @return The Santa build version, e.g. 655965194
|
||||
///
|
||||
+ (NSString *)santaBuildVersion;
|
||||
|
||||
///
|
||||
/// @return The full Santa versoin, e.g. 2024.6.655965194
|
||||
///
|
||||
+ (NSString *)santaFullVersion;
|
||||
|
||||
@end
|
||||
|
||||
@@ -74,6 +74,21 @@
|
||||
return @(model);
|
||||
}
|
||||
|
||||
+ (NSString *)santaProductVersion {
|
||||
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
|
||||
return info_dict[@"CFBundleShortVersionString"];
|
||||
}
|
||||
|
||||
+ (NSString *)santaBuildVersion {
|
||||
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
|
||||
return [[info_dict[@"CFBundleVersion"] componentsSeparatedByString:@"."] lastObject];
|
||||
}
|
||||
|
||||
+ (NSString *)santaFullVersion {
|
||||
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
|
||||
return info_dict[@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
#pragma mark - Internal
|
||||
|
||||
+ (NSDictionary *)_systemVersionDictionary {
|
||||
|
||||
@@ -823,6 +823,7 @@ objc_library(
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTMetricSet",
|
||||
"//Source/common:SNTSystemInfo",
|
||||
"//Source/common:SNTXPCControlInterface",
|
||||
"//Source/common:SystemResources",
|
||||
],
|
||||
|
||||
@@ -688,7 +688,7 @@ bool ShouldMessageTTY(const std::shared_ptr<WatchItemPolicy> &policy, const Mess
|
||||
|
||||
// Notify users on block decisions
|
||||
if (ShouldNotifyUserDecision(policyDecision) &&
|
||||
(!policy->silent || (!policy->silent_tty && msg->process->tty->path.length > 0))) {
|
||||
(!policy->silent || (!policy->silent_tty && TTYWriter::CanWrite(msg->process)))) {
|
||||
SNTCachedDecision *cd =
|
||||
[self.decisionCache cachedDecisionForFile:msg->process->executable->stat];
|
||||
|
||||
|
||||
@@ -45,12 +45,6 @@
|
||||
/// only guaranteed for the duration of the call to the block. Do not perform
|
||||
/// any async processing without extending their lifetimes.
|
||||
///
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
targetProcess:(nonnull const es_process_t *)targetProc
|
||||
entitlementsFilterCallback:
|
||||
(NSDictionary *_Nullable (^_Nonnull)(
|
||||
const char *_Nullable teamID,
|
||||
NSDictionary *_Nullable entitlements))entitlementsFilterCallback;
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
targetProcess:(nonnull const es_process_t *)targetProc
|
||||
preCodesignCheckCallback:(void (^_Nullable)(void))preCodesignCheckCallback
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
#import "Source/santad/DataLayer/SNTRuleTable.h"
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
|
||||
enum class PlatformBinaryState {
|
||||
kRuntimeTrue = 0,
|
||||
kRuntimeFalse,
|
||||
kStaticCheck,
|
||||
};
|
||||
|
||||
@interface SNTPolicyProcessor ()
|
||||
@property SNTRuleTable *ruleTable;
|
||||
@property SNTConfigurator *configurator;
|
||||
@@ -129,7 +135,7 @@
|
||||
}
|
||||
|
||||
static void UpdateCachedDecisionSigningInfo(
|
||||
SNTCachedDecision *cd, MOLCodesignChecker *csInfo,
|
||||
SNTCachedDecision *cd, MOLCodesignChecker *csInfo, PlatformBinaryState platformBinaryState,
|
||||
NSDictionary *_Nullable (^entitlementsFilterCallback)(NSDictionary *_Nullable entitlements)) {
|
||||
cd.certSHA256 = csInfo.leafCertificate.SHA256;
|
||||
cd.certCommonName = csInfo.leafCertificate.commonName;
|
||||
@@ -144,11 +150,18 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
cd.signingID = FormatSigningID(csInfo);
|
||||
}
|
||||
|
||||
// Ensure that if no teamID exists that the signing info confirms it is a
|
||||
// platform binary. If not, remove the signingID.
|
||||
// Ensure that if no teamID exists but a signingID does exist, that the binary
|
||||
// is a platform binary. If not, remove the signingID.
|
||||
if (!cd.teamID && cd.signingID) {
|
||||
if (!csInfo.platformBinary) {
|
||||
cd.signingID = nil;
|
||||
switch (platformBinaryState) {
|
||||
case PlatformBinaryState::kRuntimeTrue: break;
|
||||
case PlatformBinaryState::kStaticCheck:
|
||||
if (!csInfo.platformBinary) {
|
||||
cd.signingID = nil;
|
||||
}
|
||||
break;
|
||||
case PlatformBinaryState::kRuntimeFalse: OS_FALLTHROUGH;
|
||||
default: cd.signingID = nil; break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +183,7 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
certificateSHA256:(nullable NSString *)certificateSHA256
|
||||
teamID:(nullable NSString *)teamID
|
||||
signingID:(nullable NSString *)signingID
|
||||
platformBinaryState:(PlatformBinaryState)platformBinaryState
|
||||
isProdSignedCallback:(BOOL (^_Nonnull)())isProdSignedCallback
|
||||
entitlementsFilterCallback:(NSDictionary *_Nullable (^_Nullable)(
|
||||
NSDictionary *_Nullable entitlements))entitlementsFilterCallback
|
||||
@@ -215,7 +229,7 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
cd.signingID = nil;
|
||||
cd.cdhash = nil;
|
||||
} else {
|
||||
UpdateCachedDecisionSigningInfo(cd, csInfo, entitlementsFilterCallback);
|
||||
UpdateCachedDecisionSigningInfo(cd, csInfo, platformBinaryState, entitlementsFilterCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,18 +290,6 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
}
|
||||
}
|
||||
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
targetProcess:(nonnull const es_process_t *)targetProc
|
||||
entitlementsFilterCallback:
|
||||
(NSDictionary *_Nullable (^_Nonnull)(
|
||||
const char *_Nullable teamID,
|
||||
NSDictionary *_Nullable entitlements))entitlementsFilterCallback {
|
||||
return [self decisionForFileInfo:fileInfo
|
||||
targetProcess:targetProc
|
||||
preCodesignCheckCallback:nil
|
||||
entitlementsFilterCallback:entitlementsFilterCallback];
|
||||
}
|
||||
|
||||
- (nonnull SNTCachedDecision *)decisionForFileInfo:(nonnull SNTFileInfo *)fileInfo
|
||||
targetProcess:(nonnull const es_process_t *)targetProc
|
||||
preCodesignCheckCallback:(void (^_Nullable)(void))preCodesignCheckCallback
|
||||
@@ -338,6 +340,8 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
certificateSHA256:nil
|
||||
teamID:teamID
|
||||
signingID:signingID
|
||||
platformBinaryState:targetProc->is_platform_binary ? PlatformBinaryState::kRuntimeTrue
|
||||
: PlatformBinaryState::kRuntimeFalse
|
||||
isProdSignedCallback:^BOOL {
|
||||
return ((targetProc->codesigning_flags & CS_DEV_CODE) == 0);
|
||||
}
|
||||
@@ -369,6 +373,7 @@ static void UpdateCachedDecisionSigningInfo(
|
||||
certificateSHA256:identifiers.certificateSHA256
|
||||
teamID:identifiers.teamID
|
||||
signingID:identifiers.signingID
|
||||
platformBinaryState:PlatformBinaryState::kStaticCheck
|
||||
isProdSignedCallback:^BOOL {
|
||||
if (csInfo) {
|
||||
// Development OID values defined by Apple and used by the Security Framework
|
||||
|
||||
@@ -315,12 +315,9 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
|
||||
}],
|
||||
[[SNTKVOManager alloc] initWithObject:configurator
|
||||
selector:@selector(staticRules)
|
||||
type:[NSArray class]
|
||||
callback:^(NSArray *oldValue, NSArray *newValue) {
|
||||
NSSet *oldValueSet = [NSSet setWithArray:oldValue ?: @[]];
|
||||
NSSet *newValueSet = [NSSet setWithArray:newValue ?: @[]];
|
||||
|
||||
if ([oldValueSet isEqualToSet:newValueSet]) {
|
||||
type:[NSDictionary class]
|
||||
callback:^(NSDictionary *oldValue, NSDictionary *newValue) {
|
||||
if ([oldValue isEqualToDictionary:newValue]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTMetricSet.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
#import "Source/common/SystemResources.h"
|
||||
#import "Source/santad/Santad.h"
|
||||
#include "Source/santad/SantadDeps.h"
|
||||
@@ -111,13 +112,10 @@ int main(int argc, char *argv[]) {
|
||||
// Do not wait on child processes
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
NSDictionary *info_dict = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString *product_version = [SNTSystemInfo santaProductVersion];
|
||||
NSString *build_version = [SNTSystemInfo santaBuildVersion];
|
||||
|
||||
NSProcessInfo *pi = [NSProcessInfo processInfo];
|
||||
|
||||
NSString *product_version = info_dict[@"CFBundleShortVersionString"];
|
||||
NSString *build_version =
|
||||
[[info_dict[@"CFBundleVersion"] componentsSeparatedByString:@"."] lastObject];
|
||||
|
||||
if ([pi.arguments containsObject:@"-v"]) {
|
||||
printf("%s (build %s)\n", [product_version UTF8String], [build_version UTF8String]);
|
||||
return 0;
|
||||
|
||||
@@ -22,7 +22,7 @@ objc_library(
|
||||
name = "FCM_lib",
|
||||
srcs = ["SNTSyncFCM.m"],
|
||||
hdrs = ["SNTSyncFCM.h"],
|
||||
sdk_frameworks = ["SystemConfiguration"],
|
||||
sdk_frameworks = ["Network"],
|
||||
deps = [
|
||||
"@MOLAuthenticatingURLSession",
|
||||
],
|
||||
@@ -55,6 +55,7 @@ objc_library(
|
||||
],
|
||||
hdrs = ["SNTSyncManager.h"],
|
||||
sdk_dylibs = ["libz"],
|
||||
sdk_frameworks = ["Network"],
|
||||
deps = [
|
||||
":FCM_lib",
|
||||
":broadcaster_lib",
|
||||
|
||||
@@ -150,8 +150,8 @@ using santa::NSStringToUTF8String;
|
||||
e->set_file_bundle_version(NSStringToUTF8String(event.fileBundleVersion));
|
||||
e->set_file_bundle_version_string(NSStringToUTF8String(event.fileBundleVersionString));
|
||||
e->set_file_bundle_hash(NSStringToUTF8String(event.fileBundleHash));
|
||||
e->set_file_bundle_hash_millis([event.fileBundleHashMilliseconds longLongValue]);
|
||||
e->set_file_bundle_binary_count([event.fileBundleBinaryCount longLongValue]);
|
||||
e->set_file_bundle_hash_millis([event.fileBundleHashMilliseconds unsignedIntValue]);
|
||||
e->set_file_bundle_binary_count([event.fileBundleBinaryCount unsignedIntValue]);
|
||||
|
||||
e->set_pid([event.pid unsignedIntValue]);
|
||||
e->set_ppid([event.ppid unsignedIntValue]);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#import "Source/santasyncservice/SNTSyncFCM.h"
|
||||
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <Network/Network.h>
|
||||
|
||||
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>
|
||||
|
||||
@@ -80,7 +80,7 @@ static const uint32_t kDefaultConnectDelayMaxSeconds = 10;
|
||||
@property(copy, nonatomic) SNTSyncFCMMessageHandler messageHandler;
|
||||
|
||||
/** Is used throughout the class to reconnect to FCM after a connection loss. */
|
||||
@property SCNetworkReachabilityRef reachability;
|
||||
@property nw_path_monitor_t pathMonitor;
|
||||
|
||||
/** FCM client identities. */
|
||||
@property(nonatomic, readonly) NSString *project;
|
||||
@@ -97,21 +97,6 @@ static const uint32_t kDefaultConnectDelayMaxSeconds = 10;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark SCNetworkReachabilityCallBack
|
||||
|
||||
/** Called when the network state changes. */
|
||||
static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags,
|
||||
void *info) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (flags & kSCNetworkReachabilityFlagsReachable) {
|
||||
SNTSyncFCM *FCMClient = (__bridge SNTSyncFCM *)info;
|
||||
SEL s = @selector(reachabilityRestored);
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:FCMClient selector:s object:nil];
|
||||
[FCMClient performSelector:s withObject:nil afterDelay:1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@implementation SNTSyncFCM
|
||||
|
||||
#pragma mark init/dealloc methods
|
||||
@@ -184,11 +169,6 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
messageHandler:messageHandler];
|
||||
}
|
||||
|
||||
/** Before this object is released ensure reachability release. */
|
||||
- (void)dealloc {
|
||||
[self stopReachability];
|
||||
}
|
||||
|
||||
#pragma mark property methods
|
||||
|
||||
- (BOOL)isConnected {
|
||||
@@ -212,24 +192,27 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
|
||||
/** Start listening for network state changes on a background thread. */
|
||||
- (void)startReachability {
|
||||
if (self.reachability) return;
|
||||
if (self.pathMonitor) return;
|
||||
self.pathMonitor = nw_path_monitor_create();
|
||||
nw_path_monitor_set_queue(self.pathMonitor, dispatch_get_main_queue());
|
||||
nw_path_monitor_set_update_handler(self.pathMonitor, ^(nw_path_t path) {
|
||||
if (nw_path_get_status(path) == nw_path_status_satisfied) {
|
||||
SEL s = @selector(reachabilityRestored);
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:s object:nil];
|
||||
[self performSelector:s withObject:nil afterDelay:1];
|
||||
}
|
||||
});
|
||||
nw_path_monitor_set_cancel_handler(self.pathMonitor, ^{
|
||||
self.pathMonitor = nil;
|
||||
});
|
||||
nw_path_monitor_start(self.pathMonitor);
|
||||
LOGD(@"Reachability started.");
|
||||
self.reachability =
|
||||
SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, _connectComponents.host.UTF8String);
|
||||
SCNetworkReachabilityContext context = {.info = (__bridge void *)self};
|
||||
if (SCNetworkReachabilitySetCallback(self.reachability, reachabilityHandler, &context)) {
|
||||
SCNetworkReachabilitySetDispatchQueue(
|
||||
self.reachability, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop listening for network state changes. */
|
||||
- (void)stopReachability {
|
||||
if (self.reachability) {
|
||||
SCNetworkReachabilitySetDispatchQueue(self.reachability, NULL);
|
||||
if (self.reachability) CFRelease(self.reachability);
|
||||
self.reachability = NULL;
|
||||
}
|
||||
if (!self.pathMonitor) return;
|
||||
nw_path_monitor_cancel(self.pathMonitor);
|
||||
}
|
||||
|
||||
#pragma mark message methods
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#import <MOLAuthenticatingURLSession/MOLAuthenticatingURLSession.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <Network/Network.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
@@ -35,9 +35,7 @@
|
||||
|
||||
static const uint8_t kMaxEnqueuedSyncs = 2;
|
||||
|
||||
@interface SNTSyncManager () <SNTPushNotificationsDelegate> {
|
||||
SCNetworkReachabilityRef _reachability;
|
||||
}
|
||||
@interface SNTSyncManager () <SNTPushNotificationsDelegate>
|
||||
|
||||
@property(nonatomic) dispatch_source_t fullSyncTimer;
|
||||
@property(nonatomic) dispatch_source_t ruleSyncTimer;
|
||||
@@ -48,6 +46,7 @@ static const uint8_t kMaxEnqueuedSyncs = 2;
|
||||
@property(nonatomic) MOLXPCConnection *daemonConn;
|
||||
|
||||
@property(nonatomic) BOOL reachable;
|
||||
@property nw_path_monitor_t pathMonitor;
|
||||
|
||||
@property SNTPushNotifications *pushNotifications;
|
||||
|
||||
@@ -58,22 +57,6 @@ static const uint8_t kMaxEnqueuedSyncs = 2;
|
||||
|
||||
@end
|
||||
|
||||
// Called when the network state changes
|
||||
static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags,
|
||||
void *info) {
|
||||
// Put this check and set on the main thread to ensure serial access.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
SNTSyncManager *commandSyncManager = (__bridge SNTSyncManager *)info;
|
||||
// Only call the setter when there is a change. This will filter out the redundant calls to this
|
||||
// callback whenever the network interface states change.
|
||||
int reachable =
|
||||
(flags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable;
|
||||
if (commandSyncManager.reachable != reachable) {
|
||||
commandSyncManager.reachable = reachable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@implementation SNTSyncManager
|
||||
|
||||
#pragma mark init
|
||||
@@ -102,11 +85,6 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// Ensure reachability is always stopped
|
||||
[self stopReachability];
|
||||
}
|
||||
|
||||
#pragma mark SNTSyncServiceXPC methods
|
||||
|
||||
- (void)postEventsToSyncServer:(NSArray<SNTStoredEvent *> *)events fromBundle:(BOOL)isFromBundle {
|
||||
@@ -279,8 +257,8 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
return [self eventUploadWithSyncState:syncState];
|
||||
}
|
||||
|
||||
LOGE(@"Preflight failed, will try again once %@ is reachable",
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
SLOGE(@"Preflight failed, will try again once %@ is reachable",
|
||||
[[SNTConfigurator configurator] syncBaseURL].absoluteString);
|
||||
[self startReachability];
|
||||
return SNTSyncStatusTypePreflightFailed;
|
||||
}
|
||||
@@ -408,39 +386,36 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
- (void)setReachable:(BOOL)reachable {
|
||||
_reachable = reachable;
|
||||
if (reachable) {
|
||||
LOGD(@"Internet connection has been restored, triggering a new sync.");
|
||||
[self stopReachability];
|
||||
[self sync];
|
||||
}
|
||||
}
|
||||
|
||||
// Start listening for network state changes on a background thread
|
||||
// Start listening for network state changes.
|
||||
- (void)startReachability {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->_reachability) return;
|
||||
const char *nodename = [[SNTConfigurator configurator] syncBaseURL].host.UTF8String;
|
||||
self->_reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, nodename);
|
||||
SCNetworkReachabilityContext context = {
|
||||
.info = (__bridge_retained void *)self,
|
||||
.release = (void (*)(const void *))CFBridgingRelease,
|
||||
};
|
||||
if (SCNetworkReachabilitySetCallback(self->_reachability, reachabilityHandler, &context)) {
|
||||
SCNetworkReachabilitySetDispatchQueue(
|
||||
self->_reachability, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0));
|
||||
} else {
|
||||
[self stopReachability];
|
||||
if (self.pathMonitor) return;
|
||||
self.pathMonitor = nw_path_monitor_create();
|
||||
// Put the callback on the main thread to ensure serial access.
|
||||
nw_path_monitor_set_queue(self.pathMonitor, dispatch_get_main_queue());
|
||||
nw_path_monitor_set_update_handler(self.pathMonitor, ^(nw_path_t path) {
|
||||
// Only call the setter when there is a change. This will filter out the redundant calls to
|
||||
// this callback whenever the network interface states change.
|
||||
int reachable = nw_path_get_status(path) == nw_path_status_satisfied;
|
||||
if (self.reachable != reachable) {
|
||||
self.reachable = reachable;
|
||||
}
|
||||
});
|
||||
nw_path_monitor_set_cancel_handler(self.pathMonitor, ^{
|
||||
self.pathMonitor = nil;
|
||||
});
|
||||
nw_path_monitor_start(self.pathMonitor);
|
||||
}
|
||||
|
||||
// Stop listening for network state changes
|
||||
- (void)stopReachability {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->_reachability) {
|
||||
SCNetworkReachabilitySetDispatchQueue(self->_reachability, NULL);
|
||||
if (self->_reachability) CFRelease(self->_reachability);
|
||||
self->_reachability = NULL;
|
||||
}
|
||||
});
|
||||
if (!self.pathMonitor) return;
|
||||
nw_path_monitor_cancel(self.pathMonitor);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -38,8 +38,8 @@ using santa::NSStringToUTF8String;
|
||||
google::protobuf::Arena arena;
|
||||
auto req = google::protobuf::Arena::Create<::pbv1::PostflightRequest>(&arena);
|
||||
req->set_machine_id(NSStringToUTF8String(self.syncState.machineID));
|
||||
req->set_rules_received(self.syncState.rulesReceived);
|
||||
req->set_rules_processed(self.syncState.rulesProcessed);
|
||||
req->set_rules_received(static_cast<uint32_t>(self.syncState.rulesReceived));
|
||||
req->set_rules_processed(static_cast<uint32_t>(self.syncState.rulesProcessed));
|
||||
|
||||
::pbv1::PostflightResponse response;
|
||||
[self performRequest:[self requestWithMessage:req] intoMessage:&response timeout:30];
|
||||
|
||||
@@ -87,8 +87,7 @@ The following table expands upon the above logic to list most of the permutation
|
||||
req->set_os_version(NSStringToUTF8String([SNTSystemInfo osVersion]));
|
||||
req->set_os_build(NSStringToUTF8String([SNTSystemInfo osBuild]));
|
||||
req->set_model_identifier(NSStringToUTF8String([SNTSystemInfo modelIdentifier]));
|
||||
req->set_santa_version(
|
||||
NSStringToUTF8String([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]));
|
||||
req->set_santa_version(NSStringToUTF8String([SNTSystemInfo santaFullVersion]));
|
||||
req->set_primary_user(NSStringToUTF8String(self.syncState.machineOwner));
|
||||
|
||||
if (self.syncState.pushNotificationsToken) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#import "Source/santasyncservice/SNTSyncStage.h"
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
@@ -74,10 +75,7 @@ using santa::NSStringToUTF8String;
|
||||
contentType:@"application/x-protobuf"];
|
||||
}
|
||||
|
||||
google::protobuf::json::PrintOptions options{
|
||||
.always_print_enums_as_ints = false,
|
||||
.preserve_proto_field_names = true,
|
||||
};
|
||||
google::protobuf::json::PrintOptions options{};
|
||||
std::string json;
|
||||
absl::Status status = google::protobuf::json::MessageToJsonString(*message, &json, options);
|
||||
|
||||
@@ -88,13 +86,12 @@ using santa::NSStringToUTF8String;
|
||||
return nil;
|
||||
}
|
||||
|
||||
SLOGD(@"Request JSON: %s", json.c_str());
|
||||
return [self requestWithData:[NSData dataWithBytes:json.data() length:json.size()]
|
||||
contentType:@"application/json"];
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)requestWithData:(NSData *)requestBody contentType:(NSString *)contentType {
|
||||
if (contentType.length) contentType = @"application/octet-stream";
|
||||
if (!contentType.length) contentType = @"application/octet-stream";
|
||||
|
||||
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] initWithURL:[self stageURL]];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
@@ -179,6 +176,11 @@ using santa::NSStringToUTF8String;
|
||||
data = [self performRequest:request timeout:timeout response:&response error:&requestError];
|
||||
if (response.statusCode == 200) break;
|
||||
|
||||
// If the original request failed because of a "No network" error, break out of the loop,
|
||||
// subsequent retries are pointless and the entire sync will be retried once a connection
|
||||
// is established.
|
||||
if (requestError.code == NSURLErrorNotConnectedToInternet) break;
|
||||
|
||||
// If the original request failed because of an auth error, attempt to get a new XSRF token and
|
||||
// try again. Unfortunately some servers cause NSURLSession to return 'client cert required' or
|
||||
// 'could not parse response' when a 403 occurs and SSL cert auth is enabled.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/SNTSyncConstants.h"
|
||||
#import "Source/common/SNTSystemInfo.h"
|
||||
#import "Source/common/SNTXPCControlInterface.h"
|
||||
#import "Source/santasyncservice/SNTSyncEventUpload.h"
|
||||
#import "Source/santasyncservice/SNTSyncPostflight.h"
|
||||
@@ -60,11 +61,19 @@
|
||||
OCMStub([self.syncState.daemonConn remoteObjectProxy]).andReturn(self.daemonConnRop);
|
||||
OCMStub([self.syncState.daemonConn synchronousRemoteObjectProxy]).andReturn(self.daemonConnRop);
|
||||
|
||||
id siMock = OCMClassMock([SNTSystemInfo class]);
|
||||
OCMStub([siMock serialNumber]).andReturn(@"QYGF4QM373");
|
||||
OCMStub([siMock longHostname]).andReturn(@"full-hostname.example.com");
|
||||
OCMStub([siMock osVersion]).andReturn(@"14.5");
|
||||
OCMStub([siMock osBuild]).andReturn(@"23F79");
|
||||
OCMStub([siMock modelIdentifier]).andReturn(@"MacBookPro18,3");
|
||||
OCMStub([siMock santaFullVersion]).andReturn(@"2024.6.655965194");
|
||||
|
||||
self.syncState.session = OCMClassMock([NSURLSession class]);
|
||||
|
||||
self.syncState.syncBaseURL = [NSURL URLWithString:@"https://myserver.local/"];
|
||||
self.syncState.machineID = [[NSUUID UUID] UUIDString];
|
||||
self.syncState.machineOwner = NSUserName();
|
||||
self.syncState.machineID = @"50C7E1EB-2EF5-42D4-A084-A7966FC45A95";
|
||||
self.syncState.machineOwner = @"username1";
|
||||
}
|
||||
|
||||
#pragma mark Test Helpers
|
||||
@@ -271,7 +280,17 @@
|
||||
SNTSyncPreflight *sut = [[SNTSyncPreflight alloc] initWithState:self.syncState];
|
||||
|
||||
NSData *respData = [self dataFromFixture:@"sync_preflight_basic.json"];
|
||||
[self stubRequestBody:respData response:nil error:nil validateBlock:nil];
|
||||
[self
|
||||
stubRequestBody:respData
|
||||
response:nil
|
||||
error:nil
|
||||
validateBlock:^BOOL(NSURLRequest *req) {
|
||||
NSData *gotReqData = [req HTTPBody];
|
||||
NSData *expectedReqData = [self dataFromFixture:@"sync_preflight_request.json"];
|
||||
XCTAssertEqualObjects(gotReqData, expectedReqData);
|
||||
XCTAssertEqualObjects([req valueForHTTPHeaderField:@"Content-Type"], @"application/json");
|
||||
return YES;
|
||||
}];
|
||||
|
||||
XCTAssertTrue([sut sync]);
|
||||
XCTAssertEqual(self.syncState.clientMode, SNTClientModeMonitor);
|
||||
|
||||
@@ -35,27 +35,27 @@ enum ClientMode {
|
||||
}
|
||||
|
||||
message PreflightRequest {
|
||||
string serial_number = 1 [json_name="serial_num"];
|
||||
string hostname = 2;
|
||||
string os_version = 3;
|
||||
string os_build = 4;
|
||||
string model_identifier = 5;
|
||||
string santa_version = 6;
|
||||
string primary_user = 7;
|
||||
string push_notification_token = 8;
|
||||
string serial_number = 1 [json_name="serial_num"];
|
||||
string hostname = 2 [json_name="hostname"];
|
||||
string os_version = 3 [json_name="os_version"];
|
||||
string os_build = 4 [json_name="os_build"];
|
||||
string model_identifier = 5 [json_name="model_identifier"];
|
||||
string santa_version = 6 [json_name="santa_version"];
|
||||
string primary_user = 7 [json_name="primary_user"];
|
||||
string push_notification_token = 8 [json_name="push_notification_token"];
|
||||
|
||||
ClientMode client_mode = 9;
|
||||
bool request_clean_sync = 10;
|
||||
ClientMode client_mode = 9 [json_name="client_mode"];
|
||||
bool request_clean_sync = 10 [json_name="request_clean_sync"];
|
||||
|
||||
uint32 binary_rule_count = 11;
|
||||
uint32 certificate_rule_count = 12;
|
||||
uint32 compiler_rule_count = 13;
|
||||
uint32 transitive_rule_count = 14;
|
||||
uint32 teamid_rule_count = 15;
|
||||
uint32 signingid_rule_count = 16;
|
||||
uint32 cdhash_rule_count = 17;
|
||||
uint32 binary_rule_count = 11 [json_name="binary_rule_count"];
|
||||
uint32 certificate_rule_count = 12 [json_name="certificate_rule_count"];
|
||||
uint32 compiler_rule_count = 13 [json_name="compiler_rule_count"];
|
||||
uint32 transitive_rule_count = 14 [json_name="transitive_rule_count"];
|
||||
uint32 teamid_rule_count = 15 [json_name="teamid_rule_count"];
|
||||
uint32 signingid_rule_count = 16 [json_name="signingid_rule_count"];
|
||||
uint32 cdhash_rule_count = 17 [json_name="cdhash_rule_count"];
|
||||
// The UUID of the machine that is sending this preflight.
|
||||
string machine_id = 18;
|
||||
string machine_id = 18 [json_name="machine_id"];
|
||||
}
|
||||
|
||||
enum SyncType {
|
||||
@@ -125,16 +125,16 @@ message PreflightResponse {
|
||||
optional bool disable_unknown_event_upload = 7;
|
||||
|
||||
// Specifies the time interval in seconds between full syncs. Defaults to 600 (10 minutes). Cannot be set lower than 60.
|
||||
uint64 full_sync_interval_seconds = 8 [json_name="full_sync_interval"];
|
||||
uint32 full_sync_interval_seconds = 8 [json_name="full_sync_interval"];
|
||||
|
||||
// When push notifications are enabled, this overrides the full_sync_interval above. It is expected that Santa will not
|
||||
// need to perform a full sync as frequently when push notifications are working. Defaults to 14400 (6 hours).
|
||||
uint64 push_notification_full_sync_interval_seconds = 9 [json_name="push_notification_full_sync_interval"];
|
||||
uint32 push_notification_full_sync_interval_seconds = 9 [json_name="push_notification_full_sync_interval"];
|
||||
|
||||
// The maximum number of seconds Santa can wait before triggering a rule sync after receiving a "global rule sync" notification.
|
||||
// As these notifications cause every Santa client to try and sync, we add a random delay to each client to try and spread the
|
||||
// load out on the sync server. This defaults to 600 (10 minutes).
|
||||
uint64 push_notification_global_rule_sync_deadline_seconds = 10 [json_name="push_notification_global_rule_sync_deadline"];
|
||||
uint32 push_notification_global_rule_sync_deadline_seconds = 10 [json_name="push_notification_global_rule_sync_deadline"];
|
||||
|
||||
// These two regexes are used to allow/block executions whose path matches. The provided regex must conform to ICU format.
|
||||
// While this feature can be useful, its use should be very carefully considered as it is much riskier than real rules.
|
||||
@@ -159,8 +159,8 @@ message PreflightResponse {
|
||||
optional bool deprecated_enabled_transitive_whitelisting = 1000 [json_name="enabled_transitive_whitelisting", deprecated=true];
|
||||
optional bool deprecated_transitive_whitelisting_enabled = 1001 [json_name="transitive_whitelisting_enabled", deprecated=true];
|
||||
optional bool deprecated_bundles_enabled = 1002 [json_name="bundles_enabled", deprecated=true];
|
||||
optional uint64 deprecated_fcm_full_sync_interval_seconds = 1003 [json_name="fcm_full_sync_interval", deprecated=true];
|
||||
optional uint64 deprecated_fcm_global_rule_sync_deadline_seconds = 1004 [json_name="fcm_global_rule_sync_deadline", deprecated=true];
|
||||
optional uint32 deprecated_fcm_full_sync_interval_seconds = 1003 [json_name="fcm_full_sync_interval", deprecated=true];
|
||||
optional uint32 deprecated_fcm_global_rule_sync_deadline_seconds = 1004 [json_name="fcm_global_rule_sync_deadline", deprecated=true];
|
||||
optional string deprecated_whitelist_regex = 1005 [json_name="whitelist_regex", deprecated=true];
|
||||
optional string deprecated_blacklist_regex = 1006 [json_name="blacklist_regex", deprecated=true];
|
||||
|
||||
@@ -193,53 +193,53 @@ message Certificate {
|
||||
string cn = 2;
|
||||
string org = 3;
|
||||
string ou = 4;
|
||||
uint32 valid_from = 5;
|
||||
uint32 valid_until = 6;
|
||||
uint32 valid_from = 5 [json_name="valid_from"];
|
||||
uint32 valid_until = 6 [json_name="valid_until"];
|
||||
}
|
||||
|
||||
message Event {
|
||||
string file_sha256 = 1;
|
||||
string file_path = 2;
|
||||
string file_name = 3;
|
||||
string executing_user = 4;
|
||||
double execution_time = 5;
|
||||
repeated string logged_in_users = 6;
|
||||
repeated string current_sessions = 7;
|
||||
Decision decision = 8;
|
||||
string file_sha256 = 1 [json_name="file_sha256"];
|
||||
string file_path = 2 [json_name="file_path"];
|
||||
string file_name = 3 [json_name="file_name"];
|
||||
string executing_user = 4 [json_name="executing_user"];
|
||||
double execution_time = 5 [json_name="execution_time"];
|
||||
repeated string logged_in_users = 6 [json_name="logged_in_users"];
|
||||
repeated string current_sessions = 7 [json_name="current_sessions"];
|
||||
Decision decision = 8 [json_name="decision"];
|
||||
|
||||
string file_bundle_id = 9;
|
||||
string file_bundle_path = 10;
|
||||
string file_bundle_executable_rel_path = 11;
|
||||
string file_bundle_name = 12;
|
||||
string file_bundle_version = 13;
|
||||
string file_bundle_version_string = 14;
|
||||
string file_bundle_hash = 15;
|
||||
uint64 file_bundle_hash_millis = 16;
|
||||
uint64 file_bundle_binary_count = 17;
|
||||
string file_bundle_id = 9 [json_name="file_bundle_id"];
|
||||
string file_bundle_path = 10 [json_name="file_bundle_path"];
|
||||
string file_bundle_executable_rel_path = 11 [json_name="file_bundle_executable_rel_path"];
|
||||
string file_bundle_name = 12 [json_name="file_bundle_name"];
|
||||
string file_bundle_version = 13 [json_name="file_bundle_version"];
|
||||
string file_bundle_version_string = 14 [json_name="file_bundle_version_string"];
|
||||
string file_bundle_hash = 15 [json_name="file_bundle_hash"];
|
||||
uint32 file_bundle_hash_millis = 16 [json_name="file_bundle_hash_millis"];
|
||||
uint32 file_bundle_binary_count = 17 [json_name="file_bundle_binary_count"];
|
||||
|
||||
// pid_t is an int32
|
||||
int32 pid = 18;
|
||||
int32 ppid = 19;
|
||||
string parent_name = 20;
|
||||
int32 pid = 18 [json_name="pid"];
|
||||
int32 ppid = 19 [json_name="ppid"];
|
||||
string parent_name = 20 [json_name="parent_name"];
|
||||
|
||||
string team_id = 21;
|
||||
string signing_id = 22;
|
||||
string cdhash = 23;
|
||||
string team_id = 21 [json_name="team_id"];
|
||||
string signing_id = 22 [json_name="signing_id"];
|
||||
string cdhash = 23 [json_name="cdhash"];
|
||||
|
||||
string quarantine_data_url = 24;
|
||||
string quarantine_referer_url = 25;
|
||||
string quarantine_data_url = 24 [json_name="quarantine_data_url"];
|
||||
string quarantine_referer_url = 25 [json_name="quarantine_referer_url"];
|
||||
// Seconds since UNIX epoch. This field would ideally be an int64 but the protobuf library
|
||||
// encodes that as a string, unlike NSJSONSerialization
|
||||
uint32 quarantine_timestamp = 26;
|
||||
string quarantine_agent_bundle_id = 27;
|
||||
uint32 quarantine_timestamp = 26 [json_name="quarantine_timestamp"];
|
||||
string quarantine_agent_bundle_id = 27 [json_name="quarantine_agent_bundle_id"];
|
||||
|
||||
repeated Certificate signing_chain = 28;
|
||||
repeated Certificate signing_chain = 28 [json_name="signing_chain"];
|
||||
}
|
||||
|
||||
message EventUploadRequest {
|
||||
repeated Event events = 1;
|
||||
repeated Event events = 1 [json_name="events"];
|
||||
// The UUID of the machine where the event(s) occurred
|
||||
string machine_id = 2;
|
||||
string machine_id = 2 [json_name="machine_id"];
|
||||
}
|
||||
|
||||
message EventUploadResponse {
|
||||
@@ -294,9 +294,9 @@ message Rule {
|
||||
}
|
||||
|
||||
message RuleDownloadRequest {
|
||||
string cursor = 1;
|
||||
string cursor = 1 [json_name="cursor"];
|
||||
// The UUID of the machine that is requesting the rules.
|
||||
string machine_id = 2;
|
||||
string machine_id = 2 [json_name="machine_id"];
|
||||
}
|
||||
|
||||
message RuleDownloadResponse {
|
||||
@@ -305,10 +305,10 @@ message RuleDownloadResponse {
|
||||
}
|
||||
|
||||
message PostflightRequest {
|
||||
uint64 rules_received = 1;
|
||||
uint64 rules_processed = 2;
|
||||
uint32 rules_received = 1 [json_name="rules_received"];
|
||||
uint32 rules_processed = 2 [json_name="rules_processed"];
|
||||
// The UUID of the machine that is sending this postflight.
|
||||
string machine_id = 3;
|
||||
string machine_id = 3 [json_name="machine_id"];
|
||||
}
|
||||
|
||||
message PostflightResponse { }
|
||||
|
||||
1
Source/santasyncservice/testdata/sync_preflight_request.json
vendored
Normal file
1
Source/santasyncservice/testdata/sync_preflight_request.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"serial_num":"QYGF4QM373","hostname":"full-hostname.example.com","os_version":"14.5","os_build":"23F79","model_identifier":"MacBookPro18,3","santa_version":"2024.6.655965194","primary_user":"username1","client_mode":"MONITOR","machine_id":"50C7E1EB-2EF5-42D4-A084-A7966FC45A95"}
|
||||
@@ -14,3 +14,7 @@ gh_edit_branch: "main"
|
||||
plugins:
|
||||
- jekyll-redirect-from
|
||||
|
||||
callouts:
|
||||
important:
|
||||
title: Important
|
||||
color: blue
|
||||
|
||||
@@ -140,7 +140,7 @@ The JSON object has the following keys:
|
||||
| enable_bundles | Use previous setting | boolean | Enable bundle scanning | true |
|
||||
| enable_transitive_rules | Use previous setting | boolean | Whether or not to enable transitive allowlisting | true |
|
||||
| batch_size | Use a Santa-defined default value | integer | Number of events to upload at a time | 128 |
|
||||
| full_sync_interval | Defaults to 600 seconds | integer | Number of seconds between full syncs. Note: Santa enforces a minimum value of 60. The default value will be used if a smaller value is provided. | 600 |
|
||||
| full_sync_interval | Defaults to 600 seconds | uint32 | Number of seconds between full syncs. Note: Santa enforces a minimum value of 60. The default value will be used if a smaller value is provided. | 600 |
|
||||
| client_mode | Use previous setting | string | Operating mode to set for the client | either `MONITOR` or `LOCKDOWN` |
|
||||
| allowed_path_regex | Use previous setting | string | Regular expression to allow a binary to execute from a path | "/Users/markowsk/foo/.\*" |
|
||||
| blocked_path_regex | Use previous setting | string | Regular expression to block a binary from executing by path | "/tmp/" |
|
||||
@@ -223,8 +223,8 @@ sequenceDiagram
|
||||
| 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 | float64 | The time in milliseconds it took to find all of the binaries, hash and produce the bundle_hash | 1234775 |
|
||||
| file_bundle_binary_count | NO | integer | The number of binaries in a bundle | 13 |
|
||||
| file_bundle_hash_millis | NO | uint32 | The time in milliseconds it took to find all of the binaries, hash and produce the bundle_hash | 1234775 |
|
||||
| file_bundle_binary_count | NO | uint32 | The number of binaries in a bundle | 13 |
|
||||
| pid | NO | int | Process id of the executable that was blocked | 1234 |
|
||||
| ppid | NO | int | Parent process id of the executable that was blocked | 456 |
|
||||
| parent_name | NO | Parent process short command name of the executable that was blocked | "bar" |
|
||||
|
||||
@@ -3,6 +3,11 @@ title: Home
|
||||
nav_order: 1
|
||||
---
|
||||
|
||||
{: .important }
|
||||
**As of 2025, Santa is no longer maintained by Google.** We encourage existing
|
||||
users to migrate to an actively maintained fork of Santa, such as
|
||||
[https://github.com/northpolesec/santa](https://github.com/northpolesec/santa).
|
||||
|
||||
# Welcome to the Santa documentation
|
||||
|
||||
Santa is a binary and file access authorization system for macOS. It consists of a system extension that allows or denies attempted executions using a set of rules stored in a local database, a GUI agent that notifies the user in case of a block decision, a sync daemon responsible for syncing the database, and a server, and a command-line utility for managing the system.
|
||||
|
||||
Reference in New Issue
Block a user