Skip to content

Commit

Permalink
Add a simple event counter to SNTExecutionController (#694)
Browse files Browse the repository at this point in the history
Add a simple event counter for events per response.
  • Loading branch information
pmarkowsky committed Dec 8, 2021
1 parent e59e610 commit 916fc8c
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 4 deletions.
1 change: 1 addition & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ santa_unit_test(
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncdInterface",
"//Source/common:SantaCache",
"//Source/common:SNTMetricSet",
"@MOLCodesignChecker",
"@MOLXPCConnection",
"@OCMock",
Expand Down
17 changes: 17 additions & 0 deletions Source/santad/SNTExecutionController.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@
#include "Source/common/SNTKernelCommon.h"
#include "Source/santad/EventProviders/SNTEventProvider.h"

const static NSString *kBlockBinary = @"BlockBinary";
const static NSString *kAllowBinary = @"AllowBinary";
const static NSString *kBlockCertificate = @"BlockCertificate";
const static NSString *kAllowCertificate = @"AllowCertificate";
const static NSString *kBlockTeamID = @"BlockTeamID";
const static NSString *kAllowTeamID = @"AllowTeamID";
const static NSString *kBlockScope = @"BlockScope";
const static NSString *kAllowScope = @"AllowScope";
const static NSString *kAllowUnknown = @"AllowUnknown";
const static NSString *kBlockUnknown = @"BlockUnknown";
const static NSString *kAllowCompiler = @"AllowCompiler";
const static NSString *kAllowTransitive = @"AllowTransitive";
const static NSString *kUnknownEventState = @"Unknown";
const static NSString *kBlockPrinterWorkaround = @"BlockPrinterWorkaround";
const static NSString *kAllowNoFileInfo = @"AllowNoFileInfo";
const static NSString *kAllowNullVNode = @"AllowNullVNode";

@class MOLCodesignChecker;
@class SNTDriverManager;
@class SNTEventLog;
Expand Down
36 changes: 36 additions & 0 deletions Source/santad/SNTExecutionController.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <utmpx.h>

#include "Source/common/SNTLogging.h"
#include "Source/common/SNTMetricSet.h"

#import <MOLCodesignChecker/MOLCodesignChecker.h>

Expand Down Expand Up @@ -51,6 +52,7 @@ @interface SNTExecutionController ()
@property SNTPolicyProcessor *policyProcessor;
@property SNTRuleTable *ruleTable;
@property SNTSyncdQueue *syncdQueue;
@property SNTMetricCounter *events;

@property dispatch_queue_t eventQueue;
@end
Expand Down Expand Up @@ -78,17 +80,45 @@ - (instancetype)initWithEventProvider:(id<SNTEventProvider>)eventProvider
// This establishes the XPC connection between libsecurity and syspolicyd.
// Not doing this causes a deadlock as establishing this link goes through xpcproxy.
(void)[[MOLCodesignChecker alloc] initWithSelf];

SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
_events = [metricSet counterWithName:@"/santa/events"
fieldNames:@[ @"action_response" ]
helpText:@"Events processed by Santa per response"];
}
return self;
}

- (void)incrementEventCounters:(SNTEventState)eventType {
const NSString *eventTypeStr;

switch (eventType) {
case SNTEventStateBlockBinary: eventTypeStr = kBlockBinary; break;
case SNTEventStateAllowBinary: eventTypeStr = kAllowBinary; break;
case SNTEventStateBlockCertificate: eventTypeStr = kBlockCertificate; break;
case SNTEventStateAllowCertificate: eventTypeStr = kAllowCertificate; break;
case SNTEventStateBlockTeamID: eventTypeStr = kBlockTeamID; break;
case SNTEventStateAllowTeamID: eventTypeStr = kAllowTeamID; break;
case SNTEventStateBlockScope: eventTypeStr = kBlockScope; break;
case SNTEventStateAllowScope: eventTypeStr = kAllowScope; break;
case SNTEventStateBlockUnknown: eventTypeStr = kBlockUnknown; break;
case SNTEventStateAllowUnknown: eventTypeStr = kAllowUnknown; break;
case SNTEventStateAllowCompiler: eventTypeStr = kAllowCompiler; break;
case SNTEventStateAllowTransitive: eventTypeStr = kAllowTransitive; break;
default: eventTypeStr = kUnknownEventState; break;
}

[_events incrementForFieldValues:@[ (NSString *)eventTypeStr ]];
}

#pragma mark Binary Validation

- (void)validateBinaryWithMessage:(santa_message_t)message {
// Get info about the file. If we can't get this info, allow execution and log an error.
if (unlikely(message.path == NULL)) {
LOGE(@"Path for vnode_id is NULL: %llu/%llu", message.vnode_id.fsid, message.vnode_id.fileid);
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kAllowNullVNode ]];
return;
}

Expand All @@ -97,12 +127,14 @@ - (void)validateBinaryWithMessage:(santa_message_t)message {
if (unlikely(!binInfo)) {
LOGE(@"Failed to read file %@: %@", @(message.path), fileInfoError.localizedDescription);
[self.eventProvider postAction:ACTION_RESPOND_ALLOW forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kAllowNoFileInfo ]];
return;
}

// PrinterProxy workaround, see description above the method for more details.
if ([self printerProxyWorkaround:binInfo]) {
[self.eventProvider postAction:ACTION_RESPOND_DENY forMessage:message];
[self.events incrementForFieldValues:@[ (NSString *)kBlockPrinterWorkaround ]];
return;
}

Expand All @@ -111,6 +143,7 @@ - (void)validateBinaryWithMessage:(santa_message_t)message {
LOGD(@"%@ is larger than %zu. Letting santa-driver know we are working on it.", binInfo.path,
kLargeBinarySize);
[self.eventProvider postAction:ACTION_RESPOND_ACK forMessage:message];
// TODO(markowsky): Maybe add a metric here for how many large executables we're seeing.
}

