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

Add transitive whitelisting to Santa #224

Merged
merged 46 commits into from
Jul 20, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8f972eb
prototype code for santa transitive whitelisting
nguyen-phillip Nov 2, 2017
48b656d
prototype code for santa transitive whitelisting
nguyen-phillip Nov 2, 2017
1a9d07b
added a separate message queue to handle events related to compiler w…
nguyen-phillip Dec 4, 2017
0d77a2c
Merge remote-tracking branch 'origin/master' into compiler
nguyen-phillip Dec 4, 2017
a594175
forgot to add updated project.pbxproj file with commit
nguyen-phillip Dec 5, 2017
881322f
Replaced functionality of compiler vnode cache with new ACTION_RESPON…
nguyen-phillip Dec 7, 2017
7a32d31
Removed separate compiler message queue; send these messages to decis…
nguyen-phillip Dec 7, 2017
0f9d63a
Remove references to DevelopmentTeam from project.pbxproj
nguyen-phillip Dec 7, 2017
163ac9b
Define KAUTH_FILEOP_WRITE as a larger constant to avoid future conflicts
nguyen-phillip Dec 7, 2017
47a981e
Remove reference to Podfile.lock
nguyen-phillip Dec 7, 2017
16092cd
replace copyright notice
nguyen-phillip Dec 7, 2017
0b106e4
moved compiler process exit monitoring from userspace to kernelspace
nguyen-phillip Dec 8, 2017
9c4c7fa
switched from IOCreateThread to kernel_thread_start and friends
nguyen-phillip Dec 8, 2017
6348fd1
added timestamps to transitive rules & extra info to santactl status
nguyen-phillip Dec 8, 2017
04d8f41
Send transitive rule state back to kernel so that it is stored in dec…
nguyen-phillip Dec 12, 2017
fef9351
update timestamp of transitive rules whenever they are accessed for e…
nguyen-phillip Dec 12, 2017
0e840d2
some minor cleanup
nguyen-phillip Dec 12, 2017
c4b47e1
Stop watching RENAME actions for transitive whitelisting. Don't look …
nguyen-phillip Dec 12, 2017
43ce8aa
add ability for sync server to send down WHITELIST_COMPILER rules and…
nguyen-phillip Dec 13, 2017
83649ee
made SNTRule timestamp property readonly, plus other fixes
nguyen-phillip Dec 13, 2017
079a6d3
rename refreshTimestampForRule: to resetTimestampForRule
nguyen-phillip Dec 13, 2017
dc10e23
refresh transitive rule timestamps based on ACTION_NOTIFY_EXEC messag…
nguyen-phillip Dec 14, 2017
d35f0b5
fix broken testPreflighDatabaseCounts
nguyen-phillip Dec 14, 2017
6cb4ad5
don't pass SantaDecisionManager ref to process monitoring threads, in…
nguyen-phillip Dec 15, 2017
aa01a79
remove most of the debugging cruft
nguyen-phillip Jan 4, 2018
a08e7f8
some minor fixes
nguyen-phillip Jan 8, 2018
88b0972
fix merge conflict
nguyen-phillip Jan 8, 2018
0cb3d6a
Merge branch 'master' into compiler
nguyen-phillip Jan 8, 2018
1cd9551
Merge branch 'master' into compiler
nguyen-phillip Jan 8, 2018
8f82370
added compiler and transitive rule tests to SNTExecutionControllerTest.m
nguyen-phillip Jan 8, 2018
1b687a1
Merge branch 'compiler' of https://github.com/nguyen-phillip/santa in…
nguyen-phillip Jan 8, 2018
d2df097
merge with upstream, renumber ACTION_RESPOND_ALLOW enums
nguyen-phillip Jan 24, 2018
340f59d
* Pass SantaDecisionManager to the pid monitor threads so that compil…
nguyen-phillip Jan 25, 2018
b9c7585
Allow enabling of transitive whitelisting from mobile config.
nguyen-phillip Jan 29, 2018
c293d5d
Add a temporary allow rule to kernel's decision cache for files writt…
nguyen-phillip Feb 1, 2018
a9201dd
compiler_pid_set_ now maps pid to ppid and a given process is considered
nguyen-phillip Feb 1, 2018
91c5d71
renamed ACTION_RESPOND_TEMPORARY to ACTION_RESPOND_PENDING_TRANSITIVE…
nguyen-phillip Feb 7, 2018
e583712
Merge remote-tracking branch 'origin/master' into compiler
nguyen-phillip May 21, 2018
75de096
fix formatting
nguyen-phillip May 29, 2018
6d295c9
merged up to #267
nguyen-phillip May 29, 2018
ca00a28
Started watching for KAUTH_FILEOP_RENAME in addition to KAUTH_FILEOP_…
nguyen-phillip May 29, 2018
e0bf6dd
Merge remote-tracking branch 'origin/master' into compiler
nguyen-phillip May 30, 2018
16769e8
merge
nguyen-phillip Jul 16, 2018
93a7e86
Add kernel tests for transitive whitelisting
nguyen-phillip Jul 16, 2018
bba1aee
fix comments
nguyen-phillip Jul 16, 2018
21561ef
don't hardcode ld path; fix indentation
nguyen-phillip Jul 17, 2018
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
5 changes: 5 additions & 0 deletions Source/common/SNTCommonEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ typedef NS_ENUM(NSInteger, SNTRuleState) {
SNTRuleStateBlacklist = 2,
SNTRuleStateSilentBlacklist = 3,
SNTRuleStateRemove = 4,

SNTRuleStateWhitelistCompiler = 5,
SNTRuleStateWhitelistTransitive = 6,
};

