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

Creating transitive rules for rename events should fallback to destination path #1299

Merged
merged 2 commits into from
Feb 28, 2024
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
1 change: 1 addition & 0 deletions Source/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ objc_library(
hdrs = ["SNTFileInfo.h"],
deps = [
":SNTLogging",
":SantaVnode",
"@FMDB",
"@MOLCodesignChecker",
],
Expand Down
2 changes: 2 additions & 0 deletions Source/common/SNTCachedDecision.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
///
@interface SNTCachedDecision : NSObject

- (instancetype)init;
- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile;
- (instancetype)initWithVnode:(SantaVnode)vnode NS_DESIGNATED_INITIALIZER;

@property SantaVnode vnodeId;
@property SNTEventState decision;
Expand Down
10 changes: 9 additions & 1 deletion Source/common/SNTCachedDecision.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@

@implementation SNTCachedDecision

- (instancetype)init {
return [self initWithVnode:(SantaVnode){}];
}

- (instancetype)initWithEndpointSecurityFile:(const es_file_t *)esFile {
return [self initWithVnode:SantaVnode::VnodeForFile(esFile)];
}

- (instancetype)initWithVnode:(SantaVnode)vnode {
self = [super init];
if (self) {
_vnodeId = SantaVnode::VnodeForFile(esFile);
_vnodeId = vnode;
}
return self;
}
Expand Down
7 changes: 7 additions & 0 deletions Source/common/SNTFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#import <EndpointSecurity/EndpointSecurity.h>
#import <Foundation/Foundation.h>

#import "Source/common/SantaVnode.h"

@class MOLCodesignChecker;

///
Expand Down Expand Up @@ -220,6 +222,11 @@
///
- (NSUInteger)fileSize;

///
/// @return The devno/ino pair of the file
///
- (SantaVnode)vnode;

///
/// @return The underlying file handle.
///
Expand Down
2 changes: 2 additions & 0 deletions Source/common/SNTFileInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ @interface SNTFileInfo ()
@property NSString *path;
@property NSFileHandle *fileHandle;
@property NSUInteger fileSize;
@property SantaVnode vnode;
@property NSString *fileOwnerHomeDir;
@property NSString *sha256Storage;

Expand Down Expand Up @@ -110,6 +111,7 @@ - (instancetype)initWithResolvedPath:(NSString *)path
}

_fileSize = fileStat->st_size;
_vnode = (SantaVnode){.fsid = fileStat->st_dev, .fileid = fileStat->st_ino};

if (_fileSize == 0) return nil;