SNTCachedDecision *cd = [self.policyProcessor decisionForFileInfo:binInfo];
Expand Down Expand Up @@ -139,6 +172,9 @@ - (void)validateBinaryWithMessage:(santa_message_t)message {
// Send the decision to the kernel.
[self.eventProvider postAction:action forMessage:message];

// Increment counters;
[self incrementEventCounters:cd.decision];

// Log to database if necessary.
if (cd.decision != SNTEventStateAllowBinary && cd.decision != SNTEventStateAllowCompiler &&
cd.decision != SNTEventStateAllowTransitive && cd.decision != SNTEventStateAllowCertificate &&
Expand Down
46 changes: 42 additions & 4 deletions Source/santad/SNTExecutionControllerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#import "Source/common/SNTConfigurator.h"
#import "Source/common/SNTFileInfo.h"
#import "Source/common/SNTMetricSet.h"
#import "Source/common/SNTRule.h"
#import "Source/santad/DataLayer/SNTEventTable.h"
#import "Source/santad/DataLayer/SNTRuleTable.h"
Expand Down Expand Up @@ -86,6 +87,25 @@ - (santa_vnode_id_t)getVnodeId {
return (santa_vnode_id_t){.fsid = 1234, .fileid = 5678};
}

- (void)checkMetricCounters:(const NSString *)expectedFieldValueName
expected:(NSNumber *)expectedValue {
SNTMetricSet *metricSet = [SNTMetricSet sharedInstance];
NSDictionary *eventCounter = [metricSet export][@"metrics"][@"/santa/events"];
BOOL foundField;
for (NSDictionary *fieldValue in eventCounter[@"fields"][@"action_response"]) {
if ([expectedFieldValueName isEqualToString:fieldValue[@"value"]]) {
XCTAssertEqualObjects(expectedValue, fieldValue[@"data"],
@"%@ counter does not match expected value", expectedFieldValueName);
foundField = YES;
break;
}
}

if (!foundField) {
XCTFail(@"failed to find %@ field value", expectedFieldValueName);
}
}

- (void)testBinaryAllowRule {
OCMStub([self.mockFileInfo isMachO]).andReturn(YES);
OCMStub([self.mockFileInfo SHA256]).andReturn(@"a");
Expand All @@ -99,6 +119,7 @@ - (void)testBinaryAllowRule {
[self.sut validateBinaryWithMessage:[self getMessage]];

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:@"AllowBinary" expected:@2];
}

- (void)testBinaryBlockRule {
Expand All @@ -114,6 +135,9 @@ - (void)testBinaryBlockRule {
[self.sut validateBinaryWithMessage:[self getMessage]];

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);

// verify that we're incrementing the binary block
[self checkMetricCounters:@"BlockBinary" expected:@1];
}

- (void)testCertificateAllowRule {
Expand All @@ -132,6 +156,7 @@ - (void)testCertificateAllowRule {
[self.sut validateBinaryWithMessage:[self getMessage]];

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowCertificate expected:@1];
}

- (void)testCertificateBlockRule {
Expand All @@ -153,6 +178,7 @@ - (void)testCertificateBlockRule {

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:@"BlockCertificate" expected:@1];
}

- (void)testBinaryAllowCompilerRule {
Expand All @@ -170,6 +196,7 @@ - (void)testBinaryAllowCompilerRule {

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW_COMPILER
forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowCompiler expected:@1];
}

- (void)testBinaryAllowCompilerRuleDisabled {
Expand All @@ -186,6 +213,7 @@ - (void)testBinaryAllowCompilerRuleDisabled {
[self.sut validateBinaryWithMessage:[self getMessage]];

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowBinary expected:@1];
}

- (void)testBinaryAllowTransitiveRule {
Expand All @@ -202,6 +230,8 @@ - (void)testBinaryAllowTransitiveRule {
[self.sut validateBinaryWithMessage:[self getMessage]];

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);

[self checkMetricCounters:@"AllowBinary" expected:@2];
}

- (void)testBinaryAllowTransitiveRuleDisabled {
Expand All @@ -222,6 +252,8 @@ - (void)testBinaryAllowTransitiveRuleDisabled {

OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kAllowBinary expected:@2];
[self checkMetricCounters:kAllowTransitive expected:@1];
}

- (void)testDefaultDecision {
Expand All @@ -238,18 +270,23 @@ - (void)testDefaultDecision {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);

[self checkMetricCounters:kBlockUnknown expected:@2];
[self checkMetricCounters:kAllowUnknown expected:@1];
}

- (void)testOutOfScope {
OCMStub([self.mockFileInfo isMachO]).andReturn(NO);
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
- (void)testMissingShasum {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowScope expected:@1];
}

- (void)testMissingShasum {
- (void)testOutOfScope {
OCMStub([self.mockFileInfo isMachO]).andReturn(NO);
OCMStub([self.mockConfigurator clientMode]).andReturn(SNTClientModeLockdown);
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_ALLOW forMessage:[self getMessage]]);
[self checkMetricCounters:kAllowScope expected:@2];
}

- (void)testPageZero {
Expand All @@ -259,6 +296,7 @@ - (void)testPageZero {
[self.sut validateBinaryWithMessage:[self getMessage]];
OCMVerify([self.mockDriverManager postAction:ACTION_RESPOND_DENY forMessage:[self getMessage]]);
OCMVerifyAllWithDelay(self.mockEventDatabase, 1);
[self checkMetricCounters:kBlockUnknown expected:@3];
}

@end

0 comments on commit 916fc8c

Please sign in to comment.