typedef NS_ENUM(NSInteger, SNTClientMode) {
Expand All @@ -58,6 +61,8 @@ typedef NS_ENUM(NSInteger, SNTEventState) {
SNTEventStateAllowBinary = 1 << 25,
SNTEventStateAllowCertificate = 1 << 26,
SNTEventStateAllowScope = 1 << 27,
SNTEventStateAllowCompiler = 1 << 28,
SNTEventStateAllowTransitive = 1 << 29,

// Block and Allow masks
SNTEventStateBlock = 0xFF << 16,
Expand Down
4 changes: 3 additions & 1 deletion Source/common/SNTKernelCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ enum SantaDriverMethods {

typedef enum {
QUEUETYPE_DECISION,
QUEUETYPE_LOG
QUEUETYPE_LOG,
QUEUETYPE_COMPILER,
} santa_queuetype_t;

// Enum defining actions that can be passed down the IODataQueue and in
Expand All @@ -71,6 +72,7 @@ typedef enum {
ACTION_NOTIFY_LINK = 33,
ACTION_NOTIFY_EXCHANGE = 34,
ACTION_NOTIFY_DELETE = 35,
ACTION_NOTIFY_CLOSE = 36,

// ERROR
ACTION_ERROR = 99,
Expand Down
81 changes: 78 additions & 3 deletions Source/santa-driver/SantaDecisionManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

#include "SantaDecisionManager.h"

// This is a made-up KAUTH_FILEOP constant which represents a KAUTH_VNODE_WRITE_DATA event
// that gets passed to SantaDecisionManager's FileOpCallback method. It is defined as 8
// because that was the first unused integer from sys/kauth.h. The reason that we don't
// simply use the KAUTH_VNODE_WRITE_DATA constant as is is because it overlaps with the other
// KAUTH_FILEOP constants.
#define KAUTH_FILEOP_WRITE 8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is still needed we should give the actions used by KAUTH_SCOPE_FILEOP room to grow. I don't really like the idea of adding our own action, but if we need to make it more than 8 incase fileop adds a new action in the future.

Maybe KAUTH_FILEOP_DELETE + 32 or something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


#define super OSObject
OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);

Expand All @@ -28,6 +35,7 @@ bool SantaDecisionManager::init() {

decision_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
log_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);
compiler_dataqueue_lock_ = lck_mtx_alloc_init(sdm_lock_grp_, sdm_lock_attr_);

root_decision_cache_ = new SantaCache<uint64_t>(5000, 2);
non_root_decision_cache_ = new SantaCache<uint64_t>(500, 2);
Expand All @@ -41,6 +49,10 @@ bool SantaDecisionManager::init() {
kMaxLogQueueEvents, sizeof(santa_message_t));
if (!log_dataqueue_) return kIOReturnNoMemory;

compiler_dataqueue_ = IOSharedDataQueue::withEntries(
kMaxCompilerQueueEvents, sizeof(santa_message_t));
if (!compiler_dataqueue_) return kIOReturnNoMemory;

client_pid_ = 0;
root_fsid_ = 0;

Expand All @@ -65,6 +77,11 @@ void SantaDecisionManager::free() {
log_dataqueue_lock_ = nullptr;
}

if (compiler_dataqueue_lock_) {
lck_mtx_free(compiler_dataqueue_lock_, sdm_lock_grp_);
compiler_dataqueue_lock_ = nullptr;
}

if (sdm_lock_attr_) {
lck_attr_free(sdm_lock_attr_);
sdm_lock_attr_ = nullptr;
Expand All @@ -82,6 +99,7 @@ void SantaDecisionManager::free() {

OSSafeReleaseNULL(decision_dataqueue_);
OSSafeReleaseNULL(log_dataqueue_);
OSSafeReleaseNULL(compiler_dataqueue_);

super::free();
}
Expand Down Expand Up @@ -110,6 +128,7 @@ void SantaDecisionManager::ConnectClient(pid_t pid) {

failed_decision_queue_requests_ = 0;
failed_log_queue_requests_ = 0;
failed_compiler_queue_requests_ = 0;
}

void SantaDecisionManager::DisconnectClient(bool itDied) {
Expand Down Expand Up @@ -137,6 +156,12 @@ void SantaDecisionManager::DisconnectClient(bool itDied) {
log_dataqueue_ = IOSharedDataQueue::withEntries(
kMaxLogQueueEvents, sizeof(santa_message_t));
lck_mtx_unlock(log_dataqueue_lock_);

lck_mtx_lock(compiler_dataqueue_lock_);
compiler_dataqueue_->release();
compiler_dataqueue_ = IOSharedDataQueue::withEntries(
kMaxCompilerQueueEvents, sizeof(santa_message_t));
lck_mtx_unlock(compiler_dataqueue_lock_);
}
}

Expand All @@ -163,6 +188,12 @@ void SantaDecisionManager::SetLogPort(mach_port_t port) {
lck_mtx_unlock(log_dataqueue_lock_);
}

void SantaDecisionManager::SetCompilerPort(mach_port_t port) {
lck_mtx_lock(compiler_dataqueue_lock_);
compiler_dataqueue_->setNotificationPort(port);
lck_mtx_unlock(compiler_dataqueue_lock_);
}

IOMemoryDescriptor *SantaDecisionManager::GetDecisionMemoryDescriptor() const {
return decision_dataqueue_->getMemoryDescriptor();
}
Expand All @@ -171,6 +202,10 @@ IOMemoryDescriptor *SantaDecisionManager::GetLogMemoryDescriptor() const {
return log_dataqueue_->getMemoryDescriptor();
}

IOMemoryDescriptor *SantaDecisionManager::GetCompilerMemoryDescriptor() const {
return compiler_dataqueue_->getMemoryDescriptor();
}

#pragma mark Listener Control

kern_return_t SantaDecisionManager::StartListener() {
Expand Down Expand Up @@ -417,6 +452,26 @@ bool SantaDecisionManager::PostToLogQueue(santa_message_t *message) {
return kr;
}

bool SantaDecisionManager::PostToCompilerQueue(santa_message_t *message) {
lck_mtx_lock(compiler_dataqueue_lock_);
auto kr = compiler_dataqueue_->enqueue(message, sizeof(santa_message_t));
if (!kr) {
if (failed_compiler_queue_requests_++ == 0) {
LOGW("Dropping compiler queue messages");
}
// If enqueue failed, pop an item off the queue and try again.
uint32_t dataSize = 0;
compiler_dataqueue_->dequeue(0, &dataSize);
kr = compiler_dataqueue_->enqueue(message, sizeof(santa_message_t));
} else {
if (failed_compiler_queue_requests_ > 0) {
failed_compiler_queue_requests_--;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this pop and re-queue bit, it's unnecessary.

}
lck_mtx_unlock(compiler_dataqueue_lock_);
return kr;
}

#pragma mark Invocation Tracking & PID comparison

void SantaDecisionManager::IncrementListenerInvocations() {
Expand Down Expand Up @@ -495,6 +550,7 @@ void SantaDecisionManager::FileOpCallback(
message->ppid = (val & ~0xFFFFFFFF00000000);
}
PostToLogQueue(message);
PostToCompilerQueue(message);
delete message;
return;
}
Expand All @@ -509,9 +565,13 @@ void SantaDecisionManager::FileOpCallback(
proc_name(message->pid, message->pname, sizeof(message->pname));

switch (action) {
case KAUTH_FILEOP_CLOSE:
case KAUTH_FILEOP_WRITE:
// This is actually a KAUTH_VNODE_WRITE_DATA event.
message->action = ACTION_NOTIFY_WRITE;
break;
case KAUTH_FILEOP_CLOSE:
message->action = ACTION_NOTIFY_CLOSE;
break;
case KAUTH_FILEOP_RENAME:
message->action = ACTION_NOTIFY_RENAME;
break;
Expand All @@ -529,7 +589,16 @@ void SantaDecisionManager::FileOpCallback(
return;
}

PostToLogQueue(message);
// We don't log the ACTION_NOTIFY_CLOSE messages because they mostly duplicate the
// ACTION_NOTIFY_WRITE messages (though they aren't precisely the same)
if (message->action != ACTION_NOTIFY_CLOSE) {
PostToLogQueue(message);
}

if (message->action == ACTION_NOTIFY_CLOSE || message->action == ACTION_NOTIFY_RENAME) {
PostToCompilerQueue(message);
}

delete message;
}
}
Expand All @@ -552,6 +621,10 @@ extern "C" int fileop_scope_callback(
char *new_path = nullptr;

switch (action) {
// We only care about KAUTH_FILEOP_CLOSE events where the closed file was modified.
case KAUTH_FILEOP_CLOSE:
if (!(arg2 & KAUTH_FILEOP_CLOSE_MODIFIED))
return KAUTH_RESULT_DEFER;
case KAUTH_FILEOP_DELETE:
case KAUTH_FILEOP_EXEC:
vp = reinterpret_cast<vnode_t>(arg0);
Expand Down Expand Up @@ -604,7 +677,9 @@ extern "C" int vnode_scope_callback(
char path[MAXPATHLEN];
int pathlen = MAXPATHLEN;
vn_getpath(vp, path, &pathlen);
sdm->FileOpCallback(KAUTH_FILEOP_CLOSE, vp, path, nullptr);
// KAUTH_VNODE_WRITE_DATA events are translated into a fake KAUTH_FILEOP_WRITE event
// so that we can handle them in the FileOpCallback function.
sdm->FileOpCallback(KAUTH_FILEOP_WRITE, vp, path, nullptr);
sdm->DecrementListenerInvocations();
}

Expand Down
23 changes: 23 additions & 0 deletions Source/santa-driver/SantaDecisionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ class SantaDecisionManager : public OSObject {
*/
IOMemoryDescriptor *GetLogMemoryDescriptor() const;

/**
Called by SantaDriverClient during connection to provide the shared
dataqueue memory to the client for the compiler whitelisting queue.
*/
IOMemoryDescriptor *GetCompilerMemoryDescriptor() const;

/**
Called by SantaDriverClient when a client connects to the decision queue,
providing the pid of the client process.
Expand All @@ -75,6 +81,9 @@ class SantaDecisionManager : public OSObject {
/// Sets the Mach port for notifying the log queue.
void SetLogPort(mach_port_t port);

/// Sets the Mach port for notifying the compiler queue.
void SetCompilerPort(mach_port_t port);

/// Starts the kauth listeners.
kern_return_t StartListener();

Expand Down Expand Up @@ -168,6 +177,9 @@ class SantaDecisionManager : public OSObject {
*/
static const uint32_t kMaxLogQueueEvents = 2048;

/// The maximum number of messages that can be kept in the compiler data queue at any time.
static const uint32_t kMaxCompilerQueueEvents = 512;

/**
Fetches a response from the daemon. Handles both daemon death
and failure to post messages to the daemon.
Expand Down Expand Up @@ -208,6 +220,14 @@ class SantaDecisionManager : public OSObject {
*/
bool PostToLogQueue(santa_message_t *message);

/**
Posts the requested message to the compiler data queue.

@param message The message to send
@return bool true if sending was successful.
*/
bool PostToCompilerQueue(santa_message_t *message);

/**
Fetches the vnode_id for a given vnode.

Expand Down Expand Up @@ -283,11 +303,14 @@ class SantaDecisionManager : public OSObject {

lck_mtx_t *decision_dataqueue_lock_;
lck_mtx_t *log_dataqueue_lock_;
lck_mtx_t *compiler_dataqueue_lock_;

IOSharedDataQueue *decision_dataqueue_;
IOSharedDataQueue *log_dataqueue_;
IOSharedDataQueue *compiler_dataqueue_;
uint32_t failed_decision_queue_requests_;
uint32_t failed_log_queue_requests_;
uint32_t failed_compiler_queue_requests_;

int32_t listener_invocations_;

Expand Down
7 changes: 7 additions & 0 deletions Source/santa-driver/SantaDriverClient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ IOReturn SantaDriverClient::registerNotificationPort(
case QUEUETYPE_LOG:
decisionManager->SetLogPort(port);
break;
case QUEUETYPE_COMPILER:
decisionManager->SetCompilerPort(port);
break;
default:
return kIOReturnBadArgument;
}
Expand All @@ -99,6 +102,10 @@ IOReturn SantaDriverClient::clientMemoryForType(
*options = 0;
*memory = decisionManager->GetLogMemoryDescriptor();
break;
case QUEUETYPE_COMPILER:
*options = 0;
*memory = decisionManager->GetCompilerMemoryDescriptor();
break;
default:
return kIOReturnBadArgument;
}
Expand Down
6 changes: 6 additions & 0 deletions Source/santactl/Commands/SNTCommandFileInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,12 @@ - (SNTAttributeBlock)rule {
case SNTEventStateBlockScope:
[output appendString:@" (Scope)"];
break;
case SNTEventStateAllowCompiler:
[output appendString:@" (Compiler)"];
break;
case SNTEventStateAllowTransitive:
[output appendString:@" (Transitive)"];
break;
default:
output = @"None".mutableCopy;
break;
Expand Down
25 changes: 23 additions & 2 deletions Source/santactl/Commands/SNTCommandRule.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ + (NSString *)longHelpText {
@" --whitelist: add to whitelist\n"
@" --blacklist: add to blacklist\n"
@" --silent-blacklist: add to silent blacklist\n"
@" --compiler: whitelist and mark as a compiler\n"
@" --remove: remove existing rule\n"
@" --check: check for an existing rule\n"
@"\n"
Expand All @@ -61,15 +62,21 @@ + (NSString *)longHelpText {
@" Does not work with --check. Use the fileinfo verb to check.\n"
@" the rule state of a file.\n"
@" --sha256 {sha256}: hash to add/remove/check\n"
@" --type {ruletype}: type of rules to remove\n"
@"\n"
@" Optionally:\n"
@" --certificate: add or check a certificate sha256 rule instead of binary\n"
@" --message {message}: custom message\n");
@" --message {message}: custom message\n"
@" --force: allow manual changes even when SyncBaseUrl is set\n");
}

- (void)runWithArguments:(NSArray *)arguments {
SNTConfigurator *config = [SNTConfigurator configurator];
if ([config syncBaseURL] && ![arguments containsObject:@"--check"]) {
// TODO: --force flag was added so that we could manually add compiler rules during testing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add the --force flag in an #ifdef DEBUG block so it can only be used on debug builds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// It's possibly a bad idea and should be removed.
if ([config syncBaseURL] &&
![arguments containsObject:@"--check"] &&
![arguments containsObject:@"--force"]) {
printf("SyncBaseURL is set, rules are managed centrally.\n");
exit(1);
}
Expand All @@ -91,6 +98,8 @@ - (void)runWithArguments:(NSArray *)arguments {
newRule.state = SNTRuleStateBlacklist;
} else if ([arg caseInsensitiveCompare:@"--silent-blacklist"] == NSOrderedSame) {
newRule.state = SNTRuleStateSilentBlacklist;
} else if ([arg caseInsensitiveCompare:@"--compiler"] == NSOrderedSame) {
newRule.state = SNTRuleStateWhitelistCompiler;
} else if ([arg caseInsensitiveCompare:@"--remove"] == NSOrderedSame) {
newRule.state = SNTRuleStateRemove;
} else if ([arg caseInsensitiveCompare:@"--check"] == NSOrderedSame) {
Expand All @@ -115,6 +124,12 @@ - (void)runWithArguments:(NSArray *)arguments {
[self printErrorUsageAndExit:@"--message requires an argument"];
}
newRule.customMsg = arguments[i];
} else if ([arg caseInsensitiveCompare:@"--type"] == NSOrderedSame) {
if (++i > arguments.count - 1) {
[self printErrorUsageAndExit:@"--type requires an argument"];
}
} else if ([arg caseInsensitiveCompare:@"--force"] == NSOrderedSame) {
// Don't do anything special.
} else {
[self printErrorUsageAndExit:[@"Unknown argument: " stringByAppendingString:arg]];
}
Expand Down Expand Up @@ -191,6 +206,12 @@ - (void)printStateOfRule:(SNTRule *)rule daemonConnection:(SNTXPCConnection *)da
case SNTEventStateBlockScope:
[output appendString:@" (Scope)"];
break;
case SNTEventStateAllowCompiler:
[output appendString:@" (Compiler)"];
break;
case SNTEventStateAllowTransitive:
[output appendString:@" (Transitive)"];
break;
default:
output = @"None".mutableCopy;
break;
Expand Down
Loading