mirror of
https://github.com/google/santa.git
synced 2026-01-15 01:08:12 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
feac080fa7 | ||
|
|
d0f2a0ac4d | ||
|
|
7fc06ea9d8 | ||
|
|
1dfeeac936 | ||
|
|
ac9b5d9399 | ||
|
|
7f3f1c5448 | ||
|
|
46efd6893f | ||
|
|
50232578d6 | ||
|
|
d83be03a20 | ||
|
|
119b29b534 |
2
.github/workflows/sanitizers.yml
vendored
2
.github/workflows/sanitizers.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
DYLIB_PATH="$(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/${CLANG_VERSION}/lib/darwin/libclang_rt.${{ matrix.sanitizer }}_osx_dynamic.dylib"
|
||||
|
||||
bazel test --config=${{ matrix.sanitizer }} \
|
||||
--test_strategy=exclusive --test_output=errors \
|
||||
--test_strategy=exclusive --test_output=all \
|
||||
--test_env=DYLD_INSERT_LIBRARIES=${DYLIB_PATH} \
|
||||
--runs_per_test 5 -t- :unit_tests \
|
||||
--define=SANTA_BUILD_TYPE=adhoc
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
# Santa [](https://github.com/google/santa/actions/workflows/ci.yml)
|
||||
# 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)
|
||||
[](https://github.com/google/santa/releases/latest)
|
||||
[](https://github.com/google/santa/releases/latest)
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/google/santa/main/Source/gui/Resources/Images.xcassets/AppIcon.appiconset/santa-hat-icon-128.png" alt="Santa Icon" />
|
||||
|
||||
@@ -69,6 +69,11 @@ objc_library(
|
||||
hdrs = ["Platform.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "String",
|
||||
hdrs = ["String.h"],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "SantaVnodeHash",
|
||||
srcs = ["SantaVnodeHash.mm"],
|
||||
|
||||
@@ -129,6 +129,12 @@ typedef NS_ENUM(NSInteger, SNTSyncStatusType) {
|
||||
SNTSyncStatusTypeUnknown,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SNTSyncContentEncoding) {
|
||||
SNTSyncContentEncodingNone,
|
||||
SNTSyncContentEncodingDeflate,
|
||||
SNTSyncContentEncodingGzip,
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SNTMetricFormatType) {
|
||||
SNTMetricFormatTypeUnknown,
|
||||
SNTMetricFormatTypeRawJSON,
|
||||
|
||||
@@ -291,6 +291,14 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableSilentMode;
|
||||
|
||||
///
|
||||
/// When silent TTY mode is enabled, Santa will not emit TTY notifications for
|
||||
/// blocked processes.
|
||||
///
|
||||
/// Defaults to NO.
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableSilentTTYMode;
|
||||
|
||||
///
|
||||
/// The text to display when opening Santa.app.
|
||||
/// If unset, the default text will be displayed.
|
||||
@@ -402,6 +410,8 @@
|
||||
///
|
||||
@property(nonatomic) BOOL syncCleanRequired;
|
||||
|
||||
#pragma mark - USB Settings
|
||||
|
||||
///
|
||||
/// USB Mount Blocking. Defaults to false.
|
||||
///
|
||||
@@ -512,6 +522,12 @@
|
||||
///
|
||||
@property(readonly, nonatomic) BOOL enableBackwardsCompatibleContentEncoding;
|
||||
|
||||
///
|
||||
/// If set, "santactl sync" will use the supplied "Content-Encoding", possible
|
||||
/// settings include "gzip", "deflate", "none". If empty defaults to "deflate".
|
||||
///
|
||||
@property(readonly, nonatomic) SNTSyncContentEncoding syncClientContentEncoding;
|
||||
|
||||
///
|
||||
/// Contains the FCM project name.
|
||||
///
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
@@ -72,6 +73,7 @@ static NSString *const kMachineIDPlistFileKey = @"MachineIDPlist";
|
||||
static NSString *const kMachineIDPlistKeyKey = @"MachineIDKey";
|
||||
|
||||
static NSString *const kEnableSilentModeKey = @"EnableSilentMode";
|
||||
static NSString *const kEnableSilentTTYModeKey = @"EnableSilentTTYMode";
|
||||
static NSString *const kAboutTextKey = @"AboutText";
|
||||
static NSString *const kMoreInfoURLKey = @"MoreInfoURL";
|
||||
static NSString *const kEventDetailURLKey = @"EventDetailURL";
|
||||
@@ -107,8 +109,7 @@ static NSString *const kEnableForkAndExitLogging = @"EnableForkAndExitLogging";
|
||||
static NSString *const kIgnoreOtherEndpointSecurityClients = @"IgnoreOtherEndpointSecurityClients";
|
||||
static NSString *const kEnableDebugLogging = @"EnableDebugLogging";
|
||||
|
||||
static NSString *const kEnableBackwardsCompatibleContentEncoding =
|
||||
@"EnableBackwardsCompatibleContentEncoding";
|
||||
static NSString *const kClientContentEncoding = @"SyncClientContentEncoding";
|
||||
|
||||
static NSString *const kFCMProject = @"FCMProject";
|
||||
static NSString *const kFCMEntity = @"FCMEntity";
|
||||
@@ -128,7 +129,6 @@ static NSString *const kBlockedPathRegexKeyDeprecated = @"BlacklistRegex";
|
||||
static NSString *const kEnableAllEventUploadKey = @"EnableAllEventUpload";
|
||||
static NSString *const kDisableUnknownEventUploadKey = @"DisableUnknownEventUpload";
|
||||
|
||||
// TODO(markowsky): move these to sync server only.
|
||||
static NSString *const kMetricFormat = @"MetricFormat";
|
||||
static NSString *const kMetricURL = @"MetricURL";
|
||||
static NSString *const kMetricExportInterval = @"MetricExportInterval";
|
||||
@@ -181,6 +181,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kEnablePageZeroProtectionKey : number,
|
||||
kEnableBadSignatureProtectionKey : number,
|
||||
kEnableSilentModeKey : number,
|
||||
kEnableSilentTTYModeKey : number,
|
||||
kAboutTextKey : string,
|
||||
kMoreInfoURLKey : string,
|
||||
kEventDetailURLKey : string,
|
||||
@@ -198,6 +199,7 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kClientAuthCertificatePasswordKey : string,
|
||||
kClientAuthCertificateCNKey : string,
|
||||
kClientAuthCertificateIssuerKey : string,
|
||||
kClientContentEncoding : string,
|
||||
kServerAuthRootsDataKey : data,
|
||||
kServerAuthRootsFileKey : string,
|
||||
kMachineOwnerKey : string,
|
||||
@@ -219,7 +221,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
kEnableForkAndExitLogging : number,
|
||||
kIgnoreOtherEndpointSecurityClients : number,
|
||||
kEnableDebugLogging : number,
|
||||
kEnableBackwardsCompatibleContentEncoding : number,
|
||||
kFCMProject : string,
|
||||
kFCMEntity : string,
|
||||
kFCMAPIKey : string,
|
||||
@@ -461,10 +462,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingEnableBackwardsCompatibleContentEncoding {
|
||||
return [self configStateSet];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFcmProject {
|
||||
return [self configStateSet];
|
||||
}
|
||||
@@ -645,6 +642,11 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (BOOL)enableSilentTTYMode {
|
||||
NSNumber *number = self.configState[kEnableSilentTTYModeKey];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (NSString *)aboutText {
|
||||
return self.configState[kAboutTextKey];
|
||||
}
|
||||
@@ -708,6 +710,20 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return self.configState[kClientAuthCertificateIssuerKey];
|
||||
}
|
||||
|
||||
- (SNTSyncContentEncoding)syncClientContentEncoding {
|
||||
NSString *contentEncoding = [self.configState[kClientContentEncoding] lowercaseString];
|
||||
if ([contentEncoding isEqualToString:@"deflate"]) {
|
||||
return SNTSyncContentEncodingDeflate;
|
||||
} else if ([contentEncoding isEqualToString:@"gzip"]) {
|
||||
return SNTSyncContentEncodingGzip;
|
||||
} else if ([contentEncoding isEqualToString:@"none"]) {
|
||||
return SNTSyncContentEncodingNone;
|
||||
} else {
|
||||
// Ensure we have the same default zlib behavior Santa's always had otherwise.
|
||||
return SNTSyncContentEncodingDeflate;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)syncServerAuthRootsData {
|
||||
return self.configState[kServerAuthRootsDataKey];
|
||||
}
|
||||
@@ -881,11 +897,6 @@ static NSString *const kSyncCleanRequired = @"SyncCleanRequired";
|
||||
return [number boolValue] || self.debugFlag;
|
||||
}
|
||||
|
||||
- (BOOL)enableBackwardsCompatibleContentEncoding {
|
||||
NSNumber *number = self.configState[kEnableBackwardsCompatibleContentEncoding];
|
||||
return number ? [number boolValue] : NO;
|
||||
}
|
||||
|
||||
- (NSString *)fcmProject {
|
||||
return self.configState[kFCMProject];
|
||||
}
|
||||
|
||||
35
Source/common/String.h
Normal file
35
Source/common/String.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/// Copyright 2023 Google LLC
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
|
||||
#ifndef SANTA__COMMON__STRING_H
|
||||
#define SANTA__COMMON__STRING_H
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace santa::common {
|
||||
|
||||
static inline std::string_view NSStringToUTF8StringView(NSString *str) {
|
||||
return std::string_view(str.UTF8String, [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
static inline std::string NSStringToUTF8String(NSString *str) {
|
||||
return std::string(str.UTF8String, [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
}
|
||||
|
||||
} // namespace santa::common
|
||||
|
||||
#endif
|
||||
@@ -37,7 +37,7 @@ struct SNTDeviceMessageWindowView: View {
|
||||
Text("Device Name").bold()
|
||||
Text("Device BSD Path").bold()
|
||||
|
||||
if event!.remountArgs.count > 0 {
|
||||
if event!.remountArgs?.count ?? 0 > 0 {
|
||||
Text("Remount Mode").bold()
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ struct SNTDeviceMessageWindowView: View {
|
||||
Text(event!.mntonname)
|
||||
Text(event!.mntfromname)
|
||||
|
||||
if event!.remountArgs.count > 0 {
|
||||
if event!.remountArgs?.count ?? 0 > 0 {
|
||||
Text(event!.readableRemountArgs())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ objc_library(
|
||||
objc_library(
|
||||
name = "santactl_lib",
|
||||
srcs = [
|
||||
"main.m",
|
||||
"Commands/SNTCommandFileInfo.m",
|
||||
"Commands/SNTCommandRule.m",
|
||||
"Commands/SNTCommandStatus.m",
|
||||
"Commands/SNTCommandVersion.m",
|
||||
"Commands/SNTCommandMetrics.h",
|
||||
"Commands/SNTCommandMetrics.m",
|
||||
"Commands/SNTCommandRule.m",
|
||||
"Commands/SNTCommandStatus.m",
|
||||
"Commands/SNTCommandSync.m",
|
||||
"Commands/SNTCommandVersion.m",
|
||||
"main.m",
|
||||
] + select({
|
||||
"//:opt_build": [],
|
||||
"//conditions:default": [
|
||||
|
||||
@@ -49,6 +49,7 @@ objc_library(
|
||||
":WatchItemPolicy",
|
||||
"//Source/common:PrefixTree",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:String",
|
||||
"//Source/common:Unit",
|
||||
],
|
||||
)
|
||||
@@ -232,6 +233,7 @@ objc_library(
|
||||
"//Source/common:SNTRule",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:SantaVnode",
|
||||
"//Source/common:String",
|
||||
"@MOLCodesignChecker",
|
||||
],
|
||||
)
|
||||
@@ -382,6 +384,7 @@ objc_library(
|
||||
":EndpointSecurityClient",
|
||||
"//Source/common:SNTCommonEnums",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTMetricSet",
|
||||
"//Source/common:SantaCache",
|
||||
"//Source/common:SantaVnode",
|
||||
"//Source/common:SantaVnodeHash",
|
||||
@@ -468,6 +471,7 @@ objc_library(
|
||||
],
|
||||
deps = [
|
||||
":EndpointSecurityMessage",
|
||||
"//Source/common:String",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -501,6 +505,7 @@ objc_library(
|
||||
"//Source/common:SNTConfigurator",
|
||||
"//Source/common:SNTLogging",
|
||||
"//Source/common:SNTStoredEvent",
|
||||
"//Source/common:String",
|
||||
"//Source/common:santa_cc_proto_library_wrapper",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -34,8 +34,12 @@
|
||||
|
||||
#import "Source/common/PrefixTree.h"
|
||||
#import "Source/common/SNTLogging.h"
|
||||
#import "Source/common/String.h"
|
||||
#import "Source/common/Unit.h"
|
||||
#include "Source/santad/DataLayer/WatchItemPolicy.h"
|
||||
|
||||
using santa::common::NSStringToUTF8String;
|
||||
using santa::common::NSStringToUTF8StringView;
|
||||
using santa::common::PrefixTree;
|
||||
using santa::common::Unit;
|
||||
using santa::santad::data_layer::WatchItemPathType;
|
||||
@@ -255,7 +259,7 @@ std::variant<Unit, PathList> VerifyConfigWatchItemPaths(NSArray<id> *paths, NSEr
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
path_list.push_back({std::string(path_str.UTF8String, path_str.length), path_type});
|
||||
path_list.push_back({NSStringToUTF8String(path_str), path_type});
|
||||
} else if ([path isKindOfClass:[NSString class]]) {
|
||||
if (!LenRangeValidator(1, PATH_MAX)(path, err)) {
|
||||
PopulateError(err, [NSString stringWithFormat:@"Invalid path length: %@",
|
||||
@@ -264,8 +268,8 @@ std::variant<Unit, PathList> VerifyConfigWatchItemPaths(NSArray<id> *paths, NSEr
|
||||
return Unit{};
|
||||
}
|
||||
|
||||
path_list.push_back({std::string(((NSString *)path).UTF8String, ((NSString *)path).length),
|
||||
kWatchItemPolicyDefaultPathType});
|
||||
path_list.push_back(
|
||||
{NSStringToUTF8String(((NSString *)path)), kWatchItemPolicyDefaultPathType});
|
||||
} else {
|
||||
PopulateError(
|
||||
err, [NSString stringWithFormat:
|
||||
@@ -340,12 +344,11 @@ std::variant<Unit, ProcessList> VerifyConfigWatchItemProcesses(NSDictionary *wat
|
||||
}
|
||||
|
||||
proc_list.push_back(WatchItemPolicy::Process(
|
||||
std::string([(process[kWatchItemConfigKeyProcessesBinaryPath] ?: @"") UTF8String]),
|
||||
std::string([(process[kWatchItemConfigKeyProcessesSigningID] ?: @"") UTF8String]),
|
||||
std::string([(process[kWatchItemConfigKeyProcessesTeamID] ?: @"") UTF8String]),
|
||||
NSStringToUTF8String(process[kWatchItemConfigKeyProcessesBinaryPath] ?: @""),
|
||||
NSStringToUTF8String(process[kWatchItemConfigKeyProcessesSigningID] ?: @""),
|
||||
NSStringToUTF8String(process[kWatchItemConfigKeyProcessesTeamID] ?: @""),
|
||||
HexStringToBytes(process[kWatchItemConfigKeyProcessesCDHash]),
|
||||
std::string(
|
||||
[(process[kWatchItemConfigKeyProcessesCertificateSha256] ?: @"") UTF8String]),
|
||||
NSStringToUTF8String(process[kWatchItemConfigKeyProcessesCertificateSha256] ?: @""),
|
||||
process[kWatchItemConfigKeyProcessesPlatformBinary]
|
||||
? std::make_optional(
|
||||
(bool)[process[kWatchItemConfigKeyProcessesPlatformBinary] boolValue])
|
||||
@@ -423,8 +426,8 @@ bool ParseConfigSingleWatchItem(NSString *name, NSDictionary *watch_item,
|
||||
|
||||
for (const PathAndTypePair &path_type_pair : std::get<PathList>(path_list)) {
|
||||
policies.push_back(std::make_shared<WatchItemPolicy>(
|
||||
[name UTF8String], path_type_pair.first, path_type_pair.second, allow_read_access, audit_only,
|
||||
std::get<ProcessList>(proc_list)));
|
||||
NSStringToUTF8StringView(name), path_type_pair.first, path_type_pair.second,
|
||||
allow_read_access, audit_only, std::get<ProcessList>(proc_list)));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -645,7 +648,7 @@ void WatchItems::UpdateCurrentState(
|
||||
std::swap(currently_monitored_paths_, new_monitored_paths);
|
||||
current_config_ = new_config;
|
||||
if (new_config) {
|
||||
policy_version_ = [new_config[kWatchItemConfigKeyVersion] UTF8String];
|
||||
policy_version_ = NSStringToUTF8String(new_config[kWatchItemConfigKeyVersion]);
|
||||
} else {
|
||||
policy_version_ = "";
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <memory>
|
||||
|
||||
#import "Source/common/SNTCommonEnums.h"
|
||||
#import "Source/common/SNTMetricSet.h"
|
||||
#include "Source/common/SantaCache.h"
|
||||
#import "Source/common/SantaVnode.h"
|
||||
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
|
||||
@@ -33,16 +34,29 @@ enum class FlushCacheMode {
|
||||
kAllCaches,
|
||||
};
|
||||
|
||||
enum class FlushCacheReason {
|
||||
kClientModeChanged,
|
||||
kPathRegexChanged,
|
||||
kRulesChanged,
|
||||
kStaticRulesChanged,
|
||||
kExplicitCommand,
|
||||
kFilesystemUnmounted,
|
||||
};
|
||||
|
||||
class AuthResultCache {
|
||||
public:
|
||||
// Santa currently only flushes caches when new DENY rules are added, not
|
||||
// ALLOW rules. This means this value should be low enough so that if a
|
||||
// ALLOW rules. This means cache_deny_time_ms should be low enough so that if a
|
||||
// previously denied binary is allowed, it can be re-executed by the user in a
|
||||
// timely manner. But the value should be high enough to allow the cache to be
|
||||
// effective in the event the binary is executed in rapid succession.
|
||||
static std::unique_ptr<AuthResultCache> Create(
|
||||
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
|
||||
SNTMetricSet *metric_set, uint64_t cache_deny_time_ms = 1500);
|
||||
|
||||
AuthResultCache(
|
||||
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi,
|
||||
uint64_t cache_deny_time_ms = 1500);
|
||||
SNTMetricCounter *flush_count, uint64_t cache_deny_time_ms = 1500);
|
||||
virtual ~AuthResultCache();
|
||||
|
||||
AuthResultCache(AuthResultCache &&other) = delete;
|
||||
@@ -55,7 +69,7 @@ class AuthResultCache {
|
||||
virtual SNTAction CheckCache(const es_file_t *es_file);
|
||||
virtual SNTAction CheckCache(SantaVnode vnode_id);
|
||||
|
||||
virtual void FlushCache(FlushCacheMode mode);
|
||||
virtual void FlushCache(FlushCacheMode mode, FlushCacheReason reason);
|
||||
|
||||
virtual NSArray<NSNumber *> *CacheCounts();
|
||||
|
||||
@@ -66,6 +80,7 @@ class AuthResultCache {
|
||||
SantaCache<SantaVnode, uint64_t> *nonroot_cache_;
|
||||
|
||||
std::shared_ptr<santa::santad::event_providers::endpoint_security::EndpointSecurityAPI> esapi_;
|
||||
SNTMetricCounter *flush_count_;
|
||||
uint64_t root_devno_;
|
||||
uint64_t cache_deny_time_ns_;
|
||||
dispatch_queue_t q_;
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
using santa::santad::event_providers::endpoint_security::Client;
|
||||
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
|
||||
|
||||
static NSString *const kFlushCacheReasonClientModeChanged = @"ClientModeChanged";
|
||||
static NSString *const kFlushCacheReasonPathRegexChanged = @"PathRegexChanged";
|
||||
static NSString *const kFlushCacheReasonRulesChanged = @"RulesChanged";
|
||||
static NSString *const kFlushCacheReasonStaticRulesChanged = @"StaticRulesChanged";
|
||||
static NSString *const kFlushCacheReasonExplicitCommand = @"ExplicitCommand";
|
||||
static NSString *const kFlushCacheReasonFilesystemUnmounted = @"FilesystemUnmounted";
|
||||
|
||||
namespace santa::santad::event_providers {
|
||||
|
||||
static inline uint64_t GetCurrentUptime() {
|
||||
@@ -44,9 +51,36 @@ static inline uint64_t TimestampFromCachedValue(uint64_t cachedValue) {
|
||||
return (cachedValue & ~(0xFF00000000000000));
|
||||
}
|
||||
|
||||
NSString *const FlushCacheReasonToString(FlushCacheReason reason) {
|
||||
switch (reason) {
|
||||
case FlushCacheReason::kClientModeChanged: return kFlushCacheReasonClientModeChanged;
|
||||
case FlushCacheReason::kPathRegexChanged: return kFlushCacheReasonPathRegexChanged;
|
||||
case FlushCacheReason::kRulesChanged: return kFlushCacheReasonRulesChanged;
|
||||
case FlushCacheReason::kStaticRulesChanged: return kFlushCacheReasonStaticRulesChanged;
|
||||
case FlushCacheReason::kExplicitCommand: return kFlushCacheReasonExplicitCommand;
|
||||
case FlushCacheReason::kFilesystemUnmounted: return kFlushCacheReasonFilesystemUnmounted;
|
||||
default:
|
||||
[NSException raise:@"Invalid reason" format:@"Unknown reason value: %d", reason];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AuthResultCache> AuthResultCache::Create(std::shared_ptr<EndpointSecurityAPI> esapi,
|
||||
SNTMetricSet *metric_set,
|
||||
uint64_t cache_deny_time_ms) {
|
||||
SNTMetricCounter *flush_count =
|
||||
[metric_set counterWithName:@"/santa/flush_count"
|
||||
fieldNames:@[ @"Reason" ]
|
||||
helpText:@"Count of times the auth result cache is flushed by reason"];
|
||||
|
||||
return std::make_unique<AuthResultCache>(esapi, flush_count, cache_deny_time_ms);
|
||||
}
|
||||
|
||||
AuthResultCache::AuthResultCache(std::shared_ptr<EndpointSecurityAPI> esapi,
|
||||
uint64_t cache_deny_time_ms)
|
||||
: esapi_(esapi), cache_deny_time_ns_(cache_deny_time_ms * NSEC_PER_MSEC) {
|
||||
SNTMetricCounter *flush_count, uint64_t cache_deny_time_ms)
|
||||
: esapi_(esapi),
|
||||
flush_count_(flush_count),
|
||||
cache_deny_time_ns_(cache_deny_time_ms * NSEC_PER_MSEC) {
|
||||
root_cache_ = new SantaCache<SantaVnode, uint64_t>();
|
||||
nonroot_cache_ = new SantaCache<SantaVnode, uint64_t>();
|
||||
|
||||
@@ -118,7 +152,7 @@ SantaCache<SantaVnode, uint64_t> *AuthResultCache::CacheForVnodeID(SantaVnode vn
|
||||
return (vnode_id.fsid == root_devno_ || root_devno_ == 0) ? root_cache_ : nonroot_cache_;
|
||||
}
|
||||
|
||||
void AuthResultCache::FlushCache(FlushCacheMode mode) {
|
||||
void AuthResultCache::FlushCache(FlushCacheMode mode, FlushCacheReason reason) {
|
||||
nonroot_cache_->clear();
|
||||
if (mode == FlushCacheMode::kAllCaches) {
|
||||
root_cache_->clear();
|
||||
@@ -134,6 +168,8 @@ void AuthResultCache::FlushCache(FlushCacheMode mode) {
|
||||
shared_esapi->ClearCache(Client());
|
||||
});
|
||||
}
|
||||
|
||||
[flush_count_ incrementForFieldValues:@[ FlushCacheReasonToString(reason) ]];
|
||||
}
|
||||
|
||||
NSArray<NSNumber *> *AuthResultCache::CacheCounts() {
|
||||
|
||||
@@ -29,6 +29,13 @@
|
||||
|
||||
using santa::santad::event_providers::AuthResultCache;
|
||||
using santa::santad::event_providers::FlushCacheMode;
|
||||
using santa::santad::event_providers::FlushCacheReason;
|
||||
|
||||
namespace santa::santad::event_providers {
|
||||
extern NSString *const FlushCacheReasonToString(FlushCacheReason reason);
|
||||
}
|
||||
|
||||
using santa::santad::event_providers::FlushCacheReasonToString;
|
||||
|
||||
// Grab the st_dev number of the root volume to match the root cache
|
||||
static uint64_t RootDevno() {
|
||||
@@ -66,14 +73,14 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
|
||||
- (void)testEmptyCacheExpectedNumberOfCacheCounts {
|
||||
auto esapi = std::make_shared<MockEndpointSecurityAPI>();
|
||||
auto cache = std::make_shared<AuthResultCache>(esapi);
|
||||
std::shared_ptr<AuthResultCache> cache = AuthResultCache::Create(esapi, nil);
|
||||
|
||||
AssertCacheCounts(cache, 0, 0);
|
||||
}
|
||||
|
||||
- (void)testBasicOperation {
|
||||
auto esapi = std::make_shared<MockEndpointSecurityAPI>();
|
||||
auto cache = std::make_shared<AuthResultCache>(esapi);
|
||||
std::shared_ptr<AuthResultCache> cache = AuthResultCache::Create(esapi, nil);
|
||||
|
||||
es_file_t rootFile = MakeCacheableFile(RootDevno(), 111);
|
||||
es_file_t nonrootFile = MakeCacheableFile(RootDevno() + 123, 222);
|
||||
@@ -110,7 +117,7 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
|
||||
- (void)testFlushCache {
|
||||
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
|
||||
auto cache = std::make_shared<AuthResultCache>(mockESApi);
|
||||
std::shared_ptr<AuthResultCache> cache = AuthResultCache::Create(mockESApi, nil);
|
||||
|
||||
es_file_t rootFile = MakeCacheableFile(RootDevno(), 111);
|
||||
es_file_t nonrootFile = MakeCacheableFile(RootDevno() + 123, 111);
|
||||
@@ -121,7 +128,7 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
AssertCacheCounts(cache, 1, 1);
|
||||
|
||||
// Flush non-root only
|
||||
cache->FlushCache(FlushCacheMode::kNonRootOnly);
|
||||
cache->FlushCache(FlushCacheMode::kNonRootOnly, FlushCacheReason::kClientModeChanged);
|
||||
|
||||
AssertCacheCounts(cache, 1, 0);
|
||||
|
||||
@@ -138,7 +145,7 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
dispatch_semaphore_signal(sema);
|
||||
return true;
|
||||
}));
|
||||
cache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
cache->FlushCache(FlushCacheMode::kAllCaches, FlushCacheReason::kClientModeChanged);
|
||||
|
||||
XCTAssertEqual(0,
|
||||
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC)),
|
||||
@@ -151,7 +158,7 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
|
||||
- (void)testCacheStateMachine {
|
||||
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
|
||||
auto cache = std::make_shared<AuthResultCache>(mockESApi);
|
||||
std::shared_ptr<AuthResultCache> cache = AuthResultCache::Create(mockESApi, nil);
|
||||
|
||||
es_file_t rootFile = MakeCacheableFile(RootDevno(), 111);
|
||||
|
||||
@@ -193,7 +200,7 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
auto mockESApi = std::make_shared<MockEndpointSecurityAPI>();
|
||||
// Create a cache with a lowered cache expiry value
|
||||
uint64_t expiryMS = 250;
|
||||
auto cache = std::make_shared<AuthResultCache>(mockESApi, expiryMS);
|
||||
std::shared_ptr<AuthResultCache> cache = AuthResultCache::Create(mockESApi, nil, expiryMS);
|
||||
|
||||
es_file_t rootFile = MakeCacheableFile(RootDevno(), 111);
|
||||
|
||||
@@ -215,4 +222,21 @@ static inline void AssertCacheCounts(std::shared_ptr<AuthResultCache> cache, uin
|
||||
AssertCacheCounts(cache, 0, 0);
|
||||
}
|
||||
|
||||
- (void)testFlushCacheReasonToString {
|
||||
std::map<FlushCacheReason, NSString *> reasonToString = {
|
||||
{FlushCacheReason::kClientModeChanged, @"ClientModeChanged"},
|
||||
{FlushCacheReason::kPathRegexChanged, @"PathRegexChanged"},
|
||||
{FlushCacheReason::kRulesChanged, @"RulesChanged"},
|
||||
{FlushCacheReason::kStaticRulesChanged, @"StaticRulesChanged"},
|
||||
{FlushCacheReason::kExplicitCommand, @"ExplicitCommand"},
|
||||
{FlushCacheReason::kFilesystemUnmounted, @"FilesystemUnmounted"},
|
||||
};
|
||||
|
||||
for (const auto &kv : reasonToString) {
|
||||
XCTAssertEqualObjects(FlushCacheReasonToString(kv.first), kv.second);
|
||||
}
|
||||
|
||||
XCTAssertThrows(FlushCacheReasonToString((FlushCacheReason)12345));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -234,7 +234,7 @@ class MockAuthResultCache : public AuthResultCache {
|
||||
mockESApi->SetExpectationsESNewClient();
|
||||
mockESApi->SetExpectationsRetainReleaseMessage();
|
||||
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr);
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr, nil);
|
||||
EXPECT_CALL(*mockAuthCache, CheckCache)
|
||||
.WillOnce(testing::Return(SNTActionRequestBinary))
|
||||
.WillOnce(testing::Return(SNTActionRequestBinary))
|
||||
@@ -301,7 +301,7 @@ class MockAuthResultCache : public AuthResultCache {
|
||||
mockESApi->SetExpectationsESNewClient();
|
||||
mockESApi->SetExpectationsRetainReleaseMessage();
|
||||
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr);
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr, nil);
|
||||
EXPECT_CALL(*mockAuthCache, AddToCache(&execFile, SNTActionRespondAllowCompiler))
|
||||
.WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(*mockAuthCache, AddToCache(&execFile, SNTActionRespondAllow))
|
||||
|
||||
@@ -414,6 +414,13 @@ using santa::santad::event_providers::endpoint_security::Message;
|
||||
|
||||
[client processEnrichedMessage:enrichedMsg
|
||||
handler:^(std::shared_ptr<EnrichedMessage> msg) {
|
||||
// reset the shared_ptr to drop the held message.
|
||||
// This is a workaround for a TSAN only false positive
|
||||
// which happens if we switch back to the sem wait
|
||||
// after signaling, but _before_ the implicit release
|
||||
// of msg. In that case, the mock verify and the
|
||||
// call of the mock's Release method can data race.
|
||||
msg.reset();
|
||||
dispatch_semaphore_signal(sema);
|
||||
}];
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
using santa::santad::EventDisposition;
|
||||
using santa::santad::event_providers::AuthResultCache;
|
||||
using santa::santad::event_providers::FlushCacheMode;
|
||||
using santa::santad::event_providers::FlushCacheReason;
|
||||
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
|
||||
using santa::santad::event_providers::endpoint_security::Message;
|
||||
using santa::santad::logs::endpoint_security::Logger;
|
||||
@@ -172,6 +173,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)handleMessage:(Message &&)esMsg
|
||||
recordEventMetrics:(void (^)(EventDisposition))recordEventMetrics {
|
||||
// Process the unmount event first so that caches are flushed before any
|
||||
// other potential early returns.
|
||||
if (esMsg->event_type == ES_EVENT_TYPE_NOTIFY_UNMOUNT) {
|
||||
self->_authResultCache->FlushCache(FlushCacheMode::kNonRootOnly,
|
||||
FlushCacheReason::kFilesystemUnmounted);
|
||||
recordEventMetrics(EventDisposition::kProcessed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self.blockUSBMount) {
|
||||
// TODO: We should also unsubscribe from events when this isn't set, but
|
||||
// this is generally a low-volume event type.
|
||||
@@ -180,12 +190,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return;
|
||||
}
|
||||
|
||||
if (esMsg->event_type == ES_EVENT_TYPE_NOTIFY_UNMOUNT) {
|
||||
self->_authResultCache->FlushCache(FlushCacheMode::kNonRootOnly);
|
||||
recordEventMetrics(EventDisposition::kProcessed);
|
||||
return;
|
||||
}
|
||||
|
||||
[self processMessage:std::move(esMsg)
|
||||
handler:^(const Message &msg) {
|
||||
es_auth_result_t result = [self handleAuthMount:msg];
|
||||
|
||||
@@ -39,13 +39,14 @@
|
||||
using santa::santad::EventDisposition;
|
||||
using santa::santad::event_providers::AuthResultCache;
|
||||
using santa::santad::event_providers::FlushCacheMode;
|
||||
using santa::santad::event_providers::FlushCacheReason;
|
||||
using santa::santad::event_providers::endpoint_security::Message;
|
||||
|
||||
class MockAuthResultCache : public AuthResultCache {
|
||||
public:
|
||||
using AuthResultCache::AuthResultCache;
|
||||
|
||||
MOCK_METHOD(void, FlushCache, (FlushCacheMode mode));
|
||||
MOCK_METHOD(void, FlushCache, (FlushCacheMode mode, FlushCacheReason reason));
|
||||
};
|
||||
|
||||
@interface SNTEndpointSecurityDeviceManager (Testing)
|
||||
@@ -316,7 +317,7 @@ class MockAuthResultCache : public AuthResultCache {
|
||||
mockESApi->SetExpectationsESNewClient();
|
||||
mockESApi->SetExpectationsRetainReleaseMessage();
|
||||
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr);
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr, nil);
|
||||
EXPECT_CALL(*mockAuthCache, FlushCache);
|
||||
|
||||
SNTEndpointSecurityDeviceManager *deviceManager =
|
||||
|
||||
@@ -105,7 +105,7 @@ class MockLogger : public Logger {
|
||||
auto mockEnricher = std::make_shared<MockEnricher>();
|
||||
EXPECT_CALL(*mockEnricher, Enrich).WillOnce(testing::Return(enrichedMsg));
|
||||
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr);
|
||||
auto mockAuthCache = std::make_shared<MockAuthResultCache>(nullptr, nil);
|
||||
EXPECT_CALL(*mockAuthCache, RemoveFromCache(&targetFile)).Times(1);
|
||||
|
||||
dispatch_semaphore_t semaMetrics = dispatch_semaphore_create(0);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
@@ -30,6 +31,7 @@
|
||||
#import "Source/common/SNTConfigurator.h"
|
||||
#include "Source/common/SNTLogging.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#import "Source/common/String.h"
|
||||
#include "Source/santad/EventProviders/EndpointSecurity/EndpointSecurityAPI.h"
|
||||
#include "Source/santad/Logs/EndpointSecurity/Serializers/Utilities.h"
|
||||
#import "Source/santad/SNTDecisionCache.h"
|
||||
@@ -38,6 +40,7 @@
|
||||
using google::protobuf::Arena;
|
||||
using google::protobuf::Timestamp;
|
||||
|
||||
using santa::common::NSStringToUTF8StringView;
|
||||
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
|
||||
using santa::santad::event_providers::endpoint_security::EnrichedClose;
|
||||
using santa::santad::event_providers::endpoint_security::EnrichedEventType;
|
||||
@@ -94,15 +97,15 @@ static inline void EncodePath(std::string *buf, const es_file_t *es_file) {
|
||||
buf->append(std::string_view(es_file->path.data, es_file->path.length));
|
||||
}
|
||||
|
||||
static inline void EncodeString(std::string *buf, NSString *value) {
|
||||
static inline void EncodeString(std::function<std::string *()> lazy_f, NSString *value) {
|
||||
if (value) {
|
||||
buf->append(std::string_view([value UTF8String], [value length]));
|
||||
lazy_f()->append(NSStringToUTF8StringView(value));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void EncodeString(std::string *buf, std::string_view value) {
|
||||
static inline void EncodeString(std::function<std::string *()> lazy_f, std::string_view value) {
|
||||
if (value.length() > 0) {
|
||||
buf->append(std::string_view(value.data(), value.length()));
|
||||
lazy_f()->append(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +128,7 @@ static inline void EncodeGroupInfo(::pbv1::GroupInfo *pb_group_info, gid_t gid,
|
||||
static inline void EncodeHash(::pbv1::Hash *pb_hash, NSString *sha256) {
|
||||
if (sha256) {
|
||||
pb_hash->set_type(::pbv1::Hash::HASH_ALGO_SHA256);
|
||||
pb_hash->set_hash([sha256 UTF8String], [sha256 length]);
|
||||
EncodeString([pb_hash] { return pb_hash->mutable_hash(); }, sha256);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ static inline void EncodeFileInfo(::pbv1::FileInfo *pb_file, const es_file_t *es
|
||||
|
||||
static inline void EncodeFileInfoLight(::pbv1::FileInfoLight *pb_file, std::string_view path,
|
||||
bool truncated) {
|
||||
EncodeString(pb_file->mutable_path(), path);
|
||||
EncodeString([pb_file] { return pb_file->mutable_path(); }, path);
|
||||
pb_file->set_truncated(truncated);
|
||||
}
|
||||
|
||||
@@ -262,9 +265,7 @@ static inline void EncodeCertificateInfo(::pbv1::CertificateInfo *pb_cert_info,
|
||||
EncodeHash(pb_cert_info->mutable_hash(), cert_hash);
|
||||
}
|
||||
|
||||
if (common_name) {
|
||||
pb_cert_info->set_common_name([common_name UTF8String], [common_name length]);
|
||||
}
|
||||
EncodeString([pb_cert_info] { return pb_cert_info->mutable_common_name(); }, common_name);
|
||||
}
|
||||
|
||||
::pbv1::Execution::Decision GetDecisionEnum(SNTEventState event_state) {
|
||||
@@ -356,7 +357,7 @@ static inline void EncodeCertificateInfo(::pbv1::CertificateInfo *pb_cert_info,
|
||||
::pbv1::SantaMessage *santa_msg = Arena::CreateMessage<::pbv1::SantaMessage>(arena);
|
||||
|
||||
if (EnabledMachineID()) {
|
||||
EncodeString(santa_msg->mutable_machine_id(), MachineID());
|
||||
EncodeString([santa_msg] { return santa_msg->mutable_machine_id(); }, MachineID());
|
||||
}
|
||||
EncodeTimestamp(santa_msg->mutable_event_time(), event_time);
|
||||
EncodeTimestamp(santa_msg->mutable_processed_time(), processed_time);
|
||||
@@ -491,18 +492,11 @@ std::vector<uint8_t> Protobuf::SerializeMessage(const EnrichedExec &msg, SNTCach
|
||||
EncodeCertificateInfo(pb_exec->mutable_certificate_info(), cd.certSHA256, cd.certCommonName);
|
||||
}
|
||||
|
||||
if (cd.decisionExtra) {
|
||||
pb_exec->set_explain([cd.decisionExtra UTF8String], [cd.decisionExtra length]);
|
||||
}
|
||||
|
||||
if (cd.quarantineURL) {
|
||||
pb_exec->set_quarantine_url([cd.quarantineURL UTF8String], [cd.quarantineURL length]);
|
||||
}
|
||||
EncodeString([pb_exec] { return pb_exec->mutable_explain(); }, cd.decisionExtra);
|
||||
EncodeString([pb_exec] { return pb_exec->mutable_quarantine_url(); }, cd.quarantineURL);
|
||||
|
||||
NSString *orig_path = Utilities::OriginalPathForTranslocation(msg.es_msg().event.exec.target);
|
||||
if (orig_path) {
|
||||
pb_exec->set_original_path([orig_path UTF8String], [orig_path length]);
|
||||
}
|
||||
EncodeString([pb_exec] { return pb_exec->mutable_original_path(); }, orig_path);
|
||||
|
||||
return FinalizeProto(santa_msg);
|
||||
}
|
||||
@@ -594,8 +588,9 @@ std::vector<uint8_t> Protobuf::SerializeFileAccess(const std::string &policy_ver
|
||||
EncodeProcessInfo(file_access->mutable_instigator(), msg->version, msg->process,
|
||||
enriched_process);
|
||||
EncodeFileInfoLight(file_access->mutable_target(), target, false);
|
||||
EncodeString(file_access->mutable_policy_version(), policy_version);
|
||||
EncodeString(file_access->mutable_policy_name(), policy_name);
|
||||
EncodeString([file_access] { return file_access->mutable_policy_version(); }, policy_version);
|
||||
EncodeString([file_access] { return file_access->mutable_policy_name(); }, policy_name);
|
||||
|
||||
file_access->set_access_type(GetAccessType(msg->event_type));
|
||||
file_access->set_policy_decision(GetPolicyDecision(decision));
|
||||
|
||||
@@ -629,10 +624,12 @@ std::vector<uint8_t> Protobuf::SerializeBundleHashingEvent(SNTStoredEvent *event
|
||||
|
||||
EncodeHash(pb_bundle->mutable_file_hash(), event.fileSHA256);
|
||||
EncodeHash(pb_bundle->mutable_bundle_hash(), event.fileBundleHash);
|
||||
pb_bundle->set_bundle_name([NonNull(event.fileBundleName) UTF8String]);
|
||||
pb_bundle->set_bundle_id([NonNull(event.fileBundleID) UTF8String]);
|
||||
pb_bundle->set_bundle_path([NonNull(event.fileBundlePath) UTF8String]);
|
||||
pb_bundle->set_path([NonNull(event.filePath) UTF8String]);
|
||||
EncodeString([pb_bundle] { return pb_bundle->mutable_bundle_name(); },
|
||||
NonNull(event.fileBundleName));
|
||||
EncodeString([pb_bundle] { return pb_bundle->mutable_bundle_id(); }, NonNull(event.fileBundleID));
|
||||
EncodeString([pb_bundle] { return pb_bundle->mutable_bundle_path(); },
|
||||
NonNull(event.fileBundlePath));
|
||||
EncodeString([pb_bundle] { return pb_bundle->mutable_path(); }, NonNull(event.filePath));
|
||||
|
||||
return FinalizeProto(santa_msg);
|
||||
}
|
||||
@@ -652,14 +649,14 @@ static void EncodeDisk(::pbv1::Disk *pb_disk, ::pbv1::Disk_Action action, NSDict
|
||||
stringWithFormat:@"%@ %@", NonNull(props[@"DADeviceVendor"]), NonNull(props[@"DADeviceModel"])];
|
||||
model = [model stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
|
||||
EncodeString(pb_disk->mutable_mount(), [props[@"DAVolumePath"] path]);
|
||||
EncodeString(pb_disk->mutable_volume(), props[@"DAVolumeName"]);
|
||||
EncodeString(pb_disk->mutable_bsd_name(), props[@"DAMediaBSDName"]);
|
||||
EncodeString(pb_disk->mutable_fs(), props[@"DAVolumeKind"]);
|
||||
EncodeString(pb_disk->mutable_model(), model);
|
||||
EncodeString(pb_disk->mutable_serial(), serial);
|
||||
EncodeString(pb_disk->mutable_bus(), props[@"DADeviceProtocol"]);
|
||||
EncodeString(pb_disk->mutable_dmg_path(), dmg_path);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_mount(); }, [props[@"DAVolumePath"] path]);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_volume(); }, props[@"DAVolumeName"]);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_bsd_name(); }, props[@"DAMediaBSDName"]);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_fs(); }, props[@"DAVolumeKind"]);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_model(); }, model);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_serial(); }, serial);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_bus(); }, props[@"DADeviceProtocol"]);
|
||||
EncodeString([pb_disk] { return pb_disk->mutable_dmg_path(); }, dmg_path);
|
||||
|
||||
if (props[@"DAAppearanceTime"]) {
|
||||
// Note: `DAAppearanceTime` is set via `CFAbsoluteTimeGetCurrent`, which uses the defined
|
||||
|
||||
@@ -51,8 +51,7 @@ class SanitizableString {
|
||||
friend std::ostream &operator<<(std::ostream &ss, const SanitizableString &sani_string);
|
||||
|
||||
private:
|
||||
const char *data_;
|
||||
size_t length_;
|
||||
std::string_view data_;
|
||||
mutable bool sanitized_ = false;
|
||||
mutable std::optional<std::string> sanitized_string_;
|
||||
};
|
||||
|
||||
@@ -14,37 +14,35 @@
|
||||
|
||||
#include "Source/santad/Logs/EndpointSecurity/Serializers/SanitizableString.h"
|
||||
|
||||
#include "Source/common/String.h"
|
||||
|
||||
using santa::common::NSStringToUTF8StringView;
|
||||
|
||||
namespace santa::santad::logs::endpoint_security::serializers {
|
||||
|
||||
SanitizableString::SanitizableString(const es_file_t *file)
|
||||
: data_(file->path.data), length_(file->path.length) {}
|
||||
: data_(file->path.data, file->path.length) {}
|
||||
|
||||
SanitizableString::SanitizableString(const es_string_token_t &tok)
|
||||
: data_(tok.data), length_(tok.length) {}
|
||||
SanitizableString::SanitizableString(const es_string_token_t &tok) : data_(tok.data, tok.length) {}
|
||||
|
||||
SanitizableString::SanitizableString(NSString *str)
|
||||
: data_([str UTF8String]), length_([str length]) {}
|
||||
SanitizableString::SanitizableString(NSString *str) : data_(NSStringToUTF8StringView(str)) {}
|
||||
|
||||
SanitizableString::SanitizableString(const char *str, size_t len) : data_(str), length_(len) {}
|
||||
SanitizableString::SanitizableString(const char *str, size_t len) : data_(str, len) {}
|
||||
|
||||
std::string_view SanitizableString::String() const {
|
||||
return std::string_view(data_, length_);
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::string_view SanitizableString::Sanitized() const {
|
||||
if (!sanitized_) {
|
||||
sanitized_ = true;
|
||||
sanitized_string_ = SanitizeString(data_, length_);
|
||||
sanitized_string_ = SanitizeString(data_.data(), data_.length());
|
||||
}
|
||||
|
||||
if (sanitized_string_.has_value()) {
|
||||
return sanitized_string_.value();
|
||||
} else {
|
||||
if (data_) {
|
||||
return std::string_view(data_, length_);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ using santa::santad::data_layer::WatchItems;
|
||||
using santa::santad::data_layer::WatchItemsState;
|
||||
using santa::santad::event_providers::AuthResultCache;
|
||||
using santa::santad::event_providers::FlushCacheMode;
|
||||
using santa::santad::event_providers::FlushCacheReason;
|
||||
using santa::santad::logs::endpoint_security::Logger;
|
||||
|
||||
// Globals used by the santad watchdog thread
|
||||
@@ -84,12 +85,9 @@ double watchdogRAMPeak = 0;
|
||||
reply([counts[0] unsignedLongLongValue], [counts[1] unsignedLongLongValue]);
|
||||
}
|
||||
|
||||
- (void)flushAllCaches {
|
||||
self->_authResultCache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
}
|
||||
|
||||
- (void)flushCache:(void (^)(BOOL))reply {
|
||||
[self flushAllCaches];
|
||||
self->_authResultCache->FlushCache(FlushCacheMode::kAllCaches,
|
||||
FlushCacheReason::kExplicitCommand);
|
||||
reply(YES);
|
||||
}
|
||||
|
||||
@@ -125,7 +123,7 @@ double watchdogRAMPeak = 0;
|
||||
// The actual cache flushing happens after the new rules have been added to the database.
|
||||
if (flushCache) {
|
||||
LOGI(@"Flushing caches");
|
||||
[self flushAllCaches];
|
||||
self->_authResultCache->FlushCache(FlushCacheMode::kAllCaches, FlushCacheReason::kRulesChanged);
|
||||
}
|
||||
|
||||
reply(error);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#import "Source/common/SNTRule.h"
|
||||
#import "Source/common/SNTStoredEvent.h"
|
||||
#include "Source/common/SantaVnode.h"
|
||||
#include "Source/common/String.h"
|
||||
#import "Source/santad/DataLayer/SNTEventTable.h"
|
||||
#import "Source/santad/DataLayer/SNTRuleTable.h"
|
||||
#import "Source/santad/SNTDecisionCache.h"
|
||||
@@ -293,7 +294,7 @@ static NSString *const kPrinterProxyPostMonterey =
|
||||
NSAttributedString *s = [SNTBlockMessage attributedBlockMessageForEvent:se
|
||||
customMessage:cd.customMsg];
|
||||
|
||||
if (targetProc->tty && targetProc->tty->path.length > 0) {
|
||||
if (targetProc->tty && targetProc->tty->path.length > 0 && !config.enableSilentTTYMode) {
|
||||
NSMutableString *msg = [NSMutableString stringWithCapacity:1024];
|
||||
[msg appendFormat:@"\n\033[1mSanta\033[0m\n\n%@\n\n", s.string];
|
||||
[msg appendFormat:@"\033[1mPath: \033[0m %@\n"
|
||||
@@ -360,7 +361,8 @@ static NSString *const kPrinterProxyPostMonterey =
|
||||
|
||||
- (void)printMessage:(NSString *)msg toTTY:(const char *)path {
|
||||
int fd = open(path, O_WRONLY | O_NOCTTY);
|
||||
write(fd, msg.UTF8String, msg.length);
|
||||
std::string_view str = santa::common::NSStringToUTF8StringView(msg);
|
||||
write(fd, str.data(), str.length());
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ using santa::santad::Metrics;
|
||||
using santa::santad::data_layer::WatchItems;
|
||||
using santa::santad::event_providers::AuthResultCache;
|
||||
using santa::santad::event_providers::FlushCacheMode;
|
||||
using santa::santad::event_providers::FlushCacheReason;
|
||||
using santa::santad::event_providers::endpoint_security::EndpointSecurityAPI;
|
||||
using santa::santad::event_providers::endpoint_security::Enricher;
|
||||
using santa::santad::logs::endpoint_security::Logger;
|
||||
@@ -141,34 +142,33 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
|
||||
EstablishSyncServiceConnection(syncd_queue);
|
||||
|
||||
NSArray<SNTKVOManager *> *kvoObservers = @[
|
||||
[[SNTKVOManager alloc] initWithObject:configurator
|
||||
selector:@selector(clientMode)
|
||||
type:[NSNumber class]
|
||||
callback:^(NSNumber *oldValue, NSNumber *newValue) {
|
||||
if ([oldValue longLongValue] == [newValue longLongValue]) {
|
||||
// Note: This case apparently can happen and if not checked
|
||||
// will result in excessive notification messages sent to the
|
||||
// user when calling `postClientModeNotification` below
|
||||
return;
|
||||
}
|
||||
[[SNTKVOManager alloc]
|
||||
initWithObject:configurator
|
||||
selector:@selector(clientMode)
|
||||
type:[NSNumber class]
|
||||
callback:^(NSNumber *oldValue, NSNumber *newValue) {
|
||||
if ([oldValue longLongValue] == [newValue longLongValue]) {
|
||||
// Note: This case apparently can happen and if not checked
|
||||
// will result in excessive notification messages sent to the
|
||||
// user when calling `postClientModeNotification` below
|
||||
return;
|
||||
}
|
||||
|
||||
SNTClientMode clientMode =
|
||||
(SNTClientMode)[newValue longLongValue];
|
||||
SNTClientMode clientMode = (SNTClientMode)[newValue longLongValue];
|
||||
|
||||
switch (clientMode) {
|
||||
case SNTClientModeLockdown:
|
||||
LOGI(@"Changed client mode to Lockdown, flushing cache.");
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
break;
|
||||
case SNTClientModeMonitor:
|
||||
LOGI(@"Changed client mode to Monitor.");
|
||||
break;
|
||||
default: LOGW(@"Changed client mode to unknown value."); break;
|
||||
}
|
||||
switch (clientMode) {
|
||||
case SNTClientModeLockdown:
|
||||
LOGI(@"Changed client mode to Lockdown, flushing cache.");
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
|
||||
FlushCacheReason::kClientModeChanged);
|
||||
break;
|
||||
case SNTClientModeMonitor: LOGI(@"Changed client mode to Monitor."); break;
|
||||
default: LOGW(@"Changed client mode to unknown value."); break;
|
||||
}
|
||||
|
||||
[[notifier_queue.notifierConnection remoteObjectProxy]
|
||||
postClientModeNotification:clientMode];
|
||||
}],
|
||||
[[notifier_queue.notifierConnection remoteObjectProxy]
|
||||
postClientModeNotification:clientMode];
|
||||
}],
|
||||
[[SNTKVOManager alloc]
|
||||
initWithObject:configurator
|
||||
selector:@selector(syncBaseURL)
|
||||
@@ -233,7 +233,8 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
|
||||
}
|
||||
|
||||
LOGI(@"Changed allowlist regex, flushing cache");
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
|
||||
FlushCacheReason::kPathRegexChanged);
|
||||
}],
|
||||
[[SNTKVOManager alloc]
|
||||
initWithObject:configurator
|
||||
@@ -246,7 +247,8 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
|
||||
}
|
||||
|
||||
LOGI(@"Changed denylist regex, flushing cache");
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches,
|
||||
FlushCacheReason::kPathRegexChanged);
|
||||
}],
|
||||
[[SNTKVOManager alloc] initWithObject:configurator
|
||||
selector:@selector(blockUSBMount)
|
||||
@@ -324,7 +326,9 @@ void SantadMain(std::shared_ptr<EndpointSecurityAPI> esapi, std::shared_ptr<Logg
|
||||
callback:^(NSArray *oldValue, NSArray *newValue) {
|
||||
if ([oldValue isEqual:newValue]) return;
|
||||
LOGI(@"StaticRules set has changed, flushing cache.");
|
||||
auth_result_cache->FlushCache(FlushCacheMode::kAllCaches);
|
||||
auth_result_cache->FlushCache(
|
||||
FlushCacheMode::kAllCaches,
|
||||
FlushCacheReason::kStaticRulesChanged);
|
||||
}],
|
||||
[[SNTKVOManager alloc]
|
||||
initWithObject:configurator
|
||||
|
||||
@@ -49,6 +49,8 @@ class SantadDeps {
|
||||
std::unique_ptr<santa::santad::logs::endpoint_security::Logger> logger,
|
||||
std::shared_ptr<santa::santad::Metrics> metrics,
|
||||
std::shared_ptr<santa::santad::data_layer::WatchItems> watch_items,
|
||||
std::shared_ptr<santa::santad::event_providers::AuthResultCache>
|
||||
auth_result_cache,
|
||||
MOLXPCConnection *control_connection,
|
||||
SNTCompilerController *compiler_controller,
|
||||
SNTNotificationQueue *notifier_queue, SNTSyncdQueue *syncd_queue,
|
||||
|
||||
@@ -135,25 +135,31 @@ std::unique_ptr<SantadDeps> SantadDeps::Create(SNTConfigurator *configurator,
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return std::make_unique<SantadDeps>(
|
||||
esapi, std::move(logger), std::move(metrics), std::move(watch_items), control_connection,
|
||||
compiler_controller, notifier_queue, syncd_queue, exec_controller, prefix_tree);
|
||||
std::shared_ptr<::AuthResultCache> auth_result_cache = AuthResultCache::Create(esapi, metric_set);
|
||||
if (!auth_result_cache) {
|
||||
LOGE(@"Failed to create auth result cache");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return std::make_unique<SantadDeps>(esapi, std::move(logger), std::move(metrics),
|
||||
std::move(watch_items), std::move(auth_result_cache),
|
||||
control_connection, compiler_controller, notifier_queue,
|
||||
syncd_queue, exec_controller, prefix_tree);
|
||||
}
|
||||
|
||||
SantadDeps::SantadDeps(std::shared_ptr<EndpointSecurityAPI> esapi, std::unique_ptr<::Logger> logger,
|
||||
std::shared_ptr<::Metrics> metrics,
|
||||
std::shared_ptr<::WatchItems> watch_items,
|
||||
MOLXPCConnection *control_connection,
|
||||
SNTCompilerController *compiler_controller,
|
||||
SNTNotificationQueue *notifier_queue, SNTSyncdQueue *syncd_queue,
|
||||
SNTExecutionController *exec_controller,
|
||||
std::shared_ptr<::PrefixTree<Unit>> prefix_tree)
|
||||
SantadDeps::SantadDeps(
|
||||
std::shared_ptr<EndpointSecurityAPI> esapi, std::unique_ptr<::Logger> logger,
|
||||
std::shared_ptr<::Metrics> metrics, std::shared_ptr<::WatchItems> watch_items,
|
||||
std::shared_ptr<santa::santad::event_providers::AuthResultCache> auth_result_cache,
|
||||
MOLXPCConnection *control_connection, SNTCompilerController *compiler_controller,
|
||||
SNTNotificationQueue *notifier_queue, SNTSyncdQueue *syncd_queue,
|
||||
SNTExecutionController *exec_controller, std::shared_ptr<::PrefixTree<Unit>> prefix_tree)
|
||||
: esapi_(std::move(esapi)),
|
||||
logger_(std::move(logger)),
|
||||
metrics_(std::move(metrics)),
|
||||
watch_items_(std::move(watch_items)),
|
||||
enricher_(std::make_shared<::Enricher>()),
|
||||
auth_result_cache_(std::make_shared<::AuthResultCache>(esapi_)),
|
||||
auth_result_cache_(std::move(auth_result_cache)),
|
||||
control_connection_(control_connection),
|
||||
compiler_controller_(compiler_controller),
|
||||
notifier_queue_(notifier_queue),
|
||||
|
||||
@@ -54,6 +54,7 @@ static const uint8_t kMaxEnqueuedSyncs = 2;
|
||||
@property NSUInteger eventBatchSize;
|
||||
|
||||
@property NSString *xsrfToken;
|
||||
@property NSString *xsrfTokenHeader;
|
||||
|
||||
@end
|
||||
|
||||
@@ -125,6 +126,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
[self startReachability];
|
||||
}
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
self.xsrfTokenHeader = syncState.xsrfTokenHeader;
|
||||
}
|
||||
|
||||
- (void)postBundleEventToSyncServer:(SNTStoredEvent *)event
|
||||
@@ -158,6 +160,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
[self startReachability];
|
||||
}
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
self.xsrfTokenHeader = syncState.xsrfTokenHeader;
|
||||
}
|
||||
|
||||
- (void)isFCMListening:(void (^)(BOOL))reply {
|
||||
@@ -229,6 +232,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
BOOL ret = [p sync];
|
||||
LOGD(@"Rule download %@", ret ? @"complete" : @"failed");
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
self.xsrfTokenHeader = syncState.xsrfTokenHeader;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,6 +258,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
if ([p sync]) {
|
||||
SLOGD(@"Preflight complete");
|
||||
self.xsrfToken = syncState.xsrfToken;
|
||||
self.xsrfTokenHeader = syncState.xsrfTokenHeader;
|
||||
|
||||
// Clean up reachability if it was started for a non-network error
|
||||
[self stopReachability];
|
||||
@@ -355,6 +360,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
}
|
||||
|
||||
syncState.xsrfToken = self.xsrfToken;
|
||||
syncState.xsrfTokenHeader = self.xsrfTokenHeader;
|
||||
|
||||
NSURLSessionConfiguration *sessConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessConfig.connectionProxyDictionary = [[SNTConfigurator configurator] syncProxyConfig];
|
||||
@@ -391,10 +397,7 @@ static void reachabilityHandler(SCNetworkReachabilityRef target, SCNetworkReacha
|
||||
|
||||
syncState.session = [authURLSession session];
|
||||
syncState.daemonConn = self.daemonConn;
|
||||
|
||||
syncState.compressedContentEncoding =
|
||||
config.enableBackwardsCompatibleContentEncoding ? @"zlib" : @"deflate";
|
||||
|
||||
syncState.contentEncoding = config.syncClientContentEncoding;
|
||||
syncState.pushNotificationsToken = self.pushNotifications.token;
|
||||
|
||||
return syncState;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
/// limitations under the License.
|
||||
|
||||
#import "Source/santasyncservice/SNTSyncStage.h"
|
||||
#include "Source/common/SNTCommonEnums.h"
|
||||
|
||||
#import <MOLXPCConnection/MOLXPCConnection.h>
|
||||
|
||||
@@ -70,10 +71,27 @@
|
||||
NSString *xsrfHeader = self.syncState.xsrfTokenHeader ?: kDefaultXSRFTokenHeader;
|
||||
[req setValue:self.syncState.xsrfToken forHTTPHeaderField:xsrfHeader];
|
||||
|
||||
NSData *compressed = [requestBody zlibCompressed];
|
||||
NSData *compressed;
|
||||
NSString *contentEncodingHeader;
|
||||
|
||||
switch (self.syncState.contentEncoding) {
|
||||
case SNTSyncContentEncodingNone: break;
|
||||
case SNTSyncContentEncodingGzip:
|
||||
compressed = [requestBody gzipCompressed];
|
||||
contentEncodingHeader = @"gzip";
|
||||
break;
|
||||
case SNTSyncContentEncodingDeflate:
|
||||
compressed = [requestBody zlibCompressed];
|
||||
contentEncodingHeader = @"deflate";
|
||||
break;
|
||||
default:
|
||||
// This would be a programming error.
|
||||
LOGD(@"Unexpected value for content encoding %ld", self.syncState.contentEncoding);
|
||||
}
|
||||
|
||||
if (compressed) {
|
||||
requestBody = compressed;
|
||||
[req setValue:self.syncState.compressedContentEncoding forHTTPHeaderField:@"Content-Encoding"];
|
||||
[req setValue:contentEncodingHeader forHTTPHeaderField:@"Content-Encoding"];
|
||||
}
|
||||
|
||||
[req setHTTPBody:requestBody];
|
||||
@@ -183,9 +201,15 @@
|
||||
}
|
||||
|
||||
- (NSData *)stripXssi:(NSData *)data {
|
||||
static const char xssi[3] = {']', ')', '}'};
|
||||
if (data.length < 3 || strncmp(data.bytes, xssi, 3)) return data;
|
||||
return [data subdataWithRange:NSMakeRange(3, data.length - 3)];
|
||||
static const char xssiOne[5] = {')', ']', '}', '\'', '\n'};
|
||||
static const char xssiTwo[3] = {']', ')', '}'};
|
||||
if (data.length >= sizeof(xssiOne) && strncmp(data.bytes, xssiOne, sizeof(xssiOne)) == 0) {
|
||||
return [data subdataWithRange:NSMakeRange(sizeof(xssiOne), data.length - sizeof(xssiOne))];
|
||||
}
|
||||
if (data.length >= sizeof(xssiTwo) && strncmp(data.bytes, xssiTwo, sizeof(xssiTwo)) == 0) {
|
||||
return [data subdataWithRange:NSMakeRange(sizeof(xssiTwo), data.length - sizeof(xssiTwo))];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
- (BOOL)fetchXSRFToken {
|
||||
|
||||
@@ -74,8 +74,7 @@
|
||||
/// Array of bundle IDs to find binaries for.
|
||||
@property NSArray *bundleBinaryRequests;
|
||||
|
||||
/// The header value for ContentEncoding when sending compressed content.
|
||||
/// Either "deflate" (default) or "zlib".
|
||||
@property(copy) NSString *compressedContentEncoding;
|
||||
/// The content-encoding to use for the client uploads during the sync session.
|
||||
@property SNTSyncContentEncoding contentEncoding;
|
||||
|
||||
@end
|
||||
|
||||
@@ -39,6 +39,10 @@
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SNTSyncStage (XSSI)
|
||||
- (NSData *)stripXssi:(NSData *)data;
|
||||
@end
|
||||
|
||||
@interface SNTSyncTest : XCTestCase
|
||||
@property SNTSyncState *syncState;
|
||||
@property id<SNTDaemonControlXPC> daemonConnRop;
|
||||
@@ -156,6 +160,27 @@
|
||||
|
||||
#pragma mark - SNTSyncStage Tests
|
||||
|
||||
- (void)testStripXssi {
|
||||
SNTSyncStage *sut = [[SNTSyncStage alloc] initWithState:self.syncState];
|
||||
|
||||
char wantChar[3] = {'"', 'a', '"'};
|
||||
NSData *want = [NSData dataWithBytes:wantChar length:3];
|
||||
|
||||
char dOne[8] = {')', ']', '}', '\'', '\n', '"', 'a', '"'};
|
||||
XCTAssertEqualObjects([sut stripXssi:[NSData dataWithBytes:dOne length:8]], want, @"");
|
||||
|
||||
char dTwo[6] = {']', ')', '}', '"', 'a', '"'};
|
||||
XCTAssertEqualObjects([sut stripXssi:[NSData dataWithBytes:dTwo length:6]], want, @"");
|
||||
|
||||
char dThree[5] = {')', ']', '}', '\'', '\n'};
|
||||
XCTAssertEqualObjects([sut stripXssi:[NSData dataWithBytes:dThree length:5]], [NSData data], @"");
|
||||
|
||||
char dFour[3] = {']', ')', '}'};
|
||||
XCTAssertEqualObjects([sut stripXssi:[NSData dataWithBytes:dFour length:3]], [NSData data], @"");
|
||||
|
||||
XCTAssertEqualObjects([sut stripXssi:want], want, @"");
|
||||
}
|
||||
|
||||
- (void)testBaseFetchXSRFTokenSuccess {
|
||||
// NOTE: This test only works if the other tests don't return a 403 and run before this test.
|
||||
// The XSRF fetching code is inside a dispatch_once.
|
||||
|
||||
@@ -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. |
|
||||
| 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. |
|
||||
| EnableSilentTTYMode | Bool | If true, Santa will not post any TTY 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. |
|
||||
@@ -71,6 +72,7 @@ also known as mobileconfig files, which are in an Apple-specific XML format.
|
||||
| RemountUSBMode | 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. |
|
||||
| FileAccessPolicyPlist | String | (BETA) Path to a file access configuration plist. |
|
||||
| FileAccessPolicyUpdateIntervalSec | Integer | (BETA) Number of seconds between re-reading the file access policy config and policies/monitored paths updated. |
|
||||
| SyncClientContentEncoding | String | Sets the Content-Encoding header for requests sent to the sync service. Acceptable values are "deflate", "gzip", "none" (Defaults to deflate.) |
|
||||
|
||||
|
||||
\*overridable by the sync server: run `santactl status` to check the current
|
||||
|
||||
Reference in New Issue
Block a user