diff --git a/include/osquery/filesystem.h b/include/osquery/filesystem.h index 03113f00..d6fb1f95 100644 --- a/include/osquery/filesystem.h +++ b/include/osquery/filesystem.h @@ -149,4 +149,39 @@ Status parsePlist(const boost::filesystem::path& path, Status parsePlistContent(const std::string& fileContent, boost::property_tree::ptree& tree); #endif + +#ifdef __linux__ +/** + * @brief Iterate over proc process, returns a list of pids. + * + * @param processes output list of process pids as strings (int paths in proc). + * + * @return status of iteration. + */ +Status procProcesses(std::vector& processes); + +/** + * @brief Iterate over a proc process's descriptors, return a list of fds. + * + * @param process a string pid from proc. + * @param descriptors output list of descriptor numbers as strings. + * + * @return status of iteration, failure if the process path did not exist. + */ +Status procDescriptors(const std::string& process, + std::vector& descriptors); + +/** + * @brief Read a descriptor's virtual path. + * + * @param process a string pid from proc. + * @param descriptor a string descriptor number for a proc. + * @param result output variable with value of link. + * + * @return status of read, failure on permission error or filesystem error. + */ +Status procReadDescriptor(const std::string& process, + const std::string& descriptor, + std::string& result); +#endif } diff --git a/osquery/filesystem/CMakeLists.txt b/osquery/filesystem/CMakeLists.txt index 0ad39c01..929969d6 100644 --- a/osquery/filesystem/CMakeLists.txt +++ b/osquery/filesystem/CMakeLists.txt @@ -4,6 +4,10 @@ if(APPLE) ) ADD_OSQUERY_LINK("-framework Foundation") +elseif(UBUNTU OR CENTOS) + ADD_OSQUERY_LIBRARY(osquery_filesystem_linux + linux/proc.cpp + ) endif() ADD_OSQUERY_LIBRARY(osquery_filesystem diff --git a/osquery/filesystem/filesystem.cpp b/osquery/filesystem/filesystem.cpp index 759153e2..08f5271c 100644 --- a/osquery/filesystem/filesystem.cpp +++ b/osquery/filesystem/filesystem.cpp @@ -16,8 +16,6 @@ #include "osquery/filesystem.h" -using osquery::Status; - namespace pt = boost::property_tree; namespace fs = boost::filesystem; diff --git a/osquery/filesystem/linux/proc.cpp b/osquery/filesystem/linux/proc.cpp new file mode 100644 index 00000000..760bee00 --- /dev/null +++ b/osquery/filesystem/linux/proc.cpp @@ -0,0 +1,77 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +#include + +#include + +#include +#include + +#include + +#include "osquery/filesystem.h" + +namespace osquery { + +const std::string kLinuxProcPath = "/proc"; + +Status procProcesses(std::vector& processes) { + boost::regex process_filter("\\d+"); + + // Iterate over each process-like directory in proc. + boost::filesystem::directory_iterator it(kLinuxProcPath), end; + try { + for (; it != end; ++it) { + if (boost::filesystem::is_directory(it->status())) { + boost::smatch what; + if (boost::regex_match( + it->path().leaf().string(), what, process_filter)) { + processes.push_back(it->path().leaf().string()); + } + } + } + } catch (boost::filesystem::filesystem_error& e) { + VLOG(1) << "Exception iterating Linux processes " << e.what(); + return Status(1, e.what()); + } + + return Status(0, "OK"); +} + +Status procDescriptors(const std::string& process, + std::vector& descriptors) { + auto descriptors_path = kLinuxProcPath + "/" + process + "/fd"; + try { + // Access to the process' /fd may be restricted. + boost::filesystem::directory_iterator it(descriptors_path), end; + for (; it != end; ++it) { + descriptors.push_back(it->path().leaf().string()); + } + } catch (boost::filesystem::filesystem_error& e) { + return Status(1, "Cannot access descriptors for " + process); + } + + return Status(0, "OK"); +} + +Status procReadDescriptor(const std::string& process, + const std::string& descriptor, + std::string& result) { + auto link = kLinuxProcPath + "/" + process + "/fd/" + descriptor; + auto path_max = pathconf(link.c_str(), _PC_PATH_MAX); + auto result_path = (char*)malloc(path_max); + + memset(result_path, 0, path_max); + auto size = readlink(link.c_str(), result_path, path_max); + if (size >= 0) { + result = std::string(result_path); + } + + free(result_path); + if (size >= 0) { + return Status(0, "OK"); + } else { + return Status(1, "Could not read path"); + } +} +} diff --git a/osquery/tables/networking/linux/port_inode.cpp b/osquery/tables/networking/linux/port_inode.cpp index b56a0f1c..b21186a4 100644 --- a/osquery/tables/networking/linux/port_inode.cpp +++ b/osquery/tables/networking/linux/port_inode.cpp @@ -3,27 +3,18 @@ #include #include -#include #include #include #include #include -#include -#include -#include -#include -#include + #include #include -#include -#include - #include #include "osquery/core.h" #include "osquery/database.h" -#include "osquery/logger.h" // From uapi/linux/sock_diag.h // From linux/sock_diag.h (<= 3.6) @@ -69,12 +60,7 @@ int send_diag_msg(int sockfd, int family) { sa.nl_family = AF_NETLINK; - // IPv4 vs IPv6 - if (family == 1) { - conn_req.sdiag_family = AF_INET; - } else if (family == 2) { - conn_req.sdiag_family = AF_INET6; - } + conn_req.sdiag_family = family; conn_req.sdiag_protocol = IPPROTO_TCP; conn_req.idiag_states = TCPF_ALL & ~((1 << TCP_SYN_RECV) | @@ -101,7 +87,7 @@ int send_diag_msg(int sockfd, int family) { return retval; } -Row parse_diag_msg(struct inet_diag_msg *diag_msg, int rtalen) { +Row parse_diag_msg(struct inet_diag_msg *diag_msg, int rtalen, int family) { char local_addr_buf[INET6_ADDRSTRLEN]; char remote_addr_buf[INET6_ADDRSTRLEN]; @@ -131,17 +117,16 @@ Row parse_diag_msg(struct inet_diag_msg *diag_msg, int rtalen) { // populate the Row from diag_msg fields Row row; - row["inode"] = boost::lexical_cast(diag_msg->idiag_inode); - row["local_port"] = - boost::lexical_cast(ntohs(diag_msg->id.idiag_sport)); - row["remote_port"] = - boost::lexical_cast(ntohs(diag_msg->id.idiag_dport)); - row["local_ip"] = boost::lexical_cast(local_addr_buf); - row["remote_ip"] = boost::lexical_cast(remote_addr_buf); + row["inode"] = INTEGER(diag_msg->idiag_inode); + row["local_port"] = INTEGER(ntohs(diag_msg->id.idiag_sport)); + row["remote_port"] = INTEGER(ntohs(diag_msg->id.idiag_dport)); + row["local_ip"] = TEXT(local_addr_buf); + row["remote_ip"] = TEXT(remote_addr_buf); + row["family"] = INTEGER(family); return row; } -void getPortInode(QueryData &results, int type) { +void getPortInode(QueryData &results, int family) { int nl_sock = 0; int numbytes = 0; int rtalen = 0; @@ -156,7 +141,7 @@ void getPortInode(QueryData &results, int type) { } // send the inet_diag message - if (send_diag_msg(nl_sock, type) < 0) { + if (send_diag_msg(nl_sock, family) < 0) { close(nl_sock); return; } @@ -181,10 +166,10 @@ void getPortInode(QueryData &results, int type) { diag_msg = (struct inet_diag_msg *)NLMSG_DATA(nlh); rtalen = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*diag_msg)); try { - results.push_back(parse_diag_msg(diag_msg, rtalen)); + results.push_back(parse_diag_msg(diag_msg, rtalen, family)); } catch (std::exception &e) { - LOG(ERROR) << e.what(); + LOG(ERROR) << "Could not parse NL message " << e.what(); } nlh = NLMSG_NEXT(nlh, numbytes); @@ -195,10 +180,9 @@ void getPortInode(QueryData &results, int type) { } QueryData genPortInode() { - QueryData results; - getPortInode(results, 1); // IPv4 - getPortInode(results, 2); // IPv6 + getPortInode(results, AF_INET); + getPortInode(results, AF_INET6); return results; } } diff --git a/osquery/tables/networking/linux/socket_inode.cpp b/osquery/tables/networking/linux/socket_inode.cpp index 3c338abe..2e1c3ac1 100644 --- a/osquery/tables/networking/linux/socket_inode.cpp +++ b/osquery/tables/networking/linux/socket_inode.cpp @@ -1,66 +1,56 @@ -#include -#include +// Copyright 2004-present Facebook. All Rights Reserved. -#include -#include #include -#include -#include -#include #include -#include -#include - #include #include "osquery/core.h" #include "osquery/database.h" - +#include "osquery/filesystem.h" namespace osquery { namespace tables { void crawl_proc(QueryData &results) { - boost::filesystem::path dir_path = "/proc"; - for (boost::filesystem::directory_iterator itr(dir_path), end_itr; itr != end_itr; ++itr) { + std::vector processes; - if (boost::filesystem::is_directory(itr->status())) { - std::string d_path = itr->path().string(); + if (!osquery::procProcesses(processes).ok()) { + LOG(INFO) << "Cannot list Linux processes"; + return; + } - // make sure /proc/*/fd is there - d_path.append("/fd"); - struct stat s; - int err = stat(d_path.c_str(), &s); - if (err == -1) { + boost::regex socket_filter("[0-9]+"); + for (const auto& process : processes) { + std::vector descriptors; + if (!osquery::procDescriptors(process, descriptors).ok()) { + continue; + } + + for (const auto& descriptor : descriptors) { + std::string linkname; + if (!procReadDescriptor(process, descriptor, linkname).ok()) { + // This is an odd error case, but the symlink could not be read. continue; } - for (boost::filesystem::directory_iterator i(d_path), e_i; i != e_i; ++i) { - char* linkname = (char *)malloc(32); - std::string path = i->path().string(); - auto r = readlink(path.c_str(), linkname, 32); - std::string link_str(linkname, linkname + 32); - free(linkname); + if (linkname.find("socket") == std::string::npos) { + // This is not a socket descriptor. + continue; + } - // matches socket:[13415] - if (link_str.find("socket") != std::string::npos) { - boost::regex e("[0-9]+"); - boost::smatch inode; - boost::regex_search(link_str, inode, e); - if (inode[0].str().length() > 0) { - std::vector pid; - boost::split(pid, path, boost::is_any_of("/")); - Row r; - r["pid"] = boost::lexical_cast(pid[2].c_str()); - r["inode"] = boost::lexical_cast(inode[0].str()); - results.push_back(r); - continue; - } - } + // The linkname is in the form socket:[12345]. + boost::smatch inode; + boost::regex_search(linkname, inode, socket_filter); + if (inode[0].str().length() > 0) { + Row r; + r["pid"] = process; + r["inode"] = inode[0].str(); + results.push_back(r); } } } + return; } diff --git a/osquery/tables/specs/blacklist b/osquery/tables/specs/blacklist index c8fa3c85..1027de06 100644 --- a/osquery/tables/specs/blacklist +++ b/osquery/tables/specs/blacklist @@ -4,5 +4,3 @@ quarantine suid_bin -port_inode -socket_inode diff --git a/osquery/tables/specs/linux/port_inode.table b/osquery/tables/specs/linux/port_inode.table index 60bf1b84..b4deb35e 100644 --- a/osquery/tables/specs/linux/port_inode.table +++ b/osquery/tables/specs/linux/port_inode.table @@ -5,6 +5,7 @@ schema([ Column("local_ip", TEXT), Column("remote_ip", TEXT), Column("inode", TEXT), + Column("family", INTEGER), ]) implementation("osquery/tables/networking/linux/port_inode@genPortInode")