Skip to content

Commit

Permalink
Merge branch 'kext-listener-move'
Browse files Browse the repository at this point in the history
  • Loading branch information
russellhancox committed Feb 3, 2015
2 parents d88fa4e + 08ca3c9 commit a59d2aa
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 174 deletions.
4 changes: 2 additions & 2 deletions Santa.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -568,12 +568,12 @@
0D91BCB9174E8A7E00131A7D /* santa-driver */ = {
isa = PBXGroup;
children = (
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
0D9A7F321759144800035EB5 /* SantaDriver.h */,
0D9A7F311759144800035EB5 /* SantaDriver.cc */,
0D9A7F361759148E00035EB5 /* SantaDriverClient.h */,
0D9A7F351759148E00035EB5 /* SantaDriverClient.cc */,
0D4644C4182AF81700098690 /* SantaDecisionManager.h */,
0D4644C3182AF81700098690 /* SantaDecisionManager.cc */,
0D7A7AF2174FCF4C00B77646 /* SantaMessage.h */,
0D7A7AF1174FCF4C00B77646 /* SantaMessage.cc */,
0DA36C1F199EA46600A129D6 /* Resources */,
Expand Down
1 change: 0 additions & 1 deletion Source/common/SNTKernelCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
// List of methods supported by the driver.
enum SantaDriverMethods {
kSantaUserClientOpen,
kSantaUserClientClose,
kSantaUserClientAllowBinary,
kSantaUserClientDenyBinary,
kSantaUserClientClearCache,
Expand Down
177 changes: 100 additions & 77 deletions Source/santa-driver/SantaDecisionManager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,15 @@ OSDefineMetaClassAndStructors(SantaDecisionManager, OSObject);

#pragma mark Object Lifecycle

SantaDecisionManager *SantaDecisionManager::WithQueueAndPID(
IOSharedDataQueue *queue, pid_t pid) {
SantaDecisionManager *me = new SantaDecisionManager;
bool SantaDecisionManager::init() {
dataqueue_lock_ = IORWLockAlloc();
cached_decisions_lock_ = IORWLockAlloc();
cached_decisions_ = OSDictionary::withCapacity(1000);

if (me && !me->InitWithQueueAndPID(queue, pid)) {
me->free();
return NULL;
}

return me;
}

bool SantaDecisionManager::InitWithQueueAndPID(
IOSharedDataQueue *queue, pid_t pid) {
if (!super::init()) return false;

if (!pid) return false;
if (!queue) return false;

listener_invocations_ = 0;
dataqueue_ = queue;
owning_pid_ = pid;
owning_proc_ = proc_find(pid);

if (!(dataqueue_lock_ = IORWLockAlloc())) return FALSE;
if (!(cached_decisions_lock_ = IORWLockAlloc())) return FALSE;
if (!(cached_decisions_ = OSDictionary::withCapacity(1000))) return FALSE;

return TRUE;
return kIOReturnSuccess;
}

void SantaDecisionManager::free() {
proc_rele(owning_proc_);

if (cached_decisions_) {
cached_decisions_->release();
cached_decisions_ = NULL;
Expand All @@ -63,15 +38,95 @@ void SantaDecisionManager::free() {
cached_decisions_lock_ = NULL;
}

if (dataqueue_lock_) {
if (dataqueue_lock_ ) {
IORWLockFree(dataqueue_lock_);
dataqueue_lock_ = NULL;
}

super::free();
}

# pragma mark Cache Management
#pragma mark Client Management

void SantaDecisionManager::ConnectClient(IOSharedDataQueue *queue, pid_t pid) {
if (!pid) return;
if (!queue) return;

// Any decisions made while the daemon wasn't
// connected should be cleared
cached_decisions_->flushCollection();

dataqueue_ = queue;
dataqueue_->retain();

owning_pid_ = pid;
owning_proc_ = proc_find(pid);
}

void SantaDecisionManager::DisconnectClient() {
owning_pid_ = -1;

// Ask santad to shutdown, in case it's running.
santa_message_t message;
message.action = ACTION_REQUEST_SHUTDOWN;
message.userId = 0;
message.pid = 0;
message.ppid = 0;
message.vnode_id = 0;
PostToQueue(message);

dataqueue_->release();
dataqueue_ = NULL;

proc_rele(owning_proc_);
owning_proc_ = NULL;
}

bool SantaDecisionManager::ClientConnected() {
return owning_pid_ > 0;
}

# pragma mark Listener Control

kern_return_t SantaDecisionManager::StartListener() {
process_listener_ = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
process_scope_callback,
reinterpret_cast<void *>(this));
if (!process_listener_) return kIOReturnInternalError;
LOGD("Process listener started.");

vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
vnode_scope_callback,
reinterpret_cast<void *>(this));
if (!vnode_listener_) return kIOReturnInternalError;

LOGD("Vnode listener started.");

return kIOReturnSuccess;
}

kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;

kauth_unlisten_scope(process_listener_);
process_listener_ = NULL;

// Wait for any active invocations to finish before returning
do {
IOSleep(5);
} while (listener_invocations_);

// Delete any cached decisions
ClearCache();

LOGD("Vnode listener stopped.");
LOGD("Process listener stopped.");

return kIOReturnSuccess;
}

#pragma mark Cache Management

void SantaDecisionManager::AddToCache(
const char *identifier, santa_action_t decision, uint64_t microsecs) {
Expand Down Expand Up @@ -169,7 +224,10 @@ santa_action_t SantaDecisionManager::GetFromCache(const char *identifier) {

bool SantaDecisionManager::PostToQueue(santa_message_t message) {
IORWLockWrite(dataqueue_lock_);
bool kr = dataqueue_->enqueue(&message, sizeof(message));
bool kr = false;
if (dataqueue_) {
kr = dataqueue_->enqueue(&message, sizeof(message));
}
IORWLockUnlock(dataqueue_lock_);
return kr;
}
Expand Down Expand Up @@ -200,6 +258,15 @@ santa_action_t SantaDecisionManager::FetchDecision(
path[0] = '\0';
}

// If daemon isn't connected, allow and cache
if (owning_pid_ < 1) {
LOGI("Exeuction request without daemon running: %s", path);
AddToCache(vnode_id_str,
ACTION_RESPOND_CHECKBW_ALLOW,
GetCurrentUptime());
return ACTION_RESPOND_CHECKBW_ALLOW;
}

// Prepare to send message to daemon
santa_message_t message;
strncpy(message.path, path, MAX_PATH_LEN);
Expand Down Expand Up @@ -260,10 +327,6 @@ uint64_t SantaDecisionManager::GetCurrentUptime() {

# pragma mark Invocation Tracking & PID comparison

SInt32 SantaDecisionManager::GetListenerInvocations() {
return listener_invocations_;
}

void SantaDecisionManager::IncrementListenerInvocations() {
OSIncrementAtomic(&listener_invocations_);
}
Expand All @@ -276,46 +339,6 @@ bool SantaDecisionManager::MatchesOwningPID(const pid_t other_pid) {
return (owning_pid_ == other_pid);
}

# pragma mark Listener Control

kern_return_t SantaDecisionManager::StartListener() {
process_listener_ = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
process_scope_callback,
reinterpret_cast<void *>(this));
if (!process_listener_) return kIOReturnInternalError;
LOGD("Process listener started.");

vnode_listener_ = kauth_listen_scope(KAUTH_SCOPE_VNODE,
vnode_scope_callback,
reinterpret_cast<void *>(this));
if (!vnode_listener_) return kIOReturnInternalError;

LOGD("Vnode listener started.");

return kIOReturnSuccess;
}

kern_return_t SantaDecisionManager::StopListener() {
kauth_unlisten_scope(vnode_listener_);
vnode_listener_ = NULL;

kauth_unlisten_scope(process_listener_);
process_listener_ = NULL;

// Wait for any active invocations to finish before returning
do {
IOSleep(5);
} while (GetListenerInvocations());

// Delete any cached decisions
ClearCache();

LOGD("Vnode listener stopped.");
LOGD("Process listener stopped.");

return kIOReturnSuccess;
}

#undef super

#pragma mark Kauth Callbacks
Expand Down
78 changes: 54 additions & 24 deletions Source/santa-driver/SantaDecisionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,47 +66,77 @@ class SantaDecisionManager : public OSObject {
OSDeclareDefaultStructors(SantaDecisionManager);

public:
// Convenience constructor
// Queue remains owned by caller but must exist for lifetime of
// SantaDecisionManager instance.
static SantaDecisionManager *WithQueueAndPID(
IOSharedDataQueue *queue, pid_t pid);
/// Used for initialization after instantiation. Required because
/// constructors cannot throw inside kernel-space.
bool init();

bool InitWithQueueAndPID(IOSharedDataQueue *queue, pid_t pid);
/// Called automatically when retain count drops to 0.
void free();

// Decision Fetching / Daemon Communication
bool PostToQueue(santa_message_t);
santa_action_t FetchDecision(const kauth_cred_t credential,
const vfs_context_t vfs_context,
const vnode_t vnode);
/// Called by SantaDriverClient when a client connects, providing the data
/// queue used to pass messages and the pid of the client process.
void ConnectClient(IOSharedDataQueue *queue, pid_t pid);

// Vnode ID string
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);
/// Called by SantaDriverClient when a client disconnects
void DisconnectClient();

/// Returns whether a client is currently connected or not.
bool ClientConnected();

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

// Cache management
/// Stops both kauth listeners. After stopping new callback requests,
/// waits until all current invocations have finished before clearing the
/// cache and returning.
kern_return_t StopListener();

/// Adds a decision to the cache, with a timestamp.
void AddToCache(const char *identifier,
const santa_action_t decision,
const uint64_t microsecs);

/// Checks to see if a given identifier is in the cache and removes it.
void CacheCheck(const char *identifier);

/// Returns the number of entries in the cache.
uint64_t CacheCount();

/// Clears the cache.
void ClearCache();

/// Fetches a response from the cache, first checking to see if the
/// entry has expired.
santa_action_t GetFromCache(const char *identifier);

// Listener invocation management
SInt32 GetListenerInvocations();
void IncrementListenerInvocations();
void DecrementListenerInvocations();
/// Posts the requested message to the client data queue, if there is one.
/// Uses dataqueue_lock_ to ensure two threads don't try to write to the
/// queue at the same time.
bool PostToQueue(santa_message_t);

// Owning PID comparison
bool MatchesOwningPID(const pid_t other_pid);
/// Fetches an execution decision for a file, first using the cache and then
/// by sending a message to the daemon and waiting until a response arrives.
/// If a daemon isn't connected, will allow execution and cache, logging
/// the path to the executed file.
santa_action_t FetchDecision(const kauth_cred_t credential,
const vfs_context_t vfs_context,
const vnode_t vnode);

/// Fetches the vnode_id for a given vnode.
uint64_t GetVnodeIDForVnode(const vfs_context_t context, const vnode_t vp);

// Returns the current system uptime in microseconds
/// Returns the current system uptime in microseconds
uint64_t GetCurrentUptime();

// Starting and stopping the listener
kern_return_t StartListener();
kern_return_t StopListener();

/// Increments the count of active vnode callback's pending.
void IncrementListenerInvocations();

/// Decrements the count of active vnode callback's pending.
void DecrementListenerInvocations();

/// Returns true if other_pid is the same as the current client pid.
bool MatchesOwningPID(const pid_t other_pid);

private:
OSDictionary *cached_decisions_;
Expand Down
14 changes: 14 additions & 0 deletions Source/santa-driver/SantaDriver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ OSDefineMetaClassAndStructors(com_google_SantaDriver, IOService);
bool SantaDriver::start(IOService *provider) {
if (!super::start(provider)) return false;

santaDecisionManager = new SantaDecisionManager;
santaDecisionManager->init();
santaDecisionManager->StartListener();

registerService();

LOGI("Loaded, version %s.", OSKextGetCurrentVersionString());
Expand All @@ -31,7 +35,17 @@ bool SantaDriver::start(IOService *provider) {
}

void SantaDriver::stop(IOService *provider) {
santaDecisionManager->StopListener();
santaDecisionManager->release();
santaDecisionManager = NULL;

LOGI("Unloaded.");

super::stop(provider);
}

SantaDecisionManager* SantaDriver::GetDecisionManager() {
return santaDecisionManager;
}

#undef super
Loading

0 comments on commit a59d2aa

Please sign in to comment.