Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entitlements logging config options #1233

Merged
merged 5 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Source/common/PrefixTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ class PrefixTree {
node_count_ = 0;
}

uint32_t NodeCount() {
absl::ReaderMutexLock lock(&lock_);
return node_count_;
}

#if SANTA_PREFIX_TREE_DEBUG
void Print() {
char buf[max_depth_ + 1];
Expand All @@ -82,11 +87,6 @@ class PrefixTree {
absl::ReaderMutexLock lock(&lock_);
PrintLocked(root_, buf, 0);
}

uint32_t NodeCount() {
absl::ReaderMutexLock lock(&lock_);
return node_count_;
}
#endif

private:
Expand Down
1 change: 1 addition & 0 deletions Source/common/SNTCachedDecision.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
@property NSString *teamID;
@property NSString *signingID;
@property NSDictionary *entitlements;
@property BOOL entitlementsFiltered;

@property NSString *quarantineURL;

Expand Down
12 changes: 12 additions & 0 deletions Source/common/SNTConfigurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,18 @@
///
@property(readonly, nonatomic) NSUInteger metricExportTimeout;

///
mlw marked this conversation as resolved.
Show resolved Hide resolved
/// List of prefix strings for which individual entitlement keys with a matching
/// prefix should not be logged.
///
@property(readonly, nonatomic) NSArray<NSString *> *entitlementsPrefixFilter;

///
/// List of TeamIDs for which entitlements should not be logged. Use the string
/// "platform" to refer to platform binaries.
///
@property(readonly, nonatomic) NSArray<NSString *> *entitlementsTeamIDFilter;

///
/// Retrieve an initialized singleton configurator object using the default file path.
///
Expand Down
36 changes: 36 additions & 0 deletions Source/common/SNTConfigurator.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@
#import "Source/common/SNTStrengthify.h"
#import "Source/common/SNTSystemInfo.h"

// Ensures the given object is an NSArray and only contains NSString value types
static NSArray<NSString *> *EnsureArrayOfStrings(id obj) {
if (![obj isKindOfClass:[NSArray class]]) {
return nil;
}

for (id item in obj) {
if (![item isKindOfClass:[NSString class]]) {
return nil;
}
}

return obj;
}

@interface SNTConfigurator ()
/// A NSUserDefaults object set to use the com.google.santa suite.
@property(readonly, nonatomic) NSUserDefaults *defaults;
Expand Down Expand Up @@ -116,6 +131,9 @@ @implementation SNTConfigurator
static NSString *const kFCMEntity = @"FCMEntity";
static NSString *const kFCMAPIKey = @"FCMAPIKey";

static NSString *const kEntitlementsPrefixFilterKey = @"EntitlementsPrefixFilter";
static NSString *const kEntitlementsTeamIDFilterKey = @"EntitlementsTeamIDFilter";

// The keys managed by a sync server or mobileconfig.
static NSString *const kClientModeKey = @"ClientMode";
static NSString *const kFailClosedKey = @"FailClosed";
Expand Down Expand Up @@ -240,6 +258,8 @@ - (instancetype)init {
kEnableAllEventUploadKey : number,
kDisableUnknownEventUploadKey : number,
kOverrideFileAccessActionKey : string,
kEntitlementsPrefixFilterKey : array,
kEntitlementsTeamIDFilterKey : array,
};
_defaults = [NSUserDefaults standardUserDefaults];
[_defaults addSuiteNamed:@"com.google.santa"];
Expand Down Expand Up @@ -527,6 +547,14 @@ + (NSSet *)keyPathsForValuesAffectingOverrideFileAccessActionKey {
return [self syncAndConfigStateSet];
}

+ (NSSet *)keyPathsForValuesAffectingEntitlementsPrefixFilter {
return [self configStateSet];
}

+ (NSSet *)keyPathsForValuesAffectingEntitlementsTeamIDFilter {
return [self configStateSet];
}

#pragma mark Public Interface

- (SNTClientMode)clientMode {
Expand Down Expand Up @@ -1111,6 +1139,14 @@ - (void)clearSyncState {
self.syncState = [NSMutableDictionary dictionary];
}

- (NSArray *)entitlementsPrefixFilter {
return EnsureArrayOfStrings(self.configState[kEntitlementsPrefixFilterKey]);
}

- (NSArray *)entitlementsTeamIDFilter {
return EnsureArrayOfStrings(self.configState[kEntitlementsTeamIDFilterKey]);
}

#pragma mark Private Defaults Methods

- (NSRegularExpression *)expressionForPattern:(NSString *)pattern {
Expand Down
18 changes: 14 additions & 4 deletions Source/common/santa.proto
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,18 @@ message Entitlement {
optional string value = 2;
}

// Information about entitlements
message EntitlementInfo {
// Whether or not the set of reported entilements is complete or has been
// filtered (e.g. by configuration or clipped because too many to log).
optional bool entitlements_filtered = 1;

// The set of entitlements associated with the target executable
// Only top level keys are represented
// Values (including nested keys) are JSON serialized
repeated Entitlement entitlements = 2;
}

// Information about a process execution event
message Execution {
// The process that executed the new image (e.g. the process that called
Expand Down Expand Up @@ -296,10 +308,8 @@ message Execution {
// Applies when executables are translocated
optional string original_path = 15;

// The set of entitlements associated with the target executable
// Only top level keys are represented
// Values (including nested keys) are JSON serialized
repeated Entitlement entitlements = 16;
// Entitlement information about the target executbale
optional EntitlementInfo entitlement_info = 16;
}

// Information about a fork event
Expand Down
4 changes: 4 additions & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,12 @@ objc_library(
":SNTSyncdQueue",
":TTYWriter",
"//Source/common:BranchPrediction",
"//Source/common:PrefixTree",
"//Source/common:SNTBlockMessage",
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTConfigurator",
"//Source/common:SNTDeepCopy",
"//Source/common:SNTDropRootPrivs",
"//Source/common:SNTFileInfo",
"//Source/common:SNTLogging",
Expand All @@ -249,7 +251,9 @@ objc_library(
"//Source/common:SNTStoredEvent",
"//Source/common:SantaVnode",
"//Source/common:String",
"//Source/common:Unit",
"@MOLCodesignChecker",
"@com_google_absl//absl/synchronization",
],
)

Expand Down
2 changes: 2 additions & 0 deletions Source/santad/EventProviders/AuthResultCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ enum class FlushCacheReason {
kStaticRulesChanged,
kExplicitCommand,
kFilesystemUnmounted,
kEntitlementsPrefixFilterChanged,
kEntitlementsTeamIDFilterChanged,
};

class AuthResultCache {
Expand Down
8 changes: 8 additions & 0 deletions Source/santad/EventProviders/AuthResultCache.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
static NSString *const kFlushCacheReasonStaticRulesChanged = @"StaticRulesChanged";
static NSString *const kFlushCacheReasonExplicitCommand = @"ExplicitCommand";
static NSString *const kFlushCacheReasonFilesystemUnmounted = @"FilesystemUnmounted";
static NSString *const kFlushCacheReasonEntitlementsPrefixFilterChanged =
@"EntitlementsPrefixFilterChanged";
static NSString *const kFlushCacheReasonEntitlementsTeamIDFilterChanged =
@"EntitlementsTeamIDFilterChanged";

namespace santa::santad::event_providers {

Expand Down Expand Up @@ -59,6 +63,10 @@ static inline uint64_t TimestampFromCachedValue(uint64_t cachedValue) {
case FlushCacheReason::kStaticRulesChanged: return kFlushCacheReasonStaticRulesChanged;
case FlushCacheReason::kExplicitCommand: return kFlushCacheReasonExplicitCommand;
case FlushCacheReason::kFilesystemUnmounted: return kFlushCacheReasonFilesystemUnmounted;
case FlushCacheReason::kEntitlementsPrefixFilterChanged:
return kFlushCacheReasonEntitlementsPrefixFilterChanged;
case FlushCacheReason::kEntitlementsTeamIDFilterChanged:
return kFlushCacheReasonEntitlementsTeamIDFilterChanged;
default:
[NSException raise:@"Invalid reason"
format:@"Unknown reason value: %d", static_cast<int>(reason)];
Expand Down
5 changes: 4 additions & 1 deletion Source/santad/EventProviders/AuthResultCacheTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,16 @@ - (void)testFlushCacheReasonToString {
{FlushCacheReason::kStaticRulesChanged, @"StaticRulesChanged"},
{FlushCacheReason::kExplicitCommand, @"ExplicitCommand"},
{FlushCacheReason::kFilesystemUnmounted, @"FilesystemUnmounted"},
{FlushCacheReason::kEntitlementsPrefixFilterChanged, @"EntitlementsPrefixFilterChanged"},
{FlushCacheReason::kEntitlementsTeamIDFilterChanged, @"EntitlementsTeamIDFilterChanged"},
};

for (const auto &kv : reasonToString) {
XCTAssertEqualObjects(FlushCacheReasonToString(kv.first), kv.second);
}

XCTAssertThrows(FlushCacheReasonToString((FlushCacheReason)12345));
XCTAssertThrows(FlushCacheReasonToString(
(FlushCacheReason)(static_cast<int>(FlushCacheReason::kEntitlementsTeamIDFilterChanged) + 1)));
}

@end
19 changes: 13 additions & 6 deletions Source/santad/Logs/EndpointSecurity/Serializers/Protobuf.mm
Original file line number Diff line number Diff line change
Expand Up @@ -485,22 +485,29 @@ id StandardizedNestedObjects(id obj, int level) {
}
}

void EncodeEntitlements(::pbv1::Execution *pb_exec, NSDictionary *entitlements) {
if (!entitlements) {
void EncodeEntitlements(::pbv1::Execution *pb_exec, SNTCachedDecision *cd) {
::pbv1::EntitlementInfo *pb_entitlement_info = pb_exec->mutable_entitlement_info();

pb_entitlement_info->set_entitlements_filtered(cd.entitlementsFiltered != NO);

if (!cd.entitlements) {
return;
}

// Since nested objects with varying types is hard for the API to serialize to
// JSON, first go through and standardize types to ensure better serialization
// as well as a consitent view of data.
entitlements = StandardizedNestedObjects(entitlements, kMaxEncodeObjectLevels);
NSDictionary *entitlements = StandardizedNestedObjects(cd.entitlements, kMaxEncodeObjectLevels);

__block int numObjectsToEncode = (int)std::min(kMaxEncodeObjectEntries, entitlements.count);

pb_exec->mutable_entitlements()->Reserve(numObjectsToEncode);
pb_entitlement_info->mutable_entitlements()->Reserve(numObjectsToEncode);

[entitlements enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (numObjectsToEncode-- == 0) {
// Because entitlements are being clipped, ensure that we update that
// the set of entitlements were filtered.
pb_entitlement_info->set_entitlements_filtered(true);
*stop = YES;
return;
}
Expand Down Expand Up @@ -554,7 +561,7 @@ void EncodeEntitlements(::pbv1::Execution *pb_exec, NSDictionary *entitlements)
return;
}

::pbv1::Entitlement *pb_entitlement = pb_exec->add_entitlements();
::pbv1::Entitlement *pb_entitlement = pb_entitlement_info->add_entitlements();
EncodeString([pb_entitlement] { return pb_entitlement->mutable_key(); },
NSStringToUTF8StringView(key));
EncodeString([pb_entitlement] { return pb_entitlement->mutable_value(); },
Expand Down Expand Up @@ -639,7 +646,7 @@ void EncodeEntitlements(::pbv1::Execution *pb_exec, NSDictionary *entitlements)
NSString *orig_path = Utilities::OriginalPathForTranslocation(msg.es_msg().event.exec.target);
EncodeString([pb_exec] { return pb_exec->mutable_original_path(); }, orig_path);

EncodeEntitlements(pb_exec, cd.entitlements);
EncodeEntitlements(pb_exec, cd);

return FinalizeProto(santa_msg);
}
Expand Down
66 changes: 57 additions & 9 deletions Source/santad/Logs/EndpointSecurity/Serializers/ProtobufTest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

namespace santa::santad::logs::endpoint_security::serializers {
extern void EncodeExitStatus(::pbv1::Exit *pbExit, int exitStatus);
extern void EncodeEntitlements(::pbv1::Execution *pb_exec, NSDictionary *entitlements);
extern void EncodeEntitlements(::pbv1::Execution *pb_exec, SNTCachedDecision *cd);
extern ::pbv1::Execution::Decision GetDecisionEnum(SNTEventState event_state);
extern ::pbv1::Execution::Reason GetReasonEnum(SNTEventState event_state);
extern ::pbv1::Execution::Mode GetModeEnum(SNTClientMode mode);
Expand Down Expand Up @@ -599,19 +599,67 @@ - (void)testSerializeMessageExecJSON {
}

- (void)testEncodeEntitlements {
::pbv1::Execution pbExec;
NSMutableDictionary *ents = [NSMutableDictionary dictionary];
int kMaxEncodeObjectEntries = 64; // From Protobuf.mm
// Test basic encoding without filtered entitlements
{
::pbv1::Execution pbExec;

SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.entitlements = @{@"com.google.test" : @(YES)};

XCTAssertEqual(0, pbExec.entitlement_info().entitlements_size());
XCTAssertFalse(cd.entitlementsFiltered);
XCTAssertEqual(1, cd.entitlements.count);

EncodeEntitlements(&pbExec, cd);

for (int i = 0; i < 100; i++) {
ents[[NSString stringWithFormat:@"k%d", i]] = @(i);
XCTAssertEqual(1, pbExec.entitlement_info().entitlements_size());
XCTAssertTrue(pbExec.entitlement_info().has_entitlements_filtered());
XCTAssertFalse(pbExec.entitlement_info().entitlements_filtered());
}

XCTAssertEqual(0, pbExec.entitlements_size());
// Test basic encoding with filtered entitlements
{
::pbv1::Execution pbExec;

EncodeEntitlements(&pbExec, ents);
SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.entitlements = @{@"com.google.test" : @(YES), @"com.google.test2" : @(NO)};
cd.entitlementsFiltered = YES;

int kMaxEncodeObjectEntries = 64; // From Protobuf.mm
XCTAssertEqual(kMaxEncodeObjectEntries, pbExec.entitlements_size());
XCTAssertEqual(0, pbExec.entitlement_info().entitlements_size());
XCTAssertTrue(cd.entitlementsFiltered);
XCTAssertEqual(2, cd.entitlements.count);

EncodeEntitlements(&pbExec, cd);

XCTAssertEqual(2, pbExec.entitlement_info().entitlements_size());
XCTAssertTrue(pbExec.entitlement_info().has_entitlements_filtered());
XCTAssertTrue(pbExec.entitlement_info().entitlements_filtered());
}

// Test max number of entitlements logged
// When entitlements are clipped, `entitlements_filtered` is set to true
{
::pbv1::Execution pbExec;
NSMutableDictionary *ents = [NSMutableDictionary dictionary];

for (int i = 0; i < 100; i++) {
ents[[NSString stringWithFormat:@"k%d", i]] = @(i);
}

SNTCachedDecision *cd = [[SNTCachedDecision alloc] init];
cd.entitlements = ents;

XCTAssertEqual(0, pbExec.entitlement_info().entitlements_size());
XCTAssertFalse(cd.entitlementsFiltered);
XCTAssertGreaterThan(cd.entitlements.count, kMaxEncodeObjectEntries);

EncodeEntitlements(&pbExec, cd);

XCTAssertEqual(kMaxEncodeObjectEntries, pbExec.entitlement_info().entitlements_size());
XCTAssertTrue(pbExec.entitlement_info().has_entitlements_filtered());
XCTAssertTrue(pbExec.entitlement_info().entitlements_filtered());
}
}

- (void)testSerializeMessageExit {
Expand Down
6 changes: 5 additions & 1 deletion Source/santad/SNTExecutionController.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ const static NSString *kBlockLongPath = @"BlockLongPath";
eventTable:(SNTEventTable *)eventTable
notifierQueue:(SNTNotificationQueue *)notifierQueue
syncdQueue:(SNTSyncdQueue *)syncdQueue
ttyWriter:(std::shared_ptr<santa::santad::TTYWriter>)ttyWriter;
ttyWriter:(std::shared_ptr<santa::santad::TTYWriter>)ttyWriter
entitlementsPrefixFilter:(NSArray<NSString *> *)prefixFilter
entitlementsTeamIDFilter:(NSArray<NSString *> *)teamIDFilter;

///
/// Handles the logic of deciding whether to allow the binary to run or not, sends the response to
Expand All @@ -82,4 +84,6 @@ const static NSString *kBlockLongPath = @"BlockLongPath";
- (bool)synchronousShouldProcessExecEvent:
(const santa::santad::event_providers::endpoint_security::Message &)esMsg;

- (void)updateEntitlementsPrefixFilter:(NSArray<NSString *> *)filter;
- (void)updateEntitlementsTeamIDFilter:(NSArray<NSString *> *)filter;
@end
Loading
Loading