Expand Down
1 change: 1 addition & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,7 @@ santa_unit_test(
"//Source/common:SNTCachedDecision",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTRule",
"//Source/common:SantaVnode",
"//Source/common:TestUtils",
"@OCMock",
],
Expand Down
57 changes: 36 additions & 21 deletions Source/santad/SNTCompilerController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,21 @@ - (void)setProcess:(const audit_token_t &)tok isCompiler:(bool)isCompiler {
// Adds a fake cached decision to SNTDecisionCache for pending files. If the file
// is executed before we can create a transitive rule for it, then we can at
// least log the pending decision info.
- (void)saveFakeDecision:(const es_file_t *)esFile {
SNTCachedDecision *cd = [[SNTCachedDecision alloc] initWithEndpointSecurityFile:esFile];
- (void)saveFakeDecision:(SNTFileInfo *)fileInfo {
SNTCachedDecision *cd = [[SNTCachedDecision alloc] initWithVnode:fileInfo.vnode];
cd.decision = SNTEventStateAllowPendingTransitive;
cd.sha256 = @"pending";
[[SNTDecisionCache sharedCache] cacheDecision:cd];
}

- (void)removeFakeDecision:(const es_file_t *)esFile {
[[SNTDecisionCache sharedCache] forgetCachedDecisionForFile:esFile->stat];
- (void)removeFakeDecision:(SNTFileInfo *)fileInfo {
[[SNTDecisionCache sharedCache] forgetCachedDecisionForVnode:fileInfo.vnode];
}

- (BOOL)handleEvent:(const Message &)esMsg withLogger:(std::shared_ptr<Logger>)logger {
const es_file_t *targetFile = NULL;
SNTFileInfo *targetFile;
NSString *targetPath;
NSError *error;

switch (esMsg->event_type) {
case ES_EVENT_TYPE_NOTIFY_CLOSE:
Expand All @@ -90,7 +92,9 @@ - (BOOL)handleEvent:(const Message &)esMsg withLogger:(std::shared_ptr<Logger>)l
return NO;
}

targetFile = esMsg->event.close.target;
targetPath = @(esMsg->event.close.target->path.data);
targetFile = [[SNTFileInfo alloc] initWithEndpointSecurityFile:esMsg->event.close.target
error:&error];

break;
case ES_EVENT_TYPE_NOTIFY_RENAME:
Expand All @@ -105,7 +109,24 @@ - (BOOL)handleEvent:(const Message &)esMsg withLogger:(std::shared_ptr<Logger>)l
return NO;
}

targetFile = esMsg->event.rename.source;
targetFile = [[SNTFileInfo alloc] initWithEndpointSecurityFile:esMsg->event.rename.source
error:&error];
if (!targetFile) {
LOGD(@"Unable to locate source file for rename event while creating transitive. Falling "
@"back to destination. Path: %s, Error: %@",
esMsg->event.rename.source->path.data, error);
if (esMsg->event.rename.destination_type == ES_DESTINATION_TYPE_EXISTING_FILE) {
targetPath = @(esMsg->event.rename.destination.existing_file->path.data);
targetFile = [[SNTFileInfo alloc]
initWithEndpointSecurityFile:esMsg->event.rename.destination.existing_file
error:&error];
} else {
targetPath = [NSString
stringWithFormat:@"%s/%s", esMsg->event.rename.destination.new_path.dir->path.data,
esMsg->event.rename.destination.new_path.filename.data];
targetFile = [[SNTFileInfo alloc] initWithPath:targetPath error:&error];
}
}

break;
case ES_EVENT_TYPE_NOTIFY_EXIT:
Expand All @@ -119,6 +140,9 @@ - (BOOL)handleEvent:(const Message &)esMsg withLogger:(std::shared_ptr<Logger>)l
[self createTransitiveRule:esMsg target:targetFile logger:logger];
return YES;
} else {
LOGD(@"Unable to create SNTFileInfo while attempting to create transitive rule. Event: %d | "
@"Path: %@ | Error: %@",
(int)esMsg->event_type, targetPath, error);
return NO;
}
}
Expand All @@ -127,30 +151,21 @@ - (BOOL)handleEvent:(const Message &)esMsg withLogger:(std::shared_ptr<Logger>)l
// compiler. It checks if the closed file is executable, and if so, transitively allowlists it.
// The passed in message contains the pid of the writing process and path of closed file.
- (void)createTransitiveRule:(const Message &)esMsg
target:(const es_file_t *)targetFile
target:(SNTFileInfo *)targetFile
logger:(std::shared_ptr<Logger>)logger {
NSError *error = nil;
SNTFileInfo *fi = [[SNTFileInfo alloc] initWithEndpointSecurityFile:targetFile error:&error];
if (error) {
LOGD(@"Unable to create SNTFileInfo while attempting to create transitive rule. Event: %d | "
@"Path: %@ | Error: %@",
(int)esMsg->event_type, @(targetFile->path.data), error);
return;
}

[self saveFakeDecision:targetFile];

// Check if this file is an executable.
if (fi.isExecutable) {
if (targetFile.isExecutable) {
// Check if there is an existing (non-transitive) rule for this file. We leave existing rules
// alone, so that a allowlist or blocklist rule can't be overwritten by a transitive one.
SNTRuleTable *ruleTable = [SNTDatabaseController ruleTable];
SNTRule *prevRule = [ruleTable ruleForIdentifiers:(struct RuleIdentifiers){
.binarySHA256 = fi.SHA256,
.binarySHA256 = targetFile.SHA256,
}];
if (!prevRule || prevRule.state == SNTRuleStateAllowTransitive) {
// Construct a new transitive allowlist rule for the executable.
SNTRule *rule = [[SNTRule alloc] initWithIdentifier:fi.SHA256
SNTRule *rule = [[SNTRule alloc] initWithIdentifier:targetFile.SHA256
state:SNTRuleStateAllowTransitive
type:SNTRuleTypeBinary
customMsg:@""];
Expand All @@ -160,7 +175,7 @@ - (void)createTransitiveRule:(const Message &)esMsg
if (![ruleTable addRules:@[ rule ] ruleCleanup:SNTRuleCleanupNone error:&err]) {
LOGE(@"unable to add new transitive rule to database: %@", err.localizedDescription);
} else {
logger->LogAllowlist(esMsg, [fi.SHA256 UTF8String]);
logger->LogAllowlist(esMsg, [targetFile.SHA256 UTF8String]);
}
}
}
Expand Down
Loading
Loading