Merge pull request #1167 from wxsBSD/freebsd_processes

Implement process related tables on FreeBSD.
This commit is contained in:
Teddy Reed 2015-05-29 12:55:24 -07:00
commit 4647b8737b
12 changed files with 530 additions and 17 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Wesley Shields
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the

View File

@ -24,6 +24,11 @@ elseif(FREEBSD)
file(GLOB OSQUERY_FREEBSD_TABLES_TESTS "*/freebsd/tests/*.cpp")
ADD_OSQUERY_TABLE_TEST(${OSQUERY_FREEBSD_TABLES_TESTS})
ADD_OSQUERY_LINK_ADDITIONAL("procstat")
ADD_OSQUERY_LINK_ADDITIONAL("util")
ADD_OSQUERY_LINK_ADDITIONAL("kvm")
ADD_OSQUERY_LINK_ADDITIONAL("elf")
else()
file(GLOB OSQUERY_LINUX_TABLES "*/linux/*.cpp")
ADD_OSQUERY_LIBRARY_ADDITIONAL(osquery_tables_linux

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Wesley Shields
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the

View File

@ -0,0 +1,120 @@
/*
* 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 <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <libprocstat.h>
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include "osquery/tables/system/freebsd/procstat.h"
namespace osquery {
namespace tables {
// Heavily inspired by procstat(1) on FreeBSD.
std::pair<std::string, int>& sockaddr_to_pair(struct sockaddr_storage* sstor) {
char buffer[INET6_ADDRSTRLEN] = {0};
struct sockaddr_in6* sin6 = nullptr;
struct sockaddr_in* sin = nullptr;
struct sockaddr_un* sun = nullptr;
static std::pair<std::string, int> addr;
switch (sstor->ss_family) {
case AF_LOCAL:
sun = (struct sockaddr_un*) sstor;
addr = std::make_pair(std::string(sun->sun_path), 0);
break;
case AF_INET:
sin = (struct sockaddr_in*) sstor;
addr = std::make_pair(std::string(inet_ntoa(sin->sin_addr)),
ntohs(sin->sin_port));
break;
case AF_INET6:
sin6 = (struct sockaddr_in6*) sstor;
inet_ntop(AF_INET6, &sin6->sin6_addr, buffer, sizeof(buffer));
addr = std::make_pair(std::string(buffer), ntohs(sin6->sin6_port));
break;
default:
addr = std::make_pair(std::string(""), 0);
break;
}
return addr;
}
void genSockets(struct procstat* pstat,
struct kinfo_proc* proc,
QueryData &results) {
Row r;
struct filestat_list* files = nullptr;
struct filestat* file = nullptr;
struct sockstat sock;
int error;
std::pair<std::string, int> addr;
files = procstat_getfiles(pstat, proc, 0);
if (files == nullptr) {
return;
}
STAILQ_FOREACH(file, files, next) {
// Skip files that aren't sockets.
if (file->fs_type != PS_FST_TYPE_SOCKET) {
continue;
}
error = procstat_get_socket_info(pstat, file, &sock, nullptr);
if (error != 0) {
continue;
}
r["pid"] = INTEGER(proc->ki_pid);
r["socket"] = INTEGER(file->fs_fd);
r["family"] = INTEGER(sock.dom_family);
r["protocol"] = INTEGER(sock.proto);
addr = sockaddr_to_pair(&(sock.sa_local));
r["local_address"] = TEXT(addr.first);
r["local_port"] = INTEGER(addr.second);
addr = sockaddr_to_pair(&(sock.sa_peer));
r["remote_address"] = TEXT(addr.first);
r["remote_port"] = INTEGER(addr.second);
results.push_back(r);
}
procstat_freefiles(pstat, files);
}
QueryData genOpenSockets(QueryContext &context) {
QueryData results;
struct kinfo_proc* procs = nullptr;
struct procstat* pstat = nullptr;
auto cnt = getProcesses(context, &pstat, &procs);
for (size_t i = 0; i < cnt; i++) {
genSockets(pstat, &procs[i], results);
}
procstatCleanup(pstat, procs);
return results;
}
}
}

View File

@ -16,11 +16,6 @@ freebsd:opera_extensions
freebsd:os_version
freebsd:passwd_changes
freebsd:pci_devices
freebsd:process_envs
freebsd:process_memory_map
freebsd:process_open_files
freebsd:process_open_sockets
freebsd:processes
freebsd:routes
freebsd:system_controls
freebsd:usb_devices

View File

@ -0,0 +1,76 @@
/*
* 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 <stdlib.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/queue.h>
#include <libprocstat.h>
#include <osquery/core.h>
#include <osquery/logger.h>
#include <osquery/tables.h>
#include <osquery/filesystem.h>
#include "osquery/tables/system/freebsd/procstat.h"
namespace osquery {
namespace tables {
void genDescriptors(struct procstat* pstat,
struct kinfo_proc* proc,
QueryData& results) {
Row r;
struct filestat_list* files = nullptr;
struct filestat* file = nullptr;
files = procstat_getfiles(pstat, proc, 0);
if (files == nullptr) {
return;
}
STAILQ_FOREACH(file, files, next) {
// Skip files that aren't "open" (no fd).
if (file->fs_fd == -1) {
continue;
}
r["pid"] = INTEGER(proc->ki_pid);
if (file->fs_path == nullptr) {
r["path"] = TEXT("");
} else {
r["path"] = TEXT(file->fs_path);
}
r["fd"] = BIGINT(file->fs_fd);
results.push_back(r);
}
procstat_freefiles(pstat, files);
}
QueryData genOpenFiles(QueryContext& context) {
QueryData results;
struct kinfo_proc* procs = nullptr;
struct procstat* pstat = nullptr;
auto cnt = getProcesses(context, &pstat, &procs);
for (size_t i = 0; i < cnt; i++) {
genDescriptors(pstat, &procs[i], results);
}
procstatCleanup(pstat, procs);
return results;
}
}
}

View File

@ -8,33 +8,229 @@
*
*/
#include <string>
#include <map>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <paths.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/user.h>
#include <libprocstat.h>
#include <boost/algorithm/string/trim.hpp>
#include <osquery/core.h>
#include <osquery/tables.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include "osquery/tables/system/freebsd/procstat.h"
namespace osquery {
namespace tables {
QueryData genProcessEnvs(QueryContext& context) {
QueryData results;
void genProcessEnvironment(struct procstat* pstat,
struct kinfo_proc* proc,
QueryData& results) {
char** envs;
unsigned int i;
throw std::domain_error("Table not implemented for FreeBSD");
envs = procstat_getenvv(pstat, proc, 0);
if (envs != nullptr) {
for (i = 0; envs[i] != NULL; i++) {
Row r;
size_t idx;
std::string buf = std::string(envs[i]);
return results;
r["pid"] = INTEGER(proc->ki_pid);
idx = buf.find_first_of("=");
r["key"] = buf.substr(0, idx);
r["value"] = buf.substr(idx + 1);
results.push_back(r);
}
procstat_freeenvv(pstat);
}
}
void genProcessMap(struct procstat* pstat,
struct kinfo_proc* proc,
QueryData& results) {
struct kinfo_vmentry* vmentry;
unsigned int i;
unsigned int cnt = 0;
vmentry = procstat_getvmmap(pstat, proc, &cnt);
if (vmentry != nullptr) {
for (i = 0; i < cnt; i++) {
Row r;
r["pid"] = INTEGER(proc->ki_pid);
r["path"] = TEXT(vmentry[i].kve_path);
r["device"] = TEXT(vmentry[i].kve_vn_rdev);
r["inode"] = INTEGER(vmentry[i].kve_vn_fileid);
r["offset"] = INTEGER(vmentry[i].kve_offset);
// To match the linux implementation, convert to hex.
char addr_str[17] = {0};
sprintf(addr_str, "%016lx", vmentry[i].kve_start);
r["start"] = "0x" + TEXT(addr_str);
sprintf(addr_str, "%016lx", vmentry[i].kve_end);
r["end"] = "0x" + TEXT(addr_str);
std::string permissions;
permissions += (vmentry[i].kve_protection & KVME_PROT_READ) ? "r" : "-";
permissions += (vmentry[i].kve_protection & KVME_PROT_WRITE) ? "w" : "-";
permissions += (vmentry[i].kve_protection & KVME_PROT_EXEC) ? "x" : "-";
// COW is stored as a flag on FreeBSD, but osquery lumps it in the
// permissions column.
permissions += (vmentry[i].kve_flags & KVME_FLAG_COW) ? "p" : "-";
r["permissions"] = TEXT(permissions);
if (vmentry[i].kve_vn_fileid == 0 && r["path"].size() > 0) {
r["pseudo"] = INTEGER("1");
} else {
r["pseudo"] = INTEGER("0");
}
results.push_back(r);
}
procstat_freevmmap(pstat, vmentry);
}
}
void genProcess(struct procstat* pstat,
struct kinfo_proc* proc,
QueryData& results) {
Row r;
static char path[PATH_MAX];
char** args;
struct filestat_list* files = nullptr;
struct filestat* file = nullptr;
struct kinfo_vmentry* vmentry = nullptr;
unsigned int i;
unsigned int cnt = 0;
unsigned int pages = 0;
r["pid"] = INTEGER(proc->ki_pid);
r["parent"] = INTEGER(proc->ki_ppid);
r["name"] = TEXT(proc->ki_comm);
r["uid"] = INTEGER(proc->ki_ruid);
r["euid"] = INTEGER(proc->ki_svuid);
r["gid"] = INTEGER(proc->ki_rgid);
r["egid"] = INTEGER(proc->ki_svgid);
if (procstat_getpathname(pstat, proc, path, sizeof(path)) == 0) {
r["path"] = TEXT(path);
// If the path of the executable that started the process is available and
// the path exists on disk, set on_disk to 1. If the path is not
// available, set on_disk to -1. If, and only if, the path of the
// executable is available and the file does NOT exist on disk, set on_disk
// to 0.
r["on_disk"] = TEXT(osquery::pathExists(r["path"]).toString());
}
args = procstat_getargv(pstat, proc, 0);
if (args != nullptr) {
for (i = 0; args[i] != NULL; i++) {
r["cmdline"] += TEXT(args[i]);
// Need to add spaces between arguments, except last one.
if (args[i + 1] != NULL) {
r["cmdline"] += TEXT(" ");
}
}
procstat_freeargv(pstat);
}
files = procstat_getfiles(pstat, proc, 0);
if (files != nullptr) {
STAILQ_FOREACH(file, files, next) {
if (file->fs_uflags & PS_FST_UFLAG_CDIR) {
r["cwd"] = TEXT(file->fs_path);
}
else if (file->fs_uflags & PS_FST_UFLAG_RDIR) {
r["root"] = TEXT(file->fs_path);
}
}
procstat_freefiles(pstat, files);
}
vmentry = procstat_getvmmap(pstat, proc, &cnt);
if (vmentry != nullptr) {
// Add up all the resident pages for each vmmap entry.
for (i = 0; i < cnt; i++) {
pages += vmentry[i].kve_resident;
}
// The column is in bytes.
r["resident_size"] += INTEGER(pages * getpagesize());
procstat_freevmmap(pstat, vmentry);
}
// XXX: Not sure how to get these on FreeBSD yet.
r["wired_size"] = INTEGER("0");
r["phys_footprint"] = INTEGER("0");
r["system_time"] = INTEGER(proc->ki_rusage.ru_stime.tv_sec);
r["user_time"] = INTEGER(proc->ki_rusage.ru_utime.tv_sec);
r["start_time"] = INTEGER(proc->ki_start.tv_sec);
results.push_back(r);
}
QueryData genProcesses(QueryContext& context) {
QueryData results;
struct kinfo_proc* procs = nullptr;
struct procstat* pstat = nullptr;
throw std::domain_error("Table not implemented for FreeBSD");
auto cnt = getProcesses(context, &pstat, &procs);
for (size_t i = 0; i < cnt; i++) {
genProcess(pstat, &procs[i], results);
}
procstatCleanup(pstat, procs);
return results;
}
QueryData genProcessOpenFiles(QueryContext& context) {
QueryData genProcessEnvs(QueryContext& context) {
QueryData results;
struct kinfo_proc* procs = nullptr;
struct procstat* pstat = nullptr;
throw std::domain_error("Table not implemented for FreeBSD");
auto cnt = getProcesses(context, &pstat, &procs);
for (size_t i = 0; i < cnt; i++) {
genProcessEnvironment(pstat, &procs[i], results);
}
procstatCleanup(pstat, procs);
return results;
}
QueryData genProcessMemoryMap(QueryContext& context) {
QueryData results;
struct kinfo_proc* procs = nullptr;
struct procstat* pstat = nullptr;
auto cnt = getProcesses(context, &pstat, &procs);
for (size_t i = 0; i < cnt; i++) {
genProcessMap(pstat, &procs[i], results);
}
procstatCleanup(pstat, procs);
return results;
}
}

View File

@ -0,0 +1,92 @@
/*
* 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 <stdlib.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <libprocstat.h>
#include <osquery/tables.h>
#include <osquery/logger.h>
namespace osquery {
namespace tables {
/**
* A helper function to retrieve processes using libprocstat(3). It is the
* responsibility of the caller to call procstat_freeprocs() and
* procstat_close() when done.
*
* Returns the number of processes in the "procs" list. On failure, returns 0
* and sets pstat and procs to NULL.
*
* Errors are not well exposed in some of the calls in libprocstat(3). This is
* a bit of a bummer as libkvm(3) is much better in that respect but I would
* rather use libprocstat(3) as it provides a nicer abstraction.
*/
unsigned int getProcesses(QueryContext& context,
struct procstat** pstat,
struct kinfo_proc** procs) {
std::set<std::string> pids;
unsigned int cnt = 0;
*pstat = procstat_open_sysctl();
if (*pstat == nullptr) {
TLOG << "Problem in procstat_open_sysctl()";
return 0;
}
if (context.constraints["pid"].exists()) {
pids = context.constraints["pid"].getAll(EQUALS);
// Generate data for all pids in the vector.
// If there are comparison constraints this could apply the operator
// before generating the process structure.
for (const auto& pid : pids) {
*procs = procstat_getprocs(*pstat, KERN_PROC_PID, std::stoi(pid), &cnt);
if (*procs == nullptr) {
TLOG << "Problem retrieving processes.";
procstat_close(*pstat);
*pstat = nullptr;
return 0;
}
}
} else {
// Get all PIDS.
*procs = procstat_getprocs(*pstat, KERN_PROC_PROC, 0, &cnt);
if (*procs == nullptr) {
TLOG << "Problem retrieving processes.";
procstat_close(*pstat);
*pstat = nullptr;
return 0;
}
}
return cnt;
}
/**
* Helper function to cleanup the libprocstat(3) pointers used.
*/
void procstatCleanup(struct procstat* pstat, struct kinfo_proc* procs) {
if (procs != nullptr) {
procstat_freeprocs(pstat, procs);
}
if (pstat != nullptr) {
procstat_close(pstat);
}
}
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.
*
*/
#pragma once
#include <sys/queue.h>
#include <sys/user.h>
#include <libprocstat.h>
#include <osquery/tables.h>
namespace osquery {
namespace tables {
unsigned int getProcesses(QueryContext& context,
struct procstat** pstat,
struct kinfo_proc** procs);
void procstatCleanup(struct procstat* pstat, struct kinfo_proc* procs);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Wesley Shields
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Wesley Shields
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Wesley Shields
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the