IOKit HID events and OSX hardware_events table

This commit is contained in:
Teddy Reed 2014-12-11 18:06:08 -08:00
parent 7b56fa605d
commit acccfa94e2
6 changed files with 378 additions and 1 deletions

View File

@ -6,6 +6,7 @@ if(APPLE)
ADD_OSQUERY_LIBRARY(osquery_events_darwin
darwin/fsevents.cpp
darwin/iokit_hid.cpp
darwin/scnetwork.cpp
)
elseif(FREEBSD)

View File

@ -0,0 +1,181 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <osquery/logger.h>
#include <osquery/tables.h>
#include "osquery/core/conversions.h"
#include "osquery/events/darwin/iokit_hid.h"
namespace osquery {
REGISTER_EVENTPUBLISHER(IOKitHIDEventPublisher);
size_t IOKitHIDEventPublisher::initial_device_count_ = 0;
size_t IOKitHIDEventPublisher::initial_device_evented_count_ = 0;
boost::mutex IOKitHIDEventPublisher::iokit_match_lock_;
std::string IOKitHIDEventPublisher::getProperty(const IOHIDDeviceRef &device,
const CFStringRef &property) {
CFTypeRef value = IOHIDDeviceGetProperty(device, property);
if (value == NULL) {
return "";
}
// Only support CFNumber and CFString types.
if (CFGetTypeID(value) == CFNumberGetTypeID()) {
return stringFromCFNumber((CFDataRef)value);
} else if (CFGetTypeID(value) == CFStringGetTypeID()) {
return stringFromCFString((CFStringRef)value);
}
return "";
}
void IOKitHIDEventPublisher::restart() {
if (run_loop_ == nullptr) {
// There is no run loop to restart.
return;
}
// Remove any existing stream.
stop();
if (manager_ == nullptr) {
manager_ = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
}
// Match anything.
IOHIDManagerSetDeviceMatching(manager_, NULL);
auto status = IOHIDManagerOpen(manager_, kIOHIDOptionsTypeNone);
if (status != kIOReturnSuccess) {
LOG(ERROR) << "Cannot open IOKit HID Manager";
return;
}
// Enumerate initial set of devices matched before time=0.
CFSetRef devices = IOHIDManagerCopyDevices(manager_);
initial_device_count_ = devices == NULL ? 0 : CFSetGetCount(devices);
CFRelease(devices);
// Register callbacks.
IOHIDManagerRegisterDeviceMatchingCallback(
manager_, IOKitHIDEventPublisher::MatchingCallback, NULL);
IOHIDManagerRegisterDeviceRemovalCallback(
manager_, IOKitHIDEventPublisher::RemovalCallback, NULL);
IOHIDManagerScheduleWithRunLoop(manager_, run_loop_, kCFRunLoopDefaultMode);
manager_started_ = true;
}
void IOKitHIDEventPublisher::MatchingCallback(void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device) {
{
// Must not event on initial list of matches.
boost::lock_guard<boost::mutex> lock(iokit_match_lock_);
if (initial_device_count_ > initial_device_evented_count_) {
initial_device_evented_count_++;
return;
}
}
fire(device, "add");
}
void IOKitHIDEventPublisher::fire(IOHIDDeviceRef &device,
const std::string &action) {
auto ec = createEventContext();
ec->device = device;
ec->action = action;
// Fill in more-useful fields.
ec->vendor_id = getProperty(device, CFSTR(kIOHIDVendorIDKey));
ec->model_id = getProperty(device, CFSTR(kIOHIDProductIDKey));
ec->vendor = getProperty(device, CFSTR(kIOHIDManufacturerKey));
ec->model = getProperty(device, CFSTR(kIOHIDProductKey));
ec->transport = getProperty(device, CFSTR(kIOHIDTransportKey));
ec->primary_usage = getProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
ec->device_usage = getProperty(device, CFSTR(kIOHIDDeviceUsageKey));
// Fill in more esoteric properties.
ec->version = getProperty(device, CFSTR(kIOHIDVersionNumberKey));
ec->location = getProperty(device, CFSTR(kIOHIDLocationIDKey));
ec->serial = getProperty(device, CFSTR(kIOHIDSerialNumberKey));
ec->country_code = getProperty(device, CFSTR(kIOHIDCountryCodeKey));
EventFactory::fire<IOKitHIDEventPublisher>(ec);
}
void IOKitHIDEventPublisher::RemovalCallback(void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device) {
fire(device, "remove");
}
void IOKitHIDEventPublisher::InputValueCallback(void *context,
IOReturn result,
void *sender,
IOHIDValueRef value) {
// Nothing yet.
printf("value\n");
}
bool IOKitHIDEventPublisher::shouldFire(const IOKitHIDSubscriptionContextRef sc,
const IOKitHIDEventContextRef ec) {
return true;
}
Status IOKitHIDEventPublisher::run() {
// The run entrypoint executes in a dedicated thread.
if (run_loop_ == nullptr) {
run_loop_ = CFRunLoopGetCurrent();
// Restart the stream creation.
restart();
}
// Start the run loop, it may be removed with a tearDown.
CFRunLoopRun();
// Add artificial latency to run loop.
::sleep(1);
return Status(0, "OK");
}
void IOKitHIDEventPublisher::stop() {
// Stop the manager.
if (manager_ != nullptr) {
IOHIDManagerUnscheduleFromRunLoop(
manager_, run_loop_, kCFRunLoopDefaultMode);
IOHIDManagerClose(manager_, kIOHIDOptionsTypeNone);
manager_started_ = false;
manager_ = nullptr;
}
// Stop the run loop.
if (run_loop_ != nullptr) {
CFRunLoopStop(run_loop_);
}
}
void IOKitHIDEventPublisher::tearDown() {
stop();
// Do not keep a reference to the run loop.
run_loop_ = nullptr;
}
bool IOKitHIDEventPublisher::isManagerRunning() {
if (manager_ == nullptr || !manager_started_) {
return false;
}
if (run_loop_ == nullptr) {
return false;
}
return CFRunLoopIsWaiting(run_loop_);
}
}

