santad: Log file changes, use prefix trees (#398)

This commit is contained in:
Russell Hancox
2019-10-10 09:39:47 -04:00
parent 423479771e
commit 53b7ef86ed
18 changed files with 399 additions and 289 deletions

2
BUILD
View File

@@ -147,8 +147,8 @@ test_suite(
name = "unit_tests",
tests = [
"//Source/common:SNTFileInfoTest",
"//Source/common:SNTPrefixTreeTest",
"//Source/santa_driver:SantaCacheTest",
"//Source/santa_driver:SantaPrefixTreeTest",
"//Source/santactl:SNTCommandFileInfoTest",
"//Source/santactl:SNTCommandSyncTest",
"//Source/santad:SNTEventTableTest",

View File

@@ -7,12 +7,13 @@
objects = {
/* Begin PBXBuildFile section */
0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SNTPrefixTree.cc */; };
59502195B2982225D3706DCE /* libPods-santabundleservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A55D73A235850B9FA991865 /* libPods-santabundleservice.a */; };
AD3736AF78C41A962C26D429 /* libPods-Santa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C3E743944A9D77423AA1534 /* libPods-Santa.a */; };
B5AE6BB811766CA492133559 /* libPods-santad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3700D40B536CA7F626B76156 /* libPods-santad.a */; };
C71E472F22F0F97B00921CD9 /* santad in CopyFiles */ = {isa = PBXBuildFile; fileRef = C779C4E622F0F51400EE2541 /* santad */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
C71E473122F0FAA100921CD9 /* com.google.santa.daemon.systemextension in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7A8308022F0F81F00F856AC /* com.google.santa.daemon.systemextension */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
C72ED2B62324962400255555 /* SNTEndpointSecurityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C72ED2B52324962400255555 /* SNTEndpointSecurityManager.m */; };
C72ED2B62324962400255555 /* SNTEndpointSecurityManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = C72ED2B52324962400255555 /* SNTEndpointSecurityManager.mm */; };
C72ED2B82324A2FA00255555 /* libEndpointSecurity.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C72ED2B72324A2FA00255555 /* libEndpointSecurity.tbd */; };
C72ED2BC232584C100255555 /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C72ED2BB232584C100255555 /* libbsm.tbd */; };
C7658B142322BDD500F36578 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AEA2322B84F00F36578 /* main.m */; };
@@ -104,7 +105,7 @@
C7D35DE42322C99B000C5EB4 /* SantaDecisionManager.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B002322B84F00F36578 /* SantaDecisionManager.cc */; };
C7D35DE52322C99E000C5EB4 /* SantaDriver.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658AFF2322B84F00F36578 /* SantaDriver.cc */; };
C7D35DE62322C9A1000C5EB4 /* SantaDriverClient.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658AFA2322B84F00F36578 /* SantaDriverClient.cc */; };
C7D35DE72322C9A4000C5EB4 /* SantaPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SantaPrefixTree.cc */; };
C7D35DE72322C9A4000C5EB4 /* SNTPrefixTree.cc in Sources */ = {isa = PBXBuildFile; fileRef = C7658B022322B84F00F36578 /* SNTPrefixTree.cc */; };
C7F5C1AE233E72CC00A3F7FD /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AF62322B84F00F36578 /* main.m */; };
C7F5C1AF233E72CF00A3F7FD /* SNTBundleService.m in Sources */ = {isa = PBXBuildFile; fileRef = C7658AF22322B84F00F36578 /* SNTBundleService.m */; };
C7F5C1B0233E735E00A3F7FD /* santabundleservice in CopyFiles */ = {isa = PBXBuildFile; fileRef = C7F5C1A7233E72BC00A3F7FD /* santabundleservice */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@@ -196,7 +197,7 @@
C05543B3701F50CA798B4B11 /* Pods-sysx.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-sysx.release.xcconfig"; path = "Target Support Files/Pods-sysx/Pods-sysx.release.xcconfig"; sourceTree = "<group>"; };
C72ED2B3232495CC00255555 /* SNTEventProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTEventProvider.h; sourceTree = "<group>"; };
C72ED2B42324962400255555 /* SNTEndpointSecurityManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTEndpointSecurityManager.h; sourceTree = "<group>"; };
C72ED2B52324962400255555 /* SNTEndpointSecurityManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SNTEndpointSecurityManager.m; sourceTree = "<group>"; };
C72ED2B52324962400255555 /* SNTEndpointSecurityManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SNTEndpointSecurityManager.mm; sourceTree = "<group>"; };
C72ED2B72324A2FA00255555 /* libEndpointSecurity.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libEndpointSecurity.tbd; path = usr/lib/libEndpointSecurity.tbd; sourceTree = SDKROOT; };
C72ED2B9232584AA00255555 /* libauditd.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libauditd.tbd; path = usr/lib/libauditd.tbd; sourceTree = SDKROOT; };
C72ED2BB232584C100255555 /* libbsm.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbsm.tbd; path = usr/lib/libbsm.tbd; sourceTree = SDKROOT; };
@@ -344,13 +345,13 @@
C7658AFB2322B84F00F36578 /* SantaCacheTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SantaCacheTest.mm; sourceTree = "<group>"; };
C7658AFC2322B84F00F36578 /* SantaCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SantaCache.h; sourceTree = "<group>"; };
C7658AFD2322B84F00F36578 /* SantaDriverClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SantaDriverClient.h; sourceTree = "<group>"; };
C7658AFE2322B84F00F36578 /* SantaPrefixTreeTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SantaPrefixTreeTest.mm; sourceTree = "<group>"; };
C7658AFE2322B84F00F36578 /* SNTPrefixTreeTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SNTPrefixTreeTest.mm; sourceTree = "<group>"; };
C7658AFF2322B84F00F36578 /* SantaDriver.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SantaDriver.cc; sourceTree = "<group>"; };
C7658B002322B84F00F36578 /* SantaDecisionManager.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SantaDecisionManager.cc; sourceTree = "<group>"; };
C7658B012322B84F00F36578 /* kernel_tests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = kernel_tests.mm; sourceTree = "<group>"; };
C7658B022322B84F00F36578 /* SantaPrefixTree.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SantaPrefixTree.cc; sourceTree = "<group>"; };
C7658B022322B84F00F36578 /* SNTPrefixTree.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SNTPrefixTree.cc; sourceTree = "<group>"; };
C7658B032322B84F00F36578 /* BUILD */ = {isa = PBXFileReference; lastKnownFileType = text; path = BUILD; sourceTree = "<group>"; };
C7658B042322B84F00F36578 /* SantaPrefixTree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SantaPrefixTree.h; sourceTree = "<group>"; };
C7658B042322B84F00F36578 /* SNTPrefixTree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SNTPrefixTree.h; sourceTree = "<group>"; };
C7658B052322B84F00F36578 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C7658B062322B84F00F36578 /* SantaDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SantaDriver.h; sourceTree = "<group>"; };
C7658B072322B84F00F36578 /* SantaDecisionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SantaDecisionManager.h; sourceTree = "<group>"; };
@@ -430,7 +431,7 @@
C7658A632322B84F00F36578 /* SNTDriverManager.h */,
C7658A742322B84F00F36578 /* SNTDriverManager.m */,
C72ED2B42324962400255555 /* SNTEndpointSecurityManager.h */,
C72ED2B52324962400255555 /* SNTEndpointSecurityManager.m */,
C72ED2B52324962400255555 /* SNTEndpointSecurityManager.mm */,
C72ED2B3232495CC00255555 /* SNTEventProvider.h */,
);
path = EventProviders;
@@ -608,6 +609,9 @@
C7658ACE2322B84F00F36578 /* SNTKernelCommon.h */,
C7658ACB2322B84F00F36578 /* SNTLogging.h */,
C7658AD82322B84F00F36578 /* SNTLogging.m */,
C7658B022322B84F00F36578 /* SNTPrefixTree.cc */,
C7658B042322B84F00F36578 /* SNTPrefixTree.h */,
C7658AFE2322B84F00F36578 /* SNTPrefixTreeTest.mm */,
C7658ADB2322B84F00F36578 /* SNTRule.h */,
C7658AC82322B84F00F36578 /* SNTRule.m */,
C7658AC92322B84F00F36578 /* SNTStoredEvent.h */,
@@ -743,9 +747,6 @@
C7658B062322B84F00F36578 /* SantaDriver.h */,
C7658AFA2322B84F00F36578 /* SantaDriverClient.cc */,
C7658AFD2322B84F00F36578 /* SantaDriverClient.h */,
C7658B022322B84F00F36578 /* SantaPrefixTree.cc */,
C7658B042322B84F00F36578 /* SantaPrefixTree.h */,
C7658AFE2322B84F00F36578 /* SantaPrefixTreeTest.mm */,
);
path = santa_driver;
sourceTree = "<group>";
@@ -1195,6 +1196,7 @@
C7658B332322C08B00F36578 /* SNTRule.m in Sources */,
C7658B382322C0B400F36578 /* SNTXPCSyncdInterface.m in Sources */,
C7658B1D2322BFFA00F36578 /* main.m in Sources */,
0D9F577C2342650F005D9AA8 /* SNTPrefixTree.cc in Sources */,
C7658B282322C02300F36578 /* SNTDriverManager.m in Sources */,
C7658B372322C0B000F36578 /* SNTXPCNotifierInterface.m in Sources */,
C7658B312322C08000F36578 /* SNTLogging.m in Sources */,
@@ -1218,7 +1220,7 @@
C7658B212322C00B00F36578 /* SNTEventLog.m in Sources */,
C7658B2E2322C06800F36578 /* SNTCachedDecision.m in Sources */,
C7658B302322C07500F36578 /* SNTFileInfo.m in Sources */,
C72ED2B62324962400255555 /* SNTEndpointSecurityManager.m in Sources */,
C72ED2B62324962400255555 /* SNTEndpointSecurityManager.mm in Sources */,
C7658B352322C0A000F36578 /* SNTXPCBundleServiceInterface.m in Sources */,
C7658B272322C02000F36578 /* SNTDatabaseController.m in Sources */,
C7658B222322C00E00F36578 /* SNTFileEventLog.m in Sources */,
@@ -1238,7 +1240,7 @@
files = (
C7D35DE52322C99E000C5EB4 /* SantaDriver.cc in Sources */,
C7D35DE62322C9A1000C5EB4 /* SantaDriverClient.cc in Sources */,
C7D35DE72322C9A4000C5EB4 /* SantaPrefixTree.cc in Sources */,
C7D35DE72322C9A4000C5EB4 /* SNTPrefixTree.cc in Sources */,
C7D35DE42322C99B000C5EB4 /* SantaDecisionManager.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -84,6 +84,13 @@ objc_library(
hdrs = ["SNTLogging.h"],
)
cc_library(
name = "SNTPrefixTree",
srcs = ["SNTPrefixTree.cc"],
hdrs = ["SNTPrefixTree.h"],
copts = ["-std=c++11"],
)
objc_library(
name = "SNTRule",
srcs = ["SNTRule.m"],
@@ -182,3 +189,9 @@ santa_unit_test(
]),
deps = [":SNTFileInfo"],
)
santa_unit_test(
name = "SNTPrefixTreeTest",
srcs = ["SNTPrefixTreeTest.mm"],
deps = ["SNTPrefixTree"],
)

View File

@@ -126,6 +126,9 @@ typedef struct {
char pname[MAXPATHLEN];
// For messages that originate from EndpointSecurity, this points to a copy of the message.
void *es_message;
// For messages that originate from EndpointSecurity, this points to an NSArray of the arguments.
void *args_array;
} santa_message_t;
// Used for the kSantaUserClientCacheBucketCount request.

View File

@@ -34,6 +34,10 @@
#else // KERNEL
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
@@ -60,6 +64,10 @@ void logMessage(LogLevel level, FILE *destination, NSString *format, ...)
#define LOGW(logFormat, ...) logMessage(LOG_LEVEL_WARN, stderr, logFormat, ##__VA_ARGS__)
#define LOGE(logFormat, ...) logMessage(LOG_LEVEL_ERROR, stderr, logFormat, ##__VA_ARGS__)
#ifdef __cplusplus
} // extern C
#endif
#endif // KERNEL
#endif // SANTA__COMMON__LOGGING_H

View File

@@ -12,55 +12,51 @@
/// See the License for the specific language governing permissions and
/// limitations under the License.
#include "Source/santa_driver/SantaPrefixTree.h"
#include "Source/common/SNTPrefixTree.h"
#ifdef KERNEL
#include <libkern/locks.h>
#include "Source/common/SNTLogging.h"
#else
#include <mutex>
#include <string.h>
#define LOGD(format, ...) // NOP
#define LOGE(format, ...) // NOP
#define lck_grp_attr_alloc_init() nullptr
#define lck_grp_alloc_init(name, attr) nullptr
#define lck_attr_alloc_init() nullptr
#define lck_rw_lock_shared(l) pthread_rwlock_rdlock(&l)
#define lck_rw_unlock_shared(l) pthread_rwlock_unlock(&l)
#define lck_rw_lock_exclusive(l) pthread_rwlock_wrlock(&l)
#define lck_rw_unlock_exclusive(l) pthread_rwlock_unlock(&l)
#define lck_rw_alloc_init(g, a) new std::shared_mutex
#define lck_mtx_alloc_init(g, a) new std::mutex
#define lck_attr_free(attr) // NOP
#define lck_grp_free(grp) // NOP
#define lck_grp_attr_free(grp_attr) // NOP
#define lck_rw_lock_shared(l) l->lock_shared()
#define lck_rw_unlock_shared(l) l->unlock_shared()
#define lck_rw_lock_exclusive(l) l->lock()
#define lck_rw_unlock_exclusive(l) l->unlock()
#define lck_rw_lock_shared_to_exclusive(l) ({ l->unlock_shared(); false; })
#define lck_rw_lock_exclusive_to_shared(l) l->unlock(); l->lock_shared()
#define lck_rw_lock_shared_to_exclusive(l) ({ pthread_rwlock_unlock(&l); false; })
#define lck_rw_lock_exclusive_to_shared(l) ({ pthread_rwlock_unlock(&l); pthread_rwlock_rdlock(&l); })
#define lck_mtx_lock(l) l->lock()
#define lck_mtx_unlock(l) l->unlock()
#endif // KERNEL
SantaPrefixTree::SantaPrefixTree(uint32_t max_nodes) {
SNTPrefixTree::SNTPrefixTree(uint32_t max_nodes) {
root_ = new SantaPrefixNode();
node_count_ = 0;
max_nodes_ = max_nodes;
#ifdef KERNEL
spt_lock_grp_attr_ = lck_grp_attr_alloc_init();
spt_lock_grp_ = lck_grp_alloc_init("santa-prefix-tree-lock", spt_lock_grp_attr_);
spt_lock_attr_ = lck_attr_alloc_init();
spt_lock_ = lck_rw_alloc_init(spt_lock_grp_, spt_lock_attr_);
spt_add_lock_ = lck_mtx_alloc_init(spt_lock_grp_, spt_lock_attr_);
#else
pthread_rwlock_init(&spt_lock_, nullptr);
spt_add_lock_ = new std::mutex;
#endif
}
IOReturn SantaPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
IOReturn SNTPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
// Serialize requests to AddPrefix. Otherwise one AddPrefix thread could overwrite whole
// branches of another. HasPrefix is still free to read the tree, until AddPrefix needs to
// modify it.
@@ -156,7 +152,7 @@ IOReturn SantaPrefixTree::AddPrefix(const char *prefix, uint64_t *node_count) {
return kIOReturnSuccess;
}
bool SantaPrefixTree::HasPrefix(const char *string) {
bool SNTPrefixTree::HasPrefix(const char *string) {
lck_rw_lock_shared(spt_lock_);
auto found = false;
@@ -184,7 +180,7 @@ bool SantaPrefixTree::HasPrefix(const char *string) {
return found;
}
void SantaPrefixTree::Reset() {
void SNTPrefixTree::Reset() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
@@ -194,7 +190,7 @@ void SantaPrefixTree::Reset() {
lck_rw_unlock_exclusive(spt_lock_);
}
void SantaPrefixTree::PruneNode(SantaPrefixNode *target) {
void SNTPrefixTree::PruneNode(SantaPrefixNode *target) {
if (!target) return;
// For deep trees, a recursive approach will generate too many stack frames. Make a "stack"
@@ -226,13 +222,13 @@ void SantaPrefixTree::PruneNode(SantaPrefixNode *target) {
delete[] stack;
}
SantaPrefixTree::~SantaPrefixTree() {
SNTPrefixTree::~SNTPrefixTree() {
lck_rw_lock_exclusive(spt_lock_);
PruneNode(root_);
root_ = nullptr;
lck_rw_unlock_exclusive(spt_lock_);
#ifdef KERNEL
#ifdef KERNEL
if (spt_lock_) {
lck_rw_free(spt_lock_, spt_lock_grp_);
spt_lock_ = nullptr;
@@ -242,7 +238,6 @@ SantaPrefixTree::~SantaPrefixTree() {
lck_mtx_free(spt_add_lock_, spt_lock_grp_);
spt_add_lock_ = nullptr;
}
#endif
if (spt_lock_attr_) {
lck_attr_free(spt_lock_attr_);
@@ -258,4 +253,7 @@ SantaPrefixTree::~SantaPrefixTree() {
lck_grp_attr_free(spt_lock_grp_attr_);
spt_lock_grp_attr_ = nullptr;
}
#else
pthread_rwlock_destroy(&spt_lock_);
#endif
}

View File

@@ -22,16 +22,16 @@
#include <libkern/locks.h>
#else
// Support for unit testing.
// Requires c++17 / macOS 10.12.
// TODO(bur): Handle warnings from bumping target version of the tests to 10.12.
#include <shared_mutex>
#include <mutex>
#include <pthread.h>
#include <stdint.h>
#endif // KERNEL
///
/// SantaPrefixTree is a simple prefix tree implementation.
/// Operations are thread safe.
///
class SantaPrefixTree {
class SNTPrefixTree {
public:
// Add a prefix to the tree.
// Optionally pass node_count to get the number of nodes after the add.
@@ -43,8 +43,8 @@ class SantaPrefixTree {
// Reset the tree.
void Reset();
SantaPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
~SantaPrefixTree();
SNTPrefixTree(uint32_t max_nodes = kDefaultMaxNodes);
~SNTPrefixTree();
private:
///
@@ -85,19 +85,19 @@ class SantaPrefixTree {
uint32_t max_nodes_;
uint32_t node_count_;
#ifdef KERNEL
#ifdef KERNEL
lck_grp_t *spt_lock_grp_;
lck_grp_attr_t *spt_lock_grp_attr_;
lck_attr_t *spt_lock_attr_;
lck_rw_t *spt_lock_;
lck_mtx_t *spt_add_lock_;
#else // KERNEL
#else // KERNEL
void *spt_lock_grp_;
void *spt_lock_grp_attr_;
void *spt_lock_attr_;
std::shared_mutex *spt_lock_;
pthread_rwlock_t spt_lock_;
std::mutex *spt_add_lock_;
#endif // KERNEL
#endif // KERNEL
};
#endif /* SANTA__SANTA_DRIVER__SANTAPREFIXTREE_H */

View File

@@ -14,22 +14,22 @@
#import <XCTest/XCTest.h>
#include "Source/santa_driver/SantaPrefixTree.h"
#include "Source/common/SNTPrefixTree.h"
@interface SantaPrefixTreeTest : XCTestCase
@interface SNTPrefixTreeTest : XCTestCase
@end
@implementation SantaPrefixTreeTest
@implementation SNTPrefixTreeTest
- (void)testAddAndHas {
auto t = SantaPrefixTree();
auto t = SNTPrefixTree();
XCTAssertFalse(t.HasPrefix("/private/var/tmp/file1"));
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
}
- (void)testReset {
auto t = SantaPrefixTree();
auto t = SNTPrefixTree();
t.AddPrefix("/private/var/tmp/");
XCTAssertTrue(t.HasPrefix("/private/var/tmp/file1"));
t.Reset();
@@ -38,7 +38,7 @@
- (void)testThreading {
uint32_t count = 4096;
auto t = new SantaPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
auto t = new SNTPrefixTree(count * (uint32_t)[NSUUID UUID].UUIDString.length);
NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; ++i) {

View File

@@ -18,8 +18,6 @@ cc_library(
"SantaDriver.h",
"SantaDriverClient.cc",
"SantaDriverClient.h",
"SantaPrefixTree.cc",
"SantaPrefixTree.h",
"main.cc",
],
copts = [
@@ -39,6 +37,7 @@ cc_library(
deps = [
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLoggingKernel",
"//Source/common:SNTPrefixTree",
],
alwayslink = 1,
)
@@ -52,25 +51,6 @@ santa_unit_test(
deps = ["//Source/common:SNTKernelCommon"],
)
cc_library(
name = "SantaPrefixTree_userland_lib",
srcs = ["SantaPrefixTree.cc"],
hdrs = ["SantaPrefixTree.h"],
copts = ["-std=c++1z"],
visibility = ["//visibility:public"],
)
santa_unit_test(
name = "SantaPrefixTreeTest",
srcs = ["SantaPrefixTreeTest.mm"],
copts = ["-std=c++1z"],
minimum_os_version = "10.12",
deps = [
":SantaPrefixTree_userland_lib",
"//Source/common:SNTKernelCommon"
],
)
# Full santa-driver.kext containing all Santa components
macos_kernel_extension(
name = "santa_driver",

View File

@@ -58,7 +58,7 @@ bool SantaDecisionManager::init() {
root_fsid_ = 0;
// Setup file modification prefix filter.
filemod_prefix_filter_ = new SantaPrefixTree();
filemod_prefix_filter_ = new SNTPrefixTree();
return true;
}

View File

@@ -25,8 +25,8 @@
#include "Source/common/SNTKernelCommon.h"
#include "Source/common/SNTLogging.h"
#include "Source/common/SNTPrefixTree.h"
#include "Source/santa_driver/SantaCache.h"
#include "Source/santa_driver/SantaPrefixTree.h"
///
/// SantaDecisionManager is responsible for intercepting Vnode execute actions
@@ -347,7 +347,7 @@ class SantaDecisionManager : public OSObject {
SantaCache<santa_vnode_id_t, uint64_t> *vnode_pid_map_;
SantaCache<pid_t, pid_t> *compiler_pid_set_;
SantaPrefixTree *filemod_prefix_filter_;
SNTPrefixTree *filemod_prefix_filter_;
/**
Return the correct cache for a given identifier.

View File

@@ -15,7 +15,7 @@ objc_library(
"EventProviders/SNTDriverManager.h",
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
"EventProviders/SNTEndpointSecurityManager.m",
"EventProviders/SNTEndpointSecurityManager.mm",
"EventProviders/SNTEventProvider.h",
"Logs/SNTEventLog.h",
"Logs/SNTEventLog.m",
@@ -58,6 +58,7 @@ objc_library(
"//Source/common:SNTFileInfo",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SNTRule",
"//Source/common:SNTStoredEvent",
"//Source/common:SNTXPCControlInterface",
@@ -91,7 +92,7 @@ santa_unit_test(
"EventProviders/SNTDriverManager.h",
"EventProviders/SNTDriverManager.m",
"EventProviders/SNTEndpointSecurityManager.h",
"EventProviders/SNTEndpointSecurityManager.m",
"EventProviders/SNTEndpointSecurityManager.mm",
"EventProviders/SNTEventProvider.h",
"Logs/SNTEventLog.h",
"Logs/SNTEventLog.m",
@@ -119,6 +120,7 @@ santa_unit_test(
"//Source/common:SNTFileInfo",
"//Source/common:SNTKernelCommon",
"//Source/common:SNTLogging",
"//Source/common:SNTPrefixTree",
"//Source/common:SNTRule",
"//Source/common:SNTXPCNotifierInterface",
"//Source/common:SNTXPCSyncdInterface",

View File

@@ -1,193 +0,0 @@
/// Copyright 2019 Google Inc. All rights reserved.
///
/// 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
///
/// http://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.
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
#import "Source/common/SNTLogging.h"
#include <EndpointSecurity/EndpointSecurity.h>
#include <bsm/libbsm.h>
@interface SNTEndpointSecurityManager ()
@property(nonatomic) es_client_t *client;
@property (nonatomic, copy) void (^decisionCallback)(santa_message_t);
@property (nonatomic, copy) void (^logCallback)(santa_message_t);
@end
@implementation SNTEndpointSecurityManager
- (instancetype)init API_AVAILABLE(macos(10.15)) {
self = [super init];
if (self) {
[self establishClient];
}
return self;
}
- (void)establishClient API_AVAILABLE(macos(10.15)) {
while (!self.client) {
es_client_t *client = NULL;
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
[self messageHandler:m];
});
switch (ret) {
case ES_NEW_CLIENT_RESULT_SUCCESS:
LOGI(@"Connected to EndpointSecurity");
self.client = client;
return;
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
LOGE(@"Sleeping for 30s before restarting.");
sleep(30);
exit(ret);
default:
LOGE(@"Unable to create es client: %d. Sleeping for a minute.", ret);
sleep(60);
continue;
}
}
}
- (void)messageHandler:(const es_message_t *)m API_AVAILABLE(macos(10.15)) {
// TODO(bur/rah): Currently this class only subscribes to exec events. Move this code
// somewhere exec specific if other types of events are added to this client.
santa_message_t sm;
sm.uid = audit_token_to_ruid(m->event.exec.target->audit_token);
sm.gid = audit_token_to_rgid(m->event.exec.target->audit_token);
sm.pid = audit_token_to_pid(m->event.exec.target->audit_token);
// original_ppid stays constant even in the event a process is reparented
sm.ppid = m->event.exec.target->original_ppid;
sm.es_message = (void *)es_copy_message(m);
sm.vnode_id.fsid = m->event.exec.target->executable->stat.st_dev;
sm.vnode_id.fileid = m->event.exec.target->executable->stat.st_ino;
size_t l = m->event.exec.target->executable->path.length;
if (l + 1 > MAXPATHLEN || m->event.exec.target->executable->path_truncated) {
// TODO(bur/rah): Get path from fsid.
LOGE(@"Path is truncated!");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
return;
}
strncpy(sm.path,m->event.exec.target->executable->path.data, l);
sm.path[l] = '\0';
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_EXEC:
// // TODO(bur/rah): Probably also want to do this for is_es_client.
// // TODO(bur/rah): Since these events are not evaluated by Santa's pipline they are
// // missing bits of information such as SHA256 and REASON. Refactor the
// // logging cache.
// if (m->event.exec.target->is_platform_binary) {
// LOGD(@"platform binary: %s", sm.path);
// [self postAction:ACTION_RESPOND_ALLOW forMessage:sm];
// return;
// }
sm.action = ACTION_REQUEST_BINARY;
if (self.decisionCallback) self.decisionCallback(sm);
break;
case ES_EVENT_TYPE_NOTIFY_EXEC:
sm.action = ACTION_NOTIFY_EXEC;
if (self.logCallback) self.logCallback(sm);
break;
default:
break;
}
}
- (void)listenForDecisionRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
while (!self.connectionEstablished) usleep(100000); // 100ms
// Listen for exec auth messages.
self.decisionCallback = callback;
es_event_type_t events[] = { ES_EVENT_TYPE_AUTH_EXEC };
es_return_t sret = es_subscribe(self.client, events, 1);
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_AUTH_EXEC: %d", sret);
// There's a gap between creating a client and subscribing to events. Creating the client
// triggers a cache flush automatically but any events that happen in this gap could be allowed
// and cached, so we force the cache to flush again.
[self flushCacheNonRootOnly:YES];
}
- (void)listenForLogRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
while (!self.connectionEstablished) usleep(100000); // 100ms
// Listen for exec notify messages.
self.logCallback = callback;
es_event_type_t events[] = { ES_EVENT_TYPE_NOTIFY_EXEC };
es_return_t sret = es_subscribe(self.client, events, 1);
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_NOTIFY_EXEC: %d", sret);
}
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
API_AVAILABLE(macos(10.15)) {
es_respond_result_t ret;
switch (action) {
case ACTION_RESPOND_ALLOW:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_DENY:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_DENY, false);
break;
default:
return ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT;
}
if (sm.es_message) {
es_free_message(sm.es_message);
sm.es_message = NULL;
}
return ret;
}
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly API_AVAILABLE(macos(10.15)) {
if (!self.connectionEstablished) return YES; // if not connected, there's nothing to flush.
return es_clear_cache(self.client) == ES_CLEAR_CACHE_RESULT_SUCCESS;
}
- (void)fileModificationPrefixFilterAdd:(NSArray *)filters {
}
- (void)fileModificationPrefixFilterReset {
}
- (NSArray<NSNumber *> *)cacheCounts {
return nil;
}
- (NSArray<NSNumber *> *)cacheBucketCount {
return nil;
}
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
return ACTION_UNSET;
}
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeId {
return KERN_FAILURE;
}
- (BOOL)connectionEstablished {
return self.client != nil;
}
@end

View File

@@ -0,0 +1,306 @@
/// Copyright 2019 Google Inc. All rights reserved.
///
/// 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
///
/// http://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.
#import "Source/santad/EventProviders/SNTEndpointSecurityManager.h"
#include "Source/common/SNTPrefixTree.h"
#import "Source/common/SNTLogging.h"
#include <EndpointSecurity/EndpointSecurity.h>
#include <bsm/libbsm.h>
#import <libproc.h>
@interface SNTEndpointSecurityManager ()
@property(nonatomic) es_client_t *client;
@property(nonatomic) SNTPrefixTree *prefixTree;
@property (nonatomic, copy) void (^decisionCallback)(santa_message_t);
@property (nonatomic, copy) void (^logCallback)(santa_message_t);
@end
@implementation SNTEndpointSecurityManager
- (instancetype)init API_AVAILABLE(macos(10.15)) {
self = [super init];
if (self) {
[self establishClient];
_prefixTree = new SNTPrefixTree();
}
return self;
}
- (void)dealloc API_AVAILABLE(macos(10.15)) {
if (_prefixTree) delete _prefixTree;
if (_client) {
es_unsubscribe_all(_client);
es_delete_client(_client);
}
}
- (void)establishClient API_AVAILABLE(macos(10.15)) {
while (!self.client) {
es_client_t *client = NULL;
es_new_client_result_t ret = es_new_client(&client, ^(es_client_t *c, const es_message_t *m) {
[self messageHandler:m];
});
switch (ret) {
case ES_NEW_CLIENT_RESULT_SUCCESS:
LOGI(@"Connected to EndpointSecurity");
self.client = client;
return;
case ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED:
LOGE(@"Unable to create EndpointSecurity client, not full-disk access permitted");
LOGE(@"Sleeping for 30s before restarting.");
sleep(30);
exit(ret);
default:
LOGE(@"Unable to create es client: %d. Sleeping for a minute.", ret);
sleep(60);
continue;
}
}
}
- (void)messageHandler:(const es_message_t *)m API_AVAILABLE(macos(10.15)) {
santa_message_t sm = {};
audit_token_t audit_token = {};
void (^callback)(santa_message_t);
switch (m->event_type) {
case ES_EVENT_TYPE_AUTH_EXEC: {
// // TODO(bur/rah): Probably also want to do this for is_es_client.
// // TODO(bur/rah): Since these events are not evaluated by Santa's pipline they are
// // missing bits of information such as SHA256 and REASON. Refactor the
// // logging cache.
// if (m->event.exec.target->is_platform_binary) {
// LOGD(@"platform binary: %s", sm.path);
// [self postAction:ACTION_RESPOND_ALLOW forMessage:sm];
// return;
// }
sm.es_message = (void *)es_copy_message(m);
sm.action = ACTION_REQUEST_BINARY;
sm.vnode_id.fsid = m->event.exec.target->executable->stat.st_dev;
sm.vnode_id.fileid = m->event.exec.target->executable->stat.st_ino;
callback = self.decisionCallback;
audit_token = m->event.exec.target->audit_token;
sm.ppid = m->event.exec.target->original_ppid;
size_t l = m->event.exec.target->executable->path.length;
if (l + 1 > MAXPATHLEN || m->event.exec.target->executable->path_truncated) {
// TODO(bur/rah): Get path from fsid.
LOGE(@"Path is truncated!");
es_respond_auth_result(self.client, m, ES_AUTH_RESULT_ALLOW, true);
return;
}
strncpy(sm.path,m->event.exec.target->executable->path.data, l);
sm.path[l] = '\0';
break;
}
case ES_EVENT_TYPE_NOTIFY_EXEC: {
sm.action = ACTION_NOTIFY_EXEC;
sm.vnode_id.fsid = m->event.exec.target->executable->stat.st_dev;
sm.vnode_id.fileid = m->event.exec.target->executable->stat.st_ino;
// TODO(rah): Profile this, it might need to be improved.
uint32_t argCount = es_exec_arg_count(&(m->event.exec));
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
for (int i = 0; i < argCount; ++i) {
es_string_token_t arg = es_exec_arg(&(m->event.exec), i);
[args addObject:[[NSString alloc] initWithBytes:arg.data
length:arg.length
encoding:NSUTF8StringEncoding]];
}
sm.args_array = (void *)CFBridgingRetain(args);
callback = self.logCallback;
audit_token = m->event.exec.target->audit_token;
sm.ppid = m->event.exec.target->original_ppid;
size_t l = m->event.exec.target->executable->path.length;
strncpy(sm.path,m->event.exec.target->executable->path.data, l);
sm.path[l] = '\0';
break;
}
case ES_EVENT_TYPE_NOTIFY_CLOSE: {
if (!m->event.close.modified) return;
sm.action = ACTION_NOTIFY_WRITE;
sm.ppid = m->process->original_ppid;
strncpy(sm.path, m->event.close.target->path.data, m->event.close.target->path.length);
sm.path[m->event.close.target->path.length] = '\0';
callback = self.logCallback;
audit_token = m->process->audit_token;
break;
}
case ES_EVENT_TYPE_NOTIFY_UNLINK:
sm.action = ACTION_NOTIFY_DELETE;
sm.ppid = m->process->original_ppid;
strncpy(sm.path, m->event.unlink.target->path.data, m->event.unlink.target->path.length);
sm.path[m->event.unlink.target->path.length] = '\0';
callback = self.logCallback;
audit_token = m->process->audit_token;
case ES_EVENT_TYPE_NOTIFY_TRUNCATE: {
sm.action = ACTION_NOTIFY_DELETE;
sm.ppid = m->process->original_ppid;
strncpy(sm.path, m->event.truncate.target->path.data, m->event.truncate.target->path.length);
sm.path[m->event.truncate.target->path.length] = '\0';
callback = self.logCallback;
audit_token = m->process->audit_token;
break;
}
case ES_EVENT_TYPE_NOTIFY_LINK: {
sm.action = ACTION_NOTIFY_LINK;
sm.ppid = m->process->original_ppid;
strncpy(sm.path, m->event.link.source->path.data, m->event.link.source->path.length);
sm.path[m->event.link.source->path.length] = '\0';
strncpy(sm.newpath, m->event.link.target_filename.data, m->event.link.target_filename.length);
sm.newpath[m->event.link.target_filename.length] = '\0';
callback = self.logCallback;
audit_token = m->process->audit_token;
break;
}
case ES_EVENT_TYPE_NOTIFY_RENAME: {
sm.action = ACTION_NOTIFY_RENAME;
sm.ppid = m->process->original_ppid;
strncpy(sm.path, m->event.rename.source->path.data, m->event.rename.source->path.length);
sm.path[m->event.rename.source->path.length] = '\0';
switch(m->event.rename.destination_type) {
case ES_DESTINATION_TYPE_NEW_PATH:
strncpy(sm.newpath, m->event.rename.destination.new_path.filename.data, m->event.rename.destination.new_path.filename.length);
sm.newpath[m->event.rename.destination.new_path.filename.length] = '\0';
break;
case ES_DESTINATION_TYPE_EXISTING_FILE:
strncpy(sm.newpath, m->event.rename.destination.existing_file->path.data, m->event.rename.destination.existing_file->path.length);
sm.newpath[m->event.rename.destination.existing_file->path.length] = '\0';
break;
}
callback = self.logCallback;
audit_token = m->process->audit_token;
}
default:
break;
}
if (self.prefixTree->HasPrefix(sm.path)) {
return;
}
if (callback) {
sm.uid = audit_token_to_ruid(audit_token);
sm.gid = audit_token_to_rgid(audit_token);
sm.pid = audit_token_to_pid(audit_token);
proc_name(sm.pid, sm.pname, 1024);
callback(sm);
}
}
- (void)listenForDecisionRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
while (!self.connectionEstablished) usleep(100000); // 100ms
// Listen for exec auth messages.
self.decisionCallback = callback;
es_event_type_t events[] = { ES_EVENT_TYPE_AUTH_EXEC };
es_return_t sret = es_subscribe(self.client, events, 1);
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_AUTH_EXEC: %d", sret);
// There's a gap between creating a client and subscribing to events. Creating the client
// triggers a cache flush automatically but any events that happen in this gap could be allowed
// and cached, so we force the cache to flush again.
[self flushCacheNonRootOnly:YES];
}
- (void)listenForLogRequests:(void (^)(santa_message_t))callback API_AVAILABLE(macos(10.15)) {
while (!self.connectionEstablished) usleep(100000); // 100ms
// Listen for exec notify messages.
self.logCallback = callback;
es_event_type_t events[] = {
ES_EVENT_TYPE_NOTIFY_EXEC,
ES_EVENT_TYPE_NOTIFY_CLOSE,
ES_EVENT_TYPE_NOTIFY_TRUNCATE,
ES_EVENT_TYPE_NOTIFY_LINK,
ES_EVENT_TYPE_NOTIFY_RENAME,
ES_EVENT_TYPE_NOTIFY_UNLINK,
};
es_return_t sret = es_subscribe(self.client, events, sizeof(events) / sizeof(es_event_type_t));
if (sret != ES_RETURN_SUCCESS) LOGE(@"Unable to subscribe ES_EVENT_TYPE_NOTIFY_EXEC: %d", sret);
}
- (int)postAction:(santa_action_t)action forMessage:(santa_message_t)sm
API_AVAILABLE(macos(10.15)) {
es_respond_result_t ret;
switch (action) {
case ACTION_RESPOND_ALLOW:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_ALLOW, true);
break;
case ACTION_RESPOND_DENY:
ret = es_respond_auth_result(self.client, (es_message_t *)sm.es_message,
ES_AUTH_RESULT_DENY, false);
break;
default:
ret = ES_RESPOND_RESULT_ERR_INVALID_ARGUMENT;
}
if (sm.es_message) {
es_free_message((es_message_t *)sm.es_message);
sm.es_message = NULL;
}
return ret;
}
- (BOOL)flushCacheNonRootOnly:(BOOL)nonRootOnly API_AVAILABLE(macos(10.15)) {
if (!self.connectionEstablished) return YES; // if not connected, there's nothing to flush.
return es_clear_cache(self.client) == ES_CLEAR_CACHE_RESULT_SUCCESS;
}
- (void)fileModificationPrefixFilterAdd:(NSArray *)filters {
for (NSString *filter in filters) {
self.prefixTree->AddPrefix(filter.fileSystemRepresentation);
}
}
- (void)fileModificationPrefixFilterReset {
self.prefixTree->Reset();
}
- (NSArray<NSNumber *> *)cacheCounts {
return nil;
}
- (NSArray<NSNumber *> *)cacheBucketCount {
return nil;
}
- (santa_action_t)checkCache:(santa_vnode_id_t)vnodeID {
return ACTION_UNSET;
}
- (kern_return_t)removeCacheEntryForVnodeID:(santa_vnode_id_t)vnodeId {
return KERN_FAILURE;
}
- (BOOL)connectionEstablished {
return self.client != nil;
}
@end

View File

@@ -182,18 +182,8 @@
}
if (logArgs) {
if (@available(macOS 10.15, *)) {
es_message_t *m = (es_message_t *)message.es_message;
// TODO(rah): Profile this, it might need to be improved.
uint32_t argCount = es_exec_arg_count(&(m->event.exec));
NSMutableArray *args = [NSMutableArray arrayWithCapacity:argCount];
for (int i = 0; i < argCount; ++i) {
es_string_token_t arg = es_exec_arg(&(m->event.exec), i);
[args addObject:[[NSString alloc] initWithBytes:arg.data
length:arg.length
encoding:NSUTF8StringEncoding]];
}
if (message.args_array) {
NSArray *args = CFBridgingRelease(message.args_array);
[outLog appendFormat:@"|args=%@", [args componentsJoinedByString:@" "]];
} else {
[self addArgsForPid:message.pid toString:outLog];

View File

@@ -222,6 +222,7 @@
case ACTION_NOTIFY_WRITE: {
NSRegularExpression *re = [[SNTConfigurator configurator] fileChangesRegex];
NSString *path = @(message.path);
if (!path) break;
if ([re numberOfMatchesInString:path options:0 range:NSMakeRange(0, path.length)]) {
[self->_eventLog logFileModification:message];
}

View File

@@ -75,7 +75,7 @@
/// Return a pre-configured santa_message_ t for testing with.
- (santa_message_t)getMessage {
santa_message_t message = {0};
santa_message_t message = {};
message.pid = 12;
message.ppid = 1;
message.vnode_id = [self getVnodeId];

View File

@@ -8,7 +8,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl",
git_repository(
name = "build_bazel_rules_apple",
remote = "https://github.com/bazelbuild/rules_apple.git",
tag = "0.17.2",
tag = "0.18.0",
)
load("@build_bazel_rules_apple//apple:repositories.bzl", "apple_rules_dependencies")