mirror of
https://github.com/valitydev/osquery-1.git
synced 2024-11-08 10:23:54 +00:00
Merge pull request #1327 from mofarrell/kernel-publishers
Publisher system for kernel events in the kernel extension.
This commit is contained in:
commit
9a67c18974
@ -26,6 +26,7 @@ if(APPLE)
|
||||
|
||||
# Set OS X platform-specific include paths.
|
||||
include_directories("${CMAKE_SOURCE_DIR}/kernel/include")
|
||||
include_directories("${CMAKE_SOURCE_DIR}/kernel/src")
|
||||
include_directories("/System/Library/Frameworks/Kernel.framework/Headers")
|
||||
include_directories("/Applications/Xcode.app/Contents/Developer/Platforms/\
|
||||
MacOSX.platform/Developer/SDKs/MacOSX${APPLE_MIN_ABI}.sdk\
|
||||
@ -59,16 +60,20 @@ endif()
|
||||
|
||||
# The set of platform-agnostic implementations.
|
||||
set(BASE_KERNEL_SOURCES
|
||||
src/osquery.cpp
|
||||
src/circular_queue_kern.c
|
||||
)
|
||||
|
||||
# TODO: Add a set of platform-specific files.
|
||||
file(GLOB APPLE_KERNEL_PUBLISHER_SOURCES "src/publishers/darwin/*.c")
|
||||
# Add a set of platform-specific files.
|
||||
set(APPLE_KERNEL_SOURCES
|
||||
src/osquery.cpp
|
||||
${APPLE_KERNEL_PUBLISHER_SOURCES}
|
||||
)
|
||||
|
||||
# Define kernel targets, each should be an extension/module.
|
||||
if(APPLE)
|
||||
# TODO: Remove the OS X requirement.
|
||||
add_executable(base_kernel ${BASE_KERNEL_SOURCES})
|
||||
add_executable(base_kernel ${APPLE_KERNEL_SOURCES} ${BASE_KERNEL_SOURCES})
|
||||
set_target_properties(base_kernel PROPERTIES COMPILE_FLAGS ${KERNEL_C_FLAGS} ${KERNEL_CXX_FLAGS})
|
||||
set_target_properties(base_kernel PROPERTIES LINK_FLAGS ${KERNEL_LINKER_FLAGS})
|
||||
set_target_properties(base_kernel PROPERTIES EXCLUDE_FROM_ALL true)
|
||||
|
@ -23,6 +23,19 @@
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
/** @brief Communication version between kernel and daemon.
|
||||
*
|
||||
* A daemon may only connect to a kernel with the same communication version.
|
||||
* Bump this number when changing or adding any event structs.
|
||||
*/
|
||||
#define OSQUERY_KERNEL_COMMUNICATION_VERSION 1UL
|
||||
#ifdef KERNEL_TEST
|
||||
#define OSQUERY_KERNEL_COMM_VERSION \
|
||||
(OSQUERY_KERNEL_COMMUNICATION_VERSION | (1UL << 63))
|
||||
#else
|
||||
#define OSQUERY_KERNEL_COMM_VERSION OSQUERY_KERNEL_COMMUNICATION_VERSION
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -36,7 +49,6 @@ extern "C" {
|
||||
* a reentrant testing IOCTL call that adds test events to the cqueue structure.
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// Event feed types
|
||||
//
|
||||
@ -51,7 +63,7 @@ typedef enum {
|
||||
OSQUERY_TEST_EVENT_1,
|
||||
#endif // KERNEL_TEST
|
||||
|
||||
OSQUERY_EVENT_NUM_EVENTS // Number of different event types.
|
||||
OSQUERY_NUM_EVENTS // Number of different event types.
|
||||
} osquery_event_t;
|
||||
|
||||
typedef struct {
|
||||
@ -122,6 +134,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
osquery_event_t event;
|
||||
int subscribe;
|
||||
void *udata;
|
||||
} osquery_subscription_args_t;
|
||||
|
||||
// Flags for buffer sync options.
|
||||
@ -138,6 +151,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
size_t size; // Size of shared user kernel buffer.
|
||||
void *buffer; // (Output) Pointer to buffer location.
|
||||
uint64_t version; // osquery kernel communication version.
|
||||
} osquery_buf_allocate_args_t;
|
||||
|
||||
// TODO: Choose a proper IOCTL num.
|
||||
|
@ -12,12 +12,8 @@
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#include <kern/debug.h>
|
||||
#include <sys/vnode.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <miscfs/devfs/devfs.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
@ -25,7 +21,7 @@
|
||||
#include <IOKit/IOMemoryDescriptor.h>
|
||||
#include <IOKit/IOLib.h>
|
||||
|
||||
#include <feeds.h>
|
||||
#include "publishers.h"
|
||||
|
||||
#include "circular_queue_kern.h"
|
||||
|
||||
@ -49,7 +45,6 @@ static struct {
|
||||
void *devfs;
|
||||
int major_number;
|
||||
int open_count;
|
||||
kauth_listener_t fileop_listener;
|
||||
|
||||
lck_grp_attr_t *lck_grp_attr;
|
||||
lck_grp_t *lck_grp;
|
||||
@ -65,68 +60,6 @@ static struct {
|
||||
.major_number = OSQUERY_MAJOR
|
||||
};
|
||||
|
||||
static int fileop_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3) {
|
||||
vnode_t vp = (vnode_t)arg0;
|
||||
if (action == KAUTH_FILEOP_EXEC && vp != NULL) {
|
||||
// Someone is executing a file.
|
||||
int path_len = MAXPATHLEN;
|
||||
|
||||
osquery_process_event_t *e =
|
||||
(osquery_process_event_t *)osquery_cqueue_reserve(&osquery.cqueue,
|
||||
OSQUERY_PROCESS_EVENT);
|
||||
if (e == NULL) {
|
||||
// Failed to reserve space for the event.
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
e->pid = proc_selfpid();
|
||||
e->ppid = proc_selfppid();
|
||||
e->owner_uid = 0;
|
||||
e->owner_gid = 0;
|
||||
e->mode = -1;
|
||||
vfs_context_t context = vfs_context_create(NULL);
|
||||
if (context) {
|
||||
struct vnode_attr vattr = {0};
|
||||
VATTR_INIT(&vattr);
|
||||
VATTR_WANTED(&vattr, va_uid);
|
||||
VATTR_WANTED(&vattr, va_gid);
|
||||
VATTR_WANTED(&vattr, va_mode);
|
||||
VATTR_WANTED(&vattr, va_create_time);
|
||||
VATTR_WANTED(&vattr, va_access_time);
|
||||
VATTR_WANTED(&vattr, va_modify_time);
|
||||
VATTR_WANTED(&vattr, va_change_time);
|
||||
|
||||
if (vnode_getattr(vp, &vattr, context) == 0) {
|
||||
e->owner_uid = vattr.va_uid;
|
||||
e->owner_gid = vattr.va_gid;
|
||||
e->mode = vattr.va_mode;
|
||||
e->create_time = vattr.va_create_time.tv_sec;
|
||||
e->access_time = vattr.va_access_time.tv_sec;
|
||||
e->modify_time = vattr.va_modify_time.tv_sec;
|
||||
e->change_time = vattr.va_change_time.tv_sec;
|
||||
}
|
||||
|
||||
vfs_context_rele(context);
|
||||
}
|
||||
|
||||
e->uid = kauth_cred_getruid(credential);
|
||||
e->euid = kauth_cred_getuid(credential);
|
||||
|
||||
e->gid = kauth_cred_getrgid(credential);
|
||||
e->egid = kauth_cred_getgid(credential);
|
||||
|
||||
vn_getpath(vp, e->path, &path_len);
|
||||
|
||||
osquery_cqueue_commit(&osquery.cqueue, e);
|
||||
}
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
|
||||
static inline void setup_locks() {
|
||||
/* Create locks. Cannot be done on the stack. */
|
||||
osquery.lck_grp_attr = lck_grp_attr_alloc_init();
|
||||
@ -150,30 +83,33 @@ static inline void teardown_locks() {
|
||||
}
|
||||
|
||||
static void unsubscribe_all_events() {
|
||||
if (osquery.fileop_listener) {
|
||||
kauth_unlisten_scope(osquery.fileop_listener);
|
||||
osquery.fileop_listener = NULL;
|
||||
for (int i = 0; i < OSQUERY_NUM_EVENTS; i++) {
|
||||
if (osquery_publishers[i]) {
|
||||
osquery_publishers[i]->unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int subscribe_to_event(osquery_event_t event, int subscribe) {
|
||||
static int subscribe_to_event(osquery_event_t event,
|
||||
int subscribe,
|
||||
void *udata) {
|
||||
if (osquery.buffer == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (event) {
|
||||
case OSQUERY_PROCESS_EVENT:
|
||||
if (subscribe && osquery.fileop_listener == NULL) {
|
||||
osquery.fileop_listener =
|
||||
kauth_listen_scope(KAUTH_SCOPE_FILEOP, fileop_scope_callback, NULL);
|
||||
} else if (!subscribe && osquery.fileop_listener) {
|
||||
kauth_unlisten_scope(osquery.fileop_listener);
|
||||
osquery.fileop_listener = NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!(OSQUERY_NULL_EVENT < event && event < OSQUERY_NUM_EVENTS)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!osquery_publishers[event]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (subscribe) {
|
||||
if (osquery_publishers[event]->subscribe(&osquery.cqueue, udata)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
osquery_publishers[event]->unsubscribe();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -342,7 +278,7 @@ static int osquery_ioctl(dev_t dev, u_long cmd, caddr_t data,
|
||||
switch (cmd) {
|
||||
case OSQUERY_IOCTL_SUBSCRIPTION:
|
||||
sub = (osquery_subscription_args_t *)data;
|
||||
if ((err = subscribe_to_event(sub->event, sub->subscribe))) {
|
||||
if ((err = subscribe_to_event(sub->event, sub->subscribe, sub->udata))) {
|
||||
goto error_exit;
|
||||
}
|
||||
break;
|
||||
@ -365,6 +301,12 @@ static int osquery_ioctl(dev_t dev, u_long cmd, caddr_t data,
|
||||
case OSQUERY_IOCTL_BUF_ALLOCATE:
|
||||
alloc = (osquery_buf_allocate_args_t *)data;
|
||||
|
||||
if (alloc->version != OSQUERY_KERNEL_COMM_VERSION) {
|
||||
// Daemon tried connecting with incorrect version number.
|
||||
err = -EINVAL;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (osquery.buffer != NULL) {
|
||||
// We don't want to allocate a second buffer.
|
||||
err = -EINVAL;
|
||||
|
54
kernel/src/publishers.h
Normal file
54
kernel/src/publishers.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <feeds.h>
|
||||
#include "circular_queue_kern.h"
|
||||
|
||||
/** @brief Subscribe function type.
|
||||
*
|
||||
* This function type is called when someone subscribes to a publisher. It
|
||||
* should initialize all event callbacks and start publishing events to the
|
||||
* queue.
|
||||
*
|
||||
* @param queue The queue to publish to. A subscriber will only publish to the
|
||||
* last queue (not an issue as there should only be one queue).
|
||||
* @param udata Pointer to user data. This is in user space and must be
|
||||
* copied down to kernel space before use. A publisher may use this for
|
||||
* any additional data it wants from user space.
|
||||
* @return 0 on success, negative on failure.
|
||||
*/
|
||||
typedef int (*osquery_subscriber_t)(osquery_cqueue_t *queue, void *udata);
|
||||
/** @brief Unsubscribe function type.
|
||||
*
|
||||
* Functions of this type stop a publisher from publishing events to the queue
|
||||
* as soon as possible.
|
||||
*
|
||||
* @return Void.
|
||||
*/
|
||||
typedef void (*osquery_unsubscriber_t)();
|
||||
|
||||
/** @brief A kernel publisher must provide the following function pointers.
|
||||
*/
|
||||
typedef struct {
|
||||
osquery_subscriber_t subscribe;
|
||||
osquery_unsubscriber_t unsubscribe;
|
||||
} osquery_kernel_event_publisher_t;
|
||||
|
||||
//
|
||||
// Event publisher structs defined in implementations.
|
||||
//
|
||||
extern osquery_kernel_event_publisher_t process_events_publisher;
|
||||
|
||||
/** @brief List of the kernel event publishers.
|
||||
*/
|
||||
static osquery_kernel_event_publisher_t
|
||||
*osquery_publishers[OSQUERY_NUM_EVENTS] = {
|
||||
[OSQUERY_PROCESS_EVENT] = &process_events_publisher
|
||||
};
|
105
kernel/src/publishers/darwin/process_events.c
Normal file
105
kernel/src/publishers/darwin/process_events.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/proc.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kauth.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include "publishers.h"
|
||||
|
||||
static osquery_cqueue_t *cqueue = NULL;
|
||||
static kauth_listener_t fileop_listener = NULL;
|
||||
|
||||
static int fileop_scope_callback(kauth_cred_t credential,
|
||||
void *idata,
|
||||
kauth_action_t action,
|
||||
uintptr_t arg0,
|
||||
uintptr_t arg1,
|
||||
uintptr_t arg2,
|
||||
uintptr_t arg3) {
|
||||
vnode_t vp = (vnode_t)arg0;
|
||||
if (action == KAUTH_FILEOP_EXEC && vp != NULL) {
|
||||
// Someone is executing a file.
|
||||
int path_len = MAXPATHLEN;
|
||||
|
||||
osquery_process_event_t *e =
|
||||
(osquery_process_event_t *)osquery_cqueue_reserve(
|
||||
cqueue, OSQUERY_PROCESS_EVENT);
|
||||
if (e == NULL) {
|
||||
// Failed to reserve space for the event.
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
e->pid = proc_selfpid();
|
||||
e->ppid = proc_selfppid();
|
||||
e->owner_uid = 0;
|
||||
e->owner_gid = 0;
|
||||
e->mode = -1;
|
||||
vfs_context_t context = vfs_context_create(NULL);
|
||||
if (context) {
|
||||
struct vnode_attr vattr = {0};
|
||||
VATTR_INIT(&vattr);
|
||||
VATTR_WANTED(&vattr, va_uid);
|
||||
VATTR_WANTED(&vattr, va_gid);
|
||||
VATTR_WANTED(&vattr, va_mode);
|
||||
VATTR_WANTED(&vattr, va_create_time);
|
||||
VATTR_WANTED(&vattr, va_access_time);
|
||||
VATTR_WANTED(&vattr, va_modify_time);
|
||||
VATTR_WANTED(&vattr, va_change_time);
|
||||
|
||||
if (vnode_getattr(vp, &vattr, context) == 0) {
|
||||
e->owner_uid = vattr.va_uid;
|
||||
e->owner_gid = vattr.va_gid;
|
||||
e->mode = vattr.va_mode;
|
||||
e->create_time = vattr.va_create_time.tv_sec;
|
||||
e->access_time = vattr.va_access_time.tv_sec;
|
||||
e->modify_time = vattr.va_modify_time.tv_sec;
|
||||
e->change_time = vattr.va_change_time.tv_sec;
|
||||
}
|
||||
|
||||
vfs_context_rele(context);
|
||||
}
|
||||
|
||||
e->uid = kauth_cred_getruid(credential);
|
||||
e->euid = kauth_cred_getuid(credential);
|
||||
|
||||
e->gid = kauth_cred_getrgid(credential);
|
||||
e->egid = kauth_cred_getgid(credential);
|
||||
|
||||
vn_getpath(vp, e->path, &path_len);
|
||||
|
||||
osquery_cqueue_commit(cqueue, e);
|
||||
}
|
||||
return KAUTH_RESULT_DEFER;
|
||||
}
|
||||
|
||||
static int subscribe(osquery_cqueue_t *queue, void *udata) {
|
||||
cqueue = queue;
|
||||
if (fileop_listener != NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fileop_listener =
|
||||
kauth_listen_scope(KAUTH_SCOPE_FILEOP, fileop_scope_callback, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unsubscribe() {
|
||||
if (fileop_listener) {
|
||||
kauth_unlisten_scope(fileop_listener);
|
||||
fileop_listener = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
osquery_kernel_event_publisher_t process_events_publisher = {
|
||||
.subscribe = &subscribe,
|
||||
.unsubscribe = &unsubscribe
|
||||
};
|
@ -40,7 +40,7 @@ void KernelEventPublisher::configure() {
|
||||
for (const auto &sub : subscriptions_) {
|
||||
if (queue_ != nullptr) {
|
||||
auto sc = getSubscriptionContext(sub->context);
|
||||
queue_->subscribe(sc->event_type);
|
||||
queue_->subscribe(sc->event_type, sc->udata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ namespace osquery {
|
||||
struct KernelSubscriptionContext : public SubscriptionContext {
|
||||
/// The kernel event subscription type.
|
||||
osquery_event_t event_type;
|
||||
|
||||
/// Data to pass to the kernel.
|
||||
void *udata;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -27,6 +27,7 @@ CQueue::CQueue(size_t size) {
|
||||
osquery_buf_allocate_args_t alloc;
|
||||
alloc.size = size;
|
||||
alloc.buffer = NULL;
|
||||
alloc.version = OSQUERY_KERNEL_COMM_VERSION;
|
||||
|
||||
fd_ = open(filename, O_RDWR);
|
||||
if (fd_ < 0) {
|
||||
@ -50,10 +51,11 @@ CQueue::~CQueue() {
|
||||
}
|
||||
}
|
||||
|
||||
void CQueue::subscribe(osquery_event_t event) {
|
||||
void CQueue::subscribe(osquery_event_t event, void *udata) {
|
||||
osquery_subscription_args_t sub;
|
||||
sub.event = event;
|
||||
sub.subscribe = 1;
|
||||
sub.udata = udata;
|
||||
|
||||
if (ioctl(fd_, OSQUERY_IOCTL_SUBSCRIPTION, &sub)) {
|
||||
throw CQueueException("Could not subscribe to event.");
|
||||
|
@ -55,8 +55,9 @@ class CQueue {
|
||||
* This sets up the event callbacks so we start hearing about the given event.
|
||||
*
|
||||
* @param event The event we are interested in.
|
||||
* @param udata Pointer to udata for the event. This is for additional info.
|
||||
*/
|
||||
void subscribe(osquery_event_t event);
|
||||
void subscribe(osquery_event_t event, void *udata);
|
||||
|
||||
/**
|
||||
* @brief Dequeue's an event from the shared buffer.
|
||||
|
@ -28,6 +28,7 @@ REGISTER(ProcessEventSubscriber, "event_subscriber", "process_events");
|
||||
Status ProcessEventSubscriber::init() {
|
||||
auto sc = createSubscriptionContext();
|
||||
sc->event_type = OSQUERY_PROCESS_EVENT;
|
||||
sc->udata = NULL;
|
||||
subscribe(&ProcessEventSubscriber::Callback, sc, NULL);
|
||||
|
||||
return Status(0, "OK");
|
||||
|
Loading…
Reference in New Issue
Block a user