View File

@ -0,0 +1,136 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <CoreServices/CoreServices.h>
#include <IOKit/hid/IOHIDLib.h>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <osquery/events.h>
#include <osquery/status.h>
namespace osquery {
struct IOKitHIDSubscriptionContext : public SubscriptionContext {
// Bus type, e.g., USB.
std::string transport;
// Product name
std::string product;
std::string vendor;
// Usage types.
std::string primary_usage;
std::string device_usage;
// Get values from HID events.
bool values;
// Do not request values by default.
IOKitHIDSubscriptionContext() : values(false) {}
};
struct IOKitHIDEventContext : public EventContext {
// The native IOKit device reference.
IOHIDDeviceRef device;
// The event action: add, remove, value change.
std::string action;
// If a value was changed, include the result (optional).
std::string result;
// The publisher pre-populates several fields.
std::string vendor_id;
std::string model_id;
std::string vendor;
std::string model;
std::string transport;
std::string primary_usage;
std::string device_usage;
// More esoteric properties.
std::string version;
std::string location;
std::string serial;
std::string country_code;
};
typedef std::shared_ptr<IOKitHIDEventContext> IOKitHIDEventContextRef;
typedef std::shared_ptr<IOKitHIDSubscriptionContext>
IOKitHIDSubscriptionContextRef;
/**
* @brief An osquery EventPublisher for the Apple IOKit HID notification API.
*
*/
class IOKitHIDEventPublisher : public EventPublisher {
DECLARE_EVENTPUBLISHER(IOKitHIDEventPublisher,
IOKitHIDSubscriptionContext,
IOKitHIDEventContext)
public:
void configure() {}
void tearDown();
// Entrypoint to the run loop
Status run();
public:
/// IOKit HID hotplugged event.
static void MatchingCallback(void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device);
/// IOKit HID device removed.
static void RemovalCallback(void *context,
IOReturn result,
void *sender,
IOHIDDeviceRef device);
/// IOKit HID device value changed.
static void InputValueCallback(void *context,
IOReturn result,
void *sender,
IOHIDValueRef value);
private:
/// Helper fire fuction to parse properties/actions.
static void fire(IOHIDDeviceRef &device, const std::string &action);
public:
IOKitHIDEventPublisher()
: EventPublisher(), manager_(nullptr), run_loop_(nullptr) {}
bool shouldFire(const IOKitHIDSubscriptionContextRef mc,
const IOKitHIDEventContextRef ec);
public:
/// Provide some API usage for IOKitHID parsing.
static std::string getProperty(const IOHIDDeviceRef &device,
const CFStringRef &property);
private:
// Restart the run loop.
void restart();
// Stop the manager and the run loop.
void stop();
void schedule();
private:
// Check if the manager (and run loop) are running.
bool isManagerRunning();
private:
IOHIDManagerRef manager_;
bool manager_started_;
private:
CFRunLoopRef run_loop_;
private:
static size_t initial_device_count_;
static size_t initial_device_evented_count_;
static boost::mutex iokit_match_lock_;
};
}

View File

@ -10,6 +10,7 @@ if(APPLE)
ADD_OSQUERY_LIBRARY(osquery_tables_darwin
events/darwin/passwd_changes.cpp
events/darwin/hardware_events.cpp
networking/darwin/interfaces.cpp
networking/darwin/listening_ports.cpp
networking/darwin/routes.cpp

View File

@ -0,0 +1,58 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include "osquery/events/darwin/iokit_hid.h"
namespace osquery {
namespace tables {
/**
* @brief Track IOKit HID events.
*/
class HardwareEventSubscriber : public EventSubscriber {
DECLARE_EVENTSUBSCRIBER(HardwareEventSubscriber, IOKitHIDEventPublisher);
DECLARE_CALLBACK(Callback, IOKitHIDEventContext);
public:
void init();
Status Callback(const IOKitHIDEventContextRef ec);
};
REGISTER_EVENTSUBSCRIBER(HardwareEventSubscriber);
void HardwareEventSubscriber::init() {
auto subscription = IOKitHIDEventPublisher::createSubscriptionContext();
// We don't want hardware value changes.
subscription->values = false;
BIND_CALLBACK(Callback, subscription);
}
Status HardwareEventSubscriber::Callback(const IOKitHIDEventContextRef ec) {
Row r;
r["action"] = ec->action;
// There is no path in IOKit, there's a location ID (may be useful).
r["path"] = ec->location;
// Type and driver are the name in IOKit
r["type"] = "hid";
r["driver"] = ec->transport;
r["model_id"] = ec->model_id;
r["model"] = ec->model;
r["vendor_id"] = ec->vendor_id;
r["vendor"] = ec->vendor;
r["serial"] = ec->serial; // Not always filled in.
r["revision"] = ec->version;
r["time"] = INTEGER(ec->time);
add(r, ec->time);
return Status(0, "OK");
}
}
}

View File

@ -8,7 +8,7 @@ schema([
Column("model_id", INTEGER),
Column("vendor", TEXT),
Column("vendor_id", INTEGER),
Column("serial", INTEGER),
Column("serial", TEXT),
Column("revision", INTEGER),
Column("time", INTEGER),
])