Merge pull request #541 from theopolis/usb_parity

PCI/USB parity
This commit is contained in:
Teddy Reed 2014-12-11 10:29:24 -08:00
commit 8d1966f7ff
13 changed files with 318 additions and 66 deletions

View File

@ -47,6 +47,7 @@ std::string stringFromCFString(const CFStringRef& cf_string);
* @brief Convert a CFNumberRef to a std::string.
*/
std::string stringFromCFNumber(const CFDataRef& cf_number);
std::string stringFromCFData(const CFDataRef& cf_data);
#endif
}

View File

@ -7,12 +7,9 @@
namespace osquery {
std::string stringFromCFString(const CFStringRef& cf_string) {
CFIndex length;
char *buffer;
// Access, then convert the CFString. CFStringGetCStringPtr is less-safe.
length = CFStringGetLength(cf_string);
buffer = (char *)malloc(length + 1);
CFIndex length = CFStringGetLength(cf_string);
char* buffer = (char*)malloc(length + 1);
if (!CFStringGetCString(
cf_string, buffer, length + 1, kCFStringEncodingASCII)) {
free(buffer);
@ -25,6 +22,24 @@ std::string stringFromCFString(const CFStringRef& cf_string) {
return result;
}
std::string stringFromCFData(const CFDataRef& cf_data) {
CFRange range = CFRangeMake(0, CFDataGetLength(cf_data));
char* buffer = (char*)malloc(range.length + 1);
memset(buffer, 0, range.length + 1);
CFDataGetBytes(cf_data, range, (UInt8*)buffer);
for (CFIndex i = 0; i < range.length; ++i) {
if (buffer[i] == 0) {
buffer[i] = ' ';
}
}
// Cleanup allocations.
std::string result(buffer);
free(buffer);
return result;
}
std::string stringFromCFNumber(const CFDataRef& cf_number) {
unsigned int value;
if (CFGetTypeID(cf_number) != CFNumberGetTypeID() ||

View File

@ -70,8 +70,16 @@ Status UdevEventPublisher::run() {
std::string UdevEventPublisher::getValue(struct udev_device* device,
const std::string& property) {
auto value = udev_device_get_property_value(
device, std::string("ID_" + property).c_str());
auto value = udev_device_get_property_value(device, property.c_str());
if (value != nullptr) {
return std::string(value);
}
return "";
}
std::string UdevEventPublisher::getAttr(struct udev_device* device,
const std::string& attr) {
auto value = udev_device_get_sysattr_value(device, attr.c_str());
if (value != nullptr) {
return std::string(value);
}

View File

@ -79,12 +79,22 @@ class UdevEventPublisher : public EventPublisher {
* @brief Return a string representation of a udev property.
*
* @param device the udev device pointer.
* @param property the udev property without the "ID_" prefix.
* @param property the udev property identifier string.
* @return string representation of the property or empty if null.
*/
static std::string getValue(struct udev_device* device,
const std::string& property);
/**
* @brief Return a string representation of a udev system attribute.
*
* @param device the udev device pointer.
* @param property the udev system attribute identifier string.
* @return string representation of the attribute or empty if null.
*/
static std::string getAttr(struct udev_device* device,
const std::string& attr);
private:
/// udev handle (socket descriptor contained within).
struct udev *handle_;

View File

@ -25,6 +25,7 @@ if(APPLE)
system/darwin/processes.cpp
system/darwin/process_open_files.cpp
system/darwin/quarantine.cpp
system/darwin/pci_devices.cpp
system/darwin/usb_devices.cpp
system/darwin/startup_items.cpp
)
@ -56,6 +57,7 @@ else()
system/linux/groups.cpp
system/linux/mounts.cpp
system/linux/pci_devices.cpp
system/linux/usb_devices.cpp
system/linux/block_devices.cpp
)

View File

@ -51,17 +51,19 @@ Status HardwareEventSubscriber::Callback(const UdevEventContextRef ec) {
r["driver"] = ec->driver;
// UDEV properties.
r["model"] = UdevEventPublisher::getValue(device, "MODEL");
r["model"] = UdevEventPublisher::getValue(device, "ID_MODEL_FROM_DATABASE");
if (r["path"].empty() && r["model"].empty()) {
// Don't emit mising path/model combos.
return Status(0, "Missing path and model.");
}
r["model_id"] = INTEGER(UdevEventPublisher::getValue(device, "MODEL_ID"));
r["vendor"] = UdevEventPublisher::getValue(device, "VENDOR");
r["vendor_id"] = INTEGER(UdevEventPublisher::getValue(device, "VENDOR_ID"));
r["serial"] = INTEGER(UdevEventPublisher::getValue(device, "SERIAL_SHORT"));
r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "REVISION"));
r["model_id"] = INTEGER(UdevEventPublisher::getValue(device, "ID_MODEL_ID"));
r["vendor"] = UdevEventPublisher::getValue(device, "ID_VENDOR_FROM_DATABASE");
r["vendor_id"] =
INTEGER(UdevEventPublisher::getValue(device, "ID_VENDOR_ID"));
r["serial"] =
INTEGER(UdevEventPublisher::getValue(device, "ID_SERIAL_SHORT"));
r["revision"] = INTEGER(UdevEventPublisher::getValue(device, "ID_REVISION"));
r["time"] = INTEGER(ec->time);
add(r, ec->time);

View File

@ -1,8 +0,0 @@
table_name("pci_devices")
schema([
Column("slot", TEXT),
Column("device_class", TEXT),
Column("vendor", TEXT),
Column("model", TEXT),
])
implementation("pci_devices@genPCIDevices")

View File

@ -0,0 +1,17 @@
table_name("pci_devices")
schema([
Column("pci_slot", TEXT),
Column("pci_class", TEXT),
Column("driver", TEXT),
Column("vendor", TEXT),
Column("vendor_id", TEXT),
Column("model", TEXT),
Column("model_id", TEXT),
# Optional columns
#Column("subsystem", TEXT),
#Column("express", INTEGER),
#Column("thunderbolt", INTEGER),
#Column("removable", INTEGER),
])
implementation("pci_devices@genPCIDevices")

View File

@ -3,10 +3,10 @@ schema([
Column("usb_address", INTEGER),
Column("usb_port", INTEGER),
Column("vendor", TEXT),
Column("vendor_id", INTEGER),
Column("vendor_id", TEXT),
Column("model", TEXT),
Column("model_id", INTEGER),
Column("serial", INTEGER),
Column("model_id", TEXT),
Column("serial", TEXT),
Column("removable", INTEGER),
])
implementation("usb_devices@genUsbDevices")
implementation("usb_devices@genUSBDevices")

View File

@ -0,0 +1,118 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/filesystem.h>
#include "osquery/core/conversions.h"
namespace osquery {
namespace tables {
#define kIOPCIDeviceClassName_ "IOPCIDevice"
std::string getPCIProperty(const CFMutableDictionaryRef& details,
const std::string& key) {
std::string value;
// Get a property from the device.
auto cfkey = CFStringCreateWithCString(
kCFAllocatorDefault, key.c_str(), kCFStringEncodingUTF8);
auto property = CFDictionaryGetValue(details, cfkey);
CFRelease(cfkey);
// Several supported ways of parsing IOKit-encoded data.
if (property) {
if (CFGetTypeID(property) == CFNumberGetTypeID()) {
value = stringFromCFNumber((CFDataRef)property);
} else if (CFGetTypeID(property) == CFStringGetTypeID()) {
value = stringFromCFString((CFStringRef)property);
} else if (CFGetTypeID(property) == CFDataGetTypeID()) {
value = stringFromCFData((CFDataRef)property);
}
}
return value;
}
void genPCIDevice(const io_service_t& device, QueryData& results) {
Row r;
// Get the device details
CFMutableDictionaryRef details;
IORegistryEntryCreateCFProperties(
device, &details, kCFAllocatorDefault, kNilOptions);
r["pci_slot"] = getPCIProperty(details, "pcidebug");
std::vector<std::string> properties;
auto compatible = getPCIProperty(details, "compatible");
boost::trim(compatible);
boost::split(properties, compatible, boost::is_any_of(" "));
if (properties.size() < 2) {
VLOG(1) << "Error parsing IOKit compatible properties";
return;
}
size_t prop_index = 0;
if (properties[1].find("pci") == 0 && properties[1].find("pciclass") != 0) {
// There are two sets of PCI definitions.
prop_index = 1;
} else if (properties[0].find("pci") != 0) {
VLOG(1) << "No vendor/model found";
return;
}
std::vector<std::string> vendor;
boost::split(vendor, properties[prop_index++], boost::is_any_of(","));
r["vendor_id"] = vendor[0].substr(3);
r["model_id"] = (vendor[1].size() == 3) ? "0" + vendor[1] : vendor[1];
if (properties[prop_index].find("pciclass") == 0) {
// There is a class definition.
r["pci_class"] = properties[prop_index++].substr(9);
}
if (properties.size() > prop_index) {
// There is a driver/ID.
r["driver"] = properties[prop_index];
}
results.push_back(r);
CFRelease(details);
}
QueryData genPCIDevices(QueryContext& context) {
QueryData results;
auto matching = IOServiceMatching(kIOPCIDeviceClassName_);
if (matching == nullptr) {
// No devices matched USB, very odd.
return results;
}
io_iterator_t it;
auto kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &it);
if (kr != KERN_SUCCESS) {
return results;
}
io_service_t device;
while ((device = IOIteratorNext(it))) {
genPCIDevice(device, results);
IOObjectRelease(device);
}
IOObjectRelease(it);
return results;
}
}
}

View File

@ -53,7 +53,7 @@ void genUSBDevice(const io_service_t& device, QueryData& results) {
CFRelease(details);
}
QueryData genUsbDevices(QueryContext& context) {
QueryData genUSBDevices(QueryContext& context) {
QueryData results;
auto matching = IOServiceMatching(kIOUSBDeviceClassName);

View File

@ -1,71 +1,79 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <libudev.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include "osquery/events/linux/udev.h"
namespace osquery {
namespace tables {
const std::string kSlot = "PCI_SLOT_NAME";
const std::string kClass = "ID_PCI_CLASS_FROM_DATABASE";
const std::string kVendor = "ID_VENDOR_FROM_DATABASE";
const std::string kModel = "ID_MODEL_FROM_DATABASE";
struct udev *udev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
struct udev_device *dev;
const std::string kPCIKeySlot = "PCI_SLOT_NAME";
const std::string kPCIKeyClass = "ID_PCI_CLASS_FROM_DATABASE";
const std::string kPCIKeyVendor = "ID_VENDOR_FROM_DATABASE";
const std::string kPCIKeyModel = "ID_MODEL_FROM_DATABASE";
const std::string kPCIKeyID = "PCI_ID";
const std::string kPCIKeyDriver = "DRIVER";
QueryData genPCIDevices(QueryContext &context) {
QueryData results;
// Create the udev object
udev = udev_new();
if (!udev) {
LOG(ERROR) << "Can't create udev object";
auto udev_handle = udev_new();
if (udev_handle == nullptr) {
VLOG(1) << "Could not get udev handle.";
return results;
}
// Enumerate the list of all PCI devices
enumerate = udev_enumerate_new(udev);
// Perform enumeration/search.
auto enumerate = udev_enumerate_new(udev_handle);
udev_enumerate_add_match_subsystem(enumerate, "pci");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
// udev_list_entry_foreach is a macro which expands to
// a loop. The loop will be executed for each member in
// devices, setting dev_list_entry to a list entry
// which contains the device's path in /sys.
// Get list entries and iterate over entries.
struct udev_list_entry *device_entries, *entry;
device_entries = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(dev_list_entry, devices) {
const char *path, *tmp;
// Get the filename of the /sys entry for the PCI device
// and create a udev_device object (dev) representing it
path = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev, path);
udev_list_entry_foreach(entry, device_entries) {
const char *path = udev_list_entry_get_name(entry);
auto device = udev_device_new_from_syspath(udev_handle, path);
Row r;
if ((tmp = udev_device_get_property_value(dev, kSlot.c_str()))) {
r["slot"] = TEXT(tmp);
r["pci_slot"] = UdevEventPublisher::getValue(device, kPCIKeySlot);
r["pci_class"] = UdevEventPublisher::getValue(device, kPCIKeyClass);
r["driver"] = UdevEventPublisher::getValue(device, kPCIKeyDriver);
r["vendor"] = UdevEventPublisher::getValue(device, kPCIKeyVendor);
r["model"] = UdevEventPublisher::getValue(device, kPCIKeyModel);
// VENDOR:MODEL ID is in the form of HHHH:HHHH.
std::vector<std::string> ids;
auto device_id = UdevEventPublisher::getValue(device, kPCIKeyID);
boost::split(ids, device_id, boost::is_any_of(":"));
if (ids.size() == 2) {
r["vendor_id"] = ids[0];
r["model_id"] = ids[1];
}
if ((tmp = udev_device_get_property_value(dev, kClass.c_str()))) {
r["device_class"] = TEXT(tmp);
// Set invalid vendor/model IDs to 0.
if (r["vendor_id"].size() == 0) {
r["vendor_id"] = "0";
}
if ((tmp = udev_device_get_property_value(dev, kVendor.c_str()))) {
r["vendor"] = TEXT(tmp);
}
if ((tmp = udev_device_get_property_value(dev, kModel.c_str()))) {
r["model"] = TEXT(tmp);
if (r["model_id"].size() == 0) {
r["model_id"] = "0";
}
results.push_back(r);
udev_device_unref(dev);
udev_device_unref(device);
}
// Drop references to udev structs.
udev_enumerate_unref(enumerate);
udev_unref(udev);
udev_unref(udev_handle);
return results;
}
}

View File

@ -0,0 +1,79 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include "osquery/events/linux/udev.h"
namespace osquery {
namespace tables {
const std::string kUSBKeyVendorID = "ID_VENDOR_ID";
const std::string kUSBKeyVendor = "ID_VENDOR_FROM_DATABASE";
const std::string kUSBKeyModelID = "ID_MODEL_ID";
const std::string kUSBKeyModel = "ID_MODEL_FROM_DATABASE";
const std::string kUSBKeyDriver = "ID_USB_DRIVER";
const std::string kUSBKeySubsystem = "SUBSYSTEM";
const std::string kUSBKeySerial = "ID_SERIAL_SHORT";
const std::string kUSBKeyAddress = "BUSNUM";
const std::string kUSBKeyPort = "DEVNUM";
QueryData genUSBDevices(QueryContext &context) {
QueryData results;
auto udev_handle = udev_new();
if (udev_handle == nullptr) {
VLOG(1) << "Could not get udev handle.";
return results;
}
// Perform enumeration/search.
auto enumerate = udev_enumerate_new(udev_handle);
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
// Get list entries and iterate over entries.
struct udev_list_entry *device_entries, *entry;
device_entries = udev_enumerate_get_list_entry(enumerate);
udev_list_entry_foreach(entry, device_entries) {
const char *path = udev_list_entry_get_name(entry);
auto device = udev_device_new_from_syspath(udev_handle, path);
Row r;
// r["driver"] = UdevEventPublisher::getValue(device, kUSBKeyDriver);
r["vendor"] = UdevEventPublisher::getValue(device, kUSBKeyVendor);
r["model"] = UdevEventPublisher::getValue(device, kUSBKeyModel);
// USB-specific vendor/model ID properties.
r["model_id"] = UdevEventPublisher::getValue(device, kUSBKeyModelID);
r["vendor_id"] = UdevEventPublisher::getValue(device, kUSBKeyVendorID);
r["serial"] = UdevEventPublisher::getValue(device, kUSBKeySerial);
// Address/port accessors.
r["usb_address"] = UdevEventPublisher::getValue(device, kUSBKeyAddress);
r["usb_port"] = UdevEventPublisher::getValue(device, kUSBKeyPort);
// Removable detection.
auto removable = UdevEventPublisher::getAttr(device, "removable");
if (removable == "unknown") {
r["removable"] = "-1";
} else {
r["removable"] = "1";
}
if (r["usb_address"].size() > 0 && r["usb_port"].size() > 0) {
results.push_back(r);
}
udev_device_unref(device);
}
// Drop references to udev structs.
udev_enumerate_unref(enumerate);
udev_unref(udev_handle);
return results;
}
